Skip to content

Commit a1565fc

Browse files
committed
draft: [lld] Add thunks for hexagon
1 parent fe8af49 commit a1565fc

File tree

4 files changed

+303
-1
lines changed

4 files changed

+303
-1
lines changed

lld/ELF/Arch/Hexagon.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Symbols.h"
1111
#include "SyntheticSections.h"
1212
#include "Target.h"
13+
#include "Thunks.h"
1314
#include "lld/Common/ErrorHandler.h"
1415
#include "llvm/BinaryFormat/ELF.h"
1516
#include "llvm/Support/Endian.h"
@@ -30,6 +31,10 @@ class Hexagon final : public TargetInfo {
3031
const uint8_t *loc) const override;
3132
RelType getDynRel(RelType type) const override;
3233
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
34+
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
35+
uint64_t branchAddr, const Symbol &s,
36+
int64_t a) const override;
37+
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
3338
void relocate(uint8_t *loc, const Relocation &rel,
3439
uint64_t val) const override;
3540
void writePltHeader(uint8_t *buf) const override;
@@ -57,6 +62,8 @@ Hexagon::Hexagon(Ctx &ctx) : TargetInfo(ctx) {
5762
tlsGotRel = R_HEX_TPREL_32;
5863
tlsModuleIndexRel = R_HEX_DTPMOD_32;
5964
tlsOffsetRel = R_HEX_DTPREL_32;
65+
66+
needsThunks = true;
6067
}
6168

6269
uint32_t Hexagon::calcEFlags() const {
@@ -253,6 +260,37 @@ static uint32_t findMaskR16(uint32_t insn) {
253260

254261
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
255262

263+
bool Hexagon::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
264+
int64_t offset = dst - src;
265+
switch (type) {
266+
case llvm::ELF::R_HEX_B22_PCREL:
267+
case llvm::ELF::R_HEX_PLT_B22_PCREL:
268+
case llvm::ELF::R_HEX_GD_PLT_B22_PCREL:
269+
case llvm::ELF::R_HEX_LD_PLT_B22_PCREL:
270+
return llvm::isInt<22>(offset >> 2);
271+
case llvm::ELF::R_HEX_B15_PCREL:
272+
return llvm::isInt<15>(offset >> 2);
273+
break;
274+
case llvm::ELF::R_HEX_B13_PCREL:
275+
return llvm::isInt<13>(offset >> 2);
276+
break;
277+
case llvm::ELF::R_HEX_B9_PCREL:
278+
return llvm::isInt<9>(offset >> 2);
279+
default:
280+
return true;
281+
}
282+
llvm::report_fatal_error(StringRef(
283+
"unsupported relocation type used in isInRange: " + toString(type)));
284+
}
285+
286+
bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
287+
uint64_t branchAddr, const Symbol &s,
288+
int64_t a) const {
289+
// FIXME: needsThunk() should return false for !inRange() &&
290+
// !enoughSpaceForThunk() ?
291+
return !ctx.target->inBranchRange(type, branchAddr, s.getVA(a));
292+
}
293+
256294
void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
257295
uint64_t val) const {
258296
switch (rel.type) {

lld/ELF/Thunks.cpp

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,47 @@ class AVRThunk : public Thunk {
351351
void addSymbols(ThunkSection &isec) override;
352352
};
353353

354+
static const uint32_t MASK_END_PACKET = 3 << 14;
355+
static const uint32_t END_OF_PACKET = 3 << 14;
356+
static const uint32_t END_OF_DUPLEX = 0 << 14;
357+
358+
static std::optional<int32_t> getRealAddend(const InputSection &isec,
359+
const Relocation &rel) {
360+
const ArrayRef<uint8_t> SectContents = isec.content();
361+
if (SectContents.size() < sizeof(int32_t))
362+
// FIXME: assert? emit a diagnostic?
363+
return std::nullopt;
364+
int32_t offset = rel.offset;
365+
366+
// Search as many as 4 instructions:
367+
for (int i = 0; i < 4; i++) {
368+
uint32_t instWord = 0;
369+
const ArrayRef<uint8_t> InstWordContents = SectContents.drop_front(offset);
370+
::memcpy(&instWord, InstWordContents.data(), sizeof(instWord));
371+
if (((instWord & MASK_END_PACKET) == END_OF_PACKET) ||
372+
((instWord & MASK_END_PACKET) == END_OF_DUPLEX))
373+
break;
374+
offset += sizeof(instWord);
375+
}
376+
return offset - rel.offset;
377+
}
378+
// Hexagon CPUs need thunks for <<FIXME TBD>>
379+
// when their destination is out of range [0, 0x_?].
380+
class HexagonThunk : public Thunk {
381+
public:
382+
HexagonThunk(Ctx &ctx, const InputSection &isec, Relocation &rel,
383+
Symbol &dest)
384+
: Thunk(ctx, dest, 0), RealAddend(getRealAddend(isec, rel)),
385+
RelOffset(rel.offset) {
386+
alignment = 4;
387+
}
388+
std::optional<int32_t> RealAddend;
389+
int32_t RelOffset;
390+
uint32_t size() override { return ctx.arg.isPic ? 12 : 8; }
391+
void writeTo(uint8_t *buf) override;
392+
void addSymbols(ThunkSection &isec) override;
393+
};
394+
354395
// MIPS LA25 thunk
355396
class MipsThunk final : public Thunk {
356397
public:
@@ -1365,6 +1406,39 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
13651406
return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
13661407
}
13671408

1409+
// Hexagon Target Thunks
1410+
static uint64_t getHexagonThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) {
1411+
uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(a);
1412+
return SignExtend64<32>(v); // FIXME: sign extend to 64-bit?
1413+
}
1414+
1415+
void HexagonThunk::writeTo(uint8_t *buf) {
1416+
uint64_t s = getHexagonThunkDestVA(ctx, destination, addend);
1417+
uint64_t p = getThunkTargetSym()->getVA();
1418+
1419+
if (ctx.arg.isPic) {
1420+
write32(ctx, buf + 0, 0x00004000); // { immext(#0)
1421+
ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
1422+
write32(ctx, buf + 4, 0x6a49c00e); // r14 = add(pc,##0) }
1423+
ctx.target->relocateNoSym(buf + 4, R_HEX_6_PCREL_X, s - p);
1424+
1425+
write32(ctx, buf + 8, 0x528ec000); // { jumpr r14 }
1426+
} else {
1427+
write32(ctx, buf + 0, 0x00004000); // { immext
1428+
ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
1429+
write32(ctx, buf + 4, 0x5800c000); // jump <> }
1430+
ctx.target->relocateNoSym(buf + 4, R_HEX_B22_PCREL_X, s - p);
1431+
}
1432+
}
1433+
void HexagonThunk::addSymbols(ThunkSection &isec) {
1434+
Symbol *enclosing = isec.getEnclosingSymbol(RelOffset);
1435+
StringRef src = enclosing ? enclosing->getName() : isec.name;
1436+
1437+
addSymbol(saver().save("__trampoline_for_" + destination.getName() +
1438+
"_from_" + src),
1439+
STT_FUNC, 0, isec);
1440+
}
1441+
13681442
Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a)
13691443
: ctx(ctx), destination(d), addend(a), offset(0) {
13701444
destination.thunkAccessed = true;
@@ -1526,6 +1600,27 @@ static Thunk *addThunkAVR(Ctx &ctx, RelType type, Symbol &s, int64_t a) {
15261600
}
15271601
}
15281602

1603+
static Thunk *addThunkHexagon(Ctx &ctx, const InputSection &isec,
1604+
Relocation &rel, Symbol &s) {
1605+
switch (rel.type) {
1606+
case R_HEX_B9_PCREL:
1607+
case R_HEX_B13_PCREL:
1608+
case R_HEX_B15_PCREL:
1609+
case R_HEX_B22_PCREL:
1610+
case R_HEX_PLT_B22_PCREL:
1611+
case R_HEX_GD_PLT_B22_PCREL:
1612+
#if 0
1613+
// FIXME: we don't need this for extended rels?
1614+
case R_HEX_B32_PCREL_X:
1615+
case R_HEX_6_PCREL_X:
1616+
case R_HEX_B22_PCREL_X:
1617+
#endif
1618+
return make<HexagonThunk>(ctx, isec, rel, s);
1619+
default:
1620+
fatal("unrecognized relocation type " + toString(rel.type));
1621+
}
1622+
}
1623+
15291624
static Thunk *addThunkMips(Ctx &ctx, RelType type, Symbol &s) {
15301625
if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6(ctx))
15311626
return make<MicroMipsR6Thunk>(ctx, s);
@@ -1591,8 +1686,11 @@ Thunk *elf::addThunk(Ctx &ctx, const InputSection &isec, Relocation &rel) {
15911686
return addThunkPPC32(ctx, isec, rel, s);
15921687
case EM_PPC64:
15931688
return addThunkPPC64(ctx, rel.type, s, a);
1689+
case EM_HEXAGON:
1690+
return addThunkHexagon(ctx, isec, rel, s);
15941691
default:
1595-
llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC");
1692+
llvm_unreachable(
1693+
"add Thunk only supported for ARM, AVR, Hexagon, Mips and PowerPC");
15961694
}
15971695
}
15981696

lld/test/ELF/hexagon-thunks-packets.s

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# REQUIRES: hexagon
2+
# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-linux-musl %s -o %t.o
3+
# RUN: ld.lld %t.o -o %t
4+
# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-NONPIC %s
5+
# RUN: llvm-mc -filetype=obj --position-independent \
6+
# RUN: -triple=hexagon-unknown-linux-musl %s -o %t.o
7+
# RUN: ld.lld --pie %t.o -o %t
8+
# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-PIC %s
9+
10+
# Packets with pc-relative relocations are more interesting because
11+
# the offset must be relative to the start of the source, destination
12+
# packets and not necessarily the instruction word containing the jump/call.
13+
14+
# CHECK: Disassembly of section .text:
15+
16+
# CHECK-NONPIC: 000200b4 <__trampoline_for_myfn_a_from_.text.thunk>:
17+
# CHECK-NONPIC: { immext(#0x800040)
18+
# CHECK-NONPIC: jump 0x820118 }
19+
# CHECK-NONPIC: 000200bc <__trampoline_for_myfn_a_from_.text.thunk>:
20+
# CHECK-NONPIC: { immext(#0x800040)
21+
# CHECK-NONPIC: jump 0x820118 }
22+
23+
# CHECK-PIC: 00010150 <__trampoline_for_myfn_a_from_.text.thunk>:
24+
# CHECK-PIC: { immext(#0x800040)
25+
# CHECK-PIC: r14 = add(pc,##0x80006c) }
26+
# CHECK-PIC: { jumpr r14 }
27+
28+
# CHECK-NONPIC: 000200c4 <myfn_b>:
29+
# CHECK-NONPIC: { jumpr r31 }
30+
# CHECK-PIC: 00010168 <myfn_b>:
31+
# CHECK-PIC: { jumpr r31 }
32+
.globl myfn_b
33+
.type myfn_b, @function
34+
myfn_b:
35+
jumpr r31
36+
.size myfn_b, .-myfn_b
37+
38+
# CHECK-PIC: 0001016c <main>:
39+
.globl main
40+
.type main, @function
41+
main:
42+
{ r0 = #0
43+
call myfn_a }
44+
# CHECK-PIC: { call 0x10150
45+
# CHECK-NONPIC: { call 0x200b4
46+
# CHECK: r0 = #0x0 }
47+
call myfn_a
48+
# CHECK-PIC: call 0x10150
49+
# CHECK-NONPIC: call 0x200b4
50+
call myfn_b
51+
# CHECK-PIC: call 0x10168
52+
# CHECK-NONPIC: call 0x200c4
53+
54+
{ r2 = add(r0, r1)
55+
if (p0) call #myfn_b
56+
if (!p0) call #myfn_a }
57+
# CHECK-PIC: { if (p0) call 0x10168
58+
# CHECK-PIC: if (!p0) call 0x10150
59+
# CHECK-NONPIC: { if (p0) call 0x200bc
60+
# CHECK-NONPIC: if (!p0) call 0x200b4
61+
# CHECK: r2 = add(r0,r1) }
62+
63+
{ r2 = add(r0, r1)
64+
if (p0) call #myfn_a
65+
if (!p0) call #myfn_a }
66+
# CHECK-PIC: { if (p0) call 0x10150
67+
# CHECK-PIC: if (!p0) call 0x10150
68+
# CHECK-NONPIC: { if (p0) call 0x200b4
69+
# CHECK-NONPIC: if (!p0) call 0x200b4
70+
# CHECK: r2 = add(r0,r1) }
71+
72+
{ r2 = add(r0, r1)
73+
r1 = r4
74+
r4 = r5
75+
if (r0 == #0) jump:t #myfn_a }
76+
# CHECK-PIC: { if (r0==#0) jump:t 0x10150
77+
# CHECK-NONPIC: { if (r0==#0) jump:t 0x200b4
78+
# CHECK: r2 = add(r0,r1)
79+
# CHECK: r1 = r4; r4 = r5 }
80+
81+
{ r2 = add(r0, r1)
82+
r4 = r5
83+
if (r0 <= #0) jump:t #myfn_a
84+
p1 = cmp.eq(r0, #0); if (p1.new) jump:nt #myfn_a }
85+
# CHECK-NONPIC: { if (r0==#0) jump:t 0x200b4
86+
# CHECK-NONPIC: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x200b4
87+
# CHECK-PIC: { if (r0<=#0) jump:t 0x10150
88+
# CHECK-PIC: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x10150
89+
# CHECK: r2 = add(r0,r1)
90+
# CHECK: r4 = r5 }
91+
92+
{r0 = #0; jump #myfn_a}
93+
# CHECK-PIC: { r0 = #0x0 ; jump 0x10150 }
94+
# CHECK-NONPIC: { r0 = #0x0 ; jump 0x200b4 }
95+
{r0 = #0; jump #myfn_b}
96+
# CHECK-PIC: { r0 = #0x0 ; jump 0x10168 }
97+
# CHECK-NONPIC: { r0 = #0x0 ; jump 0x200c4 }
98+
jumpr r31
99+
.size main, .-main
100+
101+
.section .text.foo
102+
.skip 0x800000
103+
104+
.globl myfn_a
105+
.type myfn_a, @function
106+
myfn_a:
107+
{r0 = #0; jump #myfn_b}
108+
jumpr r31
109+
.size myfn_a, .-myfn_a
110+
111+
# CHECK-NONPIC: 00820118 <myfn_a>:
112+
# CHECK-NONPIC: { r0 = #0x0 ; jump 0x820120 }
113+
# CHECK-NONPIC: { jumpr r31 }
114+
115+
# CHECK-NONPIC: 00820120 <__trampoline_for_myfn_b_from_.text.thunk>:
116+
# CHECK-NONPIC: { immext(#0xff7fff80)
117+
# CHECK-NONPIC: jump 0x200c4 }
118+
119+
# CHECK-PIC: 008101c4 <__trampoline_for_myfn_b_from_.text.thunk>:
120+
# CHECK-PIC: { immext(#0xff7fff80)
121+
# CHECK-PIC: r14 = add(pc,##0xff7fffa4) } // fixme??
122+
# CHECK-PIC: { jumpr r14 }

lld/test/ELF/hexagon-thunks.s

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# REQUIRES: hexagon
2+
# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t.o
3+
# RUN: ld.lld %t.o -o %t
4+
# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-NONPIC %s
5+
# RUN: llvm-mc -filetype=obj --position-independent \
6+
# RUN: -triple=hexagon-unknown-elf %s -o %t.o
7+
8+
# RUN: ld.lld --pie %t.o -o %t
9+
# RUN: llvm-objdump -d %t 2>&1 | FileCheck --check-prefix=CHECK-PIC %s
10+
11+
.globl main
12+
.type main, @function
13+
main:
14+
call myfn
15+
jumpr r31
16+
.size main, .-main
17+
18+
.org 0x800000
19+
20+
.globl myfn
21+
.type myfn, @function
22+
myfn:
23+
jumpr r31
24+
.size myfn, .-myfn
25+
26+
# CHECK: Disassembly of section .text:
27+
28+
# CHECK-NONPIC: 000200b4 <__trampoline_for_myfn_from_.text.thunk>:
29+
# CHECK-NONPIC: { immext(#0x800000)
30+
# CHECK-NONPIC: jump 0x8200bc }
31+
# CHECK-PIC: 00010150 <__trampoline_for_myfn_from_.text.thunk>:
32+
# CHECK-PIC: { immext(#0x800000)
33+
# CHECK-PIC: r14 = add(pc,##0x80000c) }
34+
# CHECK-PIC: { jumpr r14 }
35+
36+
# CHECK-NONPIC: 000200bc <main>:
37+
# CHECK-NONPIC: call 0x200b4
38+
# CHECK-PIC: 0001015c <main>:
39+
# CHECK-PIC: call 0x10150
40+
# CHECK: jumpr r31
41+
42+
# CHECK-NONPIC: 008200bc <myfn>:
43+
# CHECK-PIC: 0081015c <myfn>:
44+
# CHECK: jumpr r31

0 commit comments

Comments
 (0)