[ELF] Allow --symbol-ordering-file and call graph profile to be used together

Port https://reviews.llvm.org/D117354 from the MachO port.

If both --symbol-ordering-file and call graph profile are present, the
--symbol-ordering-file takes precedence, but the call graph profile is
still used for symbols that don't appear in the order file.

In addition, call graph profile described sections are now ordered
before other sections.
This commit is contained in:
Fangrui Song 2025-01-05 17:13:22 -08:00
parent 8d2b070f07
commit bcc1e58448
4 changed files with 53 additions and 14 deletions

View File

@ -235,7 +235,7 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
});
DenseMap<const InputSectionBase *, int> orderMap;
int curOrder = 1;
int curOrder = -clusters.size();
for (int leader : sorted) {
for (int i = leader;;) {
orderMap[sections[i]] = curOrder++;
@ -328,7 +328,7 @@ computeCacheDirectedSortOrder(Ctx &ctx) {
// Create the final order.
DenseMap<const InputSectionBase *, int> orderMap;
int curOrder = 1;
int curOrder = -sortedSections.size();
for (uint64_t secIdx : sortedSections)
orderMap[sections[secIdx]] = curOrder++;

View File

@ -1746,13 +1746,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
if (args.hasArg(OPT_call_graph_ordering_file))
ErrAlways(ctx) << "--symbol-ordering-file and --call-graph-order-file "
"may not be used together";
if (std::optional<MemoryBufferRef> buffer =
readFile(ctx, arg->getValue())) {
if (auto buffer = readFile(ctx, arg->getValue()))
ctx.arg.symbolOrderingFile = getSymbolOrderingFile(ctx, *buffer);
// Also need to disable CallGraphProfileSort to prevent
// LLD order symbols with CGProfile
ctx.arg.callGraphProfileSort = CGProfileSortKind::None;
}
}
assert(ctx.arg.versionDefinitions.empty());

View File

@ -1080,11 +1080,14 @@ static void maybeShuffle(Ctx &ctx,
}
}
// Builds section order for handling --symbol-ordering-file.
// Return section order within an InputSectionDescription.
// If both --symbol-ordering-file and call graph profile 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.
static DenseMap<const InputSectionBase *, int> buildSectionOrder(Ctx &ctx) {
DenseMap<const InputSectionBase *, int> sectionOrder;
if (!ctx.arg.callGraphProfile.empty())
return computeCallGraphProfileOrder(ctx);
sectionOrder = computeCallGraphProfileOrder(ctx);
if (ctx.arg.symbolOrderingFile.empty())
return sectionOrder;
@ -1098,7 +1101,7 @@ static DenseMap<const InputSectionBase *, int> buildSectionOrder(Ctx &ctx) {
// appear in the symbol ordering file have the lowest priority 0.
// All explicitly mentioned symbols have negative (higher) priorities.
DenseMap<CachedHashStringRef, SymbolOrderEntry> symbolOrder;
int priority = -ctx.arg.symbolOrderingFile.size();
int priority = -sectionOrder.size() - ctx.arg.symbolOrderingFile.size();
for (StringRef s : ctx.arg.symbolOrderingFile)
symbolOrder.insert({CachedHashStringRef(s), {priority++, false}});
@ -1254,11 +1257,11 @@ static void sortSection(Ctx &ctx, OutputSection &osec,
}
}
// If no layout was provided by linker script, we want to apply default
// sorting for special input sections. This also handles --symbol-ordering-file.
// Sort sections within each InputSectionDescription.
template <class ELFT> void Writer<ELFT>::sortInputSections() {
// Build the order once since it is expensive.
// Assign negative priorities.
DenseMap<const InputSectionBase *, int> order = buildSectionOrder(ctx);
// Assign non-negative priorities due to --shuffle-sections.
maybeShuffle(ctx, order);
for (SectionCommand *cmd : ctx.script->sectionCommands)
if (auto *osd = dyn_cast<OutputDesc>(cmd))

View File

@ -0,0 +1,41 @@
# REQUIRES: x86
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
# RUN: ld.lld -e A a.o --symbol-ordering-file=order --call-graph-profile-sort=hfsort -o out
# RUN: llvm-nm --numeric-sort out | FileCheck %s
# RUN: ld.lld -e A a.o --call-graph-profile-sort=hfsort -o out1
# RUN: llvm-nm --numeric-sort out1 | FileCheck %s --check-prefix=ONLY-CG
#--- order
B
A
#--- a.s
.section .text.D,"ax"; .globl D; D:
retq
.section .text.C,"ax"; .globl C; C:
call D
.section .text.B,"ax"; .globl B; B:
retq
.section .text.A,"ax"; .globl A; A:
call B
call C
.cg_profile A, B, 100
.cg_profile A, C, 40
.cg_profile C, D, 61
# CHECK: T B
# CHECK-NEXT: T A
# CHECK-NEXT: T C
# CHECK-NEXT: T D
# ONLY-CG: T A
# ONLY-CG-NEXT: T B
# ONLY-CG-NEXT: T C
# ONLY-CG-NEXT: T D