Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4a64565
[RISCV] Canonicalize foldable branch conditions in optimizeCondBranch
preames Mar 25, 2025
40439cd
clang-format
preames Mar 25, 2025
25c88ba
[RISCV] Add late optimization pass for riscv
mikhailramalho Mar 26, 2025
8aac8e9
Fix pipeline tests
mikhailramalho Mar 26, 2025
3d28d5f
Updated description
mikhailramalho Mar 27, 2025
36d9adc
Removed messy code
mikhailramalho Mar 27, 2025
08ddd9e
Updated tests
mikhailramalho Mar 27, 2025
df34ba6
Make evaluateCondBranch, isLoadImm, and isFromLoadImm static member f…
mikhailramalho Mar 27, 2025
81dffc3
Merge remote-tracking branch 'origin/main' into riscv-late-opt1
mikhailramalho Mar 27, 2025
e53bb1b
Cleanup includes
mikhailramalho Mar 27, 2025
b9303d3
Moved declaration of Folded
mikhailramalho Mar 27, 2025
51d4ef3
Added documentation to new static functions
mikhailramalho Mar 27, 2025
e353109
Make isLoadImm private
mikhailramalho Mar 27, 2025
fbec07f
No need to update TBB and FBB
mikhailramalho Mar 27, 2025
bbda8ff
Don't run the new pass on -O0
mikhailramalho Mar 27, 2025
24e7a91
Merge remote-tracking branch 'origin/main' into riscv-late-opt1
mikhailramalho Mar 27, 2025
eedbddd
Updated test
mikhailramalho Mar 27, 2025
f710dbe
No need to clear cond
mikhailramalho Mar 27, 2025
cad5541
clang-format
mikhailramalho Mar 27, 2025
acecac2
Moved isLoadImm out of RISCVInstrInfo
mikhailramalho Mar 27, 2025
d9020ff
Removed wrong comment
mikhailramalho Mar 27, 2025
66be46c
Sort list of files
mikhailramalho Mar 27, 2025
81f8b1e
Rename trySimplifyCondBr and return early when possible
mikhailramalho Mar 27, 2025
ef5b386
Renamed pass
mikhailramalho Mar 27, 2025
953a78d
clang-format
mikhailramalho Mar 27, 2025
2505795
Update header
mikhailramalho Mar 27, 2025
0230f5d
Merge remote-tracking branch 'origin/main' into riscv-late-opt1
mikhailramalho Mar 27, 2025
46aec7c
Rename file
mikhailramalho Mar 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ add_llvm_target(RISCVCodeGen
RISCVConstantPoolValue.cpp
RISCVDeadRegisterDefinitions.cpp
RISCVMakeCompressible.cpp
RISCVLateOpt.cpp
RISCVExpandAtomicPseudoInsts.cpp
RISCVExpandPseudoInsts.cpp
RISCVFoldMemOffset.cpp
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ void initializeRISCVLandingPadSetupPass(PassRegistry &);
FunctionPass *createRISCVISelDag(RISCVTargetMachine &TM,
CodeGenOptLevel OptLevel);

FunctionPass *createRISCVLateOptPass();
void initializeRISCVLateOptPass(PassRegistry &);

FunctionPass *createRISCVMakeCompressibleOptPass();
void initializeRISCVMakeCompressibleOptPass(PassRegistry &);

Expand Down
101 changes: 67 additions & 34 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,25 @@ static RISCVCC::CondCode getCondFromBranchOpc(unsigned Opc) {
}
}

static bool evaluateCondBranch(unsigned CC, int64_t C0, int64_t C1) {
switch (CC) {
default:
llvm_unreachable("Unexpected CC");
case RISCVCC::COND_EQ:
return C0 == C1;
case RISCVCC::COND_NE:
return C0 != C1;
case RISCVCC::COND_LT:
return C0 < C1;
case RISCVCC::COND_GE:
return C0 >= C1;
case RISCVCC::COND_LTU:
return (uint64_t)C0 < (uint64_t)C1;
case RISCVCC::COND_GEU:
return (uint64_t)C0 >= (uint64_t)C1;
}
}

// The contents of values added to Cond are not examined outside of
// RISCVInstrInfo, giving us flexibility in what to push to it. For RISCV, we
// push BranchOpcode, Reg1, Reg2.
Expand Down Expand Up @@ -1295,6 +1314,49 @@ bool RISCVInstrInfo::optimizeCondBranch(MachineInstr &MI) const {
RISCVCC::CondCode CC = static_cast<RISCVCC::CondCode>(Cond[0].getImm());
assert(CC != RISCVCC::COND_INVALID);

auto modifyBranch = [&]() {
// Build the new branch and remove the old one.
BuildMI(*MBB, MI, MI.getDebugLoc(),
getBrCond(static_cast<RISCVCC::CondCode>(Cond[0].getImm())))
.add(Cond[1])
.add(Cond[2])
.addMBB(TBB);
MI.eraseFromParent();
};

// Right now we only care about LI (i.e. ADDI x0, imm)
auto isLoadImm = [](const MachineInstr *MI, int64_t &Imm) -> bool {
if (MI->getOpcode() == RISCV::ADDI && MI->getOperand(1).isReg() &&
MI->getOperand(1).getReg() == RISCV::X0) {
Imm = MI->getOperand(2).getImm();
return true;
}
return false;
};
// Either a load from immediate instruction or X0.
auto isFromLoadImm = [&](const MachineOperand &Op, int64_t &Imm) -> bool {
if (!Op.isReg())
return false;
Register Reg = Op.getReg();
if (Reg == RISCV::X0) {
Imm = 0;
return true;
}
return Reg.isVirtual() && isLoadImm(MRI.getVRegDef(Reg), Imm);
};

// Canonicalize conditional branches which can be constant folded into
// beqz or bnez. We can't modify the CFG here.
int64_t C0, C1;
if (isFromLoadImm(Cond[1], C0) && isFromLoadImm(Cond[2], C1)) {
unsigned NewCC =
evaluateCondBranch(CC, C0, C1) ? RISCVCC::COND_EQ : RISCVCC::COND_NE;
Cond[0] = MachineOperand::CreateImm(NewCC);
Cond[1] = Cond[2] = MachineOperand::CreateReg(RISCV::X0, /*isDef=*/false);
modifyBranch();
return true;
}

if (CC == RISCVCC::COND_EQ || CC == RISCVCC::COND_NE)
return false;

Expand All @@ -1315,24 +1377,6 @@ bool RISCVInstrInfo::optimizeCondBranch(MachineInstr &MI) const {
//
// To make sure this optimization is really beneficial, we only
// optimize for cases where Y had only one use (i.e. only used by the branch).

// Right now we only care about LI (i.e. ADDI x0, imm)
auto isLoadImm = [](const MachineInstr *MI, int64_t &Imm) -> bool {
if (MI->getOpcode() == RISCV::ADDI && MI->getOperand(1).isReg() &&
MI->getOperand(1).getReg() == RISCV::X0) {
Imm = MI->getOperand(2).getImm();
return true;
}
return false;
};
// Either a load from immediate instruction or X0.
auto isFromLoadImm = [&](const MachineOperand &Op, int64_t &Imm) -> bool {
if (!Op.isReg())
return false;
Register Reg = Op.getReg();
return Reg.isVirtual() && isLoadImm(MRI.getVRegDef(Reg), Imm);
};

MachineOperand &LHS = MI.getOperand(0);
MachineOperand &RHS = MI.getOperand(1);
// Try to find the register for constant Z; return
Expand All @@ -1350,8 +1394,6 @@ bool RISCVInstrInfo::optimizeCondBranch(MachineInstr &MI) const {
return Register();
};

bool Modify = false;
int64_t C0;
if (isFromLoadImm(LHS, C0) && MRI.hasOneUse(LHS.getReg())) {
// Might be case 1.
// Signed integer overflow is UB. (UINT64_MAX is bigger so we don't need
Expand All @@ -1364,7 +1406,8 @@ bool RISCVInstrInfo::optimizeCondBranch(MachineInstr &MI) const {
// We might extend the live range of Z, clear its kill flag to
// account for this.
MRI.clearKillFlags(RegZ);
Modify = true;
modifyBranch();
return true;
}
} else if (isFromLoadImm(RHS, C0) && MRI.hasOneUse(RHS.getReg())) {
// Might be case 2.
Expand All @@ -1378,22 +1421,12 @@ bool RISCVInstrInfo::optimizeCondBranch(MachineInstr &MI) const {
// We might extend the live range of Z, clear its kill flag to
// account for this.
MRI.clearKillFlags(RegZ);
Modify = true;
modifyBranch();
return true;
}
}

if (!Modify)
return false;

// Build the new branch and remove the old one.
BuildMI(*MBB, MI, MI.getDebugLoc(),
getBrCond(static_cast<RISCVCC::CondCode>(Cond[0].getImm())))
.add(Cond[1])
.add(Cond[2])
.addMBB(TBB);
MI.eraseFromParent();

return true;
return false;
}

MachineBasicBlock *
Expand Down
190 changes: 190 additions & 0 deletions llvm/lib/Target/RISCV/RISCVLateOpt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//===-- RISCVLateOpt.cpp - Late stage optimization ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// This file provides RISC-V specific target descriptions.
///
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/RISCVMCTargetDesc.h"
#include "RISCV.h"
#include "RISCVInstrInfo.h"
#include "RISCVSubtarget.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Debug.h"

using namespace llvm;

#define DEBUG_TYPE "riscv-late-opt"
#define RISCV_LATE_OPT_NAME "RISC-V Late Stage Optimizations"

namespace {

struct RISCVLateOpt : public MachineFunctionPass {
static char ID;

RISCVLateOpt() : MachineFunctionPass(ID) {}

StringRef getPassName() const override { return RISCV_LATE_OPT_NAME; }

void getAnalysisUsage(AnalysisUsage &AU) const override {
MachineFunctionPass::getAnalysisUsage(AU);
}

bool runOnMachineFunction(MachineFunction &Fn) override;

private:
bool trySimplifyCondBr(MachineInstr *MI, MachineBasicBlock *TBB,
MachineBasicBlock *FBB,
SmallVectorImpl<MachineOperand> &Cond) const;

const RISCVInstrInfo *RII = nullptr;
};
} // namespace

char RISCVLateOpt::ID = 0;
INITIALIZE_PASS(RISCVLateOpt, "riscv-late-opt", RISCV_LATE_OPT_NAME, false,
false)

bool RISCVLateOpt::trySimplifyCondBr(
MachineInstr *MI, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
SmallVectorImpl<MachineOperand> &Cond) const {

RISCVCC::CondCode CC = static_cast<RISCVCC::CondCode>(Cond[0].getImm());
assert(CC != RISCVCC::COND_INVALID);

// Right now we only care about LI (i.e. ADDI x0, imm)
auto isLoadImm = [](const MachineInstr *MI, int64_t &Imm) -> bool {
if (MI->getOpcode() == RISCV::ADDI && MI->getOperand(1).isReg() &&
MI->getOperand(1).getReg() == RISCV::X0) {
Imm = MI->getOperand(2).getImm();
return true;
}
return false;
};

MachineBasicBlock *MBB = MI->getParent();
MachineRegisterInfo &MRI = MBB->getParent()->getRegInfo();
// Either a load from immediate instruction or X0.
auto isFromLoadImm = [&](const MachineOperand &Op, int64_t &Imm) -> bool {
if (!Op.isReg())
return false;
Register Reg = Op.getReg();
if (Reg == RISCV::X0) {
Imm = 0;
return true;
}
return Reg.isVirtual() && isLoadImm(MRI.getVRegDef(Reg), Imm);
};

// Try and convert a conditional branch that can be evaluated statically
// into an unconditional branch.
MachineBasicBlock *Folded = nullptr;
int64_t C0, C1;
if (isFromLoadImm(Cond[1], C0) && isFromLoadImm(Cond[2], C1)) {
switch (CC) {
case RISCVCC::COND_INVALID:
llvm_unreachable("Unexpected CC");
case RISCVCC::COND_EQ: {
Folded = (C0 == C1) ? TBB : FBB;
break;
}
case RISCVCC::COND_NE: {
Folded = (C0 != C1) ? TBB : FBB;
break;
}
case RISCVCC::COND_LT: {
Folded = (C0 < C1) ? TBB : FBB;
break;
}
case RISCVCC::COND_GE: {
Folded = (C0 >= C1) ? TBB : FBB;
break;
}
case RISCVCC::COND_LTU: {
Folded = ((uint64_t)C0 < (uint64_t)C1) ? TBB : FBB;
break;
}
case RISCVCC::COND_GEU: {
Folded = ((uint64_t)C0 >= (uint64_t)C1) ? TBB : FBB;
break;
}
}

// Do the conversion
// Build the new unconditional branch
DebugLoc DL = MBB->findBranchDebugLoc();
if (Folded) {
BuildMI(*MBB, MI, DL, RII->get(RISCV::PseudoBR)).addMBB(Folded);
} else {
MachineFunction::iterator Fallthrough = ++MBB->getIterator();
if (Fallthrough == MBB->getParent()->end())
return false;
BuildMI(*MBB, MI, DL, RII->get(RISCV::PseudoBR)).addMBB(&*Fallthrough);
}

// Update successors of MBB->
if (Folded == TBB) {
// If we're taking TBB, then the succ to delete is the fallthrough (if
// it was a succ in the first place), or its the MBB from the
// unconditional branch.
if (!FBB) {
MachineFunction::iterator Fallthrough = ++MBB->getIterator();
if (Fallthrough != MBB->getParent()->end() &&
MBB->isSuccessor(&*Fallthrough))
MBB->removeSuccessor(&*Fallthrough, true);
} else {
MBB->removeSuccessor(FBB, true);
}
} else if (Folded == FBB) {
// If we're taking the fallthrough or unconditional branch, then the
// succ to remove is the one from the conditional branch.
MBB->removeSuccessor(TBB, true);
}

MI->eraseFromParent();
return true;
}
return false;
}

bool RISCVLateOpt::runOnMachineFunction(MachineFunction &Fn) {
if (skipFunction(Fn.getFunction()))
return false;

auto &ST = Fn.getSubtarget<RISCVSubtarget>();
RII = ST.getInstrInfo();

bool Changed = false;

for (MachineBasicBlock &MBB : Fn) {
for (MachineBasicBlock::iterator MII = MBB.begin(), MIE = MBB.end();
MII != MIE;) {
MachineInstr *MI = &*MII;
// We may be erasing MI below, increment MII now.
++MII;
if (!MI->isConditionalBranch())
continue;

MachineBasicBlock *TBB, *FBB;
SmallVector<MachineOperand, 4> Cond;
if (!RII->analyzeBranch(MBB, TBB, FBB, Cond, /*AllowModify=*/false))
Changed |= trySimplifyCondBr(MI, TBB, FBB, Cond);
}
}

return Changed;
}

/// Returns an instance of the Make Compressible Optimization pass.
FunctionPass *llvm::createRISCVLateOptPass() { return new RISCVLateOpt(); }
2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
initializeRISCVPostLegalizerCombinerPass(*PR);
initializeKCFIPass(*PR);
initializeRISCVDeadRegisterDefinitionsPass(*PR);
initializeRISCVLateOptPass(*PR);
initializeRISCVMakeCompressibleOptPass(*PR);
initializeRISCVGatherScatterLoweringPass(*PR);
initializeRISCVCodeGenPreparePass(*PR);
Expand Down Expand Up @@ -565,6 +566,7 @@ void RISCVPassConfig::addPreEmitPass() {
if (TM->getOptLevel() >= CodeGenOptLevel::Default &&
EnableRISCVCopyPropagation)
addPass(createMachineCopyPropagationPass(true));
addPass(createRISCVLateOptPass());
addPass(&BranchRelaxationPassID);
addPass(createRISCVMakeCompressibleOptPass());
}
Expand Down
Loading