[llvm] [BOLT][RISCV] Add target symbolizer for relocations (PR #217944)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 21 19:53:45 PDT 2026


https://github.com/Thrrreeee updated https://github.com/llvm/llvm-project/pull/217944

>From 5292bff12cb2b352fc092f55f053f2f6d2509a99 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Fri, 21 Aug 2026 23:21:06 +0800
Subject: [PATCH 1/2] [BOLT][RISCV] Add target symbolizer for relocations

---
 bolt/lib/Core/BinaryFunction.cpp             |  50 -------
 bolt/lib/Core/Relocation.cpp                 |   2 +-
 bolt/lib/Target/RISCV/CMakeLists.txt         |   2 +
 bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp |   7 +
 bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp  | 150 +++++++++++++++++++
 bolt/lib/Target/RISCV/RISCVMCSymbolizer.h    |  58 +++++++
 bolt/test/RISCV/reloc-bb-split-rv32.s        |  12 +-
 bolt/test/RISCV/reloc-bb-split.s             |  12 +-
 bolt/test/RISCV/reloc-got-moved-rv32.s       |  35 +++++
 bolt/test/RISCV/reloc-got-moved.s            |  35 +++++
 bolt/test/RISCV/reloc-got.s                  |  26 ++--
 bolt/test/RISCV/reloc-pcrel-moved-rv32.s     |  31 ++++
 bolt/test/RISCV/reloc-pcrel-moved.s          |  31 ++++
 bolt/test/RISCV/reloc-pcrel-rv32.s           |  11 +-
 bolt/test/RISCV/reloc-pcrel.s                |   3 +-
 15 files changed, 382 insertions(+), 83 deletions(-)
 create mode 100644 bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
 create mode 100644 bolt/lib/Target/RISCV/RISCVMCSymbolizer.h
 create mode 100644 bolt/test/RISCV/reloc-got-moved-rv32.s
 create mode 100644 bolt/test/RISCV/reloc-got-moved.s
 create mode 100644 bolt/test/RISCV/reloc-pcrel-moved-rv32.s
 create mode 100644 bolt/test/RISCV/reloc-pcrel-moved.s

diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index a81fa2f45c206..131221ecfd27f 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1330,13 +1330,6 @@ Error BinaryFunction::disassemble() {
   // basic block.
   Labels[0] = Ctx->createNamedTempSymbol("BB0");
 
-  // Map offsets in the function to a label that should always point to the
-  // corresponding instruction. This is used for labels that shouldn't point to
-  // the start of a basic block but always to a specific instruction. This is
-  // used, for example, on RISC-V where %pcrel_lo relocations point to the
-  // corresponding %pcrel_hi.
-  LabelsMapType InstructionLabels;
-
   uint64_t Size = 0; // instruction size
   for (uint64_t Offset = 0; Offset < getSize(); Offset += Size) {
     MCInst Instruction;
@@ -1501,42 +1494,6 @@ Error BinaryFunction::disassemble() {
         if (BC.isAArch64())
           handleAArch64IndirectCall(Instruction, Offset);
       }
-    } else if (BC.isRISCV()) {
-      // Check if there's a relocation associated with this instruction.
-      for (auto Itr = Relocations.lower_bound(Offset),
-                ItrE = Relocations.lower_bound(Offset + Size);
-           Itr != ItrE; ++Itr) {
-        const Relocation &Relocation = Itr->second;
-        MCSymbol *Symbol = Relocation.Symbol;
-
-        if (Relocation::isInstructionReference(Relocation.Type)) {
-          uint64_t RefOffset = Relocation.Value - getAddress();
-          LabelsMapType::iterator LI = InstructionLabels.find(RefOffset);
-
-          if (LI == InstructionLabels.end()) {
-            Symbol = BC.Ctx->createNamedTempSymbol();
-            InstructionLabels.emplace(RefOffset, Symbol);
-          } else {
-            Symbol = LI->second;
-          }
-        }
-
-        uint64_t Addend = Relocation.Addend;
-
-        // For GOT relocations, create a reference against GOT entry ignoring
-        // the relocation symbol.
-        if (Relocation::isGOT(Relocation.Type)) {
-          assert(Relocation::isPCRelative(Relocation.Type) &&
-                 "GOT relocation must be PC-relative on RISC-V");
-          Symbol = BC.registerNameAtAddress("__BOLT_got_zero", 0, 0, 0);
-          Addend = Relocation.Value + Relocation.Offset + getAddress();
-        }
-        int64_t Value = Relocation.Value;
-        const bool Result = BC.MIB->replaceImmWithSymbolRef(
-            Instruction, Symbol, Addend, Ctx.get(), Value, Relocation.Type);
-        (void)Result;
-        assert(Result && "cannot replace immediate with relocation");
-      }
     }
 
 add_instruction:
@@ -1578,13 +1535,6 @@ Error BinaryFunction::disassemble() {
   // Scope-boundary markers are only consulted while assigning offsets above.
   DebugScopeBoundaryOffsets.clear();
 
-  for (auto [Offset, Label] : InstructionLabels) {
-    InstrMapType::iterator II = Instructions.find(Offset);
-    assert(II != Instructions.end() && "reference to non-existing instruction");
-
-    BC.MIB->setInstLabel(II->second, Label);
-  }
-
   // Reset symbolizer for the disassembler.
   BC.SymbolicDisAsm->setSymbolizer(nullptr);
 
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index b0f6b6ce0eddc..cb6aaa552d05f 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -892,7 +892,7 @@ bool Relocation::isTLS(uint32_t Type) {
 }
 
 bool Relocation::isInstructionReference(uint32_t Type) {
-  if (Arch != Triple::riscv64)
+  if (Arch != Triple::riscv64 && Arch != Triple::riscv32)
     return false;
 
   switch (Type) {
diff --git a/bolt/lib/Target/RISCV/CMakeLists.txt b/bolt/lib/Target/RISCV/CMakeLists.txt
index 45645a98d132f..e7fa950de29df 100644
--- a/bolt/lib/Target/RISCV/CMakeLists.txt
+++ b/bolt/lib/Target/RISCV/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(LLVM_LINK_COMPONENTS
   MC
+  MCDisassembler
   Support
   RISCVDesc
   )
@@ -19,6 +20,7 @@ endif()
 
 add_llvm_library(LLVMBOLTTargetRISCV
   RISCVMCPlusBuilder.cpp
+  RISCVMCSymbolizer.cpp
 
   NO_EXPORT
   DISABLE_LLVM_LINK_LLVM_DYLIB
diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index 1511e4744124a..c6c04ca88aa7a 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -12,6 +12,7 @@
 
 #include "MCTargetDesc/RISCVMCAsmInfo.h"
 #include "MCTargetDesc/RISCVMCTargetDesc.h"
+#include "RISCVMCSymbolizer.h"
 #include "bolt/Core/MCPlusBuilder.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/MCContext.h"
@@ -39,6 +40,12 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
 public:
   using MCPlusBuilder::MCPlusBuilder;
 
+  std::unique_ptr<MCSymbolizer>
+  createTargetSymbolizer(BinaryFunction &Function,
+                         bool CreateNewSymbols) const override {
+    return std::make_unique<RISCVMCSymbolizer>(Function, CreateNewSymbols);
+  }
+
   bool equals(const MCSpecifierExpr &A, const MCSpecifierExpr &B,
               CompFuncTy Comp) const override {
     const auto &RISCVExprA = cast<MCSpecifierExpr>(A);
diff --git a/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
new file mode 100644
index 0000000000000..9ccbfec9faf8a
--- /dev/null
+++ b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
@@ -0,0 +1,150 @@
+//===- bolt/Target/RISCV/RISCVMCSymbolizer.cpp ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RISCVMCSymbolizer.h"
+#include "bolt/Core/BinaryContext.h"
+#include "bolt/Core/BinaryFunction.h"
+#include "bolt/Core/MCPlusBuilder.h"
+#include "bolt/Core/Relocation.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCInst.h"
+
+#define DEBUG_TYPE "bolt-symbolizer"
+
+namespace llvm {
+namespace bolt {
+
+RISCVMCSymbolizer::RISCVMCSymbolizer(BinaryFunction &Function,
+                                     bool CreateNewSymbols)
+    : MCSymbolizer(*Function.getBinaryContext().Ctx, nullptr),
+      Function(Function), CreateNewSymbols(CreateNewSymbols) {
+  // Discover instruction references before decoding starts. This lets us
+  // attach a label while decoding the referenced %pcrel_hi instruction even
+  // though its %pcrel_lo user is normally decoded later.
+  for (uint64_t SearchOffset = 0; SearchOffset < Function.getSize();) {
+    const Relocation *Rel =
+        Function.getRelocationInRange(SearchOffset, Function.getSize());
+    if (!Rel)
+      break;
+
+    if (Relocation::isInstructionReference(Rel->Type)) {
+      assert(Rel->Value >= Function.getAddress() &&
+             Rel->Value < Function.getAddress() + Function.getSize() &&
+             "RISC-V instruction reference outside of function");
+      const uint64_t ReferencedOffset = Rel->Value - Function.getAddress();
+      InstructionReferences.try_emplace(ReferencedOffset, Rel);
+      if (CreateNewSymbols)
+        InstructionLabels.try_emplace(ReferencedOffset, nullptr);
+    }
+
+    SearchOffset = Rel->Offset + 1;
+  }
+}
+
+RISCVMCSymbolizer::~RISCVMCSymbolizer() {}
+
+MCSymbol *RISCVMCSymbolizer::getOrCreateInstructionLabel(uint64_t Offset) {
+  auto [It, Inserted] = InstructionLabels.try_emplace(Offset, nullptr);
+  (void)Inserted;
+  if (!It->second)
+    It->second = Ctx.createNamedTempSymbol();
+  return It->second;
+}
+
+uint64_t RISCVMCSymbolizer::getGOTValue(const Relocation &Rel) const {
+  BinaryContext &BC = Function.getBinaryContext();
+  const uint64_t HiAddress = Function.getAddress() + Rel.Offset;
+
+  // A GOT high relocation records a combined high/low value. Locate the low
+  // relocation by its reference back to this AUIPC instead of assuming that
+  // the low instruction is adjacent.
+  auto It = InstructionReferences.find(Rel.Offset);
+  if (It != InstructionReferences.end()) {
+    const Relocation *LoRel = It->second;
+    ErrorOr<uint64_t> HiContents = BC.getUnsignedValueAtAddress(HiAddress, 4);
+    ErrorOr<uint64_t> LoContents =
+        BC.getUnsignedValueAtAddress(Function.getAddress() + LoRel->Offset,
+                                     Relocation::getSizeForType(LoRel->Type));
+    assert(HiContents && LoContents &&
+           "cannot read RISC-V GOT relocation pair");
+
+    return Relocation::extractValue(ELF::R_RISCV_PCREL_HI20, *HiContents,
+                                    HiAddress) +
+           Relocation::extractValue(LoRel->Type, *LoContents,
+                                    Function.getAddress() + LoRel->Offset);
+  }
+
+  return Rel.Value;
+}
+
+bool RISCVMCSymbolizer::tryAddingSymbolicOperand(
+    MCInst &Inst, raw_ostream &CStream, int64_t Value, uint64_t InstAddress,
+    bool IsBranch, uint64_t ImmOffset, uint64_t ImmSize, uint64_t InstSize) {
+  BinaryContext &BC = Function.getBinaryContext();
+  MCContext *Ctx = BC.Ctx.get();
+  const uint64_t InstOffset = InstAddress - Function.getAddress();
+
+  // Branches and calls are resolved by BinaryFunction's target-independent
+  // control-flow handling.
+  if (BC.MIB->isBranch(Inst) || BC.MIB->isCall(Inst))
+    return false;
+
+  // Linker processing of R_RISCV_ALIGN can leave emitted relocations at an
+  // offset inside the instruction they apply to. Match the whole instruction
+  // range, as BinaryFunction::disassemble() did before this target-specific
+  // handling moved into the symbolizer.
+  const Relocation *Rel =
+      Function.getRelocationInRange(InstOffset, InstOffset + InstSize);
+  if (!Rel)
+    return false;
+
+  MCSymbol *Symbol = Rel->Symbol;
+  uint64_t Addend = Rel->Addend;
+
+  if (Relocation::isInstructionReference(Rel->Type)) {
+    if (!CreateNewSymbols)
+      return false;
+    Symbol = getOrCreateInstructionLabel(Rel->Value - Function.getAddress());
+    // The input addend reflects the original AUIPC location. The label now
+    // follows the instruction, so the assembler must derive the low bits from
+    // its new location.
+    Addend = 0;
+  }
+
+  // GOT high relocations name the object stored in the GOT, not the GOT entry
+  // addressed by AUIPC. Preserve the actual entry address using a zero-based
+  // symbol, as the RISC-V emitter reuses the input GOT.
+  if (Relocation::isGOT(Rel->Type)) {
+    assert(Relocation::isPCRelative(Rel->Type) &&
+           "GOT relocation must be PC-relative on RISC-V");
+    Symbol = BC.registerNameAtAddress("__BOLT_got_zero", 0, 0, 0);
+    Addend = getGOTValue(*Rel) + InstAddress;
+  }
+
+  assert(Symbol && "RISC-V relocation without a symbol");
+  const MCExpr *Expr = MCSymbolRefExpr::create(Symbol, *Ctx);
+  if (Addend)
+    Expr = MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Addend, *Ctx),
+                                   *Ctx);
+  Inst.addOperand(MCOperand::createExpr(
+      BC.MIB->getTargetExprFor(Inst, Expr, *Ctx, Rel->Type)));
+
+  // MC annotations must follow every real operand. Attach the instruction
+  // label only after the symbolized immediate has been appended.
+  if (InstructionLabels.find(InstOffset) != InstructionLabels.end())
+    BC.MIB->setInstLabel(Inst, getOrCreateInstructionLabel(InstOffset));
+
+  return true;
+}
+
+void RISCVMCSymbolizer::tryAddingPcLoadReferenceComment(raw_ostream &CStream,
+                                                        int64_t Value,
+                                                        uint64_t Address) {}
+
+} // namespace bolt
+} // namespace llvm
diff --git a/bolt/lib/Target/RISCV/RISCVMCSymbolizer.h b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.h
new file mode 100644
index 0000000000000..6a1e72684366b
--- /dev/null
+++ b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.h
@@ -0,0 +1,58 @@
+//===- bolt/Target/RISCV/RISCVMCSymbolizer.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_TARGET_RISCV_RISCVMCSYMBOLIZER_H
+#define BOLT_TARGET_RISCV_RISCVMCSYMBOLIZER_H
+
+#include "bolt/Core/BinaryFunction.h"
+#include "llvm/MC/MCDisassembler/MCSymbolizer.h"
+#include <map>
+
+namespace llvm {
+namespace bolt {
+
+class RISCVMCSymbolizer : public MCSymbolizer {
+protected:
+  BinaryFunction &Function;
+  bool CreateNewSymbols{true};
+
+  /// Map function offsets referenced by %pcrel_lo relocations to labels that
+  /// must remain attached to the corresponding %pcrel_hi instructions.
+  std::map<uint64_t, MCSymbol *> InstructionLabels;
+
+  /// Map referenced instruction offsets to their %pcrel_lo relocations.
+  std::map<uint64_t, const Relocation *> InstructionReferences;
+
+  MCSymbol *getOrCreateInstructionLabel(uint64_t Offset);
+
+  /// Return the complete PC-relative value for a GOT relocation. The value
+  /// recorded when relocations are read assumes that the low instruction
+  /// immediately follows AUIPC. Reconstruct it from the matching low
+  /// relocation so linker scheduling does not affect symbolization.
+  uint64_t getGOTValue(const Relocation &Rel) const;
+
+public:
+  RISCVMCSymbolizer(BinaryFunction &Function, bool CreateNewSymbols = true);
+
+  RISCVMCSymbolizer(const RISCVMCSymbolizer &) = delete;
+  RISCVMCSymbolizer &operator=(const RISCVMCSymbolizer &) = delete;
+  ~RISCVMCSymbolizer() override;
+
+  bool tryAddingSymbolicOperand(MCInst &Inst, raw_ostream &CStream,
+                                int64_t Value, uint64_t Address, bool IsBranch,
+                                uint64_t Offset, uint64_t OpSize,
+                                uint64_t InstSize) override;
+
+  void tryAddingPcLoadReferenceComment(raw_ostream &CStream, int64_t Value,
+                                       uint64_t Address) override;
+};
+
+} // namespace bolt
+} // namespace llvm
+
+#endif
diff --git a/bolt/test/RISCV/reloc-bb-split-rv32.s b/bolt/test/RISCV/reloc-bb-split-rv32.s
index 0ad3168fb983d..a434f5c71bd63 100644
--- a/bolt/test/RISCV/reloc-bb-split-rv32.s
+++ b/bolt/test/RISCV/reloc-bb-split-rv32.s
@@ -20,10 +20,10 @@ _start:
 /// basic block should start there.
 // CHECK-LABEL: {{^}}.LBB00
 // CHECK: nop
-// CHECK-LABEL: {{^}}.Ltmp0
-// CHECK: auipc t0, %pcrel_hi(d)
-// CHECK-NEXT: lw t0, %pcrel_lo({{.*}})(t0)
-// CHECK-NEXT: j .Ltmp0
+// CHECK: {{^}}[[BRANCH_LABEL:.Ltmp[0-9]+]]
+// CHECK: auipc t0, %pcrel_hi(d) # Label: [[HI_LABEL:.Ltmp[0-9]+]]
+// CHECK-NEXT: lw t0, %pcrel_lo([[HI_LABEL]])(t0)
+// CHECK-NEXT: j [[BRANCH_LABEL]]
   nop
 1:
   auipc t0, %pcrel_hi(d)
@@ -34,8 +34,8 @@ _start:
 /// start there.
 // CHECK-LABEL: {{^}}.LFT0
 // CHECK: nop
-// CHECK: auipc t0, %pcrel_hi(d)
-// CHECK-NEXT: lw t0, %pcrel_lo({{.*}})(t0)
+// CHECK: auipc t0, %pcrel_hi(d) # Label: [[SECOND_HI:.Ltmp[0-9]+]]
+// CHECK-NEXT: lw t0, %pcrel_lo([[SECOND_HI]])(t0)
 // CHECK-NEXT: ret
   nop
 1:
diff --git a/bolt/test/RISCV/reloc-bb-split.s b/bolt/test/RISCV/reloc-bb-split.s
index 23439ee2b8367..f862dd96fd79a 100644
--- a/bolt/test/RISCV/reloc-bb-split.s
+++ b/bolt/test/RISCV/reloc-bb-split.s
@@ -17,10 +17,10 @@ _start:
 /// basic block should start there.
 // CHECK-LABEL: {{^}}.LBB00
 // CHECK: nop
-// CHECK-LABEL: {{^}}.Ltmp0
-// CHECK: auipc t0, %pcrel_hi(d) # Label: .Ltmp1
-// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp1)(t0)
-// CHECK-NEXT: j .Ltmp0
+// CHECK: {{^}}[[BRANCH_LABEL:.Ltmp[0-9]+]]
+// CHECK: auipc t0, %pcrel_hi(d) # Label: [[HI_LABEL:.Ltmp[0-9]+]]
+// CHECK-NEXT: ld t0, %pcrel_lo([[HI_LABEL]])(t0)
+// CHECK-NEXT: j [[BRANCH_LABEL]]
   nop
 1:
   auipc t0, %pcrel_hi(d)
@@ -31,8 +31,8 @@ _start:
 /// start there.
 // CHECK-LABEL: {{^}}.LFT0
 // CHECK: nop
-// CHECK-NEXT: auipc t0, %pcrel_hi(d) # Label: .Ltmp2
-// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp2)(t0)
+// CHECK-NEXT: auipc t0, %pcrel_hi(d) # Label: [[SECOND_HI:.Ltmp[0-9]+]]
+// CHECK-NEXT: ld t0, %pcrel_lo([[SECOND_HI]])(t0)
 // CHECK-NEXT: ret
   nop
 1:
diff --git a/bolt/test/RISCV/reloc-got-moved-rv32.s b/bolt/test/RISCV/reloc-got-moved-rv32.s
new file mode 100644
index 0000000000000..f4076a35112e1
--- /dev/null
+++ b/bolt/test/RISCV/reloc-got-moved-rv32.s
@@ -0,0 +1,35 @@
+## Check that the RV32 R_RISCV_GOT_HI20/%pcrel_lo pair is rebuilt when the
+## matching low instruction is not immediately after AUIPC.
+
+# RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj -o %t.o %s
+# RUN: ld.lld -q -o %t.exe %t.o
+# RUN: llvm-bolt %t.exe -o %t.bolt -reorder-functions=cdsort --check-encoding
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK: <_start>:
+# CHECK-NEXT: auipc a0, 0xffc12
+# CHECK-NEXT: li a1, 0x7
+# CHECK-NEXT: li a2, 0x9
+# CHECK-NEXT: lw a0, 0x128(a0)
+# CHECK-NEXT: ret
+
+  .data
+  .p2align 12
+  .globl d
+d:
+  .word 0
+
+  .text
+  .globl _start
+  .type _start, @function
+_start:
+  nop
+1:
+  auipc a0, %got_pcrel_hi(d)
+  addi a1, zero, 7
+  addi a2, zero, 9
+  lw a0, %pcrel_lo(1b)(a0)
+  ret
+  .reloc 0, R_RISCV_NONE
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-got-moved.s b/bolt/test/RISCV/reloc-got-moved.s
new file mode 100644
index 0000000000000..c11155c657210
--- /dev/null
+++ b/bolt/test/RISCV/reloc-got-moved.s
@@ -0,0 +1,35 @@
+## Check that R_RISCV_GOT_HI20 relocations are re-encoded correctly when the
+## matching %pcrel_lo is not in the instruction immediately after the AUIPC.
+
+# RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj -o %t.o %s
+# RUN: ld.lld -q -o %t.exe %t.o
+# RUN: llvm-bolt %t.exe -o %t.bolt -reorder-functions=cdsort --check-encoding
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK: <_start>:
+# CHECK-NEXT: auipc a0, 0xffc12
+# CHECK-NEXT: li a1, 0x7
+# CHECK-NEXT: li a2, 0x9
+# CHECK-NEXT: ld a0, 0x1e0(a0)
+# CHECK-NEXT: ret
+
+  .data
+  .p2align 12
+  .globl d
+d:
+  .dword 0
+
+  .text
+  .globl _start
+  .type _start, @function
+_start:
+  nop
+1:
+  auipc a0, %got_pcrel_hi(d)
+  addi a1, zero, 7
+  addi a2, zero, 9
+  ld a0, %pcrel_lo(1b)(a0)
+  ret
+  .reloc 0, R_RISCV_NONE
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-got.s b/bolt/test/RISCV/reloc-got.s
index 1860da3e05a3b..905d8451a6365 100644
--- a/bolt/test/RISCV/reloc-got.s
+++ b/bolt/test/RISCV/reloc-got.s
@@ -1,5 +1,6 @@
 // RUN: %clang %cflags64 -o %t %s
-// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \
+// RUN: llvm-bolt --check-encoding --print-cfg --print-only=_start \
+// RUN:    -o %t.null %t \
 // RUN:    | FileCheck %s
 
   .data
@@ -22,30 +23,27 @@ _start:
   auipc t0, %got_pcrel_hi(d)
   ld t0, %pcrel_lo(1b)(t0)
 
-/// An unrelated instruction sits between the AUIPC and its load.
-// FIXME: The AUIPC below should also use __BOLT_got_zero+[[GOT]], but BOLT
-// takes the low part from the ADDI instead of from the load that names the
-// AUIPC's label.
-// CHECK-NOT:  __BOLT_got_zero+[[GOT]])
-// CHECK:      addi t2, t2, 0x7ff
-// CHECK-NEXT: ld t1, %pcrel_lo({{\.Ltmp[0-9]+}})(t1)
+/// An unrelated instruction can sit between the AUIPC and its load. The
+/// symbolizer locates the low relocation through its reference to the AUIPC.
+// CHECK:      auipc t1, %pcrel_hi(__BOLT_got_zero+[[GOT]]) # Label: [[HI2:\.Ltmp[0-9]+]]
+// CHECK-NEXT: addi t2, t2, 0x7ff
+// CHECK-NEXT: ld t1, %pcrel_lo([[HI2]])(t1)
 2:
   auipc t1, %got_pcrel_hi(d)
   addi t2, t2, 2047
   ld t1, %pcrel_lo(2b)(t1)
   j .L1
 .L2:
+// CHECK:      ld t1, %pcrel_lo([[HI3:\.Ltmp[0-9]+]])(t1)
+// CHECK-NEXT: j
   ld t1, %pcrel_lo(3f)(t1)
   j .Lexit
 .L1:
   nop
-/// The load lives in another basic block, so nothing follows the AUIPC but
-/// the terminator.
-// FIXME: The AUIPC below should also use __BOLT_got_zero+[[GOT]], but BOLT
-// takes the low part from the jump.
+/// The low relocation can also precede the AUIPC in output basic-block order.
 // CHECK:      nop
-// CHECK-NOT:  __BOLT_got_zero+[[GOT]])
-// CHECK:      j
+// CHECK-NEXT: auipc t1, %pcrel_hi(__BOLT_got_zero+[[GOT]]) # Label: [[HI3]]
+// CHECK-NEXT: j
 3:
   auipc t1, %got_pcrel_hi(d)
   j .L2
diff --git a/bolt/test/RISCV/reloc-pcrel-moved-rv32.s b/bolt/test/RISCV/reloc-pcrel-moved-rv32.s
new file mode 100644
index 0000000000000..ceb98c9508162
--- /dev/null
+++ b/bolt/test/RISCV/reloc-pcrel-moved-rv32.s
@@ -0,0 +1,31 @@
+## Check that RV32 R_RISCV_PCREL_LO12 relocations are re-encoded relative to
+## the moved AUIPC instruction instead of retaining the input addend.
+
+# RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj -o %t.o %s
+# RUN: ld.lld -q -o %t.exe %t.o
+# RUN: llvm-bolt %t.exe -o %t.bolt -reorder-functions=cdsort --check-encoding
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK: <_start>:
+# CHECK-NEXT: auipc a0, 0xffc13
+# CHECK-NEXT: lw a0, 0x0(a0)
+# CHECK-NEXT: ret
+
+  .data
+  .p2align 12
+  .globl d
+d:
+  .word 0
+
+  .text
+  .globl _start
+  .type _start, @function
+_start:
+  nop
+1:
+  auipc a0, %pcrel_hi(d)
+  lw a0, %pcrel_lo(1b)(a0)
+  ret
+  .reloc 0, R_RISCV_NONE
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-pcrel-moved.s b/bolt/test/RISCV/reloc-pcrel-moved.s
new file mode 100644
index 0000000000000..0b69ac2ca48be
--- /dev/null
+++ b/bolt/test/RISCV/reloc-pcrel-moved.s
@@ -0,0 +1,31 @@
+## Check that R_RISCV_PCREL_LO12 relocations are re-encoded relative to the
+## moved AUIPC instruction instead of retaining the input addend.
+
+# RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj -o %t.o %s
+# RUN: ld.lld -q -o %t.exe %t.o
+# RUN: llvm-bolt %t.exe -o %t.bolt -reorder-functions=cdsort --check-encoding
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK: <_start>:
+# CHECK-NEXT: auipc a0, 0xffc13
+# CHECK-NEXT: ld a0, 0x0(a0)
+# CHECK-NEXT: ret
+
+  .data
+  .p2align 12
+  .globl d
+d:
+  .dword 0
+
+  .text
+  .globl _start
+  .type _start, @function
+_start:
+  nop
+1:
+  auipc a0, %pcrel_hi(d)
+  ld a0, %pcrel_lo(1b)(a0)
+  ret
+  .reloc 0, R_RISCV_NONE
+  .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-pcrel-rv32.s b/bolt/test/RISCV/reloc-pcrel-rv32.s
index 2e386b022b8f8..f78a9d75bf796 100644
--- a/bolt/test/RISCV/reloc-pcrel-rv32.s
+++ b/bolt/test/RISCV/reloc-pcrel-rv32.s
@@ -2,7 +2,8 @@
 
 // RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj -o %t.o %s
 // RUN: ld.lld -q -o %t %t.o
-// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \
+// RUN: llvm-bolt --check-encoding --print-cfg --print-only=_start \
+// RUN:    -o %t.null %t \
 // RUN:    | FileCheck %s
 
   .data
@@ -17,11 +18,11 @@ d:
 // CHECK: Binary Function "_start" after building cfg {
 _start:
   nop // Here to not make the _start and .Ltmp0 symbols coincide
-// CHECK: auipc t0, %pcrel_hi(d)
-// CHECK-NEXT: lw t0, %pcrel_lo({{.*}})(t0)
+// CHECK: auipc t0, %pcrel_hi(d) # Label: [[HI_LABEL:.Ltmp[0-9]+]]
+// CHECK-NEXT: lw t0, %pcrel_lo([[HI_LABEL]])(t0)
   lw t0, d
-// CHECK: auipc t1, %pcrel_hi(d)
-// CHECK-NEXT: sw t0, %pcrel_lo({{.*}})(t1)
+// CHECK: auipc t1, %pcrel_hi(d) # Label: [[SECOND_HI:.Ltmp[0-9]+]]
+// CHECK-NEXT: sw t0, %pcrel_lo([[SECOND_HI]])(t1)
   sw t0, d, t1
   ret
   .size _start, .-_start
diff --git a/bolt/test/RISCV/reloc-pcrel.s b/bolt/test/RISCV/reloc-pcrel.s
index 5320c6d12e5cd..12324b4e112c9 100644
--- a/bolt/test/RISCV/reloc-pcrel.s
+++ b/bolt/test/RISCV/reloc-pcrel.s
@@ -1,5 +1,6 @@
 // RUN: %clang %cflags64 -o %t %s
-// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \
+// RUN: llvm-bolt --check-encoding --print-cfg --print-only=_start \
+// RUN:    -o %t.null %t \
 // RUN:    | FileCheck %s
 
   .data

>From cbe17d4eb7b0752cff99d6769f75a65620e6bd94 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Sat, 22 Aug 2026 10:53:23 +0800
Subject: [PATCH 2/2] fix test comment

---
 bolt/test/RISCV/reloc-got-moved.s | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/bolt/test/RISCV/reloc-got-moved.s b/bolt/test/RISCV/reloc-got-moved.s
index c11155c657210..64844940fd382 100644
--- a/bolt/test/RISCV/reloc-got-moved.s
+++ b/bolt/test/RISCV/reloc-got-moved.s
@@ -1,5 +1,5 @@
-## Check that R_RISCV_GOT_HI20 relocations are re-encoded correctly when the
-## matching %pcrel_lo is not in the instruction immediately after the AUIPC.
+## Check that the RV64 R_RISCV_GOT_HI20/%pcrel_lo pair is rebuilt when the
+## matching low instruction is not immediately after AUIPC.
 
 # RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj -o %t.o %s
 # RUN: ld.lld -q -o %t.exe %t.o



More information about the llvm-commits mailing list