Skip to content

Commit

Permalink
[RISCV] Implement support for the BranchRelaxation pass
Browse files Browse the repository at this point in the history
Branch relaxation is needed to support branch displacements that overflow the
instruction's immediate field.

Differential Revision: https://reviews.llvm.org/D40830

llvm-svn: 322224
  • Loading branch information
asb committed Jan 10, 2018
1 parent 725ad0e commit 315cd3a
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 40 deletions.
115 changes: 110 additions & 5 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
Expand Up @@ -20,6 +20,7 @@
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TargetRegistry.h"

Expand Down Expand Up @@ -199,7 +200,8 @@ bool RISCVInstrInfo::analyzeBranch(MachineBasicBlock &MBB,

unsigned RISCVInstrInfo::removeBranch(MachineBasicBlock &MBB,
int *BytesRemoved) const {
assert(!BytesRemoved && "Code size not handled");
if (BytesRemoved)
*BytesRemoved = 0;
MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr();
if (I == MBB.end())
return 0;
Expand All @@ -210,6 +212,8 @@ unsigned RISCVInstrInfo::removeBranch(MachineBasicBlock &MBB,

// Remove the branch.
I->eraseFromParent();
if (BytesRemoved)
*BytesRemoved += getInstSizeInBytes(*I);

I = MBB.end();

Expand All @@ -221,6 +225,8 @@ unsigned RISCVInstrInfo::removeBranch(MachineBasicBlock &MBB,

// Remove the branch.
I->eraseFromParent();
if (BytesRemoved)
*BytesRemoved += getInstSizeInBytes(*I);
return 2;
}

Expand All @@ -229,7 +235,8 @@ unsigned RISCVInstrInfo::removeBranch(MachineBasicBlock &MBB,
unsigned RISCVInstrInfo::insertBranch(
MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
assert(!BytesAdded && "Code size not handled.");
if (BytesAdded)
*BytesAdded = 0;

// Shouldn't be a fall through.
assert(TBB && "InsertBranch must not be told to insert a fallthrough");
Expand All @@ -238,26 +245,124 @@ unsigned RISCVInstrInfo::insertBranch(

// Unconditional branch.
if (Cond.empty()) {
BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(TBB);
MachineInstr &MI = *BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(TBB);
if (BytesAdded)
*BytesAdded += getInstSizeInBytes(MI);
return 1;
}

// Either a one or two-way conditional branch.
unsigned Opc = Cond[0].getImm();
BuildMI(&MBB, DL, get(Opc)).add(Cond[1]).add(Cond[2]).addMBB(TBB);
MachineInstr &CondMI =
*BuildMI(&MBB, DL, get(Opc)).add(Cond[1]).add(Cond[2]).addMBB(TBB);
if (BytesAdded)
*BytesAdded += getInstSizeInBytes(CondMI);

// One-way conditional branch.
if (!FBB)
return 1;

// Two-way conditional branch.
BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(FBB);
MachineInstr &MI = *BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(FBB);
if (BytesAdded)
*BytesAdded += getInstSizeInBytes(MI);
return 2;
}

unsigned RISCVInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
MachineBasicBlock &DestBB,
const DebugLoc &DL,
int64_t BrOffset,
RegScavenger *RS) const {
assert(RS && "RegScavenger required for long branching");
assert(MBB.empty() &&
"new block should be inserted for expanding unconditional branch");
assert(MBB.pred_size() == 1);

MachineFunction *MF = MBB.getParent();
MachineRegisterInfo &MRI = MF->getRegInfo();
const auto &TM = static_cast<const RISCVTargetMachine &>(MF->getTarget());
const auto &STI = MF->getSubtarget<RISCVSubtarget>();

if (TM.isPositionIndependent() || STI.is64Bit())
report_fatal_error("Unable to insert indirect branch");

if (!isInt<32>(BrOffset))
report_fatal_error(
"Branch offsets outside of the signed 32-bit range not supported");

// FIXME: A virtual register must be used initially, as the register
// scavenger won't work with empty blocks (SIInstrInfo::insertIndirectBranch
// uses the same workaround).
unsigned ScratchReg = MRI.createVirtualRegister(&RISCV::GPRRegClass);
auto II = MBB.end();

MachineInstr &LuiMI = *BuildMI(MBB, II, DL, get(RISCV::LUI), ScratchReg)
.addMBB(&DestBB, RISCVII::MO_HI);
BuildMI(MBB, II, DL, get(RISCV::PseudoBRIND))
.addReg(ScratchReg, RegState::Kill)
.addMBB(&DestBB, RISCVII::MO_LO);

RS->enterBasicBlockEnd(MBB);
unsigned Scav = RS->scavengeRegisterBackwards(
RISCV::GPRRegClass, MachineBasicBlock::iterator(LuiMI), false, 0);
MRI.replaceRegWith(ScratchReg, Scav);
MRI.clearVirtRegs();
RS->setRegUsed(Scav);
return 8;
}

bool RISCVInstrInfo::reverseBranchCondition(
SmallVectorImpl<MachineOperand> &Cond) const {
assert((Cond.size() == 3) && "Invalid branch condition!");
Cond[0].setImm(getOppositeBranchOpcode(Cond[0].getImm()));
return false;
}

MachineBasicBlock *
RISCVInstrInfo::getBranchDestBlock(const MachineInstr &MI) const {
assert(MI.getDesc().isBranch() && "Unexpected opcode!");
// The branch target is always the last operand.
int NumOp = MI.getNumExplicitOperands();
return MI.getOperand(NumOp - 1).getMBB();
}

bool RISCVInstrInfo::isBranchOffsetInRange(unsigned BranchOp,
int64_t BrOffset) const {
// Ideally we could determine the supported branch offset from the
// RISCVII::FormMask, but this can't be used for Pseudo instructions like
// PseudoBR.
switch (BranchOp) {
default:
llvm_unreachable("Unexpected opcode!");
case RISCV::BEQ:
case RISCV::BNE:
case RISCV::BLT:
case RISCV::BGE:
case RISCV::BLTU:
case RISCV::BGEU:
return isIntN(13, BrOffset);
case RISCV::JAL:
case RISCV::PseudoBR:
return isIntN(21, BrOffset);
}
}

unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
unsigned Opcode = MI.getOpcode();

switch (Opcode) {
default: { return get(Opcode).getSize(); }
case TargetOpcode::EH_LABEL:
case TargetOpcode::IMPLICIT_DEF:
case TargetOpcode::KILL:
case TargetOpcode::DBG_VALUE:
return 0;
case TargetOpcode::INLINEASM: {
const MachineFunction &MF = *MI.getParent()->getParent();
const auto &TM = static_cast<const RISCVTargetMachine &>(MF.getTarget());
return getInlineAsmLength(MI.getOperand(0).getSymbolName(),
*TM.getMCAsmInfo());
}
}
}
12 changes: 12 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.h
Expand Up @@ -47,6 +47,8 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
const DebugLoc &DL, unsigned DstReg, uint64_t Val,
MachineInstr::MIFlag Flag = MachineInstr::NoFlags) const;

unsigned getInstSizeInBytes(const MachineInstr &MI) const override;

bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
SmallVectorImpl<MachineOperand> &Cond,
Expand All @@ -57,11 +59,21 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
const DebugLoc &dl,
int *BytesAdded = nullptr) const override;

unsigned insertIndirectBranch(MachineBasicBlock &MBB,
MachineBasicBlock &NewDestBB,
const DebugLoc &DL, int64_t BrOffset,
RegScavenger *RS = nullptr) const override;

unsigned removeBranch(MachineBasicBlock &MBB,
int *BytesRemoved = nullptr) const override;

bool
reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override;

MachineBasicBlock *getBranchDestBlock(const MachineInstr &MI) const override;

bool isBranchOffsetInRange(unsigned BranchOpc,
int64_t BrOffset) const override;
};
}
#endif
8 changes: 4 additions & 4 deletions llvm/lib/Target/RISCV/RISCVMCInstLower.cpp
Expand Up @@ -48,11 +48,12 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
const MCExpr *ME =
MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx);

if (!MO.isJTI() && MO.getOffset())
if (!MO.isJTI() && !MO.isMBB() && MO.getOffset())
ME = MCBinaryExpr::createAdd(
ME, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);

ME = RISCVMCExpr::create(ME, Kind, Ctx);
if (Kind != RISCVMCExpr::VK_RISCV_None)
ME = RISCVMCExpr::create(ME, Kind, Ctx);
return MCOperand::createExpr(ME);
}

Expand All @@ -75,8 +76,7 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO,
MCOp = MCOperand::createImm(MO.getImm());
break;
case MachineOperand::MO_MachineBasicBlock:
MCOp = MCOperand::createExpr(
MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), AP.OutContext));
MCOp = lowerSymbolOperand(MO, MO.getMBB()->getSymbol(), AP);
break;
case MachineOperand::MO_GlobalAddress:
MCOp = lowerSymbolOperand(MO, AP.getSymbol(MO.getGlobal()), AP);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/RISCV/RISCVRegisterInfo.h
Expand Up @@ -47,6 +47,10 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
bool requiresFrameIndexScavenging(const MachineFunction &MF) const override {
return true;
}

bool trackLivenessAfterRegAlloc(const MachineFunction &) const override {
return true;
}
};
}

Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
Expand Up @@ -75,6 +75,7 @@ class RISCVPassConfig : public TargetPassConfig {
}

bool addInstSelector() override;
void addPreEmitPass() override;
};
}

Expand All @@ -87,3 +88,5 @@ bool RISCVPassConfig::addInstSelector() {

return false;
}

void RISCVPassConfig::addPreEmitPass() { addPass(&BranchRelaxationPassID); }
6 changes: 3 additions & 3 deletions llvm/test/CodeGen/RISCV/analyze-branch.ll
Expand Up @@ -58,8 +58,8 @@ define void @test_bcc_fallthrough_nottaken(i32 %in) nounwind {
; RV32I-NEXT: sw s0, 8(sp)
; RV32I-NEXT: addi s0, sp, 16
; RV32I-NEXT: addi a1, zero, 42
; RV32I-NEXT: beq a0, a1, .LBB1_1
; RV32I-NEXT: # %bb.3: # %false
; RV32I-NEXT: beq a0, a1, .LBB1_3
; RV32I-NEXT: # %bb.1: # %false
; RV32I-NEXT: lui a0, %hi(test_false)
; RV32I-NEXT: addi a0, a0, %lo(test_false)
; RV32I-NEXT: .LBB1_2: # %true
Expand All @@ -68,7 +68,7 @@ define void @test_bcc_fallthrough_nottaken(i32 %in) nounwind {
; RV32I-NEXT: lw ra, 12(sp)
; RV32I-NEXT: addi sp, sp, 16
; RV32I-NEXT: ret
; RV32I-NEXT: .LBB1_1: # %true
; RV32I-NEXT: .LBB1_3: # %true
; RV32I-NEXT: lui a0, %hi(test_true)
; RV32I-NEXT: addi a0, a0, %lo(test_true)
; RV32I-NEXT: j .LBB1_2
Expand Down
79 changes: 79 additions & 0 deletions llvm/test/CodeGen/RISCV/branch-relaxation.ll
@@ -0,0 +1,79 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -filetype=obj < %s \
; RUN: -o /dev/null 2>&1
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck %s

define void @relax_bcc(i1 %a) {
; CHECK-LABEL: relax_bcc:
; CHECK: # %bb.0:
; CHECK-NEXT: addi sp, sp, -16
; CHECK-NEXT: sw ra, 12(sp)
; CHECK-NEXT: sw s0, 8(sp)
; CHECK-NEXT: addi s0, sp, 16
; CHECK-NEXT: andi a0, a0, 1
; CHECK-NEXT: bnez a0, .LBB0_1
; CHECK-NEXT: j .LBB0_2
; CHECK-NEXT: .LBB0_1: # %iftrue
; CHECK-NEXT: #APP
; CHECK-NEXT: .space 4096
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: .LBB0_2: # %tail
; CHECK-NEXT: lw s0, 8(sp)
; CHECK-NEXT: lw ra, 12(sp)
; CHECK-NEXT: addi sp, sp, 16
; CHECK-NEXT: ret
br i1 %a, label %iftrue, label %tail

iftrue:
call void asm sideeffect ".space 4096", ""()
br label %tail

tail:
ret void
}

define i32 @relax_jal(i1 %a) {
; CHECK-LABEL: relax_jal:
; CHECK: # %bb.0:
; CHECK-NEXT: addi sp, sp, -16
; CHECK-NEXT: sw ra, 12(sp)
; CHECK-NEXT: sw s0, 8(sp)
; CHECK-NEXT: addi s0, sp, 16
; CHECK-NEXT: andi a0, a0, 1
; CHECK-NEXT: bnez a0, .LBB1_1
; CHECK-NEXT: # %bb.4:
; CHECK-NEXT: lui a0, %hi(.LBB1_2)
; CHECK-NEXT: jalr zero, a0, %lo(.LBB1_2)
; CHECK-NEXT: .LBB1_1: # %iftrue
; CHECK-NEXT: #APP
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: #APP
; CHECK-NEXT: .space 1048576
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: j .LBB1_3
; CHECK-NEXT: .LBB1_2: # %jmp
; CHECK-NEXT: #APP
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: .LBB1_3: # %tail
; CHECK-NEXT: addi a0, zero, 1
; CHECK-NEXT: lw s0, 8(sp)
; CHECK-NEXT: lw ra, 12(sp)
; CHECK-NEXT: addi sp, sp, 16
; CHECK-NEXT: ret
br i1 %a, label %iftrue, label %jmp

jmp:
call void asm sideeffect "", ""()
br label %tail

iftrue:
call void asm sideeffect "", ""()
br label %space

space:
call void asm sideeffect ".space 1048576", ""()
br label %tail

tail:
ret i32 1
}

0 comments on commit 315cd3a

Please sign in to comment.