#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SparseSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/MachineTraceMetrics.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "early-ifcvt"
static cl::opt<unsigned>
BlockInstrLimit("early-ifcvt-limit", cl::init(30), cl::Hidden,
cl::desc("Maximum number of instructions per speculated block."));
static cl::opt<bool> Stress("stress-early-ifcvt", cl::Hidden,
cl::desc("Turn all knobs to 11"));
STATISTIC(NumDiamondsSeen, "Number of diamonds");
STATISTIC(NumDiamondsConv, "Number of diamonds converted");
STATISTIC(NumTrianglesSeen, "Number of triangles");
STATISTIC(NumTrianglesConv, "Number of triangles converted");
namespace {
class SSAIfConv {
const TargetInstrInfo *TII;
const TargetRegisterInfo *TRI;
MachineRegisterInfo *MRI;
public:
MachineBasicBlock *Head;
MachineBasicBlock *Tail;
MachineBasicBlock *TBB;
MachineBasicBlock *FBB;
bool isTriangle() const { return TBB == Tail || FBB == Tail; }
MachineBasicBlock *getTPred() const { return TBB == Tail ? Head : TBB; }
MachineBasicBlock *getFPred() const { return FBB == Tail ? Head : FBB; }
struct PHIInfo {
MachineInstr *PHI;
unsigned TReg = 0, FReg = 0;
int CondCycles = 0, TCycles = 0, FCycles = 0;
PHIInfo(MachineInstr *phi) : PHI(phi) {}
};
SmallVector<PHIInfo, 8> PHIs;
private:
SmallVector<MachineOperand, 4> Cond;
SmallPtrSet<MachineInstr*, 8> InsertAfter;
BitVector ClobberedRegUnits;
SparseSet<unsigned> LiveRegUnits;
MachineBasicBlock::iterator InsertionPoint;
bool canSpeculateInstrs(MachineBasicBlock *MBB);
bool canPredicateInstrs(MachineBasicBlock *MBB);
bool InstrDependenciesAllowIfConv(MachineInstr *I);
void PredicateBlock(MachineBasicBlock *MBB, bool ReversePredicate);
bool findInsertionPoint();
void replacePHIInstrs();
void rewritePHIOperands();
public:
void runOnMachineFunction(MachineFunction &MF) {
TII = MF.getSubtarget().getInstrInfo();
TRI = MF.getSubtarget().getRegisterInfo();
MRI = &MF.getRegInfo();
LiveRegUnits.clear();
LiveRegUnits.setUniverse(TRI->getNumRegUnits());
ClobberedRegUnits.clear();
ClobberedRegUnits.resize(TRI->getNumRegUnits());
}
bool canConvertIf(MachineBasicBlock *MBB, bool Predicate = false);
void convertIf(SmallVectorImpl<MachineBasicBlock *> &RemovedBlocks,
bool Predicate = false);
};
}
bool SSAIfConv::canSpeculateInstrs(MachineBasicBlock *MBB) {
if (!MBB->livein_empty()) {
LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has live-ins.\n");
return false;
}
unsigned InstrCount = 0;
for (MachineInstr &MI :
llvm::make_range(MBB->begin(), MBB->getFirstTerminator())) {
if (MI.isDebugInstr())
continue;
if (++InstrCount > BlockInstrLimit && !Stress) {
LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has more than "
<< BlockInstrLimit << " instructions.\n");
return false;
}
if (MI.isPHI()) {
LLVM_DEBUG(dbgs() << "Can't hoist: " << MI);
return false;
}
if (MI.mayLoad()) {
LLVM_DEBUG(dbgs() << "Won't speculate load: " << MI);
return false;
}
bool DontMoveAcrossStore = true;
if (!MI.isSafeToMove(nullptr, DontMoveAcrossStore)) {
LLVM_DEBUG(dbgs() << "Can't speculate: " << MI);
return false;
}
if (!InstrDependenciesAllowIfConv(&MI))
return false;
}
return true;
}
bool SSAIfConv::InstrDependenciesAllowIfConv(MachineInstr *I) {
for (const MachineOperand &MO : I->operands()) {
if (MO.isRegMask()) {
LLVM_DEBUG(dbgs() << "Won't speculate regmask: " << *I);
return false;
}
if (!MO.isReg())
continue;
Register Reg = MO.getReg();
if (MO.isDef() && Register::isPhysicalRegister(Reg))
for (MCRegUnitIterator Units(Reg.asMCReg(), TRI); Units.isValid();
++Units)
ClobberedRegUnits.set(*Units);
if (!MO.readsReg() || !Register::isVirtualRegister(Reg))
continue;
MachineInstr *DefMI = MRI->getVRegDef(Reg);
if (!DefMI || DefMI->getParent() != Head)
continue;
if (InsertAfter.insert(DefMI).second)
LLVM_DEBUG(dbgs() << printMBBReference(*I->getParent()) << " depends on "
<< *DefMI);
if (DefMI->isTerminator()) {
LLVM_DEBUG(dbgs() << "Can't insert instructions below terminator.\n");
return false;
}
}
return true;
}
bool SSAIfConv::canPredicateInstrs(MachineBasicBlock *MBB) {
if (!MBB->livein_empty()) {
LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has live-ins.\n");
return false;
}
unsigned InstrCount = 0;
for (MachineBasicBlock::iterator I = MBB->begin(),
E = MBB->getFirstTerminator();
I != E; ++I) {
if (I->isDebugInstr())
continue;
if (++InstrCount > BlockInstrLimit && !Stress) {
LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has more than "
<< BlockInstrLimit << " instructions.\n");
return false;
}
if (I->isPHI()) {
LLVM_DEBUG(dbgs() << "Can't predicate: " << *I);
return false;
}
if (!TII->isPredicable(*I) || TII->isPredicated(*I)) {
return false;
}
if (!InstrDependenciesAllowIfConv(&(*I)))
return false;
}
return true;
}
void SSAIfConv::PredicateBlock(MachineBasicBlock *MBB, bool ReversePredicate) {
auto Condition = Cond;
if (ReversePredicate)
TII->reverseBranchCondition(Condition);
for (MachineBasicBlock::iterator I = MBB->begin(),
E = MBB->getFirstTerminator();
I != E; ++I) {
if (I->isDebugInstr())
continue;
TII->PredicateInstruction(*I, Condition);
}
}
bool SSAIfConv::findInsertionPoint() {
LiveRegUnits.clear();
SmallVector<MCRegister, 8> Reads;
MachineBasicBlock::iterator FirstTerm = Head->getFirstTerminator();
MachineBasicBlock::iterator I = Head->end();
MachineBasicBlock::iterator B = Head->begin();
while (I != B) {
--I;
if (InsertAfter.count(&*I)) {
LLVM_DEBUG(dbgs() << "Can't insert code after " << *I);
return false;
}
for (const MachineOperand &MO : I->operands()) {
if (!MO.isReg())
continue;
Register Reg = MO.getReg();
if (!Register::isPhysicalRegister(Reg))
continue;
if (MO.isDef())
for (MCRegUnitIterator Units(Reg.asMCReg(), TRI); Units.isValid();
++Units)
LiveRegUnits.erase(*Units);
if (MO.readsReg())
Reads.push_back(Reg.asMCReg());
}
while (!Reads.empty())
for (MCRegUnitIterator Units(Reads.pop_back_val(), TRI); Units.isValid();
++Units)
if (ClobberedRegUnits.test(*Units))
LiveRegUnits.insert(*Units);
if (I != FirstTerm && I->isTerminator())
continue;
if (!LiveRegUnits.empty()) {
LLVM_DEBUG({
dbgs() << "Would clobber";
for (unsigned LRU : LiveRegUnits)
dbgs() << ' ' << printRegUnit(LRU, TRI);
dbgs() << " live before " << *I;
});
continue;
}
InsertionPoint = I;
LLVM_DEBUG(dbgs() << "Can insert before " << *I);
return true;
}
LLVM_DEBUG(dbgs() << "No legal insertion point found.\n");
return false;
}
bool SSAIfConv::canConvertIf(MachineBasicBlock *MBB, bool Predicate) {
Head = MBB;
TBB = FBB = Tail = nullptr;
if (Head->succ_size() != 2)
return false;
MachineBasicBlock *Succ0 = Head->succ_begin()[0];
MachineBasicBlock *Succ1 = Head->succ_begin()[1];
if (Succ0->pred_size() != 1)
std::swap(Succ0, Succ1);
if (Succ0->pred_size() != 1 || Succ0->succ_size() != 1)
return false;
Tail = Succ0->succ_begin()[0];
if (Tail != Succ1) {
if (Succ1->pred_size() != 1 || Succ1->succ_size() != 1 ||
Succ1->succ_begin()[0] != Tail)
return false;
LLVM_DEBUG(dbgs() << "\nDiamond: " << printMBBReference(*Head) << " -> "
<< printMBBReference(*Succ0) << "/"
<< printMBBReference(*Succ1) << " -> "
<< printMBBReference(*Tail) << '\n');
if (!Tail->livein_empty()) {
LLVM_DEBUG(dbgs() << "Tail has live-ins.\n");
return false;
}
} else {
LLVM_DEBUG(dbgs() << "\nTriangle: " << printMBBReference(*Head) << " -> "
<< printMBBReference(*Succ0) << " -> "
<< printMBBReference(*Tail) << '\n');
}
if (!Predicate && (Tail->empty() || !Tail->front().isPHI())) {
LLVM_DEBUG(dbgs() << "No phis in tail.\n");
return false;
}
Cond.clear();
if (TII->analyzeBranch(*Head, TBB, FBB, Cond)) {
LLVM_DEBUG(dbgs() << "Branch not analyzable.\n");
return false;
}
if (!TBB) {
LLVM_DEBUG(dbgs() << "analyzeBranch didn't find conditional branch.\n");
return false;
}
if (Cond.empty()) {
LLVM_DEBUG(dbgs() << "analyzeBranch found an unconditional branch.\n");
return false;
}
FBB = TBB == Succ0 ? Succ1 : Succ0;
PHIs.clear();
MachineBasicBlock *TPred = getTPred();
MachineBasicBlock *FPred = getFPred();
for (MachineBasicBlock::iterator I = Tail->begin(), E = Tail->end();
I != E && I->isPHI(); ++I) {
PHIs.push_back(&*I);
PHIInfo &PI = PHIs.back();
for (unsigned i = 1; i != PI.PHI->getNumOperands(); i += 2) {
if (PI.PHI->getOperand(i+1).getMBB() == TPred)
PI.TReg = PI.PHI->getOperand(i).getReg();
if (PI.PHI->getOperand(i+1).getMBB() == FPred)
PI.FReg = PI.PHI->getOperand(i).getReg();
}
assert(Register::isVirtualRegister(PI.TReg) && "Bad PHI");
assert(Register::isVirtualRegister(PI.FReg) && "Bad PHI");
if (!TII->canInsertSelect(*Head, Cond, PI.PHI->getOperand(0).getReg(),
PI.TReg, PI.FReg, PI.CondCycles, PI.TCycles,
PI.FCycles)) {
LLVM_DEBUG(dbgs() << "Can't convert: " << *PI.PHI);
return false;
}
}
InsertAfter.clear();
ClobberedRegUnits.reset();
if (Predicate) {
if (TBB != Tail && !canPredicateInstrs(TBB))
return false;
if (FBB != Tail && !canPredicateInstrs(FBB))
return false;
} else {
if (TBB != Tail && !canSpeculateInstrs(TBB))
return false;
if (FBB != Tail && !canSpeculateInstrs(FBB))
return false;
}
if (!findInsertionPoint())
return false;
if (isTriangle())
++NumTrianglesSeen;
else
++NumDiamondsSeen;
return true;
}
static bool hasSameValue(const MachineRegisterInfo &MRI,
const TargetInstrInfo *TII, Register TReg,
Register FReg) {
if (TReg == FReg)
return true;
if (!TReg.isVirtual() || !FReg.isVirtual())
return false;
const MachineInstr *TDef = MRI.getUniqueVRegDef(TReg);
const MachineInstr *FDef = MRI.getUniqueVRegDef(FReg);
if (!TDef || !FDef)
return false;
if (TDef->hasUnmodeledSideEffects())
return false;
if (TDef->mayLoadOrStore() && !TDef->isDereferenceableInvariantLoad())
return false;
if (any_of(TDef->uses(), [](const MachineOperand &MO) {
return MO.isReg() && MO.getReg().isPhysical();
}))
return false;
if (!TII->produceSameValue(*TDef, *FDef, &MRI))
return false;
int TIdx = TDef->findRegisterDefOperandIdx(TReg);
int FIdx = FDef->findRegisterDefOperandIdx(FReg);
if (TIdx == -1 || FIdx == -1)
return false;
return TIdx == FIdx;
}
void SSAIfConv::replacePHIInstrs() {
assert(Tail->pred_size() == 2 && "Cannot replace PHIs");
MachineBasicBlock::iterator FirstTerm = Head->getFirstTerminator();
assert(FirstTerm != Head->end() && "No terminators");
DebugLoc HeadDL = FirstTerm->getDebugLoc();
for (unsigned i = 0, e = PHIs.size(); i != e; ++i) {
PHIInfo &PI = PHIs[i];
LLVM_DEBUG(dbgs() << "If-converting " << *PI.PHI);
Register DstReg = PI.PHI->getOperand(0).getReg();
if (hasSameValue(*MRI, TII, PI.TReg, PI.FReg)) {
BuildMI(*Head, FirstTerm, HeadDL, TII->get(TargetOpcode::COPY), DstReg)
.addReg(PI.TReg);
} else {
TII->insertSelect(*Head, FirstTerm, HeadDL, DstReg, Cond, PI.TReg,
PI.FReg);
}
LLVM_DEBUG(dbgs() << " --> " << *std::prev(FirstTerm));
PI.PHI->eraseFromParent();
PI.PHI = nullptr;
}
}
void SSAIfConv::rewritePHIOperands() {
MachineBasicBlock::iterator FirstTerm = Head->getFirstTerminator();
assert(FirstTerm != Head->end() && "No terminators");
DebugLoc HeadDL = FirstTerm->getDebugLoc();
for (unsigned i = 0, e = PHIs.size(); i != e; ++i) {
PHIInfo &PI = PHIs[i];
unsigned DstReg = 0;
LLVM_DEBUG(dbgs() << "If-converting " << *PI.PHI);
if (hasSameValue(*MRI, TII, PI.TReg, PI.FReg)) {
DstReg = PI.TReg;
} else {
Register PHIDst = PI.PHI->getOperand(0).getReg();
DstReg = MRI->createVirtualRegister(MRI->getRegClass(PHIDst));
TII->insertSelect(*Head, FirstTerm, HeadDL,
DstReg, Cond, PI.TReg, PI.FReg);
LLVM_DEBUG(dbgs() << " --> " << *std::prev(FirstTerm));
}
for (unsigned i = PI.PHI->getNumOperands(); i != 1; i -= 2) {
MachineBasicBlock *MBB = PI.PHI->getOperand(i-1).getMBB();
if (MBB == getTPred()) {
PI.PHI->getOperand(i-1).setMBB(Head);
PI.PHI->getOperand(i-2).setReg(DstReg);
} else if (MBB == getFPred()) {
PI.PHI->removeOperand(i-1);
PI.PHI->removeOperand(i-2);
}
}
LLVM_DEBUG(dbgs() << " --> " << *PI.PHI);
}
}
void SSAIfConv::convertIf(SmallVectorImpl<MachineBasicBlock *> &RemovedBlocks,
bool Predicate) {
assert(Head && Tail && TBB && FBB && "Call canConvertIf first.");
if (isTriangle())
++NumTrianglesConv;
else
++NumDiamondsConv;
if (TBB != Tail) {
if (Predicate)
PredicateBlock(TBB, false);
Head->splice(InsertionPoint, TBB, TBB->begin(), TBB->getFirstTerminator());
}
if (FBB != Tail) {
if (Predicate)
PredicateBlock(FBB, true);
Head->splice(InsertionPoint, FBB, FBB->begin(), FBB->getFirstTerminator());
}
bool ExtraPreds = Tail->pred_size() != 2;
if (ExtraPreds)
rewritePHIOperands();
else
replacePHIInstrs();
Head->removeSuccessor(TBB);
Head->removeSuccessor(FBB, true);
if (TBB != Tail)
TBB->removeSuccessor(Tail, true);
if (FBB != Tail)
FBB->removeSuccessor(Tail, true);
DebugLoc HeadDL = Head->getFirstTerminator()->getDebugLoc();
TII->removeBranch(*Head);
if (TBB != Tail) {
RemovedBlocks.push_back(TBB);
TBB->eraseFromParent();
}
if (FBB != Tail) {
RemovedBlocks.push_back(FBB);
FBB->eraseFromParent();
}
assert(Head->succ_empty() && "Additional head successors?");
if (!ExtraPreds && Head->isLayoutSuccessor(Tail)) {
LLVM_DEBUG(dbgs() << "Joining tail " << printMBBReference(*Tail)
<< " into head " << printMBBReference(*Head) << '\n');
Head->splice(Head->end(), Tail,
Tail->begin(), Tail->end());
Head->transferSuccessorsAndUpdatePHIs(Tail);
RemovedBlocks.push_back(Tail);
Tail->eraseFromParent();
} else {
LLVM_DEBUG(dbgs() << "Converting to unconditional branch.\n");
SmallVector<MachineOperand, 0> EmptyCond;
TII->insertBranch(*Head, Tail, nullptr, EmptyCond, HeadDL);
Head->addSuccessor(Tail);
}
LLVM_DEBUG(dbgs() << *Head);
}
namespace {
class EarlyIfConverter : public MachineFunctionPass {
const TargetInstrInfo *TII;
const TargetRegisterInfo *TRI;
MCSchedModel SchedModel;
MachineRegisterInfo *MRI;
MachineDominatorTree *DomTree;
MachineLoopInfo *Loops;
MachineTraceMetrics *Traces;
MachineTraceMetrics::Ensemble *MinInstr;
SSAIfConv IfConv;
public:
static char ID;
EarlyIfConverter() : MachineFunctionPass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &MF) override;
StringRef getPassName() const override { return "Early If-Conversion"; }
private:
bool tryConvertIf(MachineBasicBlock*);
void invalidateTraces();
bool shouldConvertIf();
};
}
char EarlyIfConverter::ID = 0;
char &llvm::EarlyIfConverterID = EarlyIfConverter::ID;
INITIALIZE_PASS_BEGIN(EarlyIfConverter, DEBUG_TYPE,
"Early If Converter", false, false)
INITIALIZE_PASS_DEPENDENCY(MachineBranchProbabilityInfo)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
INITIALIZE_PASS_DEPENDENCY(MachineTraceMetrics)
INITIALIZE_PASS_END(EarlyIfConverter, DEBUG_TYPE,
"Early If Converter", false, false)
void EarlyIfConverter::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<MachineBranchProbabilityInfo>();
AU.addRequired<MachineDominatorTree>();
AU.addPreserved<MachineDominatorTree>();
AU.addRequired<MachineLoopInfo>();
AU.addPreserved<MachineLoopInfo>();
AU.addRequired<MachineTraceMetrics>();
AU.addPreserved<MachineTraceMetrics>();
MachineFunctionPass::getAnalysisUsage(AU);
}
namespace {
void updateDomTree(MachineDominatorTree *DomTree, const SSAIfConv &IfConv,
ArrayRef<MachineBasicBlock *> Removed) {
MachineDomTreeNode *HeadNode = DomTree->getNode(IfConv.Head);
for (auto *B : Removed) {
MachineDomTreeNode *Node = DomTree->getNode(B);
assert(Node != HeadNode && "Cannot erase the head node");
while (Node->getNumChildren()) {
assert(Node->getBlock() == IfConv.Tail && "Unexpected children");
DomTree->changeImmediateDominator(Node->back(), HeadNode);
}
DomTree->eraseNode(B);
}
}
void updateLoops(MachineLoopInfo *Loops,
ArrayRef<MachineBasicBlock *> Removed) {
if (!Loops)
return;
for (auto *B : Removed)
Loops->removeBlock(B);
}
}
void EarlyIfConverter::invalidateTraces() {
Traces->verifyAnalysis();
Traces->invalidate(IfConv.Head);
Traces->invalidate(IfConv.Tail);
Traces->invalidate(IfConv.TBB);
Traces->invalidate(IfConv.FBB);
Traces->verifyAnalysis();
}
static unsigned adjCycles(unsigned Cyc, int Delta) {
if (Delta < 0 && Cyc + Delta > Cyc)
return 0;
return Cyc + Delta;
}
namespace {
struct Cycles {
const char *Key;
unsigned Value;
};
template <typename Remark> Remark &operator<<(Remark &R, Cycles C) {
return R << ore::NV(C.Key, C.Value) << (C.Value == 1 ? " cycle" : " cycles");
}
}
bool EarlyIfConverter::shouldConvertIf() {
if (Stress)
return true;
if (!MinInstr)
MinInstr = Traces->getEnsemble(MachineTraceMetrics::TS_MinInstrCount);
MachineTraceMetrics::Trace TBBTrace = MinInstr->getTrace(IfConv.getTPred());
MachineTraceMetrics::Trace FBBTrace = MinInstr->getTrace(IfConv.getFPred());
LLVM_DEBUG(dbgs() << "TBB: " << TBBTrace << "FBB: " << FBBTrace);
unsigned MinCrit = std::min(TBBTrace.getCriticalPath(),
FBBTrace.getCriticalPath());
unsigned CritLimit = SchedModel.MispredictPenalty/2;
MachineBasicBlock &MBB = *IfConv.Head;
MachineOptimizationRemarkEmitter MORE(*MBB.getParent(), nullptr);
SmallVector<const MachineBasicBlock*, 1> ExtraBlocks;
if (IfConv.TBB != IfConv.Tail)
ExtraBlocks.push_back(IfConv.TBB);
unsigned ResLength = FBBTrace.getResourceLength(ExtraBlocks);
LLVM_DEBUG(dbgs() << "Resource length " << ResLength
<< ", minimal critical path " << MinCrit << '\n');
if (ResLength > MinCrit + CritLimit) {
LLVM_DEBUG(dbgs() << "Not enough available ILP.\n");
MORE.emit([&]() {
MachineOptimizationRemarkMissed R(DEBUG_TYPE, "IfConversion",
MBB.findDebugLoc(MBB.back()), &MBB);
R << "did not if-convert branch: the resulting critical path ("
<< Cycles{"ResLength", ResLength}
<< ") would extend the shorter leg's critical path ("
<< Cycles{"MinCrit", MinCrit} << ") by more than the threshold of "
<< Cycles{"CritLimit", CritLimit}
<< ", which cannot be hidden by available ILP.";
return R;
});
return false;
}
MachineTraceMetrics::Trace HeadTrace = MinInstr->getTrace(IfConv.Head);
unsigned BranchDepth =
HeadTrace.getInstrCycles(*IfConv.Head->getFirstTerminator()).Depth;
LLVM_DEBUG(dbgs() << "Branch depth: " << BranchDepth << '\n');
MachineTraceMetrics::Trace TailTrace = MinInstr->getTrace(IfConv.Tail);
struct CriticalPathInfo {
unsigned Extra; unsigned Depth; };
CriticalPathInfo Cond{};
CriticalPathInfo TBlock{};
CriticalPathInfo FBlock{};
bool ShouldConvert = true;
for (unsigned i = 0, e = IfConv.PHIs.size(); i != e; ++i) {
SSAIfConv::PHIInfo &PI = IfConv.PHIs[i];
unsigned Slack = TailTrace.getInstrSlack(*PI.PHI);
unsigned MaxDepth = Slack + TailTrace.getInstrCycles(*PI.PHI).Depth;
LLVM_DEBUG(dbgs() << "Slack " << Slack << ":\t" << *PI.PHI);
unsigned CondDepth = adjCycles(BranchDepth, PI.CondCycles);
if (CondDepth > MaxDepth) {
unsigned Extra = CondDepth - MaxDepth;
LLVM_DEBUG(dbgs() << "Condition adds " << Extra << " cycles.\n");
if (Extra > Cond.Extra)
Cond = {Extra, CondDepth};
if (Extra > CritLimit) {
LLVM_DEBUG(dbgs() << "Exceeds limit of " << CritLimit << '\n');
ShouldConvert = false;
}
}
unsigned TDepth = adjCycles(TBBTrace.getPHIDepth(*PI.PHI), PI.TCycles);
if (TDepth > MaxDepth) {
unsigned Extra = TDepth - MaxDepth;
LLVM_DEBUG(dbgs() << "TBB data adds " << Extra << " cycles.\n");
if (Extra > TBlock.Extra)
TBlock = {Extra, TDepth};
if (Extra > CritLimit) {
LLVM_DEBUG(dbgs() << "Exceeds limit of " << CritLimit << '\n');
ShouldConvert = false;
}
}
unsigned FDepth = adjCycles(FBBTrace.getPHIDepth(*PI.PHI), PI.FCycles);
if (FDepth > MaxDepth) {
unsigned Extra = FDepth - MaxDepth;
LLVM_DEBUG(dbgs() << "FBB data adds " << Extra << " cycles.\n");
if (Extra > FBlock.Extra)
FBlock = {Extra, FDepth};
if (Extra > CritLimit) {
LLVM_DEBUG(dbgs() << "Exceeds limit of " << CritLimit << '\n');
ShouldConvert = false;
}
}
}
const CriticalPathInfo Short = TBlock.Extra > FBlock.Extra ? FBlock : TBlock;
const CriticalPathInfo Long = TBlock.Extra > FBlock.Extra ? TBlock : FBlock;
if (ShouldConvert) {
MORE.emit([&]() {
MachineOptimizationRemark R(DEBUG_TYPE, "IfConversion",
MBB.back().getDebugLoc(), &MBB);
R << "performing if-conversion on branch: the condition adds "
<< Cycles{"CondCycles", Cond.Extra} << " to the critical path";
if (Short.Extra > 0)
R << ", and the short leg adds another "
<< Cycles{"ShortCycles", Short.Extra};
if (Long.Extra > 0)
R << ", and the long leg adds another "
<< Cycles{"LongCycles", Long.Extra};
R << ", each staying under the threshold of "
<< Cycles{"CritLimit", CritLimit} << ".";
return R;
});
} else {
MORE.emit([&]() {
MachineOptimizationRemarkMissed R(DEBUG_TYPE, "IfConversion",
MBB.back().getDebugLoc(), &MBB);
R << "did not if-convert branch: the condition would add "
<< Cycles{"CondCycles", Cond.Extra} << " to the critical path";
if (Cond.Extra > CritLimit)
R << " exceeding the limit of " << Cycles{"CritLimit", CritLimit};
if (Short.Extra > 0) {
R << ", and the short leg would add another "
<< Cycles{"ShortCycles", Short.Extra};
if (Short.Extra > CritLimit)
R << " exceeding the limit of " << Cycles{"CritLimit", CritLimit};
}
if (Long.Extra > 0) {
R << ", and the long leg would add another "
<< Cycles{"LongCycles", Long.Extra};
if (Long.Extra > CritLimit)
R << " exceeding the limit of " << Cycles{"CritLimit", CritLimit};
}
R << ".";
return R;
});
}
return ShouldConvert;
}
bool EarlyIfConverter::tryConvertIf(MachineBasicBlock *MBB) {
bool Changed = false;
while (IfConv.canConvertIf(MBB) && shouldConvertIf()) {
invalidateTraces();
SmallVector<MachineBasicBlock*, 4> RemovedBlocks;
IfConv.convertIf(RemovedBlocks);
Changed = true;
updateDomTree(DomTree, IfConv, RemovedBlocks);
updateLoops(Loops, RemovedBlocks);
}
return Changed;
}
bool EarlyIfConverter::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** EARLY IF-CONVERSION **********\n"
<< "********** Function: " << MF.getName() << '\n');
if (skipFunction(MF.getFunction()))
return false;
const TargetSubtargetInfo &STI = MF.getSubtarget();
if (!STI.enableEarlyIfConversion())
return false;
TII = STI.getInstrInfo();
TRI = STI.getRegisterInfo();
SchedModel = STI.getSchedModel();
MRI = &MF.getRegInfo();
DomTree = &getAnalysis<MachineDominatorTree>();
Loops = getAnalysisIfAvailable<MachineLoopInfo>();
Traces = &getAnalysis<MachineTraceMetrics>();
MinInstr = nullptr;
bool Changed = false;
IfConv.runOnMachineFunction(MF);
for (auto *DomNode : post_order(DomTree))
if (tryConvertIf(DomNode->getBlock()))
Changed = true;
return Changed;
}
namespace {
class EarlyIfPredicator : public MachineFunctionPass {
const TargetInstrInfo *TII;
const TargetRegisterInfo *TRI;
TargetSchedModel SchedModel;
MachineRegisterInfo *MRI;
MachineDominatorTree *DomTree;
MachineBranchProbabilityInfo *MBPI;
MachineLoopInfo *Loops;
SSAIfConv IfConv;
public:
static char ID;
EarlyIfPredicator() : MachineFunctionPass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &MF) override;
StringRef getPassName() const override { return "Early If-predicator"; }
protected:
bool tryConvertIf(MachineBasicBlock *);
bool shouldConvertIf();
};
}
#undef DEBUG_TYPE
#define DEBUG_TYPE "early-if-predicator"
char EarlyIfPredicator::ID = 0;
char &llvm::EarlyIfPredicatorID = EarlyIfPredicator::ID;
INITIALIZE_PASS_BEGIN(EarlyIfPredicator, DEBUG_TYPE, "Early If Predicator",
false, false)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
INITIALIZE_PASS_DEPENDENCY(MachineBranchProbabilityInfo)
INITIALIZE_PASS_END(EarlyIfPredicator, DEBUG_TYPE, "Early If Predicator", false,
false)
void EarlyIfPredicator::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<MachineBranchProbabilityInfo>();
AU.addRequired<MachineDominatorTree>();
AU.addPreserved<MachineDominatorTree>();
AU.addRequired<MachineLoopInfo>();
AU.addPreserved<MachineLoopInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool EarlyIfPredicator::shouldConvertIf() {
auto TrueProbability = MBPI->getEdgeProbability(IfConv.Head, IfConv.TBB);
if (IfConv.isTriangle()) {
MachineBasicBlock &IfBlock =
(IfConv.TBB == IfConv.Tail) ? *IfConv.FBB : *IfConv.TBB;
unsigned ExtraPredCost = 0;
unsigned Cycles = 0;
for (MachineInstr &I : IfBlock) {
unsigned NumCycles = SchedModel.computeInstrLatency(&I, false);
if (NumCycles > 1)
Cycles += NumCycles - 1;
ExtraPredCost += TII->getPredicationCost(I);
}
return TII->isProfitableToIfCvt(IfBlock, Cycles, ExtraPredCost,
TrueProbability);
}
unsigned TExtra = 0;
unsigned FExtra = 0;
unsigned TCycle = 0;
unsigned FCycle = 0;
for (MachineInstr &I : *IfConv.TBB) {
unsigned NumCycles = SchedModel.computeInstrLatency(&I, false);
if (NumCycles > 1)
TCycle += NumCycles - 1;
TExtra += TII->getPredicationCost(I);
}
for (MachineInstr &I : *IfConv.FBB) {
unsigned NumCycles = SchedModel.computeInstrLatency(&I, false);
if (NumCycles > 1)
FCycle += NumCycles - 1;
FExtra += TII->getPredicationCost(I);
}
return TII->isProfitableToIfCvt(*IfConv.TBB, TCycle, TExtra, *IfConv.FBB,
FCycle, FExtra, TrueProbability);
}
bool EarlyIfPredicator::tryConvertIf(MachineBasicBlock *MBB) {
bool Changed = false;
while (IfConv.canConvertIf(MBB, true) && shouldConvertIf()) {
SmallVector<MachineBasicBlock *, 4> RemovedBlocks;
IfConv.convertIf(RemovedBlocks, true);
Changed = true;
updateDomTree(DomTree, IfConv, RemovedBlocks);
updateLoops(Loops, RemovedBlocks);
}
return Changed;
}
bool EarlyIfPredicator::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** EARLY IF-PREDICATOR **********\n"
<< "********** Function: " << MF.getName() << '\n');
if (skipFunction(MF.getFunction()))
return false;
const TargetSubtargetInfo &STI = MF.getSubtarget();
TII = STI.getInstrInfo();
TRI = STI.getRegisterInfo();
MRI = &MF.getRegInfo();
SchedModel.init(&STI);
DomTree = &getAnalysis<MachineDominatorTree>();
Loops = getAnalysisIfAvailable<MachineLoopInfo>();
MBPI = &getAnalysis<MachineBranchProbabilityInfo>();
bool Changed = false;
IfConv.runOnMachineFunction(MF);
for (auto *DomNode : post_order(DomTree))
if (tryConvertIf(DomNode->getBlock()))
Changed = true;
return Changed;
}