[lld] [lld][ELF] Sort thunks by their destination to allow quicker convergence (PR #209962)
Pranav Kant via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 21:50:14 PDT 2026
https://github.com/pranavk created https://github.com/llvm/llvm-project/pull/209962
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.
>From ab55cc35a505182b9ba388b86bd65188efee59b8 Mon Sep 17 00:00:00 2001
From: Pranav Kant <prka at google.com>
Date: Wed, 15 Jul 2026 13:26:34 -0700
Subject: [PATCH] [lld][ELF] Sort thunks by their destination to allow quicker
convergence
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.
---
lld/ELF/Relocations.cpp | 13 +-
lld/ELF/SyntheticSections.cpp | 11 +-
lld/ELF/SyntheticSections.h | 2 +-
lld/ELF/Thunks.cpp | 4 +
lld/ELF/Thunks.h | 2 +
lld/test/ELF/aarch64-thunk-many-passes.s | 206 +++++++++++++++++++++++
6 files changed, 232 insertions(+), 6 deletions(-)
create mode 100644 lld/test/ELF/aarch64-thunk-many-passes.s
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 4702d941d28ca..99213fc63a135 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"
@@ -1940,12 +1941,12 @@ bool ThunkCreator::createThunks(uint32_t pass,
continue;
Thunk *t;
+ ThunkSection *ts;
bool isNew;
std::tie(t, isNew) = getThunk(isec, rel, src);
if (isNew) {
// Find or create a ThunkSection for the new Thunk
- ThunkSection *ts;
if (auto *tis = t->getTargetInputSection())
ts = getISThunkSec(tis);
else
@@ -1966,9 +1967,13 @@ 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)
addressesChanged |= p.second->assignOffsets();
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 06d5129844e5c..d7eadf9ce0150 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -4084,7 +4084,16 @@ InputSection *ThunkSection::getTargetInputSection() const {
return t->getTargetInputSection();
}
-bool ThunkSection::assignOffsets() {
+bool ThunkSection::assignOffsets(bool sort) {
+ if (sort) {
+ 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..5b1d01301f36b 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -629,6 +629,10 @@ 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
More information about the llvm-commits
mailing list