[RISCV] Relax out of range Zibi conditional branches (#186965)

If `.Label` is not within +-4KiB range, we convert

```
beqi/bnei reg, imm, .Label
```

to

```
bnei/beqi reg, imm, 8
j .Label
```

This is similar to what is done for the RISCV conditional branches
and `Xqcibi` conditional branches.

---------

Co-authored-by: Sudharsan Veeravalli <svs@qti.qualcomm.com>
This commit is contained in:
Pengcheng Wang 2026-03-19 21:07:53 +08:00 committed by Douglas Yung
parent 94411c9d56
commit 8efb87ae5a
No known key found for this signature in database
GPG key ID: 316C56D064CACBA5
6 changed files with 145 additions and 16 deletions

View file

@ -196,6 +196,10 @@ static unsigned getRelaxedOpcode(unsigned Opcode, ArrayRef<MCOperand> Operands,
return RISCV::PseudoLongBEQ;
case RISCV::BNE:
return RISCV::PseudoLongBNE;
case RISCV::BEQI:
return RISCV::PseudoLongBEQI;
case RISCV::BNEI:
return RISCV::PseudoLongBNEI;
case RISCV::BLT:
return RISCV::PseudoLongBLT;
case RISCV::BGE:
@ -285,6 +289,8 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst,
}
case RISCV::BEQ:
case RISCV::BNE:
case RISCV::BEQI:
case RISCV::BNEI:
case RISCV::BLT:
case RISCV::BGE:
case RISCV::BLTU:

View file

@ -255,6 +255,10 @@ static unsigned getInvertedBranchOp(unsigned BrOp) {
return RISCV::BNE;
case RISCV::PseudoLongBNE:
return RISCV::BEQ;
case RISCV::PseudoLongBEQI:
return RISCV::BNEI;
case RISCV::PseudoLongBNEI:
return RISCV::BEQI;
case RISCV::PseudoLongBLT:
return RISCV::BGE;
case RISCV::PseudoLongBGE:
@ -297,14 +301,15 @@ void RISCVMCCodeEmitter::expandLongCondBr(const MCInst &MI,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
MCRegister SrcReg1 = MI.getOperand(0).getReg();
MCRegister SrcReg2 = MI.getOperand(1).getReg();
MCOperand SrcSymbol = MI.getOperand(2);
const MCOperand &Src2 = MI.getOperand(1);
const MCOperand &SrcSymbol = MI.getOperand(2);
unsigned Opcode = MI.getOpcode();
bool IsEqTest =
Opcode == RISCV::PseudoLongBNE || Opcode == RISCV::PseudoLongBEQ;
bool UseCompressedBr = false;
if (IsEqTest && STI.hasFeature(RISCV::FeatureStdExtZca)) {
MCRegister SrcReg2 = Src2.getReg();
if (RISCV::X8 <= SrcReg1.id() && SrcReg1.id() <= RISCV::X15 &&
SrcReg2.id() == RISCV::X0) {
UseCompressedBr = true;
@ -326,7 +331,7 @@ void RISCVMCCodeEmitter::expandLongCondBr(const MCInst &MI,
} else {
unsigned InvOpc = getInvertedBranchOp(Opcode);
MCInst TmpInst =
MCInstBuilder(InvOpc).addReg(SrcReg1).addReg(SrcReg2).addImm(8);
MCInstBuilder(InvOpc).addReg(SrcReg1).addOperand(Src2).addImm(8);
uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
support::endian::write(CB, Binary, llvm::endianness::little);
Offset = 4;
@ -429,6 +434,8 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
return;
case RISCV::PseudoLongBEQ:
case RISCV::PseudoLongBNE:
case RISCV::PseudoLongBEQI:
case RISCV::PseudoLongBNEI:
case RISCV::PseudoLongBLT:
case RISCV::PseudoLongBGE:
case RISCV::PseudoLongBLTU:

View file

@ -1744,6 +1744,20 @@ class LongBccPseudo : Pseudo<(outs),
let hasNoSchedulingInfo = 1;
}
class LongBcciPseudo<DAGOperand InTyImm, int size>
: Pseudo<(outs),
(ins GPR:$rs1, InTyImm:$imm, simm21_lsb0_jal:$imm20),
[]> {
let Size = size;
let isBarrier = 1;
let isBranch = 1;
let hasSideEffects = 0;
let mayStore = 0;
let mayLoad = 0;
let isAsmParserOnly = 1;
let hasNoSchedulingInfo = 1;
}
def PseudoLongBEQ : LongBccPseudo;
def PseudoLongBNE : LongBccPseudo;
def PseudoLongBLT : LongBccPseudo;

View file

@ -1335,19 +1335,6 @@ let EmitPriority = 1 in {
// Pseudo-instructions
//===----------------------------------------------------------------------===//
class LongBcciPseudo<DAGOperand InTyImm, int size>
: Pseudo<(outs), (ins GPR:$rs1, InTyImm:$imm, simm21_lsb0_jal:$imm20), []>
{
let Size = size;
let isBarrier = 1;
let isBranch = 1;
let hasSideEffects = 0;
let mayStore = 0;
let mayLoad = 0;
let isAsmParserOnly = 1;
let hasNoSchedulingInfo = 1;
}
// This will be expanded into either:
// QC.BXXX(4 bytes) + JAL(4 bytes)
// or

View file

@ -41,6 +41,11 @@ class Branch_imm<bits<3> funct3, string opcodestr>
let Predicates = [HasStdExtZibi] in {
def BEQI : Branch_imm<0b010, "beqi">;
def BNEI : Branch_imm<0b011, "bnei">;
// For long branch relaxation.
// This will be expanded into BEQI/BNEI(4 bytes) + JAL(4 bytes).
def PseudoLongBEQI : LongBcciPseudo<imm5_zibi, 8>;
def PseudoLongBNEI : LongBcciPseudo<imm5_zibi, 8>;
} // Predicates = [HasStdExtZibi]
multiclass BccImmPat<CondCode Cond, Branch_imm Inst> {

View file

@ -0,0 +1,110 @@
# RUN: llvm-mc -filetype=obj --mattr=+experimental-zibi -triple=riscv32 %s \
# RUN: | llvm-objdump --mattr=+experimental-zibi -d -M no-aliases - \
# RUN: | FileCheck --check-prefix=CHECK-INST %s
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax,+experimental-zibi %s \
# RUN: | llvm-objdump --mattr=+experimental-zibi -dr -M no-aliases - \
# RUN: | FileCheck --check-prefix=CHECK-INST-RELAX %s
# Tests that are forward in range.
# CHECK-INST-LABEL: beqi_in_range_forward
# CHECK-INST: beqi a0, 0xa, {{.*}}
# CHECK-INST-RELAX-LABEL: beqi_in_range_forward
# CHECK-INST-RELAX: beqi a0, 0xa, {{.*}}
beqi_in_range_forward:
beqi a0, 10, .L1
.fill 1000, 4, 0
.L1:
ret
# CHECK-INST-LABEL: bnei_in_range_forward
# CHECK-INST: bnei a0, 0xa, {{.*}}
# CHECK-INST-RELAX-LABEL: bnei_in_range_forward
# CHECK-INST-RELAX: bnei a0, 0xa, {{.*}}
bnei_in_range_forward:
bnei a0, 10, .L2
.fill 1000, 4, 0
.L2:
ret
# Tests that are backward in range.
# CHECK-INST-LABEL: beqi_in_range_backward
# CHECK-INST: beqi a0, 0xa, {{.*}}
# CHECK-INST-RELAX-LABEL: beqi_in_range_backward
# CHECK-INST-RELAX: beqi a0, 0xa, {{.*}}
beqi_in_range_backward:
.L3:
.fill 1000, 4, 0
beqi a0, 10, .L3
ret
# CHECK-INST-LABEL: bnei_in_range_backward
# CHECK-INST: bnei a0, 0xa, {{.*}}
# CHECK-INST-RELAX-LABEL: bnei_in_range_backward
# CHECK-INST-RELAX: bnei a0, 0xa, {{.*}}
bnei_in_range_backward:
.L4:
.fill 1000, 4, 0
bnei a0, 10, .L4
ret
# Tests that are forward out of range.
# CHECK-INST-LABEL: beqi_out_of_range_forward
# CHECK-INST: bnei a0, 0xa, {{.*}}+0x8
# CHECK-INST-NEXT: jal zero, {{.*}}
# CHECK-INST-RELAX-LABEL: beqi_out_of_range_forward
# CHECK-INST-RELAX: bnei a0, 0xa, {{.*}}+0x8
# CHECK-INST-RELAX-NEXT: jal zero, {{.*}}
beqi_out_of_range_forward:
beqi a0, 10, .L5
.fill 1300, 4, 0
.L5:
ret
# CHECK-INST-LABEL: bnei_out_of_range_forward
# CHECK-INST: beqi a0, 0xa, {{.*}}+0x8
# CHECK-INST-NEXT: jal zero, {{.*}}
# CHECK-INST-RELAX-LABEL: bnei_out_of_range_forward
# CHECK-INST-RELAX: beqi a0, 0xa, {{.*}}+0x8
# CHECK-INST-RELAX-NEXT: jal zero, {{.*}}
bnei_out_of_range_forward:
bnei a0, 10, .L6
.fill 1300, 4, 0
.L6:
ret
# Tests that are backward out of range.
# CHECK-INST-LABEL: beqi_out_of_range_backward
# CHECK-INST: bnei a0, 0xa, {{.*}}+0x1458
# CHECK-INST-NEXT: jal zero, {{.*}}
# CHECK-INST-RELAX-LABEL: beqi_out_of_range_backward
# CHECK-INST-RELAX: bnei a0, 0xa, {{.*}}+0x1458
# CHECK-INST-RELAX-NEXT: jal zero, {{.*}}
beqi_out_of_range_backward:
.L7:
.fill 1300, 4, 0
beqi a0, 10, .L7
ret
# CHECK-INST-LABEL: bnei_out_of_range_backward
# CHECK-INST: beqi a0, 0xa, {{.*}}+0x1458
# CHECK-INST-NEXT: jal zero, {{.*}}
# CHECK-INST-RELAX-LABEL: bnei_out_of_range_backward
# CHECK-INST-RELAX: beqi a0, 0xa, {{.*}}+0x1458
# CHECK-INST-RELAX-NEXT: jal zero, {{.*}}
bnei_out_of_range_backward:
.L8:
.fill 1300, 4, 0
bnei a0, 10, .L8
ret