#include "llvm/Transforms/Scalar/LoopDeletion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopIterator.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/InitializePasses.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/LoopPassManager.h"
#include "llvm/Transforms/Utils/LoopUtils.h"
using namespace llvm;
#define DEBUG_TYPE "loop-delete"
STATISTIC(NumDeleted, "Number of loops deleted");
STATISTIC(NumBackedgesBroken,
"Number of loops for which we managed to break the backedge");
static cl::opt<bool> EnableSymbolicExecution(
"loop-deletion-enable-symbolic-execution", cl::Hidden, cl::init(true),
cl::desc("Break backedge through symbolic execution of 1st iteration "
"attempting to prove that the backedge is never taken"));
enum class LoopDeletionResult {
Unmodified,
Modified,
Deleted,
};
static LoopDeletionResult merge(LoopDeletionResult A, LoopDeletionResult B) {
if (A == LoopDeletionResult::Deleted || B == LoopDeletionResult::Deleted)
return LoopDeletionResult::Deleted;
if (A == LoopDeletionResult::Modified || B == LoopDeletionResult::Modified)
return LoopDeletionResult::Modified;
return LoopDeletionResult::Unmodified;
}
static bool isLoopDead(Loop *L, ScalarEvolution &SE,
SmallVectorImpl<BasicBlock *> &ExitingBlocks,
BasicBlock *ExitBlock, bool &Changed,
BasicBlock *Preheader, LoopInfo &LI) {
bool AllEntriesInvariant = true;
bool AllOutgoingValuesSame = true;
if (!L->hasNoExitBlocks()) {
for (PHINode &P : ExitBlock->phis()) {
Value *incoming = P.getIncomingValueForBlock(ExitingBlocks[0]);
AllOutgoingValuesSame =
all_of(makeArrayRef(ExitingBlocks).slice(1), [&](BasicBlock *BB) {
return incoming == P.getIncomingValueForBlock(BB);
});
if (!AllOutgoingValuesSame)
break;
if (Instruction *I = dyn_cast<Instruction>(incoming))
if (!L->makeLoopInvariant(I, Changed, Preheader->getTerminator())) {
AllEntriesInvariant = false;
break;
}
}
}
if (Changed)
SE.forgetLoopDispositions(L);
if (!AllEntriesInvariant || !AllOutgoingValuesSame)
return false;
for (auto &I : L->blocks())
if (any_of(*I, [](Instruction &I) {
return I.mayHaveSideEffects() && !I.isDroppable();
}))
return false;
if (L->getHeader()->getParent()->mustProgress())
return true;
LoopBlocksRPO RPOT(L);
RPOT.perform(&LI);
if (containsIrreducibleCFG<const BasicBlock *>(RPOT, LI))
return false;
SmallVector<Loop *, 8> WorkList;
WorkList.push_back(L);
while (!WorkList.empty()) {
Loop *Current = WorkList.pop_back_val();
if (hasMustProgress(Current))
continue;
const SCEV *S = SE.getConstantMaxBackedgeTakenCount(Current);
if (isa<SCEVCouldNotCompute>(S)) {
LLVM_DEBUG(
dbgs() << "Could not compute SCEV MaxBackedgeTakenCount and was "
"not required to make progress.\n");
return false;
}
WorkList.append(Current->begin(), Current->end());
}
return true;
}
static bool isLoopNeverExecuted(Loop *L) {
using namespace PatternMatch;
auto *Preheader = L->getLoopPreheader();
assert(Preheader && "Needs preheader!");
if (Preheader->isEntryBlock())
return false;
for (auto *Pred: predecessors(Preheader)) {
BasicBlock *Taken, *NotTaken;
ConstantInt *Cond;
if (!match(Pred->getTerminator(),
m_Br(m_ConstantInt(Cond), Taken, NotTaken)))
return false;
if (!Cond->getZExtValue())
std::swap(Taken, NotTaken);
if (Taken == Preheader)
return false;
}
assert(!pred_empty(Preheader) &&
"Preheader should have predecessors at this point!");
return true;
}
static Value *
getValueOnFirstIteration(Value *V, DenseMap<Value *, Value *> &FirstIterValue,
const SimplifyQuery &SQ) {
if (!isa<Instruction>(V))
return V;
auto Existing = FirstIterValue.find(V);
if (Existing != FirstIterValue.end())
return Existing->second;
Value *FirstIterV = nullptr;
if (auto *BO = dyn_cast<BinaryOperator>(V)) {
Value *LHS =
getValueOnFirstIteration(BO->getOperand(0), FirstIterValue, SQ);
Value *RHS =
getValueOnFirstIteration(BO->getOperand(1), FirstIterValue, SQ);
FirstIterV = simplifyBinOp(BO->getOpcode(), LHS, RHS, SQ);
} else if (auto *Cmp = dyn_cast<ICmpInst>(V)) {
Value *LHS =
getValueOnFirstIteration(Cmp->getOperand(0), FirstIterValue, SQ);
Value *RHS =
getValueOnFirstIteration(Cmp->getOperand(1), FirstIterValue, SQ);
FirstIterV = simplifyICmpInst(Cmp->getPredicate(), LHS, RHS, SQ);
} else if (auto *Select = dyn_cast<SelectInst>(V)) {
Value *Cond =
getValueOnFirstIteration(Select->getCondition(), FirstIterValue, SQ);
if (auto *C = dyn_cast<ConstantInt>(Cond)) {
auto *Selected = C->isAllOnesValue() ? Select->getTrueValue()
: Select->getFalseValue();
FirstIterV = getValueOnFirstIteration(Selected, FirstIterValue, SQ);
}
}
if (!FirstIterV)
FirstIterV = V;
FirstIterValue[V] = FirstIterV;
return FirstIterV;
}
static bool canProveExitOnFirstIteration(Loop *L, DominatorTree &DT,
LoopInfo &LI) {
if (!EnableSymbolicExecution)
return false;
BasicBlock *Predecessor = L->getLoopPredecessor();
BasicBlock *Latch = L->getLoopLatch();
if (!Predecessor || !Latch)
return false;
LoopBlocksRPO RPOT(L);
RPOT.perform(&LI);
if (containsIrreducibleCFG<const BasicBlock *>(RPOT, LI))
return false;
BasicBlock *Header = L->getHeader();
SmallPtrSet<BasicBlock *, 4> LiveBlocks;
DenseSet<BasicBlockEdge> LiveEdges;
LiveBlocks.insert(Header);
SmallPtrSet<BasicBlock *, 4> Visited;
auto MarkLiveEdge = [&](BasicBlock *From, BasicBlock *To) {
assert(LiveBlocks.count(From) && "Must be live!");
assert((LI.isLoopHeader(To) || !Visited.count(To)) &&
"Only canonical backedges are allowed. Irreducible CFG?");
assert((LiveBlocks.count(To) || !Visited.count(To)) &&
"We already discarded this block as dead!");
LiveBlocks.insert(To);
LiveEdges.insert({ From, To });
};
auto MarkAllSuccessorsLive = [&](BasicBlock *BB) {
for (auto *Succ : successors(BB))
MarkLiveEdge(BB, Succ);
};
auto GetSoleInputOnFirstIteration = [&](PHINode & PN)->Value * {
BasicBlock *BB = PN.getParent();
bool HasLivePreds = false;
(void)HasLivePreds;
if (BB == Header)
return PN.getIncomingValueForBlock(Predecessor);
Value *OnlyInput = nullptr;
for (auto *Pred : predecessors(BB))
if (LiveEdges.count({ Pred, BB })) {
HasLivePreds = true;
Value *Incoming = PN.getIncomingValueForBlock(Pred);
if (isa<UndefValue>(Incoming))
continue;
if (OnlyInput && OnlyInput != Incoming)
return nullptr;
OnlyInput = Incoming;
}
assert(HasLivePreds && "No live predecessors?");
return OnlyInput ? OnlyInput : UndefValue::get(PN.getType());
};
DenseMap<Value *, Value *> FirstIterValue;
auto &DL = Header->getModule()->getDataLayout();
const SimplifyQuery SQ(DL);
for (auto *BB : RPOT) {
Visited.insert(BB);
if (!LiveBlocks.count(BB))
continue;
if (LI.getLoopFor(BB) != L) {
MarkAllSuccessorsLive(BB);
continue;
}
for (auto &PN : BB->phis()) {
if (!PN.getType()->isIntegerTy())
continue;
auto *Incoming = GetSoleInputOnFirstIteration(PN);
if (Incoming && DT.dominates(Incoming, BB->getTerminator())) {
Value *FirstIterV =
getValueOnFirstIteration(Incoming, FirstIterValue, SQ);
FirstIterValue[&PN] = FirstIterV;
}
}
using namespace PatternMatch;
Value *Cond;
BasicBlock *IfTrue, *IfFalse;
auto *Term = BB->getTerminator();
if (match(Term, m_Br(m_Value(Cond),
m_BasicBlock(IfTrue), m_BasicBlock(IfFalse)))) {
auto *ICmp = dyn_cast<ICmpInst>(Cond);
if (!ICmp || !ICmp->getType()->isIntegerTy()) {
MarkAllSuccessorsLive(BB);
continue;
}
auto *KnownCondition = getValueOnFirstIteration(ICmp, FirstIterValue, SQ);
if (KnownCondition == ICmp) {
MarkAllSuccessorsLive(BB);
continue;
}
if (isa<UndefValue>(KnownCondition)) {
if (L->contains(IfTrue) && L->contains(IfFalse))
MarkLiveEdge(BB, IfTrue);
continue;
}
auto *ConstCondition = dyn_cast<ConstantInt>(KnownCondition);
if (!ConstCondition) {
MarkAllSuccessorsLive(BB);
continue;
}
if (ConstCondition->isAllOnesValue())
MarkLiveEdge(BB, IfTrue);
else
MarkLiveEdge(BB, IfFalse);
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(Term)) {
auto *SwitchValue = SI->getCondition();
auto *SwitchValueOnFirstIter =
getValueOnFirstIteration(SwitchValue, FirstIterValue, SQ);
auto *ConstSwitchValue = dyn_cast<ConstantInt>(SwitchValueOnFirstIter);
if (!ConstSwitchValue) {
MarkAllSuccessorsLive(BB);
continue;
}
auto CaseIterator = SI->findCaseValue(ConstSwitchValue);
MarkLiveEdge(BB, CaseIterator->getCaseSuccessor());
} else {
MarkAllSuccessorsLive(BB);
continue;
}
}
return !LiveEdges.count({ Latch, Header });
}
static LoopDeletionResult
breakBackedgeIfNotTaken(Loop *L, DominatorTree &DT, ScalarEvolution &SE,
LoopInfo &LI, MemorySSA *MSSA,
OptimizationRemarkEmitter &ORE) {
assert(L->isLCSSAForm(DT) && "Expected LCSSA!");
if (!L->getLoopLatch())
return LoopDeletionResult::Unmodified;
auto *BTCMax = SE.getConstantMaxBackedgeTakenCount(L);
if (!BTCMax->isZero()) {
auto *BTC = SE.getBackedgeTakenCount(L);
if (!BTC->isZero()) {
if (!isa<SCEVCouldNotCompute>(BTC) && SE.isKnownNonZero(BTC))
return LoopDeletionResult::Unmodified;
if (!canProveExitOnFirstIteration(L, DT, LI))
return LoopDeletionResult::Unmodified;
}
}
++NumBackedgesBroken;
breakLoopBackedge(L, DT, SE, LI, MSSA);
return LoopDeletionResult::Deleted;
}
static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT,
ScalarEvolution &SE, LoopInfo &LI,
MemorySSA *MSSA,
OptimizationRemarkEmitter &ORE) {
assert(L->isLCSSAForm(DT) && "Expected LCSSA!");
BasicBlock *Preheader = L->getLoopPreheader();
if (!Preheader || !L->hasDedicatedExits()) {
LLVM_DEBUG(
dbgs()
<< "Deletion requires Loop with preheader and dedicated exits.\n");
return LoopDeletionResult::Unmodified;
}
BasicBlock *ExitBlock = L->getUniqueExitBlock();
if (ExitBlock && isLoopNeverExecuted(L)) {
LLVM_DEBUG(dbgs() << "Loop is proven to never execute, delete it!");
SE.forgetLoop(L);
for (PHINode &P : ExitBlock->phis()) {
std::fill(P.incoming_values().begin(), P.incoming_values().end(),
PoisonValue::get(P.getType()));
}
ORE.emit([&]() {
return OptimizationRemark(DEBUG_TYPE, "NeverExecutes", L->getStartLoc(),
L->getHeader())
<< "Loop deleted because it never executes";
});
deleteDeadLoop(L, &DT, &SE, &LI, MSSA);
++NumDeleted;
return LoopDeletionResult::Deleted;
}
SmallVector<BasicBlock *, 4> ExitingBlocks;
L->getExitingBlocks(ExitingBlocks);
if (!ExitBlock && !L->hasNoExitBlocks()) {
LLVM_DEBUG(dbgs() << "Deletion requires at most one exit block.\n");
return LoopDeletionResult::Unmodified;
}
bool Changed = false;
if (!isLoopDead(L, SE, ExitingBlocks, ExitBlock, Changed, Preheader, LI)) {
LLVM_DEBUG(dbgs() << "Loop is not invariant, cannot delete.\n");
return Changed ? LoopDeletionResult::Modified
: LoopDeletionResult::Unmodified;
}
LLVM_DEBUG(dbgs() << "Loop is invariant, delete it!");
ORE.emit([&]() {
return OptimizationRemark(DEBUG_TYPE, "Invariant", L->getStartLoc(),
L->getHeader())
<< "Loop deleted because it is invariant";
});
deleteDeadLoop(L, &DT, &SE, &LI, MSSA);
++NumDeleted;
return LoopDeletionResult::Deleted;
}
PreservedAnalyses LoopDeletionPass::run(Loop &L, LoopAnalysisManager &AM,
LoopStandardAnalysisResults &AR,
LPMUpdater &Updater) {
LLVM_DEBUG(dbgs() << "Analyzing Loop for deletion: ");
LLVM_DEBUG(L.dump());
std::string LoopName = std::string(L.getName());
OptimizationRemarkEmitter ORE(L.getHeader()->getParent());
auto Result = deleteLoopIfDead(&L, AR.DT, AR.SE, AR.LI, AR.MSSA, ORE);
if (Result != LoopDeletionResult::Deleted)
Result = merge(Result, breakBackedgeIfNotTaken(&L, AR.DT, AR.SE, AR.LI,
AR.MSSA, ORE));
if (Result == LoopDeletionResult::Unmodified)
return PreservedAnalyses::all();
if (Result == LoopDeletionResult::Deleted)
Updater.markLoopAsDeleted(L, LoopName);
auto PA = getLoopPassPreservedAnalyses();
if (AR.MSSA)
PA.preserve<MemorySSAAnalysis>();
return PA;
}
namespace {
class LoopDeletionLegacyPass : public LoopPass {
public:
static char ID; LoopDeletionLegacyPass() : LoopPass(ID) {
initializeLoopDeletionLegacyPassPass(*PassRegistry::getPassRegistry());
}
bool runOnLoop(Loop *L, LPPassManager &) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addPreserved<MemorySSAWrapperPass>();
getLoopAnalysisUsage(AU);
}
};
}
char LoopDeletionLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(LoopDeletionLegacyPass, "loop-deletion",
"Delete dead loops", false, false)
INITIALIZE_PASS_DEPENDENCY(LoopPass)
INITIALIZE_PASS_END(LoopDeletionLegacyPass, "loop-deletion",
"Delete dead loops", false, false)
Pass *llvm::createLoopDeletionPass() { return new LoopDeletionLegacyPass(); }
bool LoopDeletionLegacyPass::runOnLoop(Loop *L, LPPassManager &LPM) {
if (skipLoop(L))
return false;
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
ScalarEvolution &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
auto *MSSAAnalysis = getAnalysisIfAvailable<MemorySSAWrapperPass>();
MemorySSA *MSSA = nullptr;
if (MSSAAnalysis)
MSSA = &MSSAAnalysis->getMSSA();
OptimizationRemarkEmitter ORE(L->getHeader()->getParent());
LLVM_DEBUG(dbgs() << "Analyzing Loop for deletion: ");
LLVM_DEBUG(L->dump());
LoopDeletionResult Result = deleteLoopIfDead(L, DT, SE, LI, MSSA, ORE);
if (Result != LoopDeletionResult::Deleted)
Result = merge(Result, breakBackedgeIfNotTaken(L, DT, SE, LI, MSSA, ORE));
if (Result == LoopDeletionResult::Deleted)
LPM.markLoopAsDeleted(*L);
return Result != LoopDeletionResult::Unmodified;
}