[lld] a5c9d71 - [lld-macho] Move order file and call graph sorting into SectionPriorities
Leonard Grey via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 25 09:22:29 PST 2022
Author: Leonard Grey
Date: 2022-01-25T12:18:15-05:00
New Revision: a5c9d717807f2801b35f5d0e9501d6398efff42d
URL: https://github.com/llvm/llvm-project/commit/a5c9d717807f2801b35f5d0e9501d6398efff42d
DIFF: https://github.com/llvm/llvm-project/commit/a5c9d717807f2801b35f5d0e9501d6398efff42d.diff
LOG: [lld-macho] Move order file and call graph sorting into SectionPriorities
See https://reviews.llvm.org/D117354 for context and discussion.
Added:
Modified:
lld/MachO/Driver.cpp
lld/MachO/SectionPriorities.cpp
lld/MachO/SectionPriorities.h
lld/MachO/Writer.cpp
Removed:
################################################################################
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 50f5c96c61f3..e4c9f4dd6024 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -15,6 +15,7 @@
#include "ObjC.h"
#include "OutputSection.h"
#include "OutputSegment.h"
+#include "SectionPriorities.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
@@ -460,60 +461,6 @@ static void addFileList(StringRef path, bool isLazy) {
// entry (the one nearest to the front of the list.)
//
// The file can also have line comments that start with '#'.
-static void parseOrderFile(StringRef path) {
- Optional<MemoryBufferRef> buffer = readFile(path);
- if (!buffer) {
- error("Could not read order file at " + path);
- return;
- }
-
- 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
- line = line.ltrim();
-
- CPUType cpuType = StringSwitch<CPUType>(line)
- .StartsWith("i386:", CPU_TYPE_I386)
- .StartsWith("x86_64:", CPU_TYPE_X86_64)
- .StartsWith("arm:", CPU_TYPE_ARM)
- .StartsWith("arm64:", CPU_TYPE_ARM64)
- .StartsWith("ppc:", CPU_TYPE_POWERPC)
- .StartsWith("ppc64:", CPU_TYPE_POWERPC64)
- .Default(CPU_TYPE_ANY);
-
- if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType)
- continue;
-
- // Drop the CPU type as well as the colon
- if (cpuType != CPU_TYPE_ANY)
- line = line.drop_until([](char c) { return c == ':'; }).drop_front();
-
- constexpr std::array<StringRef, 2> fileEnds = {".o:", ".o):"};
- for (StringRef fileEnd : fileEnds) {
- size_t pos = line.find(fileEnd);
- if (pos != StringRef::npos) {
- // Split the string around the colon
- objectFile = line.take_front(pos + fileEnd.size() - 1);
- line = line.drop_front(pos + fileEnd.size());
- break;
- }
- }
- symbol = line.trim();
-
- if (!symbol.empty()) {
- SymbolPriorityEntry &entry = config->priorities[symbol];
- if (!objectFile.empty())
- entry.objectFiles.insert(std::make_pair(objectFile, priority));
- else
- entry.anyObjectFile = std::max(entry.anyObjectFile, priority);
- }
-
- --priority;
- }
-}
-
// We expect sub-library names of the form "libfoo", which will match a dylib
// with a path of .*/libfoo.{dylib, tbd}.
// XXX ld64 seems to ignore the extension entirely when matching sub-libraries;
@@ -1081,25 +1028,6 @@ static void gatherInputSections() {
assert(inputOrder <= UnspecifiedInputOrder);
}
-static void extractCallGraphProfile() {
- TimeTraceScope timeScope("Extract call graph profile");
- for (const InputFile *file : inputFiles) {
- auto *obj = dyn_cast_or_null<ObjFile>(file);
- if (!obj)
- continue;
- for (const CallGraphEntry &entry : obj->callGraph) {
- assert(entry.fromIndex < obj->symbols.size() &&
- entry.toIndex < obj->symbols.size());
- auto *fromSym = dyn_cast_or_null<Defined>(obj->symbols[entry.fromIndex]);
- auto *toSym = dyn_cast_or_null<Defined>(obj->symbols[entry.toIndex]);
-
- if (!fromSym || !toSym)
- continue;
- config->callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
- }
- }
-}
-
static void foldIdenticalLiterals() {
// We always create a cStringSection, regardless of whether dedupLiterals is
// true. If it isn't, we simply create a non-deduplicating CStringSection.
diff --git a/lld/MachO/SectionPriorities.cpp b/lld/MachO/SectionPriorities.cpp
index 5e63ceb34d11..35510d7338e8 100644
--- a/lld/MachO/SectionPriorities.cpp
+++ b/lld/MachO/SectionPriorities.cpp
@@ -16,14 +16,20 @@
#include "InputFiles.h"
#include "Symbols.h"
#include "Target.h"
+#include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <numeric>
using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::sys;
using namespace lld;
using namespace lld::macho;
@@ -241,12 +247,133 @@ DenseMap<const InputSection *, size_t> CallGraphSort::run() {
return orderMap;
}
+static size_t getSymbolPriority(const SymbolPriorityEntry &entry,
+ const InputFile *f) {
+ // We don't use toString(InputFile *) here because it returns the full path
+ // for object files, and we only want the basename.
+ StringRef filename;
+ if (f->archiveName.empty())
+ filename = path::filename(f->getName());
+ else
+ filename = saver().save(path::filename(f->archiveName) + "(" +
+ path::filename(f->getName()) + ")");
+ return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile);
+}
+
+void macho::extractCallGraphProfile() {
+ TimeTraceScope timeScope("Extract call graph profile");
+ for (const InputFile *file : inputFiles) {
+ auto *obj = dyn_cast_or_null<ObjFile>(file);
+ if (!obj)
+ continue;
+ for (const CallGraphEntry &entry : obj->callGraph) {
+ assert(entry.fromIndex < obj->symbols.size() &&
+ entry.toIndex < obj->symbols.size());
+ auto *fromSym = dyn_cast_or_null<Defined>(obj->symbols[entry.fromIndex]);
+ auto *toSym = dyn_cast_or_null<Defined>(obj->symbols[entry.toIndex]);
+
+ if (!fromSym || !toSym)
+ continue;
+ config->callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
+ }
+ }
+}
+
+void macho::parseOrderFile(StringRef path) {
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer) {
+ error("Could not read order file at " + path);
+ return;
+ }
+
+ 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
+ line = line.ltrim();
+
+ CPUType cpuType = StringSwitch<CPUType>(line)
+ .StartsWith("i386:", CPU_TYPE_I386)
+ .StartsWith("x86_64:", CPU_TYPE_X86_64)
+ .StartsWith("arm:", CPU_TYPE_ARM)
+ .StartsWith("arm64:", CPU_TYPE_ARM64)
+ .StartsWith("ppc:", CPU_TYPE_POWERPC)
+ .StartsWith("ppc64:", CPU_TYPE_POWERPC64)
+ .Default(CPU_TYPE_ANY);
+
+ if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType)
+ continue;
+
+ // Drop the CPU type as well as the colon
+ if (cpuType != CPU_TYPE_ANY)
+ line = line.drop_until([](char c) { return c == ':'; }).drop_front();
+
+ constexpr std::array<StringRef, 2> fileEnds = {".o:", ".o):"};
+ for (StringRef fileEnd : fileEnds) {
+ size_t pos = line.find(fileEnd);
+ if (pos != StringRef::npos) {
+ // Split the string around the colon
+ objectFile = line.take_front(pos + fileEnd.size() - 1);
+ line = line.drop_front(pos + fileEnd.size());
+ break;
+ }
+ }
+ symbol = line.trim();
+
+ if (!symbol.empty()) {
+ SymbolPriorityEntry &entry = config->priorities[symbol];
+ if (!objectFile.empty())
+ entry.objectFiles.insert(std::make_pair(objectFile, priority));
+ else
+ entry.anyObjectFile = std::max(entry.anyObjectFile, priority);
+ }
+
+ --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.
-DenseMap<const InputSection *, size_t> macho::computeCallGraphProfileOrder() {
+static DenseMap<const InputSection *, size_t> computeCallGraphProfileOrder() {
TimeTraceScope timeScope("Call graph profile sort");
return CallGraphSort().run();
}
+
+// Each section gets assigned the priority of the highest-priority symbol it
+// contains.
+DenseMap<const InputSection *, size_t> macho::buildInputSectionPriorities() {
+ if (config->callGraphProfileSort)
+ return computeCallGraphProfileOrder();
+ DenseMap<const InputSection *, size_t> sectionPriorities;
+
+ if (config->priorities.empty())
+ return sectionPriorities;
+
+ auto addSym = [&](Defined &sym) {
+ if (sym.isAbsolute())
+ return;
+
+ auto it = config->priorities.find(sym.getName());
+ if (it == config->priorities.end())
+ return;
+
+ SymbolPriorityEntry &entry = it->second;
+ size_t &priority = sectionPriorities[sym.isec];
+ priority =
+ std::max(priority, getSymbolPriority(entry, sym.isec->getFile()));
+ };
+
+ // TODO: Make sure this handles weak symbols correctly.
+ for (const InputFile *file : inputFiles) {
+ if (isa<ObjFile>(file))
+ for (Symbol *sym : file->symbols)
+ if (auto *d = dyn_cast_or_null<Defined>(sym))
+ addSym(*d);
+ }
+
+ return sectionPriorities;
+}
diff --git a/lld/MachO/SectionPriorities.h b/lld/MachO/SectionPriorities.h
index f510d315e2c4..9cc4eff958cd 100644
--- a/lld/MachO/SectionPriorities.h
+++ b/lld/MachO/SectionPriorities.h
@@ -15,7 +15,40 @@
namespace lld {
namespace macho {
-llvm::DenseMap<const InputSection *, size_t> computeCallGraphProfileOrder();
+// 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();
+
+// 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. 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();
} // namespace macho
} // namespace lld
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 1fafda99e39d..2c0794e08ae3 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -849,54 +849,6 @@ template <class LP> void Writer::createLoadCommands() {
: 0));
}
-static size_t getSymbolPriority(const SymbolPriorityEntry &entry,
- const InputFile *f) {
- // We don't use toString(InputFile *) here because it returns the full path
- // for object files, and we only want the basename.
- StringRef filename;
- if (f->archiveName.empty())
- filename = path::filename(f->getName());
- else
- filename = saver().save(path::filename(f->archiveName) + "(" +
- path::filename(f->getName()) + ")");
- return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile);
-}
-
-// Each section gets assigned the priority of the highest-priority symbol it
-// contains.
-static DenseMap<const InputSection *, size_t> buildInputSectionPriorities() {
- if (config->callGraphProfileSort)
- return computeCallGraphProfileOrder();
- DenseMap<const InputSection *, size_t> sectionPriorities;
-
- if (config->priorities.empty())
- return sectionPriorities;
-
- auto addSym = [&](Defined &sym) {
- if (sym.isAbsolute())
- return;
-
- auto it = config->priorities.find(sym.getName());
- if (it == config->priorities.end())
- return;
-
- SymbolPriorityEntry &entry = it->second;
- size_t &priority = sectionPriorities[sym.isec];
- priority =
- std::max(priority, getSymbolPriority(entry, sym.isec->getFile()));
- };
-
- // TODO: Make sure this handles weak symbols correctly.
- for (const InputFile *file : inputFiles) {
- if (isa<ObjFile>(file))
- for (Symbol *sym : file->symbols)
- if (auto *d = dyn_cast_or_null<Defined>(sym))
- addSym(*d);
- }
-
- return sectionPriorities;
-}
-
// Sorting only can happen once all outputs have been collected. Here we sort
// segments, output sections within each segment, and input sections within each
// output segment.
More information about the llvm-commits
mailing list