[lld] [ELF] Add target-specific relocation scanning for Hexagon (PR #181596)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 16 14:01:58 PST 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/181596

>From c6c027e0cb34007f19135a56d0095fb8bde8f60b Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 15 Feb 2026 17:31:08 -0800
Subject: [PATCH 1/2] [ELF] Add target-specific relocation scanning for Hexagon

Implement Hexagon::scanSectionImpl, following the pattern established
for x86 and PPC64. This merges the getRelExpr and TLS handling for
SHF_ALLOC sections into the target-specific scanner, enabling
devirtualization and eliminating abstraction overhead.

- Inline relocation classification into scanSectionImpl with a switch
  on relocation type, replacing the generic rs.scan() path.
- Use processR_PC/processR_PLT_PC for common PC-relative and PLT
  relocations.
- Handle GD PLT relocations inline, always setting NEEDS_PLT. Remove
  the R_HEX_GD_PLT special case from process().
- Handle TLS IE, GD GOT, and TPREL directly, bypassing
  handleTlsRelocation. Remove EM_HEXAGON from the execOptimize check.
- Simplify getRelExpr to only handle relocations needed by
  relocateNonAlloc and scanEhSection.
---
 lld/ELF/Arch/Hexagon.cpp | 189 +++++++++++++++++++++++++++------------
 lld/ELF/RelocScan.h      |   3 +-
 lld/ELF/Relocations.cpp  |  12 +--
 3 files changed, 136 insertions(+), 68 deletions(-)

diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index d6495e12668d7..447e33dea86dc 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -8,6 +8,7 @@
 
 #include "InputFiles.h"
 #include "OutputSections.h"
+#include "RelocScan.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -37,6 +38,11 @@ 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;
+  template <class ELFT, class RelTy>
+  void scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels);
+  void scanSection(InputSectionBase &sec) override {
+    elf::scanSection1<Hexagon, ELF32LE>(*this, sec);
+  }
   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
                   uint64_t branchAddr, const Symbol &s,
                   int64_t a) const override;
@@ -99,72 +105,17 @@ static uint32_t applyMask(uint32_t mask, uint32_t data) {
   return result;
 }
 
+// Only needed to support relocations used by relocateNonAlloc and
+// preprocessRelocs.
 RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
                             const uint8_t *loc) const {
   switch (type) {
   case R_HEX_NONE:
     return R_NONE;
-  case R_HEX_6_X:
-  case R_HEX_8_X:
-  case R_HEX_9_X:
-  case R_HEX_10_X:
-  case R_HEX_11_X:
-  case R_HEX_12_X:
-  case R_HEX_16_X:
   case R_HEX_32:
-  case R_HEX_32_6_X:
-  case R_HEX_HI16:
-  case R_HEX_LO16:
-  case R_HEX_DTPREL_32:
     return R_ABS;
-  case R_HEX_B9_PCREL:
-  case R_HEX_B13_PCREL:
-  case R_HEX_B15_PCREL:
-  case R_HEX_6_PCREL_X:
   case R_HEX_32_PCREL:
     return R_PC;
-  case R_HEX_B9_PCREL_X:
-  case R_HEX_B15_PCREL_X:
-  case R_HEX_B22_PCREL:
-  case R_HEX_PLT_B22_PCREL:
-  case R_HEX_B22_PCREL_X:
-  case R_HEX_B32_PCREL_X:
-  case R_HEX_GD_PLT_B22_PCREL:
-  case R_HEX_GD_PLT_B22_PCREL_X:
-  case R_HEX_GD_PLT_B32_PCREL_X:
-    return R_PLT_PC;
-  case R_HEX_IE_32_6_X:
-  case R_HEX_IE_16_X:
-  case R_HEX_IE_HI16:
-  case R_HEX_IE_LO16:
-    return R_GOT;
-  case R_HEX_GD_GOT_11_X:
-  case R_HEX_GD_GOT_16_X:
-  case R_HEX_GD_GOT_32_6_X:
-    return R_TLSGD_GOTPLT;
-  case R_HEX_GOTREL_11_X:
-  case R_HEX_GOTREL_16_X:
-  case R_HEX_GOTREL_32_6_X:
-  case R_HEX_GOTREL_HI16:
-  case R_HEX_GOTREL_LO16:
-    return R_GOTPLTREL;
-  case R_HEX_GOT_11_X:
-  case R_HEX_GOT_16_X:
-  case R_HEX_GOT_32_6_X:
-    return R_GOTPLT;
-  case R_HEX_IE_GOT_11_X:
-  case R_HEX_IE_GOT_16_X:
-  case R_HEX_IE_GOT_32_6_X:
-  case R_HEX_IE_GOT_HI16:
-  case R_HEX_IE_GOT_LO16:
-    return R_GOTPLT;
-  case R_HEX_TPREL_11_X:
-  case R_HEX_TPREL_16:
-  case R_HEX_TPREL_16_X:
-  case R_HEX_TPREL_32_6_X:
-  case R_HEX_TPREL_HI16:
-  case R_HEX_TPREL_LO16:
-    return R_TPREL;
   default:
     Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
              << ") against symbol " << &s;
@@ -172,6 +123,130 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
   }
 }
 
+template <class ELFT, class RelTy>
+void Hexagon::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
+  RelocScan rs(ctx, &sec);
+  sec.relocations.reserve(rels.size());
+  for (auto it = rels.begin(); it != rels.end(); ++it) {
+    const RelTy &rel = *it;
+    uint32_t symIdx = rel.getSymbol(false);
+    Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
+    uint64_t offset = rel.r_offset;
+    RelType type = rel.getType(false);
+    if (sym.isUndefined() && symIdx != 0 &&
+        rs.maybeReportUndefined(cast<Undefined>(sym), offset))
+      continue;
+    int64_t addend = rs.getAddend<ELFT>(rel, type);
+    RelExpr expr;
+    // Relocation types that only need a RelExpr set `expr` and break out of
+    // the switch to reach rs.process(). Types that need special handling
+    // (fast-path helpers, TLS) call a handler and use `continue`.
+    switch (type) {
+    case R_HEX_NONE:
+      continue;
+
+    // Absolute relocations:
+    case R_HEX_6_X:
+    case R_HEX_8_X:
+    case R_HEX_9_X:
+    case R_HEX_10_X:
+    case R_HEX_11_X:
+    case R_HEX_12_X:
+    case R_HEX_16_X:
+    case R_HEX_32:
+    case R_HEX_32_6_X:
+    case R_HEX_HI16:
+    case R_HEX_LO16:
+    case R_HEX_DTPREL_32:
+      expr = R_ABS;
+      break;
+
+    // PC-relative relocations:
+    case R_HEX_B9_PCREL:
+    case R_HEX_B13_PCREL:
+    case R_HEX_B15_PCREL:
+    case R_HEX_6_PCREL_X:
+    case R_HEX_32_PCREL:
+      rs.processR_PC(type, offset, addend, sym);
+      continue;
+
+    // PLT-generating relocations:
+    case R_HEX_B9_PCREL_X:
+    case R_HEX_B15_PCREL_X:
+    case R_HEX_B22_PCREL:
+    case R_HEX_PLT_B22_PCREL:
+    case R_HEX_B22_PCREL_X:
+    case R_HEX_B32_PCREL_X:
+      rs.processR_PLT_PC(type, offset, addend, sym);
+      continue;
+    case R_HEX_GD_PLT_B22_PCREL:
+    case R_HEX_GD_PLT_B22_PCREL_X:
+    case R_HEX_GD_PLT_B32_PCREL_X:
+      sym.setFlags(NEEDS_PLT);
+      sec.addReloc({R_PLT_PC, type, offset, addend, &sym});
+      continue;
+
+    // GOT-generating relocations:
+    case R_HEX_GOT_11_X:
+    case R_HEX_GOT_16_X:
+    case R_HEX_GOT_32_6_X:
+      ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
+      expr = R_GOTPLT;
+      break;
+
+    // GOTREL relocations:
+    case R_HEX_GOTREL_11_X:
+    case R_HEX_GOTREL_16_X:
+    case R_HEX_GOTREL_32_6_X:
+    case R_HEX_GOTREL_HI16:
+    case R_HEX_GOTREL_LO16:
+      ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
+      expr = R_GOTPLTREL;
+      break;
+
+    // TLS relocations:
+    case R_HEX_TPREL_11_X:
+    case R_HEX_TPREL_16:
+    case R_HEX_TPREL_16_X:
+    case R_HEX_TPREL_32_6_X:
+    case R_HEX_TPREL_HI16:
+    case R_HEX_TPREL_LO16:
+      if (rs.checkTlsLe(offset, sym, type))
+        continue;
+      expr = R_TPREL;
+      break;
+    case R_HEX_IE_32_6_X:
+    case R_HEX_IE_16_X:
+    case R_HEX_IE_HI16:
+    case R_HEX_IE_LO16:
+      rs.handleTlsIe<false>(R_GOT, type, offset, addend, sym);
+      continue;
+    case R_HEX_IE_GOT_11_X:
+    case R_HEX_IE_GOT_16_X:
+    case R_HEX_IE_GOT_32_6_X:
+    case R_HEX_IE_GOT_HI16:
+    case R_HEX_IE_GOT_LO16:
+      ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
+      rs.handleTlsIe<false>(R_GOTPLT, type, offset, addend, sym);
+      continue;
+    case R_HEX_GD_GOT_11_X:
+    case R_HEX_GD_GOT_16_X:
+    case R_HEX_GD_GOT_32_6_X:
+      sym.setFlags(NEEDS_TLSGD);
+      ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
+      sec.addReloc({R_TLSGD_GOTPLT, type, offset, addend, &sym});
+      continue;
+
+    default:
+      Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
+               << "unknown relocation (" << type.v << ") against symbol "
+               << &sym;
+      continue;
+    }
+    rs.process(expr, type, offset, sym, addend);
+  }
+}
+
 // There are (arguably too) many relocation masks for the DSP's
 // R_HEX_6_X type.  The table below is used to select the correct mask
 // for the given instruction.
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index aaa3f9fd6e9d3..1ce4d8417dc3c 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -103,9 +103,10 @@ class RelocScan {
   }
 
   // Handle TLS Initial-Exec relocation.
+  template <bool enableIeToLe = true>
   void handleTlsIe(RelExpr ieExpr, RelType type, uint64_t offset,
                    int64_t addend, Symbol &sym) {
-    if (!ctx.arg.shared && !sym.isPreemptible) {
+    if (enableIeToLe && !ctx.arg.shared && !sym.isPreemptible) {
       // Optimize to Local Exec.
       sec->addReloc({R_TPREL, type, offset, addend, &sym});
     } else {
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 1074e7f24db43..6ff5160b109c2 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -926,13 +926,7 @@ void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
   const bool isIfunc = sym.isGnuIFunc();
   if (!sym.isPreemptible && !isIfunc) {
     if (expr != R_GOT_PC) {
-      // R_HEX_GD_PLT_B22_PCREL (call a at GDPLT) is transformed into
-      // call __tls_get_addr even if the symbol is non-preemptible.
-      if (!(ctx.arg.emachine == EM_HEXAGON &&
-            (type == R_HEX_GD_PLT_B22_PCREL ||
-             type == R_HEX_GD_PLT_B22_PCREL_X ||
-             type == R_HEX_GD_PLT_B32_PCREL_X)))
-        expr = fromPlt(expr);
+      expr = fromPlt(expr);
     } else if (!isAbsoluteOrTls(sym)) {
       expr = ctx.target->adjustGotPcExpr(type, addend,
                                          sec->content().data() + offset);
@@ -1204,14 +1198,12 @@ unsigned RelocScan::handleTlsRelocation(RelExpr expr, RelType type,
        type == R_LARCH_TLS_DESC_LD || type == R_LARCH_TLS_DESC_CALL ||
        type == R_LARCH_TLS_DESC_PCREL20_S2);
 
-  // ARM, Hexagon, LoongArch and RISC-V do not support GD/LD to IE/LE
-  // optimizations.
+  // ARM, LoongArch and RISC-V do not support GD/LD to IE/LE optimizations.
   // RISC-V supports TLSDESC to IE/LE optimizations.
   // For PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
   // optimization as well.
   bool execOptimize =
       !ctx.arg.shared && ctx.arg.emachine != EM_ARM &&
-      ctx.arg.emachine != EM_HEXAGON &&
       (ctx.arg.emachine != EM_LOONGARCH || execOptimizeInLoongArch) &&
       !(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL);
 

>From 55b7c34398f34da4d69f23f58d4f6b15ffffe677 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 16 Feb 2026 14:01:49 -0800
Subject: [PATCH 2/2] comment

---
 lld/ELF/Arch/Hexagon.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 447e33dea86dc..5b82757c7c5e7 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -219,6 +219,7 @@ void Hexagon::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     case R_HEX_IE_16_X:
     case R_HEX_IE_HI16:
     case R_HEX_IE_LO16:
+      // There is no IE to LE optimization.
       rs.handleTlsIe<false>(R_GOT, type, offset, addend, sym);
       continue;
     case R_HEX_IE_GOT_11_X:



More information about the llvm-commits mailing list