[lld] [lld][ELF] Sort thunks by their destination to allow quicker convergence (PR #209962)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 22:04:54 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld
Author: Pranav Kant (pranavk)
<details>
<summary>Changes</summary>
When thunks in a thunk section are not ordered, we end up upgrading short thunks to long thunks in separate passes. Depending on the number of thunks, address assignment may take too long to converge or not converge at all.
By sorting the thunks by their destination address, we make sure upgrading one thunk to long doesn't have ripple effect of following thunks going long in subsequent passes.
Fixes #<!-- -->61250
This also fixes multiple of our internal large binaries that were running into this.
---
Full diff: https://github.com/llvm/llvm-project/pull/209962.diff
6 Files Affected:
- (modified) lld/ELF/Relocations.cpp (+7-2)
- (modified) lld/ELF/SyntheticSections.cpp (+15-1)
- (modified) lld/ELF/SyntheticSections.h (+1-1)
- (modified) lld/ELF/Thunks.cpp (+5)
- (modified) lld/ELF/Thunks.h (+2)
- (added) lld/test/ELF/aarch64-thunk-many-passes.s (+206)
``````````diff
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 4702d941d28ca..e29e6ba0dbc03 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -38,6 +38,7 @@
#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Demangle/Demangle.h"
@@ -1966,8 +1967,12 @@ bool ThunkCreator::createThunks(uint32_t pass,
rel.addend = -getPCBias(ctx, *isec, rel);
}
- for (auto &p : isd->thunkSections)
- addressesChanged |= p.first->assignOffsets();
+ for (auto &p : isd->thunkSections) {
+ // Ordering thunks by their destination allows for quicker
+ // convergence. Bulk of thunks are created in pass 0. So sorting them
+ // then has most impact and should be sufficient in most cases.
+ addressesChanged |= p.first->assignOffsets(pass == 0);
+ }
});
for (auto &p : thunkedSections)
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 06d5129844e5c..e0becb988f885 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -4084,7 +4084,21 @@ InputSection *ThunkSection::getTargetInputSection() const {
return t->getTargetInputSection();
}
-bool ThunkSection::assignOffsets() {
+bool ThunkSection::assignOffsets(bool sort) {
+ if (sort) {
+ // We ignore branches that are in different directions.
+ // For branches in same direction, we want to order them by their
+ // destination VA such that upgrading a short thunk to long doesn't make
+ // following thunks out of reach in subsequent passes. This helps with
+ // quicker convergence.
+ uint64_t sectionVA = getVA();
+ llvm::stable_sort(thunks, [sectionVA](const Thunk *a, const Thunk *b) {
+ bool aFwd = a->getDestVA() > sectionVA;
+ bool bFwd = b->getDestVA() > sectionVA;
+ return aFwd != bFwd ? bFwd : a->getDestVA() > b->getDestVA();
+ });
+ }
+
uint64_t off = 0;
bool changed = false;
for (Thunk *t : thunks) {
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 523f6587899fe..b0ecc1c013afb 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -1225,7 +1225,7 @@ class ThunkSection final : public SyntheticSection {
size_t getSize() const override;
void writeTo(uint8_t *buf) override;
InputSection *getTargetInputSection() const;
- bool assignOffsets();
+ bool assignOffsets(bool sort = false);
// When true, round up reported size of section to 4 KiB. See comment
// in addThunkSection() for more details.
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index 1f161685f178d..c8f01b0e004d0 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -629,6 +629,11 @@ void Thunk::setOffset(uint64_t newOffset) {
offset = newOffset;
}
+uint64_t Thunk::getDestVA() const {
+ return destination.isInPlt(ctx) ? destination.getPltVA(ctx)
+ : destination.getVA(ctx, addend);
+}
+
// AArch64 Thunk base class.
static uint64_t getAArch64ThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) {
uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx, a);
diff --git a/lld/ELF/Thunks.h b/lld/ELF/Thunks.h
index 446345b8517f9..d9ac156e1c914 100644
--- a/lld/ELF/Thunks.h
+++ b/lld/ELF/Thunks.h
@@ -60,6 +60,8 @@ class Thunk {
// enabled.
virtual bool needsSyntheticLandingPad() { return false; }
+ virtual uint64_t getDestVA() const;
+
Defined *getThunkTargetSym() const { return syms[0]; }
Ctx &ctx;
diff --git a/lld/test/ELF/aarch64-thunk-many-passes.s b/lld/test/ELF/aarch64-thunk-many-passes.s
new file mode 100644
index 0000000000000..a17e2236150b2
--- /dev/null
+++ b/lld/test/ELF/aarch64-thunk-many-passes.s
@@ -0,0 +1,206 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t
+// RUN: ld.lld %t -o %t1 2>&1 | FileCheck %s
+
+// CHECK-NOT: error: address assignment did not converge
+
+.section .text.call1, "ax", %progbits
+.balign 8
+.global _start
+_start:
+ bl fn1
+ .space 16
+.section .text.call2, "ax", %progbits
+ bl fn2
+ .space 16
+.section .text.call3, "ax", %progbits
+ bl fn3
+ .space 16
+.section .text.call4, "ax", %progbits
+ bl fn4
+ .space 16
+.section .text.call5, "ax", %progbits
+ bl fn5
+ .space 16
+.section .text.call6, "ax", %progbits
+ bl fn6
+ .space 16
+.section .text.call7, "ax", %progbits
+ bl fn7
+ .space 16
+.section .text.call8, "ax", %progbits
+ bl fn8
+ .space 16
+.section .text.call9, "ax", %progbits
+ bl fn9
+ .space 16
+.section .text.call10, "ax", %progbits
+ bl fn10
+ .space 16
+.section .text.call11, "ax", %progbits
+ bl fn11
+ .space 16
+.section .text.call12, "ax", %progbits
+ bl fn12
+ .space 16
+.section .text.call13, "ax", %progbits
+ bl fn13
+ .space 16
+.section .text.call14, "ax", %progbits
+ bl fn14
+ .space 16
+.section .text.call15, "ax", %progbits
+ bl fn15
+ .space 16
+.section .text.call16, "ax", %progbits
+ bl fn16
+ .space 16
+.section .text.call17, "ax", %progbits
+ bl fn17
+ .space 16
+.section .text.call18, "ax", %progbits
+ bl fn18
+ .space 16
+.section .text.call19, "ax", %progbits
+ bl fn19
+ .space 16
+.section .text.call20, "ax", %progbits
+ bl fn20
+ .space 16
+.section .text.call21, "ax", %progbits
+ bl fn21
+ .space 16
+.section .text.call22, "ax", %progbits
+ bl fn22
+ .space 16
+.section .text.call23, "ax", %progbits
+ bl fn23
+ .space 16
+.section .text.call24, "ax", %progbits
+ bl fn24
+ .space 16
+.section .text.call25, "ax", %progbits
+ bl fn25
+ .space 16
+.section .text.call26, "ax", %progbits
+ bl fn26
+ .space 16
+.section .text.call27, "ax", %progbits
+ bl fn27
+ .space 16
+.section .text.call28, "ax", %progbits
+ bl fn28
+ .space 16
+.section .text.call29, "ax", %progbits
+ bl fn29
+ .space 16
+.section .text.call30, "ax", %progbits
+ bl fn30
+ .space 16
+.section .text.call31, "ax", %progbits
+ bl fn31
+ .space 16
+.section .text.call32, "ax", %progbits
+ bl fn32
+
+.section .text.space, "ax", %progbits
+.space 134217232
+
+.section .text.targets, "ax", %progbits
+.balign 4
+.global fn1, fn2, fn3, fn4, fn5, fn6, fn7, fn8, fn9, fn10, fn11, fn12, fn13, fn14, fn15, fn16, fn17, fn18, fn19, fn20, fn21, fn22, fn23, fn24, fn25, fn26, fn27, fn28, fn29, fn30, fn31, fn32
+fn1:
+ ret
+.space 12
+fn2:
+ ret
+.space 12
+fn3:
+ ret
+.space 12
+fn4:
+ ret
+.space 12
+fn5:
+ ret
+.space 12
+fn6:
+ ret
+.space 12
+fn7:
+ ret
+.space 12
+fn8:
+ ret
+.space 12
+fn9:
+ ret
+.space 12
+fn10:
+ ret
+.space 12
+fn11:
+ ret
+.space 12
+fn12:
+ ret
+.space 12
+fn13:
+ ret
+.space 12
+fn14:
+ ret
+.space 12
+fn15:
+ ret
+.space 12
+fn16:
+ ret
+.space 12
+fn17:
+ ret
+.space 12
+fn18:
+ ret
+.space 12
+fn19:
+ ret
+.space 12
+fn20:
+ ret
+.space 12
+fn21:
+ ret
+.space 12
+fn22:
+ ret
+.space 12
+fn23:
+ ret
+.space 12
+fn24:
+ ret
+.space 12
+fn25:
+ ret
+.space 12
+fn26:
+ ret
+.space 12
+fn27:
+ ret
+.space 12
+fn28:
+ ret
+.space 12
+fn29:
+ ret
+.space 12
+fn30:
+ ret
+.space 12
+fn31:
+ ret
+.space 12
+fn32:
+ ret
``````````
</details>
https://github.com/llvm/llvm-project/pull/209962
More information about the llvm-commits
mailing list