[llvm] [BOLT][RISCV] Fix AUIPC/JALR call rewriting (PR #216882)

Alexander Wilson via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 17 16:47:17 PDT 2026


https://github.com/rdtscp created https://github.com/llvm/llvm-project/pull/216882

R_RISCV_CALL and R_RISCV_CALL_PLT cover an AUIPC/JALR pair, but BOLT treated them as four-byte relocations and decoded only the AUIPC immediate. Read both instructions and combine their signed high and low immediates so relocated call targets retain the low 12 bits.

LTO can also leave linker-resolved intra-section AUIPC/JALR calls without relocations. Recognize valid standard call and tail-call pairs during disassembly, reconstruct the target (including JALR target-bit clearing), and attach a symbol reference before function reordering.

This follows the RISC-V Unprivileged ISA RV32I sections "Integer Computational Instructions" (AUIPC) and "Control Transfer Instructions" (JALR): https://docs.riscv.org/reference/isa/v20260120/unpriv/rv32.html

It also follows the RISC-V ELF psABI "Relocations" chapter, specifically "Procedure Calls", where R_RISCV_CALL and R_RISCV_CALL_PLT apply to the AUIPC/JALR pair: https://riscv-non-isa.github.io/riscv-elf-psabi-doc/#_relocations

Tested with the focused RISC-V CoreTests and the full BOLT RISCV lit directory (38 passed).

Assisted-by: Codex

>From 2b15e7aba033e244b2d70a9bb8aec187ff8f9e26 Mon Sep 17 00:00:00 2001
From: Alexander Wilson <rdtscp at meta.com>
Date: Mon, 17 Aug 2026 16:13:51 -0700
Subject: [PATCH] [BOLT][RISCV] Fix AUIPC/JALR call rewriting

R_RISCV_CALL and R_RISCV_CALL_PLT cover an AUIPC/JALR pair, but BOLT treated them as four-byte relocations and decoded only the AUIPC immediate. Read both instructions and combine their signed high and low immediates so relocated call targets retain the low 12 bits.

LTO can also leave linker-resolved intra-section AUIPC/JALR calls without relocations. Recognize valid standard call and tail-call pairs during disassembly, reconstruct the target (including JALR target-bit clearing), and attach a symbol reference before function reordering.

This follows the RISC-V Unprivileged ISA RV32I sections "Integer Computational Instructions" (AUIPC) and "Control Transfer Instructions" (JALR):
https://docs.riscv.org/reference/isa/v20260120/unpriv/rv32.html

It also follows the RISC-V ELF psABI "Relocations" chapter, specifically "Procedure Calls", where R_RISCV_CALL and R_RISCV_CALL_PLT apply to the AUIPC/JALR pair:
https://riscv-non-isa.github.io/riscv-elf-psabi-doc/#_relocations

Tested with the focused RISC-V CoreTests and the full BOLT RISCV lit directory (38 passed).

Assisted-by: Codex
---
 bolt/include/bolt/Core/MCPlusBuilder.h        |  17 +++
 bolt/lib/Core/BinaryFunction.cpp              |  23 ++++
 bolt/lib/Core/Relocation.cpp                  |  10 +-
 bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp  |  43 ++++++
 .../RISCV/Inputs/unsymbolized-call-order.txt  |   3 +
 bolt/test/RISCV/call-relocation-pair.s        |  60 ++++++++
 bolt/test/RISCV/unsymbolized-call.s           |  57 ++++++++
 bolt/unittests/Core/CMakeLists.txt            |   1 +
 bolt/unittests/Core/RISCVMCPlusBuilder.cpp    | 130 ++++++++++++++++++
 9 files changed, 341 insertions(+), 3 deletions(-)
 create mode 100644 bolt/test/RISCV/Inputs/unsymbolized-call-order.txt
 create mode 100644 bolt/test/RISCV/call-relocation-pair.s
 create mode 100644 bolt/test/RISCV/unsymbolized-call.s
 create mode 100644 bolt/unittests/Core/RISCVMCPlusBuilder.cpp

diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index be0d58af14fc4..25a0dde6dba34 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -913,6 +913,23 @@ class MCPlusBuilder {
     return false;
   }
 
+  /// Return true if \p First and \p Second form an AUIPC/JALR call pair with
+  /// linker-resolved immediates instead of a symbol reference. Such pairs can
+  /// be emitted for intra-section calls and need to be resymbolized before the
+  /// caller is moved.
+  virtual bool isUnsymbolizedRISCVCall(const MCInst &First,
+                                       const MCInst &Second) const {
+    return false;
+  }
+
+  /// Return the byte offset from AUIPC to the target of an unsymbolized
+  /// AUIPC/JALR call pair, including JALR's clearing of target bit zero.
+  virtual int64_t getUnsymbolizedRISCVCallOffset(const MCInst &First,
+                                                 const MCInst &Second) const {
+    llvm_unreachable("not implemented");
+    return 0;
+  }
+
   /// Used to fill the executable space with instructions
   /// that will trap.
   virtual StringRef getTrapFillValue() const {
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index a81fa2f45c206..8a3d22e4cd974 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -24,6 +24,7 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Demangle/Demangle.h"
 #include "llvm/MC/MCAsmInfo.h"
 #include "llvm/MC/MCContext.h"
@@ -1421,6 +1422,28 @@ Error BinaryFunction::disassemble() {
     if (IsUnsupported)
       setIgnored();
 
+    // Recover linker-resolved intra-section calls without relocations. The
+    // current instruction is the JALR and the AUIPC is four bytes earlier in
+    // the instruction map. Resymbolizing the AUIPC lets BOLT update the pair
+    // if function reordering moves the caller relative to the callee.
+    if (BC.isRISCV() && Offset >= 4) {
+      auto PrevII = Instructions.find(Offset - 4);
+      if (PrevII != Instructions.end() &&
+          BC.MIB->isUnsymbolizedRISCVCall(PrevII->second, Instruction)) {
+        const uint64_t Target =
+            AbsoluteInstrAddr - 4 +
+            BC.MIB->getUnsymbolizedRISCVCallOffset(PrevII->second, Instruction);
+        if (BinaryFunction *TargetBF = BC.getBinaryFunctionAtAddress(Target)) {
+          int64_t Value = 0;
+          const bool Replaced = BC.MIB->replaceImmWithSymbolRef(
+              PrevII->second, TargetBF->getSymbol(), /*Addend=*/0, Ctx.get(),
+              Value, ELF::R_RISCV_CALL_PLT);
+          (void)Replaced;
+          assert(Replaced && "cannot symbolize RISC-V call");
+        }
+      }
+    }
+
     if (MIB->isBranch(Instruction) || MIB->isCall(Instruction)) {
       uint64_t TargetAddress = 0;
       if (MIB->evaluateBranch(Instruction, AbsoluteInstrAddr, Size,
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index 55d5e07042897..2d19b24c8c05a 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -229,8 +229,6 @@ static size_t getSizeForTypeRISCV(uint32_t Type) {
   case ELF::R_RISCV_PCREL_LO12_I:
   case ELF::R_RISCV_PCREL_LO12_S:
   case ELF::R_RISCV_32_PCREL:
-  case ELF::R_RISCV_CALL:
-  case ELF::R_RISCV_CALL_PLT:
   case ELF::R_RISCV_ADD32:
   case ELF::R_RISCV_SUB32:
   case ELF::R_RISCV_HI20:
@@ -239,6 +237,8 @@ static size_t getSizeForTypeRISCV(uint32_t Type) {
   case ELF::R_RISCV_32:
     return 4;
   case ELF::R_RISCV_64:
+  case ELF::R_RISCV_CALL:
+  case ELF::R_RISCV_CALL_PLT:
   case ELF::R_RISCV_GOT_HI20:
   case ELF::R_RISCV_TLS_GOT_HI20:
   case ELF::R_RISCV_TLS_GD_HI20:
@@ -502,7 +502,11 @@ static uint64_t extractValueRISCV(uint32_t Type, uint64_t Contents,
     return extractJImmRISCV(Contents);
   case ELF::R_RISCV_CALL:
   case ELF::R_RISCV_CALL_PLT:
-    return extractUImmRISCV(Contents);
+    // The psABI "Relocations" chapter's "Procedure Calls" section defines
+    // R_RISCV_CALL and R_RISCV_CALL_PLT over an AUIPC/JALR pair. Decode both
+    // instructions so the addend includes the low 12 bits carried by JALR.
+    return extractUImmRISCV(Contents & 0xffffffff) +
+           extractIImmRISCV(Contents >> 32);
   case ELF::R_RISCV_BRANCH:
     return extractBImmRISCV(Contents);
   case ELF::R_RISCV_GOT_HI20:
diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index 1511e4744124a..73cc33b1f06d8 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -19,6 +19,7 @@
 #include "llvm/MC/MCInstBuilder.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
 
 #define DEBUG_TYPE "mcplus"
 
@@ -27,6 +28,23 @@ using namespace bolt;
 
 namespace {
 
+bool isValidUnsymbolizedCallAUIPC(const MCInst &Inst) {
+  if (Inst.getOpcode() != RISCV::AUIPC || Inst.getNumOperands() != 2)
+    return false;
+
+  const MCOperand &Destination = Inst.getOperand(0);
+  return Destination.isReg() && Destination.getReg() != RISCV::X0 &&
+         Inst.getOperand(1).isImm();
+}
+
+bool isValidUnsymbolizedCallJALR(const MCInst &Inst) {
+  if (Inst.getOpcode() != RISCV::JALR || Inst.getNumOperands() != 3)
+    return false;
+
+  return Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() &&
+         Inst.getOperand(2).isImm();
+}
+
 class RISCVMCPlusBuilder : public MCPlusBuilder {
   bool isRV64() const { return STI->hasFeature(RISCV::Feature64Bit); }
   unsigned regSize() const { return isRV64() ? 8 : 4; }
@@ -504,6 +522,31 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
     return true;
   }
 
+  bool isUnsymbolizedRISCVCall(const MCInst &First,
+                               const MCInst &Second) const override {
+    if (!isValidUnsymbolizedCallAUIPC(First) ||
+        !isValidUnsymbolizedCallJALR(Second))
+      return false;
+
+    if (Second.getOperand(1).getReg() != First.getOperand(0).getReg())
+      return false;
+
+    const MCPhysReg Link = Second.getOperand(0).getReg();
+    return Link == RISCV::X1 || Link == RISCV::X0;
+  }
+
+  int64_t getUnsymbolizedRISCVCallOffset(const MCInst &First,
+                                         const MCInst &Second) const override {
+    // The RV32I "Integer Computational Instructions" section defines AUIPC's
+    // offset as the sign-extended 20-bit U-immediate shifted left by 12. The
+    // "Control Transfer Instructions" section defines JALR as adding its
+    // sign-extended 12-bit I-immediate and clearing target bit zero. Mask the
+    // decoded AUIPC operand back to its encoded field before sign extension.
+    const int64_t Hi = SignExtend64<32>(
+        (static_cast<uint64_t>(First.getOperand(1).getImm()) & 0xfffff) << 12);
+    return (Hi + Second.getOperand(2).getImm()) & ~1LL;
+  }
+
   uint16_t getMinFunctionAlignment() const override {
     if (STI->hasFeature(RISCV::FeatureStdExtC) ||
         STI->hasFeature(RISCV::FeatureStdExtZca))
diff --git a/bolt/test/RISCV/Inputs/unsymbolized-call-order.txt b/bolt/test/RISCV/Inputs/unsymbolized-call-order.txt
new file mode 100644
index 0000000000000..801f0aa6638b9
--- /dev/null
+++ b/bolt/test/RISCV/Inputs/unsymbolized-call-order.txt
@@ -0,0 +1,3 @@
+target
+relocated_call
+_start
diff --git a/bolt/test/RISCV/call-relocation-pair.s b/bolt/test/RISCV/call-relocation-pair.s
new file mode 100644
index 0000000000000..2e90333377561
--- /dev/null
+++ b/bolt/test/RISCV/call-relocation-pair.s
@@ -0,0 +1,60 @@
+// Test that R_RISCV_CALL and R_RISCV_CALL_PLT cover and decode the complete
+// AUIPC/JALR instruction pair.
+
+// RUN: llvm-mc -triple riscv64 -mattr=-relax -filetype=obj -o %t.o %s
+// RUN: ld.lld --no-relax --emit-relocs -o %t %t.o
+// RUN: llvm-readelf --relocations %t | FileCheck --check-prefix=RELOCS %s
+// RUN: llvm-bolt --print-fix-riscv-calls --print-only=_start -o %t.bolt %t \
+// RUN:     | FileCheck --check-prefix=BOLT %s
+// RUN: llvm-objdump -d %t.bolt | FileCheck --check-prefix=OBJDUMP %s
+
+// RELOCS: R_RISCV_CALL {{.*}} target_call
+// RELOCS: R_RISCV_CALL_PLT {{.*}} target_call_plt
+
+// BOLT-LABEL: Binary Function "_start" after fix-riscv-calls {
+// BOLT:       nop
+// BOLT-NEXT:  call target_call
+// BOLT-NEXT:  nop
+// BOLT-NEXT:  call target_call_plt
+
+// OBJDUMP-LABEL: <_start>:
+// OBJDUMP:       nop
+// OBJDUMP-NEXT:  auipc ra,
+// OBJDUMP-NEXT:  jalr {{.*}}(ra)
+// OBJDUMP-NEXT:  nop
+// OBJDUMP-NEXT:  auipc ra,
+// OBJDUMP-NEXT:  jalr {{.*}}(ra)
+// OBJDUMP-LABEL: <target_call>:
+// OBJDUMP-LABEL: <target_call_plt>:
+
+  .text
+  .option norvc
+  .option norelax
+
+  .globl _start
+  .type _start, at function
+_start:
+  .reloc ., R_RISCV_CALL, target_call
+  auipc ra, 0
+  jalr ra
+  .reloc ., R_RISCV_CALL_PLT, target_call_plt
+  auipc ra, 0
+  jalr ra
+  ret
+  .size _start, .-_start
+
+  .skip (1 << 21) + 0x7c
+
+  .globl target_call
+  .type target_call, at function
+target_call:
+  ret
+  .size target_call, .-target_call
+
+  .skip 0x84
+
+  .globl target_call_plt
+  .type target_call_plt, at function
+target_call_plt:
+  ret
+  .size target_call_plt, .-target_call_plt
diff --git a/bolt/test/RISCV/unsymbolized-call.s b/bolt/test/RISCV/unsymbolized-call.s
new file mode 100644
index 0000000000000..d9fb765b22e7f
--- /dev/null
+++ b/bolt/test/RISCV/unsymbolized-call.s
@@ -0,0 +1,57 @@
+// Test recovery of a linker-resolved AUIPC/JALR pair that has no relocation,
+// even though the rest of the executable retains relocations.
+
+// RUN: llvm-mc -triple riscv64 -mattr=-relax -filetype=obj -o %t.o %s
+// RUN: ld.lld --no-relax --emit-relocs -o %t %t.o
+// RUN: llvm-objdump -dr %t | FileCheck --check-prefix=INPUT %s
+// RUN: llvm-bolt --print-cfg --print-fix-riscv-calls --print-only=_start \
+// RUN:     --reorder-functions=user \
+// RUN:     --function-order=%p/Inputs/unsymbolized-call-order.txt \
+// RUN:     -o %t.bolt %t | FileCheck --check-prefix=BOLT %s
+// RUN: llvm-objdump -d %t.bolt | FileCheck --check-prefix=OBJDUMP %s
+
+// INPUT-LABEL: <_start>:
+// INPUT:       auipc ra, 0x200
+// INPUT-NEXT:  jalr 0x88(ra) <target>
+// INPUT-NEXT:  ret
+// INPUT-LABEL: <relocated_call>:
+// INPUT:       R_RISCV_CALL_PLT target
+
+// BOLT-LABEL: Binary Function "_start" after building cfg {
+// BOLT:       auipc ra, target
+// BOLT-NEXT:  jalr {{.*}}(ra)
+// BOLT-LABEL: Binary Function "_start" after fix-riscv-calls {
+// BOLT:       call target
+
+// OBJDUMP-LABEL: <target>:
+// OBJDUMP-LABEL: <_start>:
+// OBJDUMP:       jal {{.*}} <target>
+
+  .text
+  .option norvc
+  .option norelax
+
+  .globl _start
+  .type _start, at function
+_start:
+  // The target starts 0x200088 bytes after this AUIPC. Spell out the resolved
+  // immediates so this pair has no relocation, as happens after LTO linking.
+  auipc ra, 0x200
+  jalr ra, 0x88(ra)
+  ret
+  .size _start, .-_start
+
+  .skip (1 << 21) + 0x7c
+
+  .globl target
+  .type target, at function
+target:
+  ret
+  .size target, .-target
+
+  .globl relocated_call
+  .type relocated_call, at function
+relocated_call:
+  call target
+  ret
+  .size relocated_call, .-relocated_call
diff --git a/bolt/unittests/Core/CMakeLists.txt b/bolt/unittests/Core/CMakeLists.txt
index 297dec7449202..b4e0e95acff1b 100644
--- a/bolt/unittests/Core/CMakeLists.txt
+++ b/bolt/unittests/Core/CMakeLists.txt
@@ -11,6 +11,7 @@ add_bolt_unittest(CoreTests
   MCPlusBuilder.cpp
   MemoryMaps.cpp
   DynoStats.cpp
+  RISCVMCPlusBuilder.cpp
 
   # FIXME CoreTests uses `llvm::detail::TakeError(llvm::Error)`, but linking
   #       to LLVMTestingSupport introduces a transitive dependency on the
diff --git a/bolt/unittests/Core/RISCVMCPlusBuilder.cpp b/bolt/unittests/Core/RISCVMCPlusBuilder.cpp
new file mode 100644
index 0000000000000..c917765469542
--- /dev/null
+++ b/bolt/unittests/Core/RISCVMCPlusBuilder.cpp
@@ -0,0 +1,130 @@
+//===- bolt/unittest/Core/RISCVMCPlusBuilder.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef RISCV_AVAILABLE
+
+#include "MCTargetDesc/RISCVMCTargetDesc.h"
+#include "bolt/Core/BinaryContext.h"
+#include "bolt/Rewrite/RewriteInstance.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::bolt;
+using namespace llvm::object;
+
+namespace {
+
+class RISCVMCPlusBuilderTest : public testing::Test {
+protected:
+  void SetUp() override {
+    LLVMInitializeRISCVTargetInfo();
+    LLVMInitializeRISCVTargetMC();
+    LLVMInitializeRISCVDisassembler();
+
+    memcpy(ElfBuf, "\177ELF", 4);
+    auto *EHdr = reinterpret_cast<ELF64LE::Ehdr *>(ElfBuf);
+    EHdr->e_ident[ELF::EI_CLASS] = ELF::ELFCLASS64;
+    EHdr->e_ident[ELF::EI_DATA] = ELF::ELFDATA2LSB;
+    EHdr->e_machine = ELF::EM_RISCV;
+    MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
+    ObjFile = cantFail(ObjectFile::createObjectFile(Source));
+
+    Relocation::Arch = Triple::riscv64;
+    SubtargetFeatures Features("+m,+a,+f,+d,+c");
+    BC = cantFail(BinaryContext::createBinaryContext(
+        ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
+        ObjFile->getFileName(), &Features, true, DWARFContext::create(*ObjFile),
+        {llvm::outs(), llvm::errs()}));
+    BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
+        createMCPlusBuilder(Triple::riscv64, BC->MIA.get(), BC->MII.get(),
+                            BC->MRI.get(), BC->STI.get())));
+  }
+
+  static MCInst makeAUIPC(MCPhysReg Destination, int64_t Immediate) {
+    return MCInstBuilder(RISCV::AUIPC).addReg(Destination).addImm(Immediate);
+  }
+
+  static MCInst makeJALR(MCPhysReg Link, MCPhysReg Base, int64_t Immediate) {
+    return MCInstBuilder(RISCV::JALR)
+        .addReg(Link)
+        .addReg(Base)
+        .addImm(Immediate);
+  }
+
+  char ElfBuf[sizeof(ELF64LE::Ehdr)] = {};
+  std::unique_ptr<ObjectFile> ObjFile;
+  std::unique_ptr<BinaryContext> BC;
+};
+
+TEST_F(RISCVMCPlusBuilderTest,
+       UnsymbolizedCall_StandardCallAndTailCall_AreRecognized) {
+  const MCInst CallAUIPC = makeAUIPC(RISCV::X1, 1);
+  const MCInst CallJALR = makeJALR(RISCV::X1, RISCV::X1, -2048);
+  const MCInst OddCallJALR = makeJALR(RISCV::X1, RISCV::X1, -2047);
+  EXPECT_TRUE(BC->MIB->isUnsymbolizedRISCVCall(CallAUIPC, CallJALR));
+  EXPECT_EQ(2048, BC->MIB->getUnsymbolizedRISCVCallOffset(CallAUIPC, CallJALR));
+  EXPECT_EQ(2048,
+            BC->MIB->getUnsymbolizedRISCVCallOffset(CallAUIPC, OddCallJALR));
+
+  const MCInst TailAUIPC = makeAUIPC(RISCV::X6, 0xfffff);
+  const MCInst TailJALR = makeJALR(RISCV::X0, RISCV::X6, -4);
+  EXPECT_TRUE(BC->MIB->isUnsymbolizedRISCVCall(TailAUIPC, TailJALR));
+  EXPECT_EQ(-4100,
+            BC->MIB->getUnsymbolizedRISCVCallOffset(TailAUIPC, TailJALR));
+}
+
+TEST_F(RISCVMCPlusBuilderTest, UnsymbolizedCall_NearMatches_AreRejected) {
+  const MCInst AUIPC = makeAUIPC(RISCV::X1, 1);
+  const MCInst JALR = makeJALR(RISCV::X1, RISCV::X1, 0);
+
+  EXPECT_FALSE(BC->MIB->isUnsymbolizedRISCVCall(
+      MCInstBuilder(RISCV::LUI).addReg(RISCV::X1).addImm(1), JALR));
+  EXPECT_FALSE(BC->MIB->isUnsymbolizedRISCVCall(
+      AUIPC, MCInstBuilder(RISCV::JAL).addReg(RISCV::X1).addImm(0)));
+  EXPECT_FALSE(BC->MIB->isUnsymbolizedRISCVCall(
+      AUIPC, makeJALR(RISCV::X1, RISCV::X6, 0)));
+  EXPECT_FALSE(BC->MIB->isUnsymbolizedRISCVCall(
+      makeAUIPC(RISCV::X0, 1), makeJALR(RISCV::X1, RISCV::X0, 0)));
+  EXPECT_FALSE(BC->MIB->isUnsymbolizedRISCVCall(
+      makeAUIPC(RISCV::X5, 1), makeJALR(RISCV::X5, RISCV::X5, 0)));
+
+  MCSymbol *Target = BC->Ctx->createTempSymbol();
+  const MCInst SymbolizedAUIPC =
+      MCInstBuilder(RISCV::AUIPC)
+          .addReg(RISCV::X1)
+          .addExpr(MCSymbolRefExpr::create(Target, *BC->Ctx));
+  EXPECT_FALSE(BC->MIB->isUnsymbolizedRISCVCall(SymbolizedAUIPC, JALR));
+}
+
+TEST(RISCVRelocationTest, CallRelocations_InstructionPair_IsDecoded) {
+  Relocation::Arch = Triple::riscv64;
+  const auto EncodePair = [](uint32_t Hi20, int32_t Lo12) {
+    const uint32_t AUIPC = (Hi20 << 12) | 0x17;
+    const uint32_t JALR = ((static_cast<uint32_t>(Lo12) & 0xfff) << 20) | 0x67;
+    return static_cast<uint64_t>(AUIPC) | (static_cast<uint64_t>(JALR) << 32);
+  };
+
+  EXPECT_EQ(8U, Relocation::getSizeForType(ELF::R_RISCV_CALL));
+  EXPECT_EQ(8U, Relocation::getSizeForType(ELF::R_RISCV_CALL_PLT));
+
+  const uint64_t Forward = EncodePair(1, -2048);
+  EXPECT_EQ(2048U, Relocation::extractValue(ELF::R_RISCV_CALL, Forward, 0));
+
+  const uint64_t Backward = EncodePair(0xfffff, -4);
+  EXPECT_EQ(static_cast<uint64_t>(-4100),
+            Relocation::extractValue(ELF::R_RISCV_CALL_PLT, Backward, 0));
+}
+
+} // namespace
+
+#endif // RISCV_AVAILABLE



More information about the llvm-commits mailing list