[llvm] [BOLT][RISCV] Fix conditional tail call (PR #209474)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 06:21:41 PDT 2026


https://github.com/Thrrreeee created https://github.com/llvm/llvm-project/pull/209474

When BOLT encounters an external-reference branch, `BinaryFunction::handleExternalReference()` first calls convertJmpToTailCall(). The RISC-V implementation previously bailed out with unsupported tail call opcode, because it only accepted `JAL`, `JALR`, `C_J`, and `C_JR`. As a result, conditional branches targeting external symbols could not be converted into (conditional) tail calls.
This patch adds the RISC-V backend to cooperate with BOLT's existing conditional-tail-call machinery.

PR [#160042](https://github.com/llvm/llvm-project/pull/160042) explicitly adds every conditional branch opcode to `convertJmpToTailCall().` That causes the instruction to first receive the regular kTailCall annotation, before `handleExternalReference()` subsequently overrides it with `kConditionalTailCall`.

Added regression tests covering conditional-tail-call conversion for all base and compressed RISC-V conditional branches (`BEQ`, `BNE`, `BLT`, `BGE`, `BLTU`, `BGEU`, `C_BEQZ`, `C_BNEZ`).

>From aaa119bb45a5aabf9905b56694105e150eb033d6 Mon Sep 17 00:00:00 2001
From: Thrrreeeee <1379998393 at qq.com>
Date: Tue, 14 Jul 2026 20:58:30 +0800
Subject: [PATCH] [BOLT][RISCV] Fix conditional tail call

---
 bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp | 12 +++-
 bolt/test/RISCV/conditional-tail-call.s      | 66 ++++++++++++++++++++
 2 files changed, 77 insertions(+), 1 deletion(-)
 create mode 100644 bolt/test/RISCV/conditional-tail-call.s

diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index d1a0572277874..a1461645409a3 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -215,7 +215,7 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
 
     switch (Inst.getOpcode()) {
     default:
-      llvm_unreachable("unsupported tail call opcode");
+      return false;
     case RISCV::JAL:
     case RISCV::JALR:
     case RISCV::C_J:
@@ -227,6 +227,14 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
     return true;
   }
 
+  bool convertTailCallToJmp(MCInst &Inst) override {
+    removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
+    clearOffset(Inst);
+    if (getConditionalTailCall(Inst))
+      unsetConditionalTailCall(Inst);
+    return true;
+  }
+
   void createReturn(MCInst &Inst) const override {
     // TODO "c.jr ra" when RVC is enabled
     Inst.setOpcode(RISCV::JALR);
@@ -328,6 +336,8 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
     default:
       return false;
     case RISCV::C_J:
+    case RISCV::PseudoCALL:
+    case RISCV::PseudoTAIL:
       OpNum = 0;
       return true;
     case RISCV::AUIPC:
diff --git a/bolt/test/RISCV/conditional-tail-call.s b/bolt/test/RISCV/conditional-tail-call.s
new file mode 100644
index 0000000000000..4a7c6e84ca4a4
--- /dev/null
+++ b/bolt/test/RISCV/conditional-tail-call.s
@@ -0,0 +1,66 @@
+// Check that all base and compressed RISC-V conditional branches targeting
+// another function are expanded to tail-call blocks, survive block reordering,
+// and are emitted with the correct target.
+
+// RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj -o %t.o %s
+// RUN: ld.lld -o %t %t.o
+// RUN: llvm-bolt %t -o %t.bolt --reorder-blocks=reverse --print-cfg \
+// RUN:   --print-only=conditional_tail_calls 2>&1 | FileCheck %s --check-prefix=CFG
+// RUN: llvm-objdump -d --disassemble-symbols=conditional_tail_calls %t.bolt \
+// RUN:   | FileCheck %s --check-prefix=DISASM
+
+// CFG-LABEL: Binary Function "conditional_tail_calls" after building cfg {
+// CFG:      beq a0, a1, .LTC0
+// CFG:      bne a0, a1, .LTC1
+// CFG:      blt a0, a1, .LTC2
+// CFG:      bge a0, a1, .LTC3
+// CFG:      bltu a0, a1, .LTC4
+// CFG:      bgeu a0, a1, .LTC5
+// CFG:      beqz a0, .LTC6
+// CFG:      bnez a0, .LTC7
+// CFG-COUNT-8: tail callee
+// CFG: BOLT-INFO: basic block reordering modified layout of 1 functions
+
+// DISASM-LABEL: <conditional_tail_calls>:
+// DISASM-NEXT: {{.*}} beq a0, a1, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} bne a0, a1, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} blt a0, a1, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} bge a0, a1, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} bltu a0, a1, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} bgeu a0, a1, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} beqz a0, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} bnez a0, {{.*}} <callee>
+// DISASM-NEXT: {{.*}} ret
+
+  .text
+  .option rvc
+
+  .globl conditional_tail_calls
+  .type conditional_tail_calls, @function
+  .p2align 1
+conditional_tail_calls:
+  beq a0, a1, .Lcallee
+  bne a0, a1, .Lcallee
+  blt a0, a1, .Lcallee
+  bge a0, a1, .Lcallee
+  bltu a0, a1, .Lcallee
+  bgeu a0, a1, .Lcallee
+  c.beqz a0, .Lcallee
+  c.bnez a0, .Lcallee
+  ret
+  .size conditional_tail_calls, .-conditional_tail_calls
+
+  .globl callee
+  .type callee, @function
+  .p2align 1
+callee:
+.Lcallee:
+  ret
+  .size callee, .-callee
+
+  .globl _start
+  .type _start, @function
+  .p2align 1
+_start:
+  ret
+  .size _start, .-_start



More information about the llvm-commits mailing list