[llvm] [BOLT][RISCV] Implement indirect PLT calls (PR #219184)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 04:45:37 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-bolt
Author: Thrrreeee (Thrrreeee)
<details>
<summary>Changes</summary>
This patch implements `MCPlusBuilder::createIndirectPLTCall` for RISC-V, enabling BOLT's `--plt=hot` and `--plt=all` optimizations for RISC-V binaries.
The PLT call pass replaces direct calls and tail calls to PLT entries with indirect calls through the corresponding resolved GOT slot. The generated sequence is:
auipc t3, %pcrel_hi(target@<!-- -->GOT)
l[dw] t3, %pcrel_lo(.Lpcrel_hi)(t3)
jalr ra, t3, 0
---
Full diff: https://github.com/llvm/llvm-project/pull/219184.diff
2 Files Affected:
- (modified) bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp (+53)
- (added) bolt/test/RISCV/plt-call.test (+44)
``````````diff
diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index 2388505ffd8ed..4c4b934b92d48 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -306,6 +306,59 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
return createCall(RISCV::PseudoTAIL, Inst, Target, Ctx);
}
+ InstructionListType createIndirectPLTCall(MCInst &&DirectCall,
+ const MCSymbol *TargetLocation,
+ MCContext *Ctx) override {
+ const bool IsTailCall = isTailCall(DirectCall);
+ assert(((DirectCall.getOpcode() == RISCV::PseudoCALL && !IsTailCall) ||
+ (DirectCall.getOpcode() == RISCV::PseudoTAIL && IsTailCall)) &&
+ "RISC-V direct (tail) call instruction expected");
+
+ // Load the resolved function address directly from its GOT slot:
+ //
+ // auipc t3, %pcrel_hi(TargetLocation)
+ // l[dw] t3, %pcrel_lo(.Lpcrel_hi)(t3)
+ // jalr ra, t3, 0
+ //
+ // A tail call uses zero instead of ra as the JALR destination.
+ InstructionListType Code;
+ // Use t3 (x28), the scratch register used by linker-generated RISC-V
+ // PLT/IPLT entries. It is caller-saved, is not an argument register, and
+ // the original call through the PLT already clobbers it.
+ const MCPhysReg PLTScratchReg = RISCV::X28;
+ MCSymbol *AUIPCLabel = Ctx->createNamedTempSymbol("pcrel_hi");
+
+ MCInst InstAUIPC =
+ MCInstBuilder(RISCV::AUIPC).addReg(PLTScratchReg).addImm(0);
+ // TargetLocation is already registered at the existing GOT slot, so use a
+ // direct PC-relative relocation to that slot instead of R_RISCV_GOT_HI20,
+ // which is used when starting from the referenced function symbol.
+ setOperandToSymbolRef(InstAUIPC, /*OpNum=*/1, TargetLocation,
+ /*Addend=*/0, Ctx, ELF::R_RISCV_PCREL_HI20);
+ setInstLabel(InstAUIPC, AUIPCLabel);
+ Code.emplace_back(std::move(InstAUIPC));
+
+ // Load the call target from the GOT slot using LD on RV64 or LW on RV32.
+ MCInst InstLoad = MCInstBuilder(loadOpc())
+ .addReg(PLTScratchReg)
+ .addReg(PLTScratchReg)
+ .addImm(0);
+ // Pair the I-type LD/LW immediate with the label on AUIPC. RISC-V
+ // R_RISCV_PCREL_LO12_I relocations name the corresponding HI20 location.
+ setOperandToSymbolRef(InstLoad, /*OpNum=*/2, AUIPCLabel,
+ /*Addend=*/0, Ctx, ELF::R_RISCV_PCREL_LO12_I);
+ Code.emplace_back(std::move(InstLoad));
+
+ MCInst InstCall = MCInstBuilder(RISCV::JALR)
+ .addReg(IsTailCall ? RISCV::X0 : RISCV::X1)
+ .addReg(PLTScratchReg)
+ .addImm(0);
+ moveAnnotations(std::move(DirectCall), InstCall);
+ Code.emplace_back(std::move(InstCall));
+
+ return Code;
+ }
+
bool analyzeBranch(InstructionIterator Begin, InstructionIterator End,
const MCSymbol *&TBB, const MCSymbol *&FBB,
MCInst *&CondBranch,
diff --git a/bolt/test/RISCV/plt-call.test b/bolt/test/RISCV/plt-call.test
new file mode 100644
index 0000000000000..76f850e892e59
--- /dev/null
+++ b/bolt/test/RISCV/plt-call.test
@@ -0,0 +1,44 @@
+// Verify that PLTCall optimization works on RISC-V.
+
+// RUN: split-file %s %t.dir
+// RUN: llvm-mc -triple=riscv64 -filetype=obj -o %t.dir/main.o %t.dir/main.s
+// RUN: llvm-mc -triple=riscv64 -filetype=obj -o %t.dir/lib.o %t.dir/lib.s
+// RUN: ld.lld -shared -soname libplt.so -o %t.dir/libplt.so %t.dir/lib.o
+// RUN: ld.lld --no-pie --emit-relocs -z now \
+// RUN: -dynamic-linker /lib/ld.so.1 %t.dir/main.o %t.dir/libplt.so \
+// RUN: -o %t.exe
+// RUN: llvm-bolt %t.exe -o %t.bolt --plt=all --print-plt \
+// RUN: --print-only=_start | FileCheck %s
+
+// Call to foo.
+// CHECK: auipc t3, %pcrel_hi(foo at GOT)
+// CHECK-NEXT: ld t3, %pcrel_lo({{.*}})(t3)
+// CHECK-NEXT: jalr t3 # PLTCall: 1
+
+// Tail call to bar.
+// CHECK: auipc t3, %pcrel_hi(bar at GOT)
+// CHECK-NEXT: ld t3, %pcrel_lo({{.*}})(t3)
+// CHECK-NEXT: jr t3 # TAILCALL # PLTCall: 1
+
+//--- main.s
+ .text
+ .globl _start
+ .type _start, @function
+_start:
+ call foo
+ tail bar
+ .size _start, .-_start
+
+//--- lib.s
+ .text
+ .globl foo
+ .type foo, @function
+foo:
+ ret
+ .size foo, .-foo
+
+ .globl bar
+ .type bar, @function
+bar:
+ ret
+ .size bar, .-bar
``````````
</details>
https://github.com/llvm/llvm-project/pull/219184
More information about the llvm-commits
mailing list