mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-05 00:49:43 +00:00
[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<SegmentProtection> segmentProtections;
|
||||
|
||||
llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
|
||||
llvm::MapVector<std::pair<const InputSection *, const InputSection *>,
|
||||
uint64_t>
|
||||
callGraphProfile;
|
||||
bool callGraphProfileSort = false;
|
||||
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);
|
||||
if (!orderFile.empty())
|
||||
parseOrderFile(orderFile);
|
||||
priorityBuilder.parseOrderFile(orderFile);
|
||||
|
||||
referenceStubBinder();
|
||||
|
||||
@ -1510,7 +1510,7 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
|
||||
|
||||
gatherInputSections();
|
||||
if (config->callGraphProfileSort)
|
||||
extractCallGraphProfile();
|
||||
priorityBuilder.extractCallGraphProfile();
|
||||
|
||||
if (config->deadStrip)
|
||||
markLive();
|
||||
|
@ -34,9 +34,11 @@ using namespace llvm::sys;
|
||||
using namespace lld;
|
||||
using namespace lld::macho;
|
||||
|
||||
PriorityBuilder macho::priorityBuilder;
|
||||
|
||||
namespace {
|
||||
|
||||
size_t lowestPriority = std::numeric_limits<size_t>::max();
|
||||
size_t highestAvailablePriority = std::numeric_limits<size_t>::max();
|
||||
|
||||
struct Edge {
|
||||
int from;
|
||||
@ -62,7 +64,7 @@ struct Cluster {
|
||||
|
||||
class CallGraphSort {
|
||||
public:
|
||||
CallGraphSort();
|
||||
CallGraphSort(const MapVector<SectionPair, uint64_t> &profile);
|
||||
|
||||
DenseMap<const InputSection *, size_t> run();
|
||||
|
||||
@ -75,13 +77,9 @@ private:
|
||||
constexpr int MAX_DENSITY_DEGRADATION = 8;
|
||||
} // end anonymous namespace
|
||||
|
||||
using SectionPair = std::pair<const InputSection *, const InputSection *>;
|
||||
|
||||
// Take the edge list in config->callGraphProfile, resolve symbol names to
|
||||
// Symbols, and generate a graph between InputSections with the provided
|
||||
// weights.
|
||||
CallGraphSort::CallGraphSort() {
|
||||
MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
|
||||
// Take the edge list in callGraphProfile, resolve symbol names to Symbols, and
|
||||
// generate a graph between InputSections with the provided weights.
|
||||
CallGraphSort::CallGraphSort(const MapVector<SectionPair, uint64_t> &profile) {
|
||||
DenseMap<const InputSection *, int> secToCluster;
|
||||
|
||||
auto getOrCreateCluster = [&](const InputSection *isec) -> int {
|
||||
@ -94,7 +92,7 @@ CallGraphSort::CallGraphSort() {
|
||||
};
|
||||
|
||||
// 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 toSec = c.first.second->canonical();
|
||||
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.
|
||||
// NB: This is opposite from COFF/ELF to be compatible with the existing
|
||||
// order-file code.
|
||||
int curOrder = lowestPriority;
|
||||
int curOrder = highestAvailablePriority;
|
||||
for (int leader : sorted) {
|
||||
for (int i = leader;;) {
|
||||
orderMap[sections[i]] = curOrder--;
|
||||
@ -251,12 +249,12 @@ DenseMap<const InputSection *, size_t> CallGraphSort::run() {
|
||||
return orderMap;
|
||||
}
|
||||
|
||||
static Optional<size_t> getSymbolPriority(const Defined *sym) {
|
||||
Optional<size_t> macho::PriorityBuilder::getSymbolPriority(const Defined *sym) {
|
||||
if (sym->isAbsolute())
|
||||
return None;
|
||||
|
||||
auto it = config->priorities.find(sym->getName());
|
||||
if (it == config->priorities.end())
|
||||
auto it = priorities.find(sym->getName());
|
||||
if (it == priorities.end())
|
||||
return None;
|
||||
const SymbolPriorityEntry &entry = it->second;
|
||||
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);
|
||||
}
|
||||
|
||||
void macho::extractCallGraphProfile() {
|
||||
void macho::PriorityBuilder::extractCallGraphProfile() {
|
||||
TimeTraceScope timeScope("Extract call graph profile");
|
||||
bool hasOrderFile = !config->priorities.empty();
|
||||
bool hasOrderFile = !priorities.empty();
|
||||
for (const InputFile *file : inputFiles) {
|
||||
auto *obj = dyn_cast_or_null<ObjFile>(file);
|
||||
if (!obj)
|
||||
@ -289,13 +287,13 @@ void macho::extractCallGraphProfile() {
|
||||
(hasOrderFile &&
|
||||
(getSymbolPriority(fromSym) || getSymbolPriority(toSym))))
|
||||
continue;
|
||||
config->callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
|
||||
callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void macho::parseOrderFile(StringRef path) {
|
||||
assert(config->callGraphProfile.empty() &&
|
||||
void macho::PriorityBuilder::parseOrderFile(StringRef path) {
|
||||
assert(callGraphProfile.empty() &&
|
||||
"Order file must be parsed before call graph profile is processed");
|
||||
Optional<MemoryBufferRef> buffer = readFile(path);
|
||||
if (!buffer) {
|
||||
@ -304,7 +302,6 @@ void macho::parseOrderFile(StringRef path) {
|
||||
}
|
||||
|
||||
MemoryBufferRef mbref = *buffer;
|
||||
size_t priority = std::numeric_limits<size_t>::max();
|
||||
for (StringRef line : args::getLines(mbref)) {
|
||||
StringRef objectFile, symbol;
|
||||
line = line.take_until([](char c) { return c == '#'; }); // ignore comments
|
||||
@ -339,34 +336,34 @@ void macho::parseOrderFile(StringRef path) {
|
||||
symbol = line.trim();
|
||||
|
||||
if (!symbol.empty()) {
|
||||
SymbolPriorityEntry &entry = config->priorities[symbol];
|
||||
SymbolPriorityEntry &entry = priorities[symbol];
|
||||
if (!objectFile.empty())
|
||||
entry.objectFiles.insert(std::make_pair(objectFile, priority));
|
||||
entry.objectFiles.insert(
|
||||
std::make_pair(objectFile, highestAvailablePriority));
|
||||
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.
|
||||
//
|
||||
// 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>
|
||||
macho::PriorityBuilder::buildInputSectionPriorities() {
|
||||
DenseMap<const InputSection *, size_t> sectionPriorities;
|
||||
if (config->callGraphProfileSort)
|
||||
sectionPriorities = computeCallGraphProfileOrder();
|
||||
if (config->callGraphProfileSort) {
|
||||
// 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;
|
||||
|
||||
auto addSym = [&](const Defined *sym) {
|
||||
|
@ -15,41 +15,53 @@
|
||||
namespace lld {
|
||||
namespace macho {
|
||||
|
||||
// Reads every input section's call graph profile, and combines them into
|
||||
// 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();
|
||||
using SectionPair = std::pair<const InputSection *, const InputSection *>;
|
||||
|
||||
// Reads the order file at `path` into config->priorities.
|
||||
//
|
||||
// An order file has one entry per line, in the following format:
|
||||
//
|
||||
// <cpu>:<object file>:<symbol name>
|
||||
//
|
||||
// <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);
|
||||
class PriorityBuilder {
|
||||
public:
|
||||
// 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
|
||||
// of the vertices are specified in the order file are discarded.
|
||||
void extractCallGraphProfile();
|
||||
|
||||
// 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();
|
||||
// Reads the order file at `path` into config->priorities.
|
||||
//
|
||||
// An order file has one entry per line, in the following format:
|
||||
//
|
||||
// <cpu>:<object file>:<symbol name>
|
||||
//
|
||||
// <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
|
||||
// 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 lld
|
||||
|
||||
|
@ -860,7 +860,7 @@ static void sortSegmentsAndSections() {
|
||||
sortOutputSegments();
|
||||
|
||||
DenseMap<const InputSection *, size_t> isecPriorities =
|
||||
buildInputSectionPriorities();
|
||||
priorityBuilder.buildInputSectionPriorities();
|
||||
|
||||
uint32_t sectionIndex = 0;
|
||||
for (OutputSegment *seg : outputSegments) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user