mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
[BOLT][RISCV] Handle long tail calls (#67098)
Long tail calls use the following instruction sequence on RISC-V: ``` 1: auipc xi, %pcrel_hi(sym) jalr zero, %pcrel_lo(1b)(xi) ``` Since the second instruction in isolation looks like an indirect branch, this confused BOLT and most functions containing a long tail call got marked with "unknown control flow" and didn't get optimized as a consequence. This patch fixes this by detecting long tail call sequence in `analyzeIndirectBranch`. `FixRISCVCallsPass` also had to be updated to expand long tail calls to `PseudoTAIL` instead of `PseudoCALL`. Besides this, this patch also fixes a minor issue with compressed tail calls (`c.jr`) not being detected. Note that I had to change `BinaryFunction::postProcessIndirectBranches` slightly: the documentation of `MCPlusBuilder::analyzeIndirectBranch` mentions that the [`Begin`, `End`) range contains the instructions immediately preceding `Instruction`. However, in `postProcessIndirectBranches`, *all* the instructions in the BB where passed in the range. This made it difficult to find the preceding instruction so I made sure *only* the preceding instructions are passed.
This commit is contained in:
parent
d20fbc9007
commit
7fa33773e3
@ -1761,7 +1761,8 @@ bool BinaryFunction::postProcessIndirectBranches(
|
||||
uint64_t LastJT = 0;
|
||||
uint16_t LastJTIndexReg = BC.MIB->getNoRegister();
|
||||
for (BinaryBasicBlock &BB : blocks()) {
|
||||
for (MCInst &Instr : BB) {
|
||||
for (BinaryBasicBlock::iterator II = BB.begin(); II != BB.end(); ++II) {
|
||||
MCInst &Instr = *II;
|
||||
if (!BC.MIB->isIndirectBranch(Instr))
|
||||
continue;
|
||||
|
||||
@ -1789,7 +1790,7 @@ bool BinaryFunction::postProcessIndirectBranches(
|
||||
const MCExpr *DispExpr;
|
||||
MCInst *PCRelBaseInstr;
|
||||
IndirectBranchType Type = BC.MIB->analyzeIndirectBranch(
|
||||
Instr, BB.begin(), BB.end(), PtrSize, MemLocInstr, BaseRegNum,
|
||||
Instr, BB.begin(), II, PtrSize, MemLocInstr, BaseRegNum,
|
||||
IndexRegNum, DispValue, DispExpr, PCRelBaseInstr);
|
||||
if (Type != IndirectBranchType::UNKNOWN || MemLocInstr != nullptr)
|
||||
continue;
|
||||
|
@ -43,7 +43,12 @@ void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) {
|
||||
|
||||
MCInst OldCall = *NextII;
|
||||
auto L = BC.scopeLock();
|
||||
MIB->createCall(*II, Target, Ctx);
|
||||
|
||||
if (MIB->isTailCall(*NextII))
|
||||
MIB->createTailCall(*II, Target, Ctx);
|
||||
else
|
||||
MIB->createCall(*II, Target, Ctx);
|
||||
|
||||
MIB->moveAnnotations(std::move(OldCall), *II);
|
||||
|
||||
// The original offset was set on the jalr of the auipc+jalr pair. Since
|
||||
|
@ -87,6 +87,7 @@ public:
|
||||
return false;
|
||||
case RISCV::JALR:
|
||||
case RISCV::C_JALR:
|
||||
case RISCV::C_JR:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -158,6 +159,17 @@ public:
|
||||
DispValue = 0;
|
||||
DispExpr = nullptr;
|
||||
PCRelBaseOut = nullptr;
|
||||
|
||||
// Check for the following long tail call sequence:
|
||||
// 1: auipc xi, %pcrel_hi(sym)
|
||||
// jalr zero, %pcrel_lo(1b)(xi)
|
||||
if (Instruction.getOpcode() == RISCV::JALR && Begin != End) {
|
||||
MCInst &PrevInst = *std::prev(End);
|
||||
if (isRISCVCall(PrevInst, Instruction) &&
|
||||
Instruction.getOperand(0).getReg() == RISCV::X0)
|
||||
return IndirectBranchType::POSSIBLE_TAIL_CALL;
|
||||
}
|
||||
|
||||
return IndirectBranchType::UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
// RUN: llvm-mc -triple riscv64 -filetype obj -o %t.o %s
|
||||
// RUN: ld.lld --emit-relocs -o %t %t.o
|
||||
// RUN: llvm-bolt --enable-bat --print-cfg --print-fix-riscv-calls \
|
||||
// RUN: --print-only=_start -o /dev/null %t | FileCheck %s
|
||||
// RUN: -o /dev/null %t | FileCheck %s
|
||||
|
||||
.text
|
||||
.global f
|
||||
@ -19,11 +19,24 @@ f:
|
||||
// CHECK-NEXT: jal ra, f # Offset: 8
|
||||
// CHECK-NEXT: jal zero, f # TAILCALL # Offset: 12
|
||||
|
||||
// CHECK-LABEL: Binary Function "long_tail" after building cfg {
|
||||
// CHECK: auipc t1, f
|
||||
// CHECK-NEXT: jalr zero, -24(t1) # TAILCALL # Offset: 8
|
||||
|
||||
// CHECK-LABEL: Binary Function "compressed_tail" after building cfg {
|
||||
// CHECK: jr a0 # TAILCALL # Offset: 0
|
||||
|
||||
// CHECK-LABEL: Binary Function "_start" after fix-riscv-calls {
|
||||
// CHECK: call f # Offset: 0
|
||||
// CHECK-NEXT: call f # Offset: 8
|
||||
// CHECK-NEXT: tail f # TAILCALL # Offset: 12
|
||||
|
||||
// CHECK-LABEL: Binary Function "long_tail" after fix-riscv-calls {
|
||||
// CHECK: tail f # TAILCALL # Offset: 4
|
||||
|
||||
// CHECK-LABEL: Binary Function "compressed_tail" after fix-riscv-calls {
|
||||
// CHECK: jr a0 # TAILCALL # Offset: 0
|
||||
|
||||
.globl _start
|
||||
.p2align 1
|
||||
_start:
|
||||
@ -31,3 +44,21 @@ _start:
|
||||
jal f
|
||||
jal zero, f
|
||||
.size _start, .-_start
|
||||
|
||||
.globl long_tail
|
||||
.p2align 1
|
||||
long_tail:
|
||||
// NOTE: BOLT assumes indirect calls in single-BB functions are tail calls
|
||||
// so artificially introduce a second BB to force RISC-V-specific analysis
|
||||
// to get triggered.
|
||||
beq a0, a1, 1f
|
||||
1:
|
||||
tail f
|
||||
.size long_tail, .-long_tail
|
||||
|
||||
.globl compressed_tail
|
||||
.p2align 1
|
||||
.option rvc
|
||||
compressed_tail:
|
||||
c.jr a0
|
||||
.size compressed_tail, .-compressed_tail
|
||||
|
Loading…
Reference in New Issue
Block a user