[lld][Macho][NFC] Encapsulate priorities map in a priority class
`config->priorities` has been used to hold the intermediate state during the construction of the order in which sections should be laid out. This is not a good place to hold this state since the intermediate state is not a "configuration" for LLD. It should be encapsulated in a class for building a mapping from section to priority (which I created in this diff as the `PriorityBuilder` class). The same thing is being done for `config->callGraphProfile`. Reviewed By: #lld-macho, int3 Differential Revision: https://reviews.llvm.org/D122156
This commit is contained in:
parent
31dc248ffc
commit
f858fba631
|
@ -170,10 +170,6 @@ struct Configuration {
|
||||||
std::vector<SectionAlign> sectionAlignments;
|
std::vector<SectionAlign> sectionAlignments;
|
||||||
std::vector<SegmentProtection> segmentProtections;
|
std::vector<SegmentProtection> segmentProtections;
|
||||||
|
|
||||||
llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
|
|
||||||
llvm::MapVector<std::pair<const InputSection *, const InputSection *>,
|
|
||||||
uint64_t>
|
|
||||||
callGraphProfile;
|
|
||||||
bool callGraphProfileSort = false;
|
bool callGraphProfileSort = false;
|
||||||
llvm::StringRef printSymbolOrder;
|
llvm::StringRef printSymbolOrder;
|
||||||
|
|
||||||
|
|
|
@ -1453,7 +1453,7 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
|
||||||
|
|
||||||
StringRef orderFile = args.getLastArgValue(OPT_order_file);
|
StringRef orderFile = args.getLastArgValue(OPT_order_file);
|
||||||
if (!orderFile.empty())
|
if (!orderFile.empty())
|
||||||
parseOrderFile(orderFile);
|
priorityBuilder.parseOrderFile(orderFile);
|
||||||
|
|
||||||
referenceStubBinder();
|
referenceStubBinder();
|
||||||
|
|
||||||
|
@ -1510,7 +1510,7 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
|
||||||
|
|
||||||
gatherInputSections();
|
gatherInputSections();
|
||||||
if (config->callGraphProfileSort)
|
if (config->callGraphProfileSort)
|
||||||
extractCallGraphProfile();
|
priorityBuilder.extractCallGraphProfile();
|
||||||
|
|
||||||
if (config->deadStrip)
|
if (config->deadStrip)
|
||||||
markLive();
|
markLive();
|
||||||
|
|
|
@ -34,9 +34,11 @@ using namespace llvm::sys;
|
||||||
using namespace lld;
|
using namespace lld;
|
||||||
using namespace lld::macho;
|
using namespace lld::macho;
|
||||||
|
|
||||||
|
PriorityBuilder macho::priorityBuilder;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
size_t lowestPriority = std::numeric_limits<size_t>::max();
|
size_t highestAvailablePriority = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
struct Edge {
|
struct Edge {
|
||||||
int from;
|
int from;
|
||||||
|
@ -62,7 +64,7 @@ struct Cluster {
|
||||||
|
|
||||||
class CallGraphSort {
|
class CallGraphSort {
|
||||||
public:
|
public:
|
||||||
CallGraphSort();
|
CallGraphSort(const MapVector<SectionPair, uint64_t> &profile);
|
||||||
|
|
||||||
DenseMap<const InputSection *, size_t> run();
|
DenseMap<const InputSection *, size_t> run();
|
||||||
|
|
||||||
|
@ -75,13 +77,9 @@ private:
|
||||||
constexpr int MAX_DENSITY_DEGRADATION = 8;
|
constexpr int MAX_DENSITY_DEGRADATION = 8;
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
using SectionPair = std::pair<const InputSection *, const InputSection *>;
|
// Take the edge list in callGraphProfile, resolve symbol names to Symbols, and
|
||||||
|
// generate a graph between InputSections with the provided weights.
|
||||||
// Take the edge list in config->callGraphProfile, resolve symbol names to
|
CallGraphSort::CallGraphSort(const MapVector<SectionPair, uint64_t> &profile) {
|
||||||
// Symbols, and generate a graph between InputSections with the provided
|
|
||||||
// weights.
|
|
||||||
CallGraphSort::CallGraphSort() {
|
|
||||||
MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
|
|
||||||
DenseMap<const InputSection *, int> secToCluster;
|
DenseMap<const InputSection *, int> secToCluster;
|
||||||
|
|
||||||
auto getOrCreateCluster = [&](const InputSection *isec) -> int {
|
auto getOrCreateCluster = [&](const InputSection *isec) -> int {
|
||||||
|
@ -94,7 +92,7 @@ CallGraphSort::CallGraphSort() {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the graph
|
// Create the graph
|
||||||
for (std::pair<SectionPair, uint64_t> &c : profile) {
|
for (const std::pair<SectionPair, uint64_t> &c : profile) {
|
||||||
const auto fromSec = c.first.first->canonical();
|
const auto fromSec = c.first.first->canonical();
|
||||||
const auto toSec = c.first.second->canonical();
|
const auto toSec = c.first.second->canonical();
|
||||||
uint64_t weight = c.second;
|
uint64_t weight = c.second;
|
||||||
|
@ -212,7 +210,7 @@ DenseMap<const InputSection *, size_t> CallGraphSort::run() {
|
||||||
// priority 0 and be placed at the end of sections.
|
// priority 0 and be placed at the end of sections.
|
||||||
// NB: This is opposite from COFF/ELF to be compatible with the existing
|
// NB: This is opposite from COFF/ELF to be compatible with the existing
|
||||||
// order-file code.
|
// order-file code.
|
||||||
int curOrder = lowestPriority;
|
int curOrder = highestAvailablePriority;
|
||||||
for (int leader : sorted) {
|
for (int leader : sorted) {
|
||||||
for (int i = leader;;) {
|
for (int i = leader;;) {
|
||||||
orderMap[sections[i]] = curOrder--;
|
orderMap[sections[i]] = curOrder--;
|
||||||
|
@ -251,12 +249,12 @@ DenseMap<const InputSection *, size_t> CallGraphSort::run() {
|
||||||
return orderMap;
|
return orderMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Optional<size_t> getSymbolPriority(const Defined *sym) {
|
Optional<size_t> macho::PriorityBuilder::getSymbolPriority(const Defined *sym) {
|
||||||
if (sym->isAbsolute())
|
if (sym->isAbsolute())
|
||||||
return None;
|
return None;
|
||||||
|
|
||||||
auto it = config->priorities.find(sym->getName());
|
auto it = priorities.find(sym->getName());
|
||||||
if (it == config->priorities.end())
|
if (it == priorities.end())
|
||||||
return None;
|
return None;
|
||||||
const SymbolPriorityEntry &entry = it->second;
|
const SymbolPriorityEntry &entry = it->second;
|
||||||
const InputFile *f = sym->isec->getFile();
|
const InputFile *f = sym->isec->getFile();
|
||||||
|
@ -273,9 +271,9 @@ static Optional<size_t> getSymbolPriority(const Defined *sym) {
|
||||||
return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile);
|
return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void macho::extractCallGraphProfile() {
|
void macho::PriorityBuilder::extractCallGraphProfile() {
|
||||||
TimeTraceScope timeScope("Extract call graph profile");
|
TimeTraceScope timeScope("Extract call graph profile");
|
||||||
bool hasOrderFile = !config->priorities.empty();
|
bool hasOrderFile = !priorities.empty();
|
||||||
for (const InputFile *file : inputFiles) {
|
for (const InputFile *file : inputFiles) {
|
||||||
auto *obj = dyn_cast_or_null<ObjFile>(file);
|
auto *obj = dyn_cast_or_null<ObjFile>(file);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
|
@ -289,13 +287,13 @@ void macho::extractCallGraphProfile() {
|
||||||
(hasOrderFile &&
|
(hasOrderFile &&
|
||||||
(getSymbolPriority(fromSym) || getSymbolPriority(toSym))))
|
(getSymbolPriority(fromSym) || getSymbolPriority(toSym))))
|
||||||
continue;
|
continue;
|
||||||
config->callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
|
callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void macho::parseOrderFile(StringRef path) {
|
void macho::PriorityBuilder::parseOrderFile(StringRef path) {
|
||||||
assert(config->callGraphProfile.empty() &&
|
assert(callGraphProfile.empty() &&
|
||||||
"Order file must be parsed before call graph profile is processed");
|
"Order file must be parsed before call graph profile is processed");
|
||||||
Optional<MemoryBufferRef> buffer = readFile(path);
|
Optional<MemoryBufferRef> buffer = readFile(path);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
|
@ -304,7 +302,6 @@ void macho::parseOrderFile(StringRef path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryBufferRef mbref = *buffer;
|
MemoryBufferRef mbref = *buffer;
|
||||||
size_t priority = std::numeric_limits<size_t>::max();
|
|
||||||
for (StringRef line : args::getLines(mbref)) {
|
for (StringRef line : args::getLines(mbref)) {
|
||||||
StringRef objectFile, symbol;
|
StringRef objectFile, symbol;
|
||||||
line = line.take_until([](char c) { return c == '#'; }); // ignore comments
|
line = line.take_until([](char c) { return c == '#'; }); // ignore comments
|
||||||
|
@ -339,34 +336,34 @@ void macho::parseOrderFile(StringRef path) {
|
||||||
symbol = line.trim();
|
symbol = line.trim();
|
||||||
|
|
||||||
if (!symbol.empty()) {
|
if (!symbol.empty()) {
|
||||||
SymbolPriorityEntry &entry = config->priorities[symbol];
|
SymbolPriorityEntry &entry = priorities[symbol];
|
||||||
if (!objectFile.empty())
|
if (!objectFile.empty())
|
||||||
entry.objectFiles.insert(std::make_pair(objectFile, priority));
|
entry.objectFiles.insert(
|
||||||
|
std::make_pair(objectFile, highestAvailablePriority));
|
||||||
else
|
else
|
||||||
entry.anyObjectFile = std::max(entry.anyObjectFile, priority);
|
entry.anyObjectFile =
|
||||||
|
std::max(entry.anyObjectFile, highestAvailablePriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
--priority;
|
--highestAvailablePriority;
|
||||||
}
|
}
|
||||||
lowestPriority = priority;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sections by the profile data provided by __LLVM,__cg_profile sections.
|
DenseMap<const InputSection *, size_t>
|
||||||
//
|
macho::PriorityBuilder::buildInputSectionPriorities() {
|
||||||
// This first builds a call graph based on the profile data then merges sections
|
|
||||||
// according to the C³ heuristic. All clusters are then sorted by a density
|
|
||||||
// metric to further improve locality.
|
|
||||||
static DenseMap<const InputSection *, size_t> computeCallGraphProfileOrder() {
|
|
||||||
TimeTraceScope timeScope("Call graph profile sort");
|
|
||||||
return CallGraphSort().run();
|
|
||||||
}
|
|
||||||
|
|
||||||
DenseMap<const InputSection *, size_t> macho::buildInputSectionPriorities() {
|
|
||||||
DenseMap<const InputSection *, size_t> sectionPriorities;
|
DenseMap<const InputSection *, size_t> sectionPriorities;
|
||||||
if (config->callGraphProfileSort)
|
if (config->callGraphProfileSort) {
|
||||||
sectionPriorities = computeCallGraphProfileOrder();
|
// Sort sections by the profile data provided by __LLVM,__cg_profile
|
||||||
|
// sections.
|
||||||
|
//
|
||||||
|
// This first builds a call graph based on the profile data then merges
|
||||||
|
// sections according to the C³ heuristic. All clusters are then sorted by a
|
||||||
|
// density metric to further improve locality.
|
||||||
|
TimeTraceScope timeScope("Call graph profile sort");
|
||||||
|
sectionPriorities = CallGraphSort(callGraphProfile).run();
|
||||||
|
}
|
||||||
|
|
||||||
if (config->priorities.empty())
|
if (priorities.empty())
|
||||||
return sectionPriorities;
|
return sectionPriorities;
|
||||||
|
|
||||||
auto addSym = [&](const Defined *sym) {
|
auto addSym = [&](const Defined *sym) {
|
||||||
|
|
|
@ -15,41 +15,53 @@
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace macho {
|
namespace macho {
|
||||||
|
|
||||||
// Reads every input section's call graph profile, and combines them into
|
using SectionPair = std::pair<const InputSection *, const InputSection *>;
|
||||||
// config->callGraphProfile. If an order file is present, any edges where one
|
|
||||||
// or both of the vertices are specified in the order file are discarded.
|
|
||||||
void extractCallGraphProfile();
|
|
||||||
|
|
||||||
// Reads the order file at `path` into config->priorities.
|
class PriorityBuilder {
|
||||||
//
|
public:
|
||||||
// An order file has one entry per line, in the following format:
|
// Reads every input section's call graph profile, and combines them into
|
||||||
//
|
// callGraphProfile. If an order file is present, any edges where one or both
|
||||||
// <cpu>:<object file>:<symbol name>
|
// of the vertices are specified in the order file are discarded.
|
||||||
//
|
void extractCallGraphProfile();
|
||||||
// <cpu> and <object file> are optional. If not specified, then that entry
|
|
||||||
// matches any symbol of that name. Parsing this format is not quite
|
|
||||||
// straightforward because the symbol name itself can contain colons, so when
|
|
||||||
// encountering a colon, we consider the preceding characters to decide if it
|
|
||||||
// can be a valid CPU type or file path.
|
|
||||||
//
|
|
||||||
// If a symbol is matched by multiple entries, then it takes the lowest-ordered
|
|
||||||
// entry (the one nearest to the front of the list.)
|
|
||||||
//
|
|
||||||
// The file can also have line comments that start with '#'.
|
|
||||||
void parseOrderFile(StringRef path);
|
|
||||||
|
|
||||||
// Returns layout priorities for some or all input sections. Sections are laid
|
// Reads the order file at `path` into config->priorities.
|
||||||
// out in decreasing order; that is, a higher priority section will be closer
|
//
|
||||||
// to the beginning of its output section.
|
// An order file has one entry per line, in the following format:
|
||||||
//
|
//
|
||||||
// If either an order file or a call graph profile are present, this is used
|
// <cpu>:<object file>:<symbol name>
|
||||||
// as the source of priorities. If both are present, the order file takes
|
//
|
||||||
// precedence, but the call graph profile is still used for symbols that don't
|
// <cpu> and <object file> are optional. If not specified, then that entry
|
||||||
// appear in the order file. If neither is present, an empty map is returned.
|
// matches any symbol of that name. Parsing this format is not quite
|
||||||
//
|
// straightforward because the symbol name itself can contain colons, so when
|
||||||
// Each section gets assigned the priority of the highest-priority symbol it
|
// encountering a colon, we consider the preceding characters to decide if it
|
||||||
// contains.
|
// can be a valid CPU type or file path.
|
||||||
llvm::DenseMap<const InputSection *, size_t> buildInputSectionPriorities();
|
//
|
||||||
|
// If a symbol is matched by multiple entries, then it takes the
|
||||||
|
// lowest-ordered entry (the one nearest to the front of the list.)
|
||||||
|
//
|
||||||
|
// The file can also have line comments that start with '#'.
|
||||||
|
void parseOrderFile(StringRef path);
|
||||||
|
|
||||||
|
// Returns layout priorities for some or all input sections. Sections are laid
|
||||||
|
// out in decreasing order; that is, a higher priority section will be closer
|
||||||
|
// to the beginning of its output section.
|
||||||
|
//
|
||||||
|
// If either an order file or a call graph profile are present, this is used
|
||||||
|
// as the source of priorities. If both are present, the order file takes
|
||||||
|
// precedence, but the call graph profile is still used for symbols that don't
|
||||||
|
// appear in the order file. If neither is present, an empty map is returned.
|
||||||
|
//
|
||||||
|
// Each section gets assigned the priority of the highest-priority symbol it
|
||||||
|
// contains.
|
||||||
|
llvm::DenseMap<const InputSection *, size_t> buildInputSectionPriorities();
|
||||||
|
|
||||||
|
private:
|
||||||
|
llvm::Optional<size_t> getSymbolPriority(const Defined *sym);
|
||||||
|
llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
|
||||||
|
llvm::MapVector<SectionPair, uint64_t> callGraphProfile;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern PriorityBuilder priorityBuilder;
|
||||||
} // namespace macho
|
} // namespace macho
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
||||||
|
|
|
@ -860,7 +860,7 @@ static void sortSegmentsAndSections() {
|
||||||
sortOutputSegments();
|
sortOutputSegments();
|
||||||
|
|
||||||
DenseMap<const InputSection *, size_t> isecPriorities =
|
DenseMap<const InputSection *, size_t> isecPriorities =
|
||||||
buildInputSectionPriorities();
|
priorityBuilder.buildInputSectionPriorities();
|
||||||
|
|
||||||
uint32_t sectionIndex = 0;
|
uint32_t sectionIndex = 0;
|
||||||
for (OutputSegment *seg : outputSegments) {
|
for (OutputSegment *seg : outputSegments) {
|
||||||
|
|
Loading…
Reference in a new issue