#include "llvm/Transforms/Scalar/LoopPredication.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
#include "llvm/Analysis/GuardUtils.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/GuardUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/LoopUtils.h"
#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
#define DEBUG_TYPE "loop-predication"
STATISTIC(TotalConsidered, "Number of guards considered");
STATISTIC(TotalWidened, "Number of checks widened");
using namespace llvm;
static cl::opt<bool> EnableIVTruncation("loop-predication-enable-iv-truncation",
cl::Hidden, cl::init(true));
static cl::opt<bool> EnableCountDownLoop("loop-predication-enable-count-down-loop",
cl::Hidden, cl::init(true));
static cl::opt<bool>
SkipProfitabilityChecks("loop-predication-skip-profitability-checks",
cl::Hidden, cl::init(false));
static cl::opt<float> LatchExitProbabilityScale(
"loop-predication-latch-probability-scale", cl::Hidden, cl::init(2.0),
cl::desc("scale factor for the latch probability. Value should be greater "
"than 1. Lower values are ignored"));
static cl::opt<bool> PredicateWidenableBranchGuards(
"loop-predication-predicate-widenable-branches-to-deopt", cl::Hidden,
cl::desc("Whether or not we should predicate guards "
"expressed as widenable branches to deoptimize blocks"),
cl::init(true));
namespace {
struct LoopICmp {
ICmpInst::Predicate Pred;
const SCEVAddRecExpr *IV;
const SCEV *Limit;
LoopICmp(ICmpInst::Predicate Pred, const SCEVAddRecExpr *IV,
const SCEV *Limit)
: Pred(Pred), IV(IV), Limit(Limit) {}
LoopICmp() = default;
void dump() {
dbgs() << "LoopICmp Pred = " << Pred << ", IV = " << *IV
<< ", Limit = " << *Limit << "\n";
}
};
class LoopPredication {
AliasAnalysis *AA;
DominatorTree *DT;
ScalarEvolution *SE;
LoopInfo *LI;
MemorySSAUpdater *MSSAU;
Loop *L;
const DataLayout *DL;
BasicBlock *Preheader;
LoopICmp LatchCheck;
bool isSupportedStep(const SCEV* Step);
Optional<LoopICmp> parseLoopICmp(ICmpInst *ICI);
Optional<LoopICmp> parseLoopLatchICmp();
Instruction *findInsertPt(Instruction *User, ArrayRef<Value*> Ops);
Instruction *findInsertPt(const SCEVExpander &Expander, Instruction *User,
ArrayRef<const SCEV *> Ops);
bool isLoopInvariantValue(const SCEV* S);
Value *expandCheck(SCEVExpander &Expander, Instruction *Guard,
ICmpInst::Predicate Pred, const SCEV *LHS,
const SCEV *RHS);
Optional<Value *> widenICmpRangeCheck(ICmpInst *ICI, SCEVExpander &Expander,
Instruction *Guard);
Optional<Value *> widenICmpRangeCheckIncrementingLoop(LoopICmp LatchCheck,
LoopICmp RangeCheck,
SCEVExpander &Expander,
Instruction *Guard);
Optional<Value *> widenICmpRangeCheckDecrementingLoop(LoopICmp LatchCheck,
LoopICmp RangeCheck,
SCEVExpander &Expander,
Instruction *Guard);
unsigned collectChecks(SmallVectorImpl<Value *> &Checks, Value *Condition,
SCEVExpander &Expander, Instruction *Guard);
bool widenGuardConditions(IntrinsicInst *II, SCEVExpander &Expander);
bool widenWidenableBranchGuardConditions(BranchInst *Guard, SCEVExpander &Expander);
bool isLoopProfitableToPredicate();
bool predicateLoopExits(Loop *L, SCEVExpander &Rewriter);
public:
LoopPredication(AliasAnalysis *AA, DominatorTree *DT, ScalarEvolution *SE,
LoopInfo *LI, MemorySSAUpdater *MSSAU)
: AA(AA), DT(DT), SE(SE), LI(LI), MSSAU(MSSAU){};
bool runOnLoop(Loop *L);
};
class LoopPredicationLegacyPass : public LoopPass {
public:
static char ID;
LoopPredicationLegacyPass() : LoopPass(ID) {
initializeLoopPredicationLegacyPassPass(*PassRegistry::getPassRegistry());
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<BranchProbabilityInfoWrapperPass>();
getLoopAnalysisUsage(AU);
AU.addPreserved<MemorySSAWrapperPass>();
}
bool runOnLoop(Loop *L, LPPassManager &LPM) override {
if (skipLoop(L))
return false;
auto *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
auto *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
auto *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
auto *MSSAWP = getAnalysisIfAvailable<MemorySSAWrapperPass>();
std::unique_ptr<MemorySSAUpdater> MSSAU;
if (MSSAWP)
MSSAU = std::make_unique<MemorySSAUpdater>(&MSSAWP->getMSSA());
auto *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
LoopPredication LP(AA, DT, SE, LI, MSSAU ? MSSAU.get() : nullptr);
return LP.runOnLoop(L);
}
};
char LoopPredicationLegacyPass::ID = 0;
}
INITIALIZE_PASS_BEGIN(LoopPredicationLegacyPass, "loop-predication",
"Loop predication", false, false)
INITIALIZE_PASS_DEPENDENCY(BranchProbabilityInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopPass)
INITIALIZE_PASS_END(LoopPredicationLegacyPass, "loop-predication",
"Loop predication", false, false)
Pass *llvm::createLoopPredicationPass() {
return new LoopPredicationLegacyPass();
}
PreservedAnalyses LoopPredicationPass::run(Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &U) {
std::unique_ptr<MemorySSAUpdater> MSSAU;
if (AR.MSSA)
MSSAU = std::make_unique<MemorySSAUpdater>(AR.MSSA);
LoopPredication LP(&AR.AA, &AR.DT, &AR.SE, &AR.LI,
MSSAU ? MSSAU.get() : nullptr);
if (!LP.runOnLoop(&L))
return PreservedAnalyses::all();
auto PA = getLoopPassPreservedAnalyses();
if (AR.MSSA)
PA.preserve<MemorySSAAnalysis>();
return PA;
}
Optional<LoopICmp>
LoopPredication::parseLoopICmp(ICmpInst *ICI) {
auto Pred = ICI->getPredicate();
auto *LHS = ICI->getOperand(0);
auto *RHS = ICI->getOperand(1);
const SCEV *LHSS = SE->getSCEV(LHS);
if (isa<SCEVCouldNotCompute>(LHSS))
return None;
const SCEV *RHSS = SE->getSCEV(RHS);
if (isa<SCEVCouldNotCompute>(RHSS))
return None;
if (SE->isLoopInvariant(LHSS, L)) {
std::swap(LHS, RHS);
std::swap(LHSS, RHSS);
Pred = ICmpInst::getSwappedPredicate(Pred);
}
const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(LHSS);
if (!AR || AR->getLoop() != L)
return None;
return LoopICmp(Pred, AR, RHSS);
}
Value *LoopPredication::expandCheck(SCEVExpander &Expander,
Instruction *Guard,
ICmpInst::Predicate Pred, const SCEV *LHS,
const SCEV *RHS) {
Type *Ty = LHS->getType();
assert(Ty == RHS->getType() && "expandCheck operands have different types?");
if (SE->isLoopInvariant(LHS, L) && SE->isLoopInvariant(RHS, L)) {
IRBuilder<> Builder(Guard);
if (SE->isLoopEntryGuardedByCond(L, Pred, LHS, RHS))
return Builder.getTrue();
if (SE->isLoopEntryGuardedByCond(L, ICmpInst::getInversePredicate(Pred),
LHS, RHS))
return Builder.getFalse();
}
Value *LHSV =
Expander.expandCodeFor(LHS, Ty, findInsertPt(Expander, Guard, {LHS}));
Value *RHSV =
Expander.expandCodeFor(RHS, Ty, findInsertPt(Expander, Guard, {RHS}));
IRBuilder<> Builder(findInsertPt(Guard, {LHSV, RHSV}));
return Builder.CreateICmp(Pred, LHSV, RHSV);
}
static bool isSafeToTruncateWideIVType(const DataLayout &DL,
ScalarEvolution &SE,
const LoopICmp LatchCheck,
Type *RangeCheckType) {
if (!EnableIVTruncation)
return false;
assert(DL.getTypeSizeInBits(LatchCheck.IV->getType()).getFixedSize() >
DL.getTypeSizeInBits(RangeCheckType).getFixedSize() &&
"Expected latch check IV type to be larger than range check operand "
"type!");
auto *Limit = dyn_cast<SCEVConstant>(LatchCheck.Limit);
auto *Start = dyn_cast<SCEVConstant>(LatchCheck.IV->getStart());
if (!Limit || !Start)
return false;
if (!SE.getMonotonicPredicateType(LatchCheck.IV, LatchCheck.Pred))
return false;
auto RangeCheckTypeBitSize =
DL.getTypeSizeInBits(RangeCheckType).getFixedSize();
return Start->getAPInt().getActiveBits() < RangeCheckTypeBitSize &&
Limit->getAPInt().getActiveBits() < RangeCheckTypeBitSize;
}
static Optional<LoopICmp> generateLoopLatchCheck(const DataLayout &DL,
ScalarEvolution &SE,
const LoopICmp LatchCheck,
Type *RangeCheckType) {
auto *LatchType = LatchCheck.IV->getType();
if (RangeCheckType == LatchType)
return LatchCheck;
if (DL.getTypeSizeInBits(LatchType).getFixedSize() <
DL.getTypeSizeInBits(RangeCheckType).getFixedSize())
return None;
if (!isSafeToTruncateWideIVType(DL, SE, LatchCheck, RangeCheckType))
return None;
LoopICmp NewLatchCheck;
NewLatchCheck.Pred = LatchCheck.Pred;
NewLatchCheck.IV = dyn_cast<SCEVAddRecExpr>(
SE.getTruncateExpr(LatchCheck.IV, RangeCheckType));
if (!NewLatchCheck.IV)
return None;
NewLatchCheck.Limit = SE.getTruncateExpr(LatchCheck.Limit, RangeCheckType);
LLVM_DEBUG(dbgs() << "IV of type: " << *LatchType
<< "can be represented as range check type:"
<< *RangeCheckType << "\n");
LLVM_DEBUG(dbgs() << "LatchCheck.IV: " << *NewLatchCheck.IV << "\n");
LLVM_DEBUG(dbgs() << "LatchCheck.Limit: " << *NewLatchCheck.Limit << "\n");
return NewLatchCheck;
}
bool LoopPredication::isSupportedStep(const SCEV* Step) {
return Step->isOne() || (Step->isAllOnesValue() && EnableCountDownLoop);
}
Instruction *LoopPredication::findInsertPt(Instruction *Use,
ArrayRef<Value*> Ops) {
for (Value *Op : Ops)
if (!L->isLoopInvariant(Op))
return Use;
return Preheader->getTerminator();
}
Instruction *LoopPredication::findInsertPt(const SCEVExpander &Expander,
Instruction *Use,
ArrayRef<const SCEV *> Ops) {
for (const SCEV *Op : Ops)
if (!SE->isLoopInvariant(Op, L) ||
!Expander.isSafeToExpandAt(Op, Preheader->getTerminator()))
return Use;
return Preheader->getTerminator();
}
bool LoopPredication::isLoopInvariantValue(const SCEV* S) {
if (SE->isLoopInvariant(S, L))
return true;
if (const SCEVUnknown *U = dyn_cast<SCEVUnknown>(S))
if (const auto *LI = dyn_cast<LoadInst>(U->getValue()))
if (LI->isUnordered() && L->hasLoopInvariantOperands(LI))
if (AA->pointsToConstantMemory(LI->getOperand(0)) ||
LI->hasMetadata(LLVMContext::MD_invariant_load))
return true;
return false;
}
Optional<Value *> LoopPredication::widenICmpRangeCheckIncrementingLoop(
LoopICmp LatchCheck, LoopICmp RangeCheck,
SCEVExpander &Expander, Instruction *Guard) {
auto *Ty = RangeCheck.IV->getType();
const SCEV *GuardStart = RangeCheck.IV->getStart();
const SCEV *GuardLimit = RangeCheck.Limit;
const SCEV *LatchStart = LatchCheck.IV->getStart();
const SCEV *LatchLimit = LatchCheck.Limit;
if (!isLoopInvariantValue(GuardStart) ||
!isLoopInvariantValue(GuardLimit) ||
!isLoopInvariantValue(LatchStart) ||
!isLoopInvariantValue(LatchLimit)) {
LLVM_DEBUG(dbgs() << "Can't expand limit check!\n");
return None;
}
if (!Expander.isSafeToExpandAt(LatchStart, Guard) ||
!Expander.isSafeToExpandAt(LatchLimit, Guard)) {
LLVM_DEBUG(dbgs() << "Can't expand limit check!\n");
return None;
}
const SCEV *RHS =
SE->getAddExpr(SE->getMinusSCEV(GuardLimit, GuardStart),
SE->getMinusSCEV(LatchStart, SE->getOne(Ty)));
auto LimitCheckPred =
ICmpInst::getFlippedStrictnessPredicate(LatchCheck.Pred);
LLVM_DEBUG(dbgs() << "LHS: " << *LatchLimit << "\n");
LLVM_DEBUG(dbgs() << "RHS: " << *RHS << "\n");
LLVM_DEBUG(dbgs() << "Pred: " << LimitCheckPred << "\n");
auto *LimitCheck =
expandCheck(Expander, Guard, LimitCheckPred, LatchLimit, RHS);
auto *FirstIterationCheck = expandCheck(Expander, Guard, RangeCheck.Pred,
GuardStart, GuardLimit);
IRBuilder<> Builder(findInsertPt(Guard, {FirstIterationCheck, LimitCheck}));
return Builder.CreateAnd(FirstIterationCheck, LimitCheck);
}
Optional<Value *> LoopPredication::widenICmpRangeCheckDecrementingLoop(
LoopICmp LatchCheck, LoopICmp RangeCheck,
SCEVExpander &Expander, Instruction *Guard) {
auto *Ty = RangeCheck.IV->getType();
const SCEV *GuardStart = RangeCheck.IV->getStart();
const SCEV *GuardLimit = RangeCheck.Limit;
const SCEV *LatchStart = LatchCheck.IV->getStart();
const SCEV *LatchLimit = LatchCheck.Limit;
if (!isLoopInvariantValue(GuardStart) ||
!isLoopInvariantValue(GuardLimit) ||
!isLoopInvariantValue(LatchStart) ||
!isLoopInvariantValue(LatchLimit)) {
LLVM_DEBUG(dbgs() << "Can't expand limit check!\n");
return None;
}
if (!Expander.isSafeToExpandAt(LatchStart, Guard) ||
!Expander.isSafeToExpandAt(LatchLimit, Guard)) {
LLVM_DEBUG(dbgs() << "Can't expand limit check!\n");
return None;
}
auto *PostDecLatchCheckIV = LatchCheck.IV->getPostIncExpr(*SE);
if (RangeCheck.IV != PostDecLatchCheckIV) {
LLVM_DEBUG(dbgs() << "Not the same. PostDecLatchCheckIV: "
<< *PostDecLatchCheckIV
<< " and RangeCheckIV: " << *RangeCheck.IV << "\n");
return None;
}
auto LimitCheckPred =
ICmpInst::getFlippedStrictnessPredicate(LatchCheck.Pred);
auto *FirstIterationCheck = expandCheck(Expander, Guard,
ICmpInst::ICMP_ULT,
GuardStart, GuardLimit);
auto *LimitCheck = expandCheck(Expander, Guard, LimitCheckPred, LatchLimit,
SE->getOne(Ty));
IRBuilder<> Builder(findInsertPt(Guard, {FirstIterationCheck, LimitCheck}));
return Builder.CreateAnd(FirstIterationCheck, LimitCheck);
}
static void normalizePredicate(ScalarEvolution *SE, Loop *L,
LoopICmp& RC) {
if (ICmpInst::isEquality(RC.Pred) &&
RC.IV->getStepRecurrence(*SE)->isOne() &&
SE->isKnownPredicate(ICmpInst::ICMP_ULE, RC.IV->getStart(), RC.Limit))
RC.Pred = RC.Pred == ICmpInst::ICMP_NE ?
ICmpInst::ICMP_ULT : ICmpInst::ICMP_UGE;
}
Optional<Value *> LoopPredication::widenICmpRangeCheck(ICmpInst *ICI,
SCEVExpander &Expander,
Instruction *Guard) {
LLVM_DEBUG(dbgs() << "Analyzing ICmpInst condition:\n");
LLVM_DEBUG(ICI->dump());
auto RangeCheck = parseLoopICmp(ICI);
if (!RangeCheck) {
LLVM_DEBUG(dbgs() << "Failed to parse the loop latch condition!\n");
return None;
}
LLVM_DEBUG(dbgs() << "Guard check:\n");
LLVM_DEBUG(RangeCheck->dump());
if (RangeCheck->Pred != ICmpInst::ICMP_ULT) {
LLVM_DEBUG(dbgs() << "Unsupported range check predicate("
<< RangeCheck->Pred << ")!\n");
return None;
}
auto *RangeCheckIV = RangeCheck->IV;
if (!RangeCheckIV->isAffine()) {
LLVM_DEBUG(dbgs() << "Range check IV is not affine!\n");
return None;
}
auto *Step = RangeCheckIV->getStepRecurrence(*SE);
if (!isSupportedStep(Step)) {
LLVM_DEBUG(dbgs() << "Range check and latch have IVs different steps!\n");
return None;
}
auto *Ty = RangeCheckIV->getType();
auto CurrLatchCheckOpt = generateLoopLatchCheck(*DL, *SE, LatchCheck, Ty);
if (!CurrLatchCheckOpt) {
LLVM_DEBUG(dbgs() << "Failed to generate a loop latch check "
"corresponding to range type: "
<< *Ty << "\n");
return None;
}
LoopICmp CurrLatchCheck = *CurrLatchCheckOpt;
assert(Step->getType() ==
CurrLatchCheck.IV->getStepRecurrence(*SE)->getType() &&
"Range and latch steps should be of same type!");
if (Step != CurrLatchCheck.IV->getStepRecurrence(*SE)) {
LLVM_DEBUG(dbgs() << "Range and latch have different step values!\n");
return None;
}
if (Step->isOne())
return widenICmpRangeCheckIncrementingLoop(CurrLatchCheck, *RangeCheck,
Expander, Guard);
else {
assert(Step->isAllOnesValue() && "Step should be -1!");
return widenICmpRangeCheckDecrementingLoop(CurrLatchCheck, *RangeCheck,
Expander, Guard);
}
}
unsigned LoopPredication::collectChecks(SmallVectorImpl<Value *> &Checks,
Value *Condition,
SCEVExpander &Expander,
Instruction *Guard) {
unsigned NumWidened = 0;
SmallVector<Value *, 4> Worklist(1, Condition);
SmallPtrSet<Value *, 4> Visited;
Value *WideableCond = nullptr;
do {
Value *Condition = Worklist.pop_back_val();
if (!Visited.insert(Condition).second)
continue;
Value *LHS, *RHS;
using namespace llvm::PatternMatch;
if (match(Condition, m_And(m_Value(LHS), m_Value(RHS)))) {
Worklist.push_back(LHS);
Worklist.push_back(RHS);
continue;
}
if (match(Condition,
m_Intrinsic<Intrinsic::experimental_widenable_condition>())) {
WideableCond = Condition;
continue;
}
if (ICmpInst *ICI = dyn_cast<ICmpInst>(Condition)) {
if (auto NewRangeCheck = widenICmpRangeCheck(ICI, Expander,
Guard)) {
Checks.push_back(*NewRangeCheck);
NumWidened++;
continue;
}
}
Checks.push_back(Condition);
} while (!Worklist.empty());
if (WideableCond)
Checks.push_back(WideableCond);
return NumWidened;
}
bool LoopPredication::widenGuardConditions(IntrinsicInst *Guard,
SCEVExpander &Expander) {
LLVM_DEBUG(dbgs() << "Processing guard:\n");
LLVM_DEBUG(Guard->dump());
TotalConsidered++;
SmallVector<Value *, 4> Checks;
unsigned NumWidened = collectChecks(Checks, Guard->getOperand(0), Expander,
Guard);
if (NumWidened == 0)
return false;
TotalWidened += NumWidened;
IRBuilder<> Builder(findInsertPt(Guard, Checks));
Value *AllChecks = Builder.CreateAnd(Checks);
auto *OldCond = Guard->getOperand(0);
Guard->setOperand(0, AllChecks);
RecursivelyDeleteTriviallyDeadInstructions(OldCond, nullptr , MSSAU);
LLVM_DEBUG(dbgs() << "Widened checks = " << NumWidened << "\n");
return true;
}
bool LoopPredication::widenWidenableBranchGuardConditions(
BranchInst *BI, SCEVExpander &Expander) {
assert(isGuardAsWidenableBranch(BI) && "Must be!");
LLVM_DEBUG(dbgs() << "Processing guard:\n");
LLVM_DEBUG(BI->dump());
TotalConsidered++;
SmallVector<Value *, 4> Checks;
unsigned NumWidened = collectChecks(Checks, BI->getCondition(),
Expander, BI);
if (NumWidened == 0)
return false;
TotalWidened += NumWidened;
IRBuilder<> Builder(findInsertPt(BI, Checks));
Value *AllChecks = Builder.CreateAnd(Checks);
auto *OldCond = BI->getCondition();
BI->setCondition(AllChecks);
RecursivelyDeleteTriviallyDeadInstructions(OldCond, nullptr , MSSAU);
assert(isGuardAsWidenableBranch(BI) &&
"Stopped being a guard after transform?");
LLVM_DEBUG(dbgs() << "Widened checks = " << NumWidened << "\n");
return true;
}
Optional<LoopICmp> LoopPredication::parseLoopLatchICmp() {
using namespace PatternMatch;
BasicBlock *LoopLatch = L->getLoopLatch();
if (!LoopLatch) {
LLVM_DEBUG(dbgs() << "The loop doesn't have a single latch!\n");
return None;
}
auto *BI = dyn_cast<BranchInst>(LoopLatch->getTerminator());
if (!BI || !BI->isConditional()) {
LLVM_DEBUG(dbgs() << "Failed to match the latch terminator!\n");
return None;
}
BasicBlock *TrueDest = BI->getSuccessor(0);
assert(
(TrueDest == L->getHeader() || BI->getSuccessor(1) == L->getHeader()) &&
"One of the latch's destinations must be the header");
auto *ICI = dyn_cast<ICmpInst>(BI->getCondition());
if (!ICI) {
LLVM_DEBUG(dbgs() << "Failed to match the latch condition!\n");
return None;
}
auto Result = parseLoopICmp(ICI);
if (!Result) {
LLVM_DEBUG(dbgs() << "Failed to parse the loop latch condition!\n");
return None;
}
if (TrueDest != L->getHeader())
Result->Pred = ICmpInst::getInversePredicate(Result->Pred);
if (!Result->IV->isAffine()) {
LLVM_DEBUG(dbgs() << "The induction variable is not affine!\n");
return None;
}
auto *Step = Result->IV->getStepRecurrence(*SE);
if (!isSupportedStep(Step)) {
LLVM_DEBUG(dbgs() << "Unsupported loop stride(" << *Step << ")!\n");
return None;
}
auto IsUnsupportedPredicate = [](const SCEV *Step, ICmpInst::Predicate Pred) {
if (Step->isOne()) {
return Pred != ICmpInst::ICMP_ULT && Pred != ICmpInst::ICMP_SLT &&
Pred != ICmpInst::ICMP_ULE && Pred != ICmpInst::ICMP_SLE;
} else {
assert(Step->isAllOnesValue() && "Step should be -1!");
return Pred != ICmpInst::ICMP_UGT && Pred != ICmpInst::ICMP_SGT &&
Pred != ICmpInst::ICMP_UGE && Pred != ICmpInst::ICMP_SGE;
}
};
normalizePredicate(SE, L, *Result);
if (IsUnsupportedPredicate(Step, Result->Pred)) {
LLVM_DEBUG(dbgs() << "Unsupported loop latch predicate(" << Result->Pred
<< ")!\n");
return None;
}
return Result;
}
bool LoopPredication::isLoopProfitableToPredicate() {
if (SkipProfitabilityChecks)
return true;
SmallVector<std::pair<BasicBlock *, BasicBlock *>, 8> ExitEdges;
L->getExitEdges(ExitEdges);
if (ExitEdges.size() == 1)
return true;
auto *LatchBlock = L->getLoopLatch();
assert(LatchBlock && "Should have a single latch at this point!");
auto *LatchTerm = LatchBlock->getTerminator();
assert(LatchTerm->getNumSuccessors() == 2 &&
"expected to be an exiting block with 2 succs!");
unsigned LatchBrExitIdx =
LatchTerm->getSuccessor(0) == L->getHeader() ? 1 : 0;
auto *LatchExitBlock = LatchTerm->getSuccessor(LatchBrExitIdx);
if (isa<UnreachableInst>(LatchTerm) ||
LatchExitBlock->getTerminatingDeoptimizeCall())
return false;
auto IsValidProfileData = [](MDNode *ProfileData, const Instruction *Term) {
if (!ProfileData || !ProfileData->getOperand(0))
return false;
if (MDString *MDS = dyn_cast<MDString>(ProfileData->getOperand(0)))
if (!MDS->getString().equals("branch_weights"))
return false;
if (ProfileData->getNumOperands() != 1 + Term->getNumSuccessors())
return false;
return true;
};
MDNode *LatchProfileData = LatchTerm->getMetadata(LLVMContext::MD_prof);
if (!IsValidProfileData(LatchProfileData, LatchTerm))
return true;
auto ComputeBranchProbability =
[&](const BasicBlock *ExitingBlock,
const BasicBlock *ExitBlock) -> BranchProbability {
auto *Term = ExitingBlock->getTerminator();
MDNode *ProfileData = Term->getMetadata(LLVMContext::MD_prof);
unsigned NumSucc = Term->getNumSuccessors();
if (IsValidProfileData(ProfileData, Term)) {
uint64_t Numerator = 0, Denominator = 0, ProfVal = 0;
for (unsigned i = 0; i < NumSucc; i++) {
ConstantInt *CI =
mdconst::extract<ConstantInt>(ProfileData->getOperand(i + 1));
ProfVal = CI->getValue().getZExtValue();
if (Term->getSuccessor(i) == ExitBlock)
Numerator += ProfVal;
Denominator += ProfVal;
}
return BranchProbability::getBranchProbability(Numerator, Denominator);
} else {
assert(LatchBlock != ExitingBlock &&
"Latch term should always have profile data!");
return BranchProbability::getBranchProbability(1, NumSucc);
}
};
BranchProbability LatchExitProbability =
ComputeBranchProbability(LatchBlock, LatchExitBlock);
float ScaleFactor = LatchExitProbabilityScale;
if (ScaleFactor < 1) {
LLVM_DEBUG(
dbgs()
<< "Ignored user setting for loop-predication-latch-probability-scale: "
<< LatchExitProbabilityScale << "\n");
LLVM_DEBUG(dbgs() << "The value is set to 1.0\n");
ScaleFactor = 1.0;
}
const auto LatchProbabilityThreshold = LatchExitProbability * ScaleFactor;
for (const auto &ExitEdge : ExitEdges) {
BranchProbability ExitingBlockProbability =
ComputeBranchProbability(ExitEdge.first, ExitEdge.second);
if (ExitingBlockProbability > LatchProbabilityThreshold)
return false;
}
return true;
}
static BranchInst *FindWidenableTerminatorAboveLoop(Loop *L, LoopInfo &LI) {
BasicBlock *BB = L->getLoopPreheader();
if (!BB)
return nullptr;
do {
if (BasicBlock *Pred = BB->getSinglePredecessor())
if (BB == Pred->getSingleSuccessor()) {
BB = Pred;
continue;
}
break;
} while (true);
if (BasicBlock *Pred = BB->getSinglePredecessor()) {
auto *Term = Pred->getTerminator();
Value *Cond, *WC;
BasicBlock *IfTrueBB, *IfFalseBB;
if (parseWidenableBranch(Term, Cond, WC, IfTrueBB, IfFalseBB) &&
IfTrueBB == BB)
return cast<BranchInst>(Term);
}
return nullptr;
}
static const SCEV *getMinAnalyzeableBackedgeTakenCount(ScalarEvolution &SE,
DominatorTree &DT,
Loop *L) {
SmallVector<BasicBlock *, 16> ExitingBlocks;
L->getExitingBlocks(ExitingBlocks);
SmallVector<const SCEV *, 4> ExitCounts;
for (BasicBlock *ExitingBB : ExitingBlocks) {
const SCEV *ExitCount = SE.getExitCount(L, ExitingBB);
if (isa<SCEVCouldNotCompute>(ExitCount))
continue;
assert(DT.dominates(ExitingBB, L->getLoopLatch()) &&
"We should only have known counts for exiting blocks that "
"dominate latch!");
ExitCounts.push_back(ExitCount);
}
if (ExitCounts.size() < 2)
return SE.getCouldNotCompute();
return SE.getUMinFromMismatchedTypes(ExitCounts);
}
bool LoopPredication::predicateLoopExits(Loop *L, SCEVExpander &Rewriter) {
SmallVector<BasicBlock *, 16> ExitingBlocks;
L->getExitingBlocks(ExitingBlocks);
if (ExitingBlocks.empty())
return false;
auto *Latch = L->getLoopLatch();
if (!Latch)
return false;
auto *WidenableBR = FindWidenableTerminatorAboveLoop(L, *LI);
if (!WidenableBR)
return false;
const SCEV *LatchEC = SE->getExitCount(L, Latch);
if (isa<SCEVCouldNotCompute>(LatchEC))
return false;
bool ChangedLoop = false;
for (auto *ExitingBB : ExitingBlocks) {
if (LI->getLoopFor(ExitingBB) != L)
continue;
auto *BI = dyn_cast<BranchInst>(ExitingBB->getTerminator());
if (!BI)
continue;
Use *Cond, *WC;
BasicBlock *IfTrueBB, *IfFalseBB;
if (parseWidenableBranch(BI, Cond, WC, IfTrueBB, IfFalseBB) &&
L->contains(IfTrueBB)) {
WC->set(ConstantInt::getTrue(IfTrueBB->getContext()));
ChangedLoop = true;
}
}
if (ChangedLoop)
SE->forgetLoop(L);
const SCEV *MinEC = getMinAnalyzeableBackedgeTakenCount(*SE, *DT, L);
if (isa<SCEVCouldNotCompute>(MinEC) || MinEC->getType()->isPointerTy() ||
!SE->isLoopInvariant(MinEC, L) ||
!Rewriter.isSafeToExpandAt(MinEC, WidenableBR))
return ChangedLoop;
auto *IP = cast<Instruction>(WidenableBR->getCondition());
IP->moveBefore(WidenableBR);
if (MSSAU)
if (auto *MUD = MSSAU->getMemorySSA()->getMemoryAccess(IP))
MSSAU->moveToPlace(MUD, WidenableBR->getParent(),
MemorySSA::BeforeTerminator);
Rewriter.setInsertPoint(IP);
IRBuilder<> B(IP);
bool InvalidateLoop = false;
Value *MinECV = nullptr; for (BasicBlock *ExitingBB : ExitingBlocks) {
if (LI->getLoopFor(ExitingBB) != L)
continue;
auto *BI = dyn_cast<BranchInst>(ExitingBB->getTerminator());
if (!BI)
continue;
if (isa<Constant>(BI->getCondition()))
continue;
const SCEV *ExitCount = SE->getExitCount(L, ExitingBB);
if (isa<SCEVCouldNotCompute>(ExitCount) ||
ExitCount->getType()->isPointerTy() ||
!Rewriter.isSafeToExpandAt(ExitCount, WidenableBR))
continue;
const bool ExitIfTrue = !L->contains(*succ_begin(ExitingBB));
BasicBlock *ExitBB = BI->getSuccessor(ExitIfTrue ? 0 : 1);
if (!ExitBB->getPostdominatingDeoptimizeCall())
continue;
Value *ECV = Rewriter.expandCodeFor(ExitCount);
if (!MinECV)
MinECV = Rewriter.expandCodeFor(MinEC);
Value *RHS = MinECV;
if (ECV->getType() != RHS->getType()) {
Type *WiderTy = SE->getWiderType(ECV->getType(), RHS->getType());
ECV = B.CreateZExt(ECV, WiderTy);
RHS = B.CreateZExt(RHS, WiderTy);
}
assert(!Latch || DT->dominates(ExitingBB, Latch));
Value *NewCond = B.CreateICmp(ICmpInst::ICMP_UGT, ECV, RHS);
NewCond = B.CreateFreeze(NewCond);
widenWidenableBranch(WidenableBR, NewCond);
Value *OldCond = BI->getCondition();
BI->setCondition(ConstantInt::get(OldCond->getType(), !ExitIfTrue));
InvalidateLoop = true;
}
if (InvalidateLoop)
SE->forgetLoop(L);
return true;
}
bool LoopPredication::runOnLoop(Loop *Loop) {
L = Loop;
LLVM_DEBUG(dbgs() << "Analyzing ");
LLVM_DEBUG(L->dump());
Module *M = L->getHeader()->getModule();
auto *GuardDecl =
M->getFunction(Intrinsic::getName(Intrinsic::experimental_guard));
bool HasIntrinsicGuards = GuardDecl && !GuardDecl->use_empty();
auto *WCDecl = M->getFunction(
Intrinsic::getName(Intrinsic::experimental_widenable_condition));
bool HasWidenableConditions =
PredicateWidenableBranchGuards && WCDecl && !WCDecl->use_empty();
if (!HasIntrinsicGuards && !HasWidenableConditions)
return false;
DL = &M->getDataLayout();
Preheader = L->getLoopPreheader();
if (!Preheader)
return false;
auto LatchCheckOpt = parseLoopLatchICmp();
if (!LatchCheckOpt)
return false;
LatchCheck = *LatchCheckOpt;
LLVM_DEBUG(dbgs() << "Latch check:\n");
LLVM_DEBUG(LatchCheck.dump());
if (!isLoopProfitableToPredicate()) {
LLVM_DEBUG(dbgs() << "Loop not profitable to predicate!\n");
return false;
}
SmallVector<IntrinsicInst *, 4> Guards;
SmallVector<BranchInst *, 4> GuardsAsWidenableBranches;
for (const auto BB : L->blocks()) {
for (auto &I : *BB)
if (isGuard(&I))
Guards.push_back(cast<IntrinsicInst>(&I));
if (PredicateWidenableBranchGuards &&
isGuardAsWidenableBranch(BB->getTerminator()))
GuardsAsWidenableBranches.push_back(
cast<BranchInst>(BB->getTerminator()));
}
SCEVExpander Expander(*SE, *DL, "loop-predication");
bool Changed = false;
for (auto *Guard : Guards)
Changed |= widenGuardConditions(Guard, Expander);
for (auto *Guard : GuardsAsWidenableBranches)
Changed |= widenWidenableBranchGuardConditions(Guard, Expander);
Changed |= predicateLoopExits(L, Expander);
if (MSSAU && VerifyMemorySSA)
MSSAU->getMemorySSA()->verifyMemorySSA();
return Changed;
}