[lld] [lld] Add thunks for hexagon (PR #111217)
Brian Cain via llvm-commits
llvm-commits at lists.llvm.org
Sun Nov 24 10:49:14 PST 2024
https://github.com/androm3da updated https://github.com/llvm/llvm-project/pull/111217
>From 992ffbdf1184599f188bd90f09b3d25f778da7b8 Mon Sep 17 00:00:00 2001
From: Brian Cain <bcain at quicinc.com>
Date: Sun, 24 Nov 2024 10:48:58 -0800
Subject: [PATCH] [lld] Add thunks for hexagon
Co-authored-by: Alexey Karyakin <akaryaki at quicinc.com>
---
lld/ELF/Arch/Hexagon.cpp | 35 ++++++++
lld/ELF/Relocations.cpp | 53 +++++++++---
lld/ELF/Thunks.cpp | 71 ++++++++++++++-
lld/test/ELF/hexagon-jump-error.s | 32 -------
lld/test/ELF/hexagon-thunks-packets.s | 119 ++++++++++++++++++++++++++
lld/test/ELF/hexagon-thunks.s | 44 ++++++++++
6 files changed, 308 insertions(+), 46 deletions(-)
delete mode 100644 lld/test/ELF/hexagon-jump-error.s
create mode 100644 lld/test/ELF/hexagon-thunks-packets.s
create mode 100644 lld/test/ELF/hexagon-thunks.s
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 3c6fe0daee6bd5..9a04dc0cab1973 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -10,6 +10,7 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Endian.h"
@@ -30,6 +31,10 @@ class Hexagon final : public TargetInfo {
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) const override;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void writePltHeader(uint8_t *buf) const override;
@@ -57,6 +62,8 @@ Hexagon::Hexagon(Ctx &ctx) : TargetInfo(ctx) {
tlsGotRel = R_HEX_TPREL_32;
tlsModuleIndexRel = R_HEX_DTPMOD_32;
tlsOffsetRel = R_HEX_DTPREL_32;
+
+ needsThunks = true;
}
uint32_t Hexagon::calcEFlags() const {
@@ -252,6 +259,34 @@ static uint32_t findMaskR16(Ctx &ctx, uint32_t insn) {
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
+bool Hexagon::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ int64_t offset = dst - src;
+ switch (type) {
+ case llvm::ELF::R_HEX_B22_PCREL:
+ case llvm::ELF::R_HEX_PLT_B22_PCREL:
+ case llvm::ELF::R_HEX_GD_PLT_B22_PCREL:
+ case llvm::ELF::R_HEX_LD_PLT_B22_PCREL:
+ return llvm::isInt<22>(offset >> 2);
+ case llvm::ELF::R_HEX_B15_PCREL:
+ return llvm::isInt<15>(offset >> 2);
+ break;
+ case llvm::ELF::R_HEX_B13_PCREL:
+ return llvm::isInt<13>(offset >> 2);
+ break;
+ case llvm::ELF::R_HEX_B9_PCREL:
+ return llvm::isInt<9>(offset >> 2);
+ default:
+ return true;
+ }
+ llvm_unreachable("unsupported relocation");
+}
+
+bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) const {
+ return !ctx.target->inBranchRange(type, branchAddr, s.getVA(ctx, a));
+}
+
void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
switch (rel.type) {
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index cf554e2e3ce108..c71d8e24544029 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2031,17 +2031,44 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> outputSections) {
});
}
-static int64_t getPCBias(Ctx &ctx, RelType type) {
- if (ctx.arg.emachine != EM_ARM)
- return 0;
- switch (type) {
- case R_ARM_THM_JUMP19:
- case R_ARM_THM_JUMP24:
- case R_ARM_THM_CALL:
- return 4;
- default:
- return 8;
+static const uint32_t HEXAGON_MASK_END_PACKET = 3 << 14;
+static const uint32_t HEXAGON_END_OF_PACKET = 3 << 14;
+static const uint32_t HEXAGON_END_OF_DUPLEX = 0 << 14;
+
+// Return the distance between the packet start and the instruction in the
+// relocation.
+static int getHexagonPacketOffset(const InputSection &isec,
+ const Relocation &rel) {
+ const ArrayRef<uint8_t> SectContents = isec.content();
+
+ // Search back as many as 3 instructions.
+ for (unsigned i = 0;; i++) {
+ if (i == 3 || rel.offset < (i + 1) * 4)
+ return i * 4;
+ uint32_t instWord = 0;
+ const ArrayRef<uint8_t> InstWordContents =
+ SectContents.drop_front(rel.offset - (i + 1) * 4);
+ ::memcpy(&instWord, InstWordContents.data(), sizeof(instWord));
+ if (((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_PACKET) ||
+ ((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_DUPLEX))
+ return i * 4;
+ }
+}
+static int64_t getPCBias(Ctx &ctx, const InputSection &isec,
+ const Relocation &rel) {
+ if (ctx.arg.emachine == EM_ARM) {
+ switch (rel.type) {
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_CALL:
+ return 4;
+ default:
+ return 8;
+ }
}
+ if (ctx.arg.emachine == EM_HEXAGON)
+ return -getHexagonPacketOffset(isec, rel);
+ return 0;
}
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
@@ -2053,7 +2080,7 @@ ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os,
const Relocation &rel,
uint64_t src) {
// See the comment in getThunk for -pcBias below.
- const int64_t pcBias = getPCBias(ctx, rel.type);
+ const int64_t pcBias = getPCBias(ctx, *isec, rel);
for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
ThunkSection *ts = tp.first;
uint64_t tsBase = os->addr + ts->outSecOff - pcBias;
@@ -2214,7 +2241,7 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
// out in the relocation addend. We compensate for the PC bias so that
// an Arm and Thumb relocation to the same destination get the same keyAddend,
// which is usually 0.
- const int64_t pcBias = getPCBias(ctx, rel.type);
+ const int64_t pcBias = getPCBias(ctx, *isec, rel);
const int64_t keyAddend = rel.addend + pcBias;
// We use a ((section, offset), addend) pair to find the thunk position if
@@ -2373,7 +2400,7 @@ bool ThunkCreator::createThunks(uint32_t pass,
// STT_SECTION + non-zero addend, clear the addend after
// redirection.
if (ctx.arg.emachine != EM_MIPS)
- rel.addend = -getPCBias(ctx, rel.type);
+ rel.addend = -getPCBias(ctx, *isec, rel);
}
for (auto &p : isd->thunkSections)
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index 629ce356ce2e7d..12e0b351eafc0c 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -402,6 +402,21 @@ class AVRThunk : public Thunk {
void addSymbols(ThunkSection &isec) override;
};
+// Hexagon CPUs need thunks for <<FIXME TBD>>
+// when their destination is out of range [0, 0x_?].
+class HexagonThunk : public Thunk {
+public:
+ HexagonThunk(Ctx &ctx, const InputSection &isec, Relocation &rel,
+ Symbol &dest)
+ : Thunk(ctx, dest, 0), relOffset(rel.offset) {
+ alignment = 4;
+ }
+ uint32_t relOffset;
+ uint32_t size() override { return ctx.arg.isPic ? 12 : 8; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+};
+
// MIPS LA25 thunk
class MipsThunk final : public Thunk {
public:
@@ -1468,6 +1483,39 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
}
+// Hexagon Target Thunks
+static uint64_t getHexagonThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) {
+ uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx, a);
+ return SignExtend64<32>(v); // FIXME: sign extend to 64-bit?
+}
+
+void HexagonThunk::writeTo(uint8_t *buf) {
+ uint64_t s = getHexagonThunkDestVA(ctx, destination, addend);
+ uint64_t p = getThunkTargetSym()->getVA(ctx);
+
+ if (ctx.arg.isPic) {
+ write32(ctx, buf + 0, 0x00004000); // { immext(#0)
+ ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+ write32(ctx, buf + 4, 0x6a49c00e); // r14 = add(pc,##0) }
+ ctx.target->relocateNoSym(buf + 4, R_HEX_6_PCREL_X, s - p);
+
+ write32(ctx, buf + 8, 0x528ec000); // { jumpr r14 }
+ } else {
+ write32(ctx, buf + 0, 0x00004000); // { immext
+ ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+ write32(ctx, buf + 4, 0x5800c000); // jump <> }
+ ctx.target->relocateNoSym(buf + 4, R_HEX_B22_PCREL_X, s - p);
+ }
+}
+void HexagonThunk::addSymbols(ThunkSection &isec) {
+ Symbol *enclosing = isec.getEnclosingSymbol(relOffset);
+ StringRef src = enclosing ? enclosing->getName() : isec.name;
+
+ addSymbol(saver().save("__trampoline_for_" + destination.getName() +
+ "_from_" + src),
+ STT_FUNC, 0, isec);
+}
+
Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a)
: ctx(ctx), destination(d), addend(a), offset(0) {
destination.thunkAccessed = true;
@@ -1637,6 +1685,24 @@ static std::unique_ptr<Thunk> addThunkAVR(Ctx &ctx, RelType type, Symbol &s,
}
}
+static std::unique_ptr<Thunk> addThunkHexagon(Ctx &ctx,
+ const InputSection &isec,
+ Relocation &rel, Symbol &s) {
+ switch (rel.type) {
+ case R_HEX_B9_PCREL:
+ case R_HEX_B13_PCREL:
+ case R_HEX_B15_PCREL:
+ case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
+ return std::make_unique<HexagonThunk>(ctx, isec, rel, s);
+ default:
+ Fatal(ctx) << "unrecognized relocation " << rel.type << " to " << &s
+ << " for hexagon target";
+ llvm_unreachable("");
+ }
+}
+
static std::unique_ptr<Thunk> addThunkMips(Ctx &ctx, RelType type, Symbol &s) {
if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6(ctx))
return std::make_unique<MicroMipsR6Thunk>(ctx, s);
@@ -1706,8 +1772,11 @@ std::unique_ptr<Thunk> elf::addThunk(Ctx &ctx, const InputSection &isec,
return addThunkPPC32(ctx, isec, rel, s);
case EM_PPC64:
return addThunkPPC64(ctx, rel.type, s, a);
+ case EM_HEXAGON:
+ return addThunkHexagon(ctx, isec, rel, s);
default:
- llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC");
+ llvm_unreachable(
+ "add Thunk only supported for ARM, AVR, Hexagon, Mips and PowerPC");
}
}
diff --git a/lld/test/ELF/hexagon-jump-error.s b/lld/test/ELF/hexagon-jump-error.s
deleted file mode 100644
index 53860b5daf2b16..00000000000000
--- a/lld/test/ELF/hexagon-jump-error.s
+++ /dev/null
@@ -1,32 +0,0 @@
-# REQUIRES: hexagon
-# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t.o
-## Use --threads=1 to keep emitted warnings across sections sequential.
-# RUN: not ld.lld %t.o -o /dev/null --threads=1 2>&1 | FileCheck --implicit-check-not "out of range" %s
-
- .globl _start
- .type _start, @function
-_start:
-
-# CHECK: relocation R_HEX_B9_PCREL out of range: 1028 is not in [-1024, 1023]
-{r0 = #0; jump #1f}
-.space (1<<10)
-.section b9, "ax"
-1:
-
-# CHECK: relocation R_HEX_B13_PCREL out of range: 16388 is not in [-16384, 16383]
-if (r0==#0) jump:t #1f
-.space (1<<14)
-.section b13, "ax"
-1:
-
-# CHECK: relocation R_HEX_B15_PCREL out of range: 65540 is not in [-65536, 65535]
-if (p0) jump #1f
-.space (1<<16)
-.section b15, "ax"
-1:
-
-# CHECK: relocation R_HEX_B22_PCREL out of range: 8388612 is not in [-8388608, 8388607]
-jump #1f
-.space (1<<23)
-.section b22, "ax"
-1:
diff --git a/lld/test/ELF/hexagon-thunks-packets.s b/lld/test/ELF/hexagon-thunks-packets.s
new file mode 100644
index 00000000000000..fb7e785dfc4893
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunks-packets.s
@@ -0,0 +1,119 @@
+# REQUIRES: hexagon
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-linux-musl %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-NONPIC %s
+# RUN: llvm-mc -filetype=obj --position-independent \
+# RUN: -triple=hexagon-unknown-linux-musl %s -o %t.o
+# RUN: ld.lld --pie %t.o -o %t
+# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-PIC %s
+
+# Packets with pc-relative relocations are more interesting because
+# the offset must be relative to the start of the source, destination
+# packets and not necessarily the instruction word containing the jump/call.
+
+# CHECK: Disassembly of section .text:
+
+# CHECK-NONPIC: 000200b4 <__trampoline_for_myfn_a_from_.text.thunk>:
+# CHECK-NONPIC: { immext(#0x1000040)
+# CHECK-NONPIC: jump 0x1020110 }
+
+# CHECK-PIC: 00010150 <__trampoline_for_myfn_a_from_.text.thunk>:
+# CHECK-PIC: { immext(#0x1000040)
+# CHECK-PIC: r14 = add(pc,##0x1000060) }
+# CHECK-PIC: { jumpr r14 }
+
+# CHECK-NONPIC: 000200bc <myfn_b>:
+# CHECK-NONPIC: { jumpr r31 }
+# CHECK-PIC: 0001015c <myfn_b>:
+# CHECK-PIC: { jumpr r31 }
+ .globl myfn_b
+ .type myfn_b, @function
+myfn_b:
+ jumpr r31
+ .size myfn_b, .-myfn_b
+
+# CHECK-PIC: 00010160 <main>:
+ .globl main
+ .type main, @function
+main:
+ { r0 = #0
+ call myfn_a }
+# CHECK-PIC: { call 0x10150
+# CHECK-NONPIC: { call 0x200b4
+# CHECK: r0 = #0x0 }
+ call myfn_a
+# CHECK-PIC: call 0x10150
+# CHECK-NONPIC: call 0x200b4
+ call myfn_b
+# CHECK-PIC: call 0x1015c
+# CHECK-NONPIC: call 0x200bc
+
+ { r2 = add(r0, r1)
+ if (p0) call #myfn_b
+ if (!p0) call #myfn_a }
+# CHECK-PIC: { if (p0) call 0x1015c
+# CHECK-PIC: if (!p0) call 0x10150
+# CHECK-NONPIC: { if (p0) call 0x200bc
+# CHECK-NONPIC: if (!p0) call 0x200b4
+# CHECK: r2 = add(r0,r1) }
+
+ { r2 = add(r0, r1)
+ if (p0) call #myfn_a
+ if (!p0) call #myfn_a }
+# CHECK-PIC: { if (p0) call 0x10150
+# CHECK-PIC: if (!p0) call 0x10150
+# CHECK-NONPIC: { if (p0) call 0x200b4
+# CHECK-NONPIC: if (!p0) call 0x200b4
+# CHECK: r2 = add(r0,r1) }
+
+ { r2 = add(r0, r1)
+ r1 = r4
+ r4 = r5
+ if (r0 == #0) jump:t #myfn_a }
+# CHECK-PIC: { if (r0==#0) jump:t 0x10150
+# CHECK-NONPIC: { if (r0==#0) jump:t 0x200b4
+# CHECK: r2 = add(r0,r1)
+# CHECK: r1 = r4; r4 = r5 }
+
+ { r2 = add(r0, r1)
+ r4 = r5
+ if (r0 <= #0) jump:t #myfn_a
+ p1 = cmp.eq(r0, #0); if (p1.new) jump:nt #myfn_a }
+# CHECK-NONPIC: { if (r0<=#0) jump:t 0x200b4
+# CHECK-NONPIC: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x200b4
+# CHECK-PIC: { if (r0<=#0) jump:t 0x10150
+# CHECK-PIC: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x10150
+# CHECK: r2 = add(r0,r1)
+# CHECK: r4 = r5 }
+
+ {r0 = #0; jump #myfn_a}
+# CHECK-PIC: { r0 = #0x0 ; jump 0x10150 }
+# CHECK-NONPIC: { r0 = #0x0 ; jump 0x200b4 }
+ {r0 = #0; jump #myfn_b}
+# CHECK-PIC: { r0 = #0x0 ; jump 0x1015c }
+# CHECK-NONPIC: { r0 = #0x0 ; jump 0x200bc }
+ jumpr r31
+ .size main, .-main
+
+ .section .text.foo
+ .skip 0x1000000
+
+ .globl myfn_a
+ .type myfn_a, @function
+myfn_a:
+ {r0 = #0; jump #myfn_b}
+ jumpr r31
+ .size myfn_a, .-myfn_a
+
+# CHECK-NONPIC: 01020110 <myfn_a>:
+# CHECK-NONPIC: { r0 = #0x0 ; jump 0x1020118 }
+# CHECK-NONPIC: { jumpr r31 }
+
+# CHECK-NONPIC: 01020118 <__trampoline_for_myfn_b_from_.text.thunk>:
+# CHECK-NONPIC: { immext(#0xfeffff80)
+# CHECK-NONPIC: jump 0x200bc }
+
+# CHECK-PIC: 010101b8 <__trampoline_for_myfn_b_from_.text.thunk>:
+# CHECK-PIC: { immext(#0xfeffff80)
+# CHECK-PIC: r14 = add(pc,##0xfeffffa4) }
+# CHECK-PIC: { jumpr r14 }
diff --git a/lld/test/ELF/hexagon-thunks.s b/lld/test/ELF/hexagon-thunks.s
new file mode 100644
index 00000000000000..1d15a0daa67841
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunks.s
@@ -0,0 +1,44 @@
+# REQUIRES: hexagon
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-NONPIC %s
+# RUN: llvm-mc -filetype=obj \
+# RUN: -triple=hexagon-unknown-elf %s -o %t.o
+
+# RUN: ld.lld --pie %t.o -o %t
+# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-PIC %s
+
+ .globl main
+ .type main, @function
+main:
+ call myfn
+ jumpr r31
+ .size main, .-main
+
+ .org 0x1000000
+
+ .globl myfn
+ .type myfn, @function
+myfn:
+ jumpr r31
+ .size myfn, .-myfn
+
+# CHECK: Disassembly of section .text:
+
+# CHECK-NONPIC: 000200b4 <__trampoline_for_myfn_from_.text.thunk>:
+# CHECK-NONPIC: { immext(#0x1000000)
+# CHECK-NONPIC: jump 0x10200bc }
+# CHECK-PIC: 00010150 <__trampoline_for_myfn_from_.text.thunk>:
+# CHECK-PIC: { immext(#0x1000000)
+# CHECK-PIC: r14 = add(pc,##0x100000c) }
+# CHECK-PIC: { jumpr r14 }
+
+# CHECK-NONPIC: 000200bc <main>:
+# CHECK-NONPIC: call 0x200b4
+# CHECK-PIC: 0001015c <main>:
+# CHECK-PIC: call 0x10150
+# CHECK: jumpr r31
+
+# CHECK-NONPIC: 010200bc <myfn>:
+# CHECK-PIC: 0101015c <myfn>:
+# CHECK: jumpr r31
More information about the llvm-commits
mailing list