[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 22:00:04 PDT 2026
https://github.com/pranavk updated https://github.com/llvm/llvm-project/pull/209962
>From b319f66c0e4c66d376c902b6d0807aabbf5c0c2a 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 | 11 +-
lld/ELF/SyntheticSections.cpp | 16 +-
lld/ELF/SyntheticSections.h | 2 +-
lld/ELF/Thunks.cpp | 5 +
lld/ELF/Thunks.h | 2 +
lld/test/ELF/aarch64-thunk-many-passes.s | 206 +++++++++++++++++++++++
6 files changed, 237 insertions(+), 5 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..1dc96a483cb94 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,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
More information about the llvm-commits
mailing list