[lld] draft: [lld] Add thunks for hexagon (PR #111217)

Brian Cain via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 4 15:03:56 PDT 2024


https://github.com/androm3da created https://github.com/llvm/llvm-project/pull/111217

This draft is a starting point for adding thunks to lld for hexagon.

Unfortunately there's at least one known issue: mid-packet jumps are calculating the wrong offset to the thunk.  I think I've misunderstood how to correctly account for the original addend and the recalculated addend.

Other potential issues:

* the symbols generated for thunks don't match `qcld` aka `hexagon-link`.  Should that be a concern at all, or can the symbols for each linker be defined independently?
* the symbols generated for thunks aren't necessarily unique: should they be?
* some small-ish cleanup/TODO/FIXMEs

Happy to accept any feedback while I try to work through the remaining known issues.

>From 1e619a55bfe3cd7737047d0134849bf331596121 Mon Sep 17 00:00:00 2001
From: Brian Cain <bcain at quicinc.com>
Date: Fri, 20 Sep 2024 22:13:33 -0700
Subject: [PATCH] draft: [lld] Add thunks for hexagon

---
 lld/ELF/Arch/Hexagon.cpp              |  38 ++++++++
 lld/ELF/Thunks.cpp                    | 100 ++++++++++++++++++++-
 lld/test/ELF/hexagon-thunks-packets.s | 122 ++++++++++++++++++++++++++
 lld/test/ELF/hexagon-thunks.s         |  44 ++++++++++
 4 files changed, 303 insertions(+), 1 deletion(-)
 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 d689fc2a152101..6d6d49c3b539d9 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 {
@@ -239,6 +246,37 @@ static uint32_t findMaskR16(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::report_fatal_error(StringRef(
+      "unsupported relocation type used in isInRange: " + toString(type)));
+}
+
+bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+                         uint64_t branchAddr, const Symbol &s,
+                         int64_t a) const {
+  // FIXME: needsThunk() should return false for !inRange() &&
+  // !enoughSpaceForThunk() ?
+  return !ctx.target->inBranchRange(type, branchAddr, s.getVA(a));
+}
+
 void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
                        uint64_t val) const {
   switch (rel.type) {
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index ef97530679469d..c27422a5bc66b0 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -351,6 +351,47 @@ class AVRThunk : public Thunk {
   void addSymbols(ThunkSection &isec) override;
 };
 
+static const uint32_t MASK_END_PACKET = 3 << 14;
+static const uint32_t END_OF_PACKET = 3 << 14;
+static const uint32_t END_OF_DUPLEX = 0 << 14;
+
+static std::optional<int32_t> getRealAddend(const InputSection &isec,
+                                            const Relocation &rel) {
+  const ArrayRef<uint8_t> SectContents = isec.content();
+  if (SectContents.size() < sizeof(int32_t))
+    // FIXME: assert?  emit a diagnostic?
+    return std::nullopt;
+  int32_t offset = rel.offset;
+
+  // Search as many as 4 instructions:
+  for (int i = 0; i < 4; i++) {
+    uint32_t instWord = 0;
+    const ArrayRef<uint8_t> InstWordContents = SectContents.drop_front(offset);
+    ::memcpy(&instWord, InstWordContents.data(), sizeof(instWord));
+    if (((instWord & MASK_END_PACKET) == END_OF_PACKET) ||
+        ((instWord & MASK_END_PACKET) == END_OF_DUPLEX))
+      break;
+    offset += sizeof(instWord);
+  }
+  return offset - rel.offset;
+}
+// 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), RealAddend(getRealAddend(isec, rel)),
+        RelOffset(rel.offset) {
+    alignment = 4;
+  }
+  std::optional<int32_t> RealAddend;
+  int32_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:
@@ -1352,6 +1393,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(const Symbol &s, int64_t a) {
+  uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(a);
+  return SignExtend64<32>(v); // FIXME: sign extend to 64-bit?
+}
+
+void HexagonThunk::writeTo(uint8_t *buf) {
+  uint64_t s = getHexagonThunkDestVA(destination, addend);
+  uint64_t p = getThunkTargetSym()->getVA();
+
+  if (ctx.arg.isPic) {
+    write32(buf + 0, 0x00004000); // {  immext(#0)
+    ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+    write32(buf + 4, 0x6a49c00e); //    r14 = add(pc,##0) }
+    ctx.target->relocateNoSym(buf + 4, R_HEX_6_PCREL_X, s - p);
+
+    write32(buf + 8, 0x528ec000); // {  jumpr r14 }
+  } else {
+    write32(buf + 0, 0x00004000); //  { immext
+    ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+    write32(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;
@@ -1513,6 +1587,27 @@ static Thunk *addThunkAVR(Ctx &ctx, RelType type, Symbol &s, int64_t a) {
   }
 }
 
+static 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:
+#if 0
+    // FIXME: we don't need this for extended rels?
+  case R_HEX_B32_PCREL_X:
+  case R_HEX_6_PCREL_X:
+  case R_HEX_B22_PCREL_X:
+#endif
+    return make<HexagonThunk>(ctx, isec, rel, s);
+  default:
+    fatal("unrecognized relocation type " + toString(rel.type));
+  }
+}
+
 static Thunk *addThunkMips(Ctx &ctx, RelType type, Symbol &s) {
   if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6())
     return make<MicroMipsR6Thunk>(ctx, s);
@@ -1578,8 +1673,11 @@ Thunk *elf::addThunk(Ctx &ctx, const InputSection &isec, Relocation &rel) {
     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-thunks-packets.s b/lld/test/ELF/hexagon-thunks-packets.s
new file mode 100644
index 00000000000000..e50558763119ed
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunks-packets.s
@@ -0,0 +1,122 @@
+# 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(#0x800040)
+# CHECK-NONPIC:   jump 0x820118 }
+# CHECK-NONPIC: 000200bc <__trampoline_for_myfn_a_from_.text.thunk>:
+# CHECK-NONPIC: { immext(#0x800040)
+# CHECK-NONPIC:   jump 0x820118 }
+
+# CHECK-PIC:    00010150 <__trampoline_for_myfn_a_from_.text.thunk>:
+# CHECK-PIC:    { immext(#0x800040)
+# CHECK-PIC:      r14 = add(pc,##0x80006c) }
+# CHECK-PIC:    { jumpr r14 }
+
+# CHECK-NONPIC: 000200c4 <myfn_b>:
+# CHECK-NONPIC: { jumpr r31 }
+# CHECK-PIC:    00010168 <myfn_b>:
+# CHECK-PIC:    { jumpr r31 }
+    .globl myfn_b
+    .type  myfn_b, @function
+myfn_b:
+    jumpr r31
+    .size  myfn_b, .-myfn_b
+
+# CHECK-PIC:    0001016c <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 0x10168
+# CHECK-NONPIC: call 0x200c4
+
+    { r2 = add(r0, r1)
+      if (p0) call #myfn_b
+      if (!p0) call #myfn_a }
+# CHECK-PIC:     { if (p0) call 0x10168
+# 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 0x10168 }
+# CHECK-NONPIC: { r0 = #0x0 ; jump 0x200c4 }
+    jumpr r31
+    .size   main, .-main
+
+    .section .text.foo
+    .skip 0x800000
+
+    .globl myfn_a
+    .type  myfn_a, @function
+myfn_a:
+    {r0 = #0; jump #myfn_b}
+    jumpr r31
+    .size  myfn_a, .-myfn_a
+
+# CHECK-NONPIC: 00820118 <myfn_a>:
+# CHECK-NONPIC: { r0 = #0x0 ; jump 0x820120 }
+# CHECK-NONPIC: { jumpr r31 }
+
+# CHECK-NONPIC: 00820120 <__trampoline_for_myfn_b_from_.text.thunk>:
+# CHECK-NONPIC: { immext(#0xff7fff80)
+# CHECK-NONPIC:   jump 0x200c4 }
+
+# CHECK-PIC:    008101c4 <__trampoline_for_myfn_b_from_.text.thunk>:
+# CHECK-PIC:    { immext(#0xff7fff80)
+# CHECK-PIC:      r14 = add(pc,##0xff7fffa4) } // fixme??
+# 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..1c5ba86842d8c1
--- /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 --position-independent \
+# 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 0x800000
+
+    .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(#0x800000)
+# CHECK-NONPIC:    jump 0x8200bc }
+# CHECK-PIC:     00010150 <__trampoline_for_myfn_from_.text.thunk>:
+# CHECK-PIC:     { immext(#0x800000)
+# CHECK-PIC:       r14 = add(pc,##0x80000c) }
+# 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:  008200bc <myfn>:
+# CHECK-PIC:     0081015c <myfn>:
+# CHECK:           jumpr r31



More information about the llvm-commits mailing list