-
Notifications
You must be signed in to change notification settings - Fork 14.5k
ELF: Add --preferred-function-alignment option. #149448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: users/pcc/spr/main.elf-add-preferred-function-alignment-flag
Are you sure you want to change the base?
ELF: Add --preferred-function-alignment option. #149448
Conversation
Created using spr 1.3.6-beta.1
@llvm/pr-subscribers-lld @llvm/pr-subscribers-lld-elf Author: Peter Collingbourne (pcc) Changesclang and gcc have -falign-functions which controls sh_addralign (modulo The effect of this flag in lld is to increase the alignment of all Full diff: https://github.com/llvm/llvm-project/pull/149448.diff 10 Files Affected:
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index dc3cb4ee9d6b7..551123fd832dc 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -354,6 +354,9 @@ void X86_64::relaxCFIJumpTables() const {
sec->file, sec->name, sec->type, sec->flags, sec->entsize,
sec->entsize,
sec->contentMaybeDecompress().slice(begin, end - begin));
+ // Ensure that --preferred-function-alignment does not mess with the
+ // placement of this section.
+ slice->retainAlignment = true;
for (const Relocation &r : ArrayRef<Relocation>(rbegin, rend)) {
slice->relocations.push_back(
Relocation{r.expr, r.type, r.offset - begin, r.addend, r.sym});
@@ -421,6 +424,9 @@ void X86_64::relaxCFIJumpTables() const {
// table. First add a slice for the unmodified jump table entries
// before this one.
addSectionSlice(begin, cur, rbegin, rcur);
+ // Ensure that --preferred-function-alignment does not mess with the
+ // placement of this section.
+ target->retainAlignment = true;
// Add the target to our replacement list, and set the target's
// replacement list to the empty list. This removes it from its
// original position and adds it here, as well as causing
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 4c771e47a0e72..2ccbadc4b2ca3 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -358,6 +358,7 @@ struct Config {
bool optRemarksWithHotness;
bool picThunk;
bool pie;
+ std::optional<uint64_t> preferredFunctionAlignment;
bool printGcSections;
bool printIcfSections;
bool printMemoryUsage;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index a194ae71d559a..e88e5bf9162df 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1508,6 +1508,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
if (auto *arg = args.getLastArg(OPT_package_metadata))
parsePackageMetadata(ctx, *arg);
ctx.arg.pie = args.hasFlag(OPT_pie, OPT_no_pie, false);
+ if (args.hasArg(OPT_preferred_function_alignment))
+ ctx.arg.preferredFunctionAlignment =
+ args::getInteger(args, OPT_preferred_function_alignment, 0);
ctx.arg.printIcfSections =
args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
ctx.arg.printGcSections =
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 68e3feb1bd048..299aba8cb7d2f 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -58,7 +58,7 @@ InputSectionBase::InputSectionBase(InputFile *file, StringRef name,
Kind sectionKind)
: SectionBase(sectionKind, file, name, type, flags, link, info, addralign,
entsize),
- bss(0), decodedCrel(0), keepUnique(0), nopFiller(0),
+ bss(0), decodedCrel(0), keepUnique(0), nopFiller(0), retainAlignment(0),
content_(data.data()), size(data.size()) {
// In order to reduce memory allocation, we assume that mergeable
// sections are smaller than 4 GiB, which is not an unreasonable
diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index 98e7d5d4ff0cd..0fb437d8e4d7b 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -183,6 +183,11 @@ class InputSectionBase : public SectionBase {
LLVM_PREFERRED_TYPE(bool)
uint8_t nopFiller : 1;
+ // If true, --preferred-function-alignment has no effect on this section.
+ // Set by the CFI jump table relaxation pass.
+ LLVM_PREFERRED_TYPE(bool)
+ uint8_t retainAlignment : 1;
+
mutable bool compressed = false;
// Input sections are part of an output section. Special sections
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index cc91680550b4b..510fd22b6be37 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -406,6 +406,9 @@ def pop_state: F<"pop-state">,
def push_state: F<"push-state">,
HelpText<"Save the current state of --as-needed, -static and --whole-archive">;
+def preferred_function_alignment: JJ<"preferred-function-alignment=">,
+ HelpText<"Align functions to the given alignment if possible">;
+
def print_map: F<"print-map">,
HelpText<"Print a link map to the standard output">;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index abc156cb93bdc..556dda54e0db7 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1499,6 +1499,20 @@ static void randomizeSectionPadding(Ctx &ctx) {
}
}
+static void applyPreferredFunctionAlignment(Ctx &ctx) {
+ if (!ctx.arg.preferredFunctionAlignment)
+ return;
+ SmallVector<InputSection *, 0> storage;
+ for (OutputSection *osec : ctx.outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
+ continue;
+ for (InputSection *sec : getInputSections(*osec, storage))
+ if (!isa<SyntheticSection>(sec) && !sec->retainAlignment)
+ sec->addralign = std::max<uint32_t>(
+ sec->addralign, *ctx.arg.preferredFunctionAlignment);
+ }
+}
+
// We need to generate and finalize the content that depends on the address of
// InputSections. As the generation of the content may also alter InputSection
// addresses we must converge to a fixed point. We do that here. See the comment
@@ -1530,6 +1544,9 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
ctx.target->relaxCFIJumpTables();
+ if (ctx.arg.preferredFunctionAlignment)
+ applyPreferredFunctionAlignment(ctx);
+
uint32_t pass = 0, assignPasses = 0;
for (;;) {
bool changed = ctx.target->needsThunks
diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 7edc522b4f6a4..3bedc16e19178 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -520,6 +520,13 @@ Currently the same as yes.
Don't use.
.El
+.It Fl -preferred-function-alignment Ns = Ns Ar align
+Specify the preferred function alignment. This flag increases the
+alignment of all user-provided executable (SHF_EXECINSTR) sections to the
+specified value unless retaining the original alignment is beneficial
+for performance reasons. For example, when using LLVM's control flow
+integrity feature, functions may retain their original alignment if
+necessary in order to move them into the jump table.
.It Fl -print-gc-sections
List removed unused sections.
.It Fl -print-icf-sections
diff --git a/lld/test/ELF/preferred-function-alignment.s b/lld/test/ELF/preferred-function-alignment.s
new file mode 100644
index 0000000000000..cb0d58f8ff186
--- /dev/null
+++ b/lld/test/ELF/preferred-function-alignment.s
@@ -0,0 +1,15 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+// RUN: ld.lld --preferred-function-alignment=32 -o %t %t.o
+// RUN: llvm-nm -n %t | FileCheck %s
+
+// CHECK: 0000000000201120 t f1
+.section .text.f1,"ax",@progbits
+f1:
+ret
+
+// CHECK: 0000000000201140 t f2
+.section .text.f2,"ax",@progbits
+f2:
+ret
+
diff --git a/lld/test/ELF/x86_64-relax-jump-tables.s b/lld/test/ELF/x86_64-relax-jump-tables.s
index 782d1be655a7d..8f4738888114b 100644
--- a/lld/test/ELF/x86_64-relax-jump-tables.s
+++ b/lld/test/ELF/x86_64-relax-jump-tables.s
@@ -2,6 +2,8 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
// RUN: ld.lld %t.o -shared -o %t
// RUN: llvm-objdump -d --show-all-symbols %t | FileCheck %s
+// RUN: ld.lld %t.o -shared -o %t32 --preferred-function-alignment=32
+// RUN: llvm-objdump -d --show-all-symbols %t32 | FileCheck %s
// Mostly positive cases, except for f2.
.section .text.jt1,"ax",@llvm_cfi_jump_table,8
|
I haven't had the chance to review this patch in detail, but I’m concerned about the number of linker options required for the CFI feature. While I recognize its benefits, the complexity seems excessive. |
That's a fair point, I was also concerned about the number of options required here. We do need some way to communicate the preferred alignment separately from the minimum alignment. Without this, almost no sections would be eligible for jump table relaxation the way things work now. An alternative approach would be to communicate both alignments in the object file. That way, the optimization would work by default with no flags passed to clang or lld but more work would be needed on the toolchain side. What I had in mind was that sh_addralign would contain the preferred alignment (which would also be controlled by -falign-functions), and we would introduce a new LLVM-specific ELF section type containing an array of (relocation pointing to section, minimum alignment) pairs. Another possibility is that we go back to the approach from the initial prototype of #147424 where the relocation from the jump table section gives the linker permission to reduce the alignment, but this is a bit of a hack and may cause problems e.g. if the code generator embeds constants in the code section that are read with movaps. Let me know what you think. |
clang and gcc have -falign-functions which controls sh_addralign (modulo
bugs, see #149444 #149445), i.e. the minimum function alignment. If we
wish to express the preferred function alignment, ELF provides us with no
means of doing so. Preferred function alignment is useful in combination
with CFI jump table relaxation (#147424) as it allows functions to
be well aligned in the usual case with the associated performance
benefits while giving the linker permission to move functions into a
lesser-aligned jump table entry when that is the right tradeoff for the
best performance. The intent is that a user wishing to take advantage
of both CFI jump table relaxation and preferred function alignment will
pass -falign-functions=M to clang and --preferred-function-alignment=N
to lld for a minimum alignment M and preferred alignment N.
The effect of this option in lld is to increase the alignment of all
user-provided sections, but only if they are not part of a CFI jump table.