[llvm] [BOLT][RISCV] Fix AUIPC/JALR call rewriting (PR #216882)
Alexander Wilson via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 06:39:31 PDT 2026
https://github.com/rdtscp updated https://github.com/llvm/llvm-project/pull/216882
>From 7a8dc5a99536d3def585807a7591d5be8f555d6f Mon Sep 17 00:00:00 2001
From: Alexander Wilson <rdtscp at meta.com>
Date: Fri, 21 Aug 2026 07:49:54 -0700
Subject: [PATCH] [BOLT][RISCV] Fix RV64 AUIPC/JALR call rewriting
R_RISCV_CALL and R_RISCV_CALL_PLT cover an AUIPC/JALR pair. Read both instructions and combine their signed high and low immediates so relocated call targets retain the low 12 bits.
Also recover RV64 linker-resolved standard call and tail-call pairs without relocations. Relocation-less recovery remains intentionally RV64-only. Alternate link-register preservation is provided by the merged #218408.
This revision addresses review feedback by building on the merged generic RISC-V symbolizer and GOT handling from #217944 and retaining only call-specific changes here.
This follows the RISC-V Unprivileged ISA sections on AUIPC and JALR:
https://docs.riscv.org/reference/isa/v20260120/unpriv/rv32.html
It also follows the RISC-V ELF psABI procedure-call relocations:
https://riscv-non-isa.github.io/riscv-elf-psabi-doc/#_relocations
Assisted-by: Codex
---
bolt/include/bolt/Core/BinaryFunction.h | 5 +
bolt/include/bolt/Core/MCPlusBuilder.h | 17 +++
bolt/lib/Core/Relocation.cpp | 10 +-
bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp | 55 ++++++++-
bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp | 48 +++++++-
bolt/test/RISCV/call-relocation-pair.s | 78 +++++++++++++
bolt/test/RISCV/unsymbolized-call-edge.s | 116 +++++++++++++++++++
bolt/test/RISCV/unsymbolized-call-entry.s | 60 ++++++++++
bolt/test/RISCV/unsymbolized-call.s | 60 ++++++++++
9 files changed, 441 insertions(+), 8 deletions(-)
create mode 100644 bolt/test/RISCV/call-relocation-pair.s
create mode 100644 bolt/test/RISCV/unsymbolized-call-edge.s
create mode 100644 bolt/test/RISCV/unsymbolized-call-entry.s
create mode 100644 bolt/test/RISCV/unsymbolized-call.s
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 14d7f9b5b5359..56529108e033c 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -945,6 +945,11 @@ class BinaryFunction {
/// Returns the raw binary encoding of this function.
ErrorOr<ArrayRef<uint8_t>> getData() const;
+ /// Return true if data embedded in the function starts at \p Offset.
+ bool isDataInCodeAt(uint64_t Offset) const {
+ return getSizeOfDataInCodeAt(Offset) != 0;
+ }
+
BinaryFunction &updateState(BinaryFunction::State State) {
CurrentState = State;
return *this;
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/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index cb6aaa552d05f..62ba95511a2c0 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 2388505ffd8ed..b56e0d6448833 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -28,6 +28,34 @@ using namespace bolt;
namespace {
+bool isValidUnsymbolizedCallAUIPC(const MCInst &Inst) {
+ if (Inst.getOpcode() != RISCV::AUIPC ||
+ MCPlus::getNumPrimeOperands(Inst) != 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 || MCPlus::getNumPrimeOperands(Inst) != 3)
+ return false;
+
+ return Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() &&
+ Inst.getOperand(2).isImm();
+}
+
+bool hasSupportedUnsymbolizedCallRegisters(const MCInst &First,
+ const MCInst &Second) {
+ const MCRegister Base = First.getOperand(0).getReg();
+ if (Second.getOperand(1).getReg() != Base)
+ return false;
+
+ const MCRegister Link = Second.getOperand(0).getReg();
+ return Link == RISCV::X0 || (Base == RISCV::X1 && Link == RISCV::X1);
+}
+
class RISCVMCPlusBuilder : public MCPlusBuilder {
bool isRV64() const { return STI->hasFeature(RISCV::Feature64Bit); }
unsigned regSize() const { return isRV64() ? 8 : 4; }
@@ -504,7 +532,9 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
}
bool isCallAuipc(const MCInst &Inst) const {
- if (Inst.getOpcode() != RISCV::AUIPC)
+ if (Inst.getOpcode() != RISCV::AUIPC ||
+ MCPlus::getNumPrimeOperands(Inst) != 2 || !Inst.getOperand(0).isReg() ||
+ Inst.getOperand(0).getReg() == RISCV::X0)
return false;
const auto &ImmOp = Inst.getOperand(1);
@@ -525,13 +555,32 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
}
bool isRISCVCall(const MCInst &First, const MCInst &Second) const override {
- if (!isCallAuipc(First))
+ if (!isCallAuipc(First) || !isValidUnsymbolizedCallJALR(Second))
return false;
- assert(Second.getOpcode() == RISCV::JALR);
return true;
}
+ bool isUnsymbolizedRISCVCall(const MCInst &First,
+ const MCInst &Second) const override {
+ if (!isValidUnsymbolizedCallAUIPC(First) ||
+ !isValidUnsymbolizedCallJALR(Second))
+ return false;
+
+ return hasSupportedUnsymbolizedCallRegisters(First, Second);
+ }
+
+ 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. The
+ // symbolic AUIPC decoder passes the sign-extended, shifted value directly.
+ const int64_t Hi = First.getOperand(1).getImm();
+ 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/lib/Target/RISCV/RISCVMCSymbolizer.cpp b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
index b38179a36ea2d..2df269a25af8b 100644
--- a/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCSymbolizer.cpp
@@ -7,11 +7,13 @@
//===----------------------------------------------------------------------===//
#include "RISCVMCSymbolizer.h"
+#include "MCTargetDesc/RISCVMCTargetDesc.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/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#define DEBUG_TYPE "bolt-symbolizer"
@@ -98,8 +100,50 @@ bool RISCVMCSymbolizer::tryAddingSymbolicOperand(
// handling moved into the symbolizer.
const Relocation *Rel =
Function.getRelocationInRange(InstOffset, InstOffset + InstSize);
- if (!Rel)
- return false;
+ if (!Rel) {
+ // Recover RV64 linker-resolved intra-section calls without relocations.
+ // Decode the following JALR and attach a call expression to the AUIPC
+ // before function reordering can move the caller relative to the callee.
+ if (Inst.getOpcode() != RISCV::AUIPC || !CreateNewSymbols ||
+ !BC.TheTriple->isRISCV64() || InstOffset + 8 > Function.getSize() ||
+ Function.isDataInCodeAt(InstOffset + 4) ||
+ Function.getRelocationInRange(InstOffset, InstOffset + 8))
+ return false;
+
+ ErrorOr<ArrayRef<uint8_t>> FunctionData = Function.getData();
+ if (!FunctionData)
+ return false;
+
+ MCInst JALR;
+ uint64_t JALRSize = 0;
+ if (!BC.DisAsm->getInstruction(JALR, JALRSize,
+ FunctionData->slice(InstOffset + 4),
+ InstAddress + 4, nulls()) ||
+ JALRSize != 4)
+ return false;
+
+ MCInst AUIPC = Inst;
+ AUIPC.addOperand(MCOperand::createImm(Value));
+ if (!BC.MIB->isUnsymbolizedRISCVCall(AUIPC, JALR))
+ return false;
+
+ const uint64_t Target =
+ InstAddress + BC.MIB->getUnsymbolizedRISCVCallOffset(AUIPC, JALR);
+ BinaryFunction *TargetBF = BC.getBinaryFunctionContainingAddress(Target);
+ if (!TargetBF)
+ return false;
+
+ BC.addInterproceduralReference(&Function, Target);
+ MCSymbol *TargetSymbol =
+ BC.handleExternalBranchTarget(Target, Function, *TargetBF);
+ if (!TargetSymbol)
+ return false;
+
+ const MCExpr *Expr = MCSymbolRefExpr::create(TargetSymbol, *Ctx);
+ Inst.addOperand(MCOperand::createExpr(
+ BC.MIB->getTargetExprFor(Inst, Expr, *Ctx, ELF::R_RISCV_CALL_PLT)));
+ return true;
+ }
MCSymbol *Symbol = Rel->Symbol;
uint64_t Addend = Rel->Addend;
diff --git a/bolt/test/RISCV/call-relocation-pair.s b/bolt/test/RISCV/call-relocation-pair.s
new file mode 100644
index 0000000000000..ea1c55d1ebcde
--- /dev/null
+++ b/bolt/test/RISCV/call-relocation-pair.s
@@ -0,0 +1,78 @@
+// 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
+// RELOCS: R_RISCV_CALL_PLT {{.*}} target_backward
+
+// 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
+// BOLT-NEXT: nop
+// BOLT-NEXT: call target_backward
+
+// 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-NEXT: nop
+// OBJDUMP-NEXT: jal {{.*}} <target_backward>
+// OBJDUMP-LABEL: <target_call>:
+// OBJDUMP-LABEL: <target_call_plt>:
+
+ .text
+ .option norvc
+ .option norelax
+
+ .globl target_backward
+ .type target_backward, at function
+target_backward:
+ ret
+ .size target_backward, .-target_backward
+
+ // Put _start more than one page after target_backward so the backwards call
+ // exercises signed high and low immediates.
+ .skip 0x1000
+
+ .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
+ .reloc ., R_RISCV_CALL_PLT, target_backward
+ 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-edge.s b/bolt/test/RISCV/unsymbolized-call-edge.s
new file mode 100644
index 0000000000000..8630147d2a803
--- /dev/null
+++ b/bolt/test/RISCV/unsymbolized-call-edge.s
@@ -0,0 +1,116 @@
+// Test signed target reconstruction, JALR target-bit clearing, tail calls,
+// and rejection of AUIPC/JALR near matches without relocations.
+
+// RUN: llvm-mc -triple riscv64 -mattr=-relax -filetype=obj -o %t.o %s
+// RUN: ld.lld --no-relax --emit-relocs -e backward_target -o %t %t.o
+// RUN: echo forward_target > %t.order
+// RUN: echo odd_call >> %t.order
+// RUN: echo near_matches >> %t.order
+// RUN: echo backward_tail >> %t.order
+// RUN: echo backward_target >> %t.order
+// RUN: echo relocated_call >> %t.order
+// RUN: llvm-bolt --print-cfg --print-fix-riscv-calls \
+// RUN: --print-only=backward_tail --print-only=odd_call \
+// RUN: --print-only=near_matches --reorder-functions=user \
+// RUN: --function-order=%t.order \
+// RUN: -o %t.bolt %t | FileCheck --check-prefix=BOLT %s
+// RUN: llvm-objdump -d %t.bolt | FileCheck --check-prefix=OBJDUMP %s
+// RUN: llvm-mc -triple riscv32 -mattr=-relax -filetype=obj -o %t.32.o %s
+// RUN: ld.lld --no-relax --emit-relocs -e backward_target -o %t.32 %t.32.o
+// RUN: llvm-bolt --print-cfg --print-only=odd_call -o %t.32.bolt %t.32 \
+// RUN: | FileCheck --check-prefix=RV32 %s
+
+// BOLT-LABEL: Binary Function "backward_tail" after building cfg {
+// BOLT: auipc t1, backward_target
+// BOLT-NEXT: jr {{.*}}(t1)
+// BOLT-LABEL: Binary Function "odd_call" after building cfg {
+// BOLT: auipc ra, forward_target
+// BOLT-NEXT: jalr {{.*}}(ra)
+// BOLT-LABEL: Binary Function "near_matches" after building cfg {
+// BOLT: auipc t0, 0x0
+// BOLT-NEXT: jalr t1
+// BOLT-NEXT: auipc t0, 0x0
+// BOLT-NEXT: jalr t0
+// BOLT-LABEL: Binary Function "backward_tail" after fix-riscv-calls {
+// BOLT: nop
+// BOLT-NEXT: tail backward_target
+// BOLT-LABEL: Binary Function "odd_call" after fix-riscv-calls {
+// BOLT: nop
+// BOLT-NEXT: call forward_target
+// BOLT-LABEL: Binary Function "near_matches" after fix-riscv-calls {
+// BOLT: auipc t0, 0x0
+// BOLT-NEXT: jalr t1
+// BOLT-NEXT: auipc t0, 0x0
+// BOLT-NEXT: jalr t0
+
+// RV32-LABEL: Binary Function "odd_call" after building cfg {
+// RV32: auipc ra, 0
+// RV32-NEXT: jalr 0x89(ra)
+
+// OBJDUMP-LABEL: <odd_call>:
+// OBJDUMP: jal {{.*}} <forward_target>
+// OBJDUMP-LABEL: <near_matches>:
+// OBJDUMP: auipc t0, 0x0
+// OBJDUMP-NEXT: jalr t1
+// OBJDUMP-NEXT: auipc t0, 0x0
+// OBJDUMP-NEXT: jalr t0
+// OBJDUMP-LABEL: <backward_tail>:
+// OBJDUMP: j {{.*}} <backward_target>
+
+ .text
+ .option norvc
+ .option norelax
+
+ .globl backward_target
+ .type backward_target, at function
+backward_target:
+ ret
+ .size backward_target, .-backward_target
+
+ .skip 0x1000
+
+ .globl backward_tail
+ .type backward_tail, at function
+backward_tail:
+ // backward_target is 0x1004 bytes before this AUIPC.
+ auipc t1, 0xfffff
+ jalr zero, -4(t1)
+ .size backward_tail, .-backward_tail
+
+ .globl odd_call
+ .type odd_call, at function
+odd_call:
+ // JALR clears bit zero, so 0x89 targets forward_target at offset 0x88.
+ auipc ra, 0
+ jalr ra, 0x89(ra)
+ ret
+ .size odd_call, .-odd_call
+
+ .skip 0x7c
+
+ .globl forward_target
+ .type forward_target, at function
+forward_target:
+ ret
+ .size forward_target, .-forward_target
+
+ .globl near_matches
+ .type near_matches, at function
+near_matches:
+ // The JALR base does not match the AUIPC destination.
+ auipc t0, 0
+ jalr ra, 0(t1)
+ // The JALR link register is neither zero nor the AUIPC destination.
+ auipc t0, 0
+ jalr ra, 0(t0)
+ ret
+ .size near_matches, .-near_matches
+
+ // Retain a relocation so BOLT can reorder the functions containing the
+ // linker-resolved instruction pairs above.
+ .globl relocated_call
+ .type relocated_call, at function
+relocated_call:
+ call forward_target
+ ret
+ .size relocated_call, .-relocated_call
diff --git a/bolt/test/RISCV/unsymbolized-call-entry.s b/bolt/test/RISCV/unsymbolized-call-entry.s
new file mode 100644
index 0000000000000..e7a8a2199d838
--- /dev/null
+++ b/bolt/test/RISCV/unsymbolized-call-entry.s
@@ -0,0 +1,60 @@
+// Test recovery of an RV64 linker-resolved call targeting an entry point
+// inside a function.
+
+// 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: echo target > %t.order
+// RUN: echo relocated_call >> %t.order
+// RUN: echo _start >> %t.order
+// RUN: llvm-bolt --print-cfg --print-fix-riscv-calls --print-only=_start \
+// RUN: --reorder-functions=user \
+// RUN: --function-order=%t.order \
+// 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 0x8c(ra) <target_entry>
+
+// BOLT-LABEL: Binary Function "_start" after building cfg {
+// BOLT: auipc ra, {{.*}}target_entry{{.*}}
+// BOLT-NEXT: jalr {{.*}}(ra)
+// BOLT-LABEL: Binary Function "_start" after fix-riscv-calls {
+// BOLT: call {{.*}}target_entry{{.*}}
+
+// OBJDUMP-LABEL: <target>:
+// OBJDUMP: addi a0, a0, {{(0x)?1}}
+// OBJDUMP-LABEL: <target_entry>:
+// OBJDUMP: ret
+// OBJDUMP-LABEL: <_start>:
+// OBJDUMP: jal {{.*}} <target_entry>
+
+ .text
+ .option norvc
+ .option norelax
+
+ .globl _start
+ .type _start, at function
+_start:
+ auipc ra, 0x200
+ jalr ra, 0x8c(ra)
+ ret
+ .size _start, .-_start
+
+ .skip (1 << 21) + 0x7c
+
+ .globl target
+ .type target, at function
+target:
+ addi a0, a0, 1
+target_entry:
+ 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/test/RISCV/unsymbolized-call.s b/bolt/test/RISCV/unsymbolized-call.s
new file mode 100644
index 0000000000000..555852a07b1fa
--- /dev/null
+++ b/bolt/test/RISCV/unsymbolized-call.s
@@ -0,0 +1,60 @@
+// 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: echo target > %t.order
+// RUN: echo relocated_call >> %t.order
+// RUN: echo _start >> %t.order
+// RUN: llvm-bolt --print-cfg --print-fix-riscv-calls --print-only=_start \
+// RUN: --reorder-functions=user \
+// RUN: --function-order=%t.order \
+// 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
More information about the llvm-commits
mailing list