Skip to content

Commit c9f4d25

Browse files
committed
[COFF] Create range extension thunks for ARM64
On ARM64, this is normally necessary only after a module exceeds 128 MB in size (while the limit for thumb is 16 MB). For conditional branches, the range limit is only 1 MB though (the same as for thumb), and for the tbz instruction, the range is only 32 KB, which allows for a test much smaller than the full 128 MB. This fixes PR40467. Differential Revision: https://reviews.llvm.org/D57575 llvm-svn: 352929
1 parent b2b0cab commit c9f4d25

File tree

5 files changed

+97
-32
lines changed

5 files changed

+97
-32
lines changed

lld/COFF/Chunks.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,18 +670,38 @@ const uint8_t ArmThunk[] = {
670670
0xe7, 0x44, // L1: add pc, ip
671671
};
672672

673-
size_t RangeExtensionThunk::getSize() const {
673+
size_t RangeExtensionThunkARM::getSize() const {
674674
assert(Config->Machine == ARMNT);
675675
return sizeof(ArmThunk);
676676
}
677677

678-
void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
678+
void RangeExtensionThunkARM::writeTo(uint8_t *Buf) const {
679679
assert(Config->Machine == ARMNT);
680680
uint64_t Offset = Target->getRVA() - RVA - 12;
681681
memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
682682
applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
683683
}
684684

685+
// A position independent ARM64 adrp+add thunk, with a maximum range of
686+
// +/- 4 GB, which is enough for any PE-COFF.
687+
const uint8_t Arm64Thunk[] = {
688+
0x10, 0x00, 0x00, 0x90, // adrp x16, Dest
689+
0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest
690+
0x00, 0x02, 0x1f, 0xd6, // br x16
691+
};
692+
693+
size_t RangeExtensionThunkARM64::getSize() const {
694+
assert(Config->Machine == ARM64);
695+
return sizeof(Arm64Thunk);
696+
}
697+
698+
void RangeExtensionThunkARM64::writeTo(uint8_t *Buf) const {
699+
assert(Config->Machine == ARM64);
700+
memcpy(Buf + OutputSectionOff, Arm64Thunk, sizeof(Arm64Thunk));
701+
applyArm64Addr(Buf + OutputSectionOff + 0, Target->getRVA(), RVA, 12);
702+
applyArm64Imm(Buf + OutputSectionOff + 4, Target->getRVA() & 0xfff, 0);
703+
}
704+
685705
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
686706
Res->emplace_back(getRVA());
687707
}

lld/COFF/Chunks.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,18 @@ class ImportThunkChunkARM64 : public Chunk {
357357
Defined *ImpSymbol;
358358
};
359359

360-
class RangeExtensionThunk : public Chunk {
360+
class RangeExtensionThunkARM : public Chunk {
361361
public:
362-
explicit RangeExtensionThunk(Defined *T) : Target(T) {}
362+
explicit RangeExtensionThunkARM(Defined *T) : Target(T) {}
363+
size_t getSize() const override;
364+
void writeTo(uint8_t *Buf) const override;
365+
366+
Defined *Target;
367+
};
368+
369+
class RangeExtensionThunkARM64 : public Chunk {
370+
public:
371+
explicit RangeExtensionThunkARM64(Defined *T) : Target(T) {}
363372
size_t getSize() const override;
364373
void writeTo(uint8_t *Buf) const override;
365374

lld/COFF/Writer.cpp

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -335,16 +335,31 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
335335
// Check whether the target address S is in range from a relocation
336336
// of type RelType at address P.
337337
static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
338-
assert(Config->Machine == ARMNT);
339-
int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
340-
switch (RelType) {
341-
case IMAGE_REL_ARM_BRANCH20T:
342-
return isInt<21>(Diff);
343-
case IMAGE_REL_ARM_BRANCH24T:
344-
case IMAGE_REL_ARM_BLX23T:
345-
return isInt<25>(Diff);
346-
default:
347-
return true;
338+
if (Config->Machine == ARMNT) {
339+
int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
340+
switch (RelType) {
341+
case IMAGE_REL_ARM_BRANCH20T:
342+
return isInt<21>(Diff);
343+
case IMAGE_REL_ARM_BRANCH24T:
344+
case IMAGE_REL_ARM_BLX23T:
345+
return isInt<25>(Diff);
346+
default:
347+
return true;
348+
}
349+
} else if (Config->Machine == ARM64) {
350+
int64_t Diff = AbsoluteDifference(S, P) + Margin;
351+
switch (RelType) {
352+
case IMAGE_REL_ARM64_BRANCH26:
353+
return isInt<28>(Diff);
354+
case IMAGE_REL_ARM64_BRANCH19:
355+
return isInt<21>(Diff);
356+
case IMAGE_REL_ARM64_BRANCH14:
357+
return isInt<16>(Diff);
358+
default:
359+
return true;
360+
}
361+
} else {
362+
llvm_unreachable("Unexpected architecture");
348363
}
349364
}
350365

@@ -356,7 +371,17 @@ getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
356371
Defined *&LastThunk = LastThunks[Target->getRVA()];
357372
if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
358373
return {LastThunk, false};
359-
RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
374+
Chunk *C;
375+
switch (Config->Machine) {
376+
case ARMNT:
377+
C = make<RangeExtensionThunkARM>(Target);
378+
break;
379+
case ARM64:
380+
C = make<RangeExtensionThunkARM64>(Target);
381+
break;
382+
default:
383+
llvm_unreachable("Unexpected architecture");
384+
}
360385
Defined *D = make<DefinedSynthetic>("", C);
361386
LastThunk = D;
362387
return {D, true};
@@ -458,7 +483,7 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) {
458483
// Assign addresses and add thunks if necessary.
459484
void Writer::finalizeAddresses() {
460485
assignAddresses();
461-
if (Config->Machine != ARMNT)
486+
if (Config->Machine != ARMNT && Config->Machine != ARM64)
462487
return;
463488

464489
size_t OrigNumChunks = 0;

lld/test/COFF/arm64-branch-range.test

Lines changed: 0 additions & 16 deletions
This file was deleted.

lld/test/COFF/arm64-thunks.s

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// REQUIRES: aarch64
2+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %s -o %t.obj
3+
// RUN: lld-link -entry:main -subsystem:console %t.obj -out:%t.exe -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
4+
// RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DISASM %s
5+
6+
// VERBOSE: Added 1 thunks with margin {{.*}} in 1 passes
7+
8+
.globl main
9+
.globl func1
10+
.text
11+
main:
12+
tbz w0, #0, func1
13+
ret
14+
.section .text$a, "xr"
15+
.space 0x8000
16+
.section .text$b, "xr"
17+
func1:
18+
ret
19+
20+
// DISASM: 0000000140001000 .text:
21+
// DISASM: 140001000: 40 00 00 36 tbz w0, #0, #8 <.text+0x8>
22+
// DISASM: 140001004: c0 03 5f d6 ret
23+
// DISASM: 140001008: 50 00 00 90 adrp x16, #32768
24+
// DISASM: 14000100c: 10 52 00 91 add x16, x16, #20
25+
// DISASM: 140001010: 00 02 1f d6 br x16
26+
27+
// DISASM: 140009014: c0 03 5f d6 ret

0 commit comments

Comments
 (0)