//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AliasSetTracker.h"
#include "llvm/Analysis/GuardUtils.h"
#include "llvm/Analysis/Loads.h"
#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/LoopIterator.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/MemorySSA.h"
#define DEBUG_TYPE "licm"
+STATISTIC(NumCreatedBlocks, "Number of blocks created");
+STATISTIC(NumClonedBranches, "Number of branches cloned");
STATISTIC(NumSunk, "Number of instructions sunk out of loop");
STATISTIC(NumHoisted, "Number of instructions hoisted out of loop");
STATISTIC(NumMovedLoads, "Number of load insts hoisted or sunk");
DisablePromotion("disable-licm-promotion", cl::Hidden, cl::init(false),
cl::desc("Disable memory promotion in LICM pass"));
+static cl::opt<bool> ControlFlowHoisting(
+ "licm-control-flow-hoisting", cl::Hidden, cl::init(false),
+ cl::desc("Enable control flow (and PHI) hoisting in LICM"));
+
static cl::opt<uint32_t> MaxNumUsesTraversed(
"licm-max-num-uses-traversed", cl::Hidden, cl::init(8),
cl::desc("Max num uses visited for identifying load "
const LoopSafetyInfo *SafetyInfo,
TargetTransformInfo *TTI, bool &FreeInLoop);
static void hoist(Instruction &I, const DominatorTree *DT, const Loop *CurLoop,
- ICFLoopSafetyInfo *SafetyInfo,
+ BasicBlock *Dest, ICFLoopSafetyInfo *SafetyInfo,
OptimizationRemarkEmitter *ORE);
static bool sink(Instruction &I, LoopInfo *LI, DominatorTree *DT,
const Loop *CurLoop, ICFLoopSafetyInfo *SafetyInfo,
return Changed;
}
+// This is a helper class for hoistRegion to make it able to hoist control flow
+// in order to be able to hoist phis. The way this works is that we initially
+// start hoisting to the loop preheader, and when we see a loop invariant branch
+// we make note of this. When we then come to hoist an instruction that's
+// conditional on such a branch we duplicate the branch and the relevant control
+// flow, then hoist the instruction into the block corresponding to its original
+// block in the duplicated control flow.
+class ControlFlowHoister {
+private:
+ // Information about the loop we are hoisting from
+ LoopInfo *LI;
+ DominatorTree *DT;
+ Loop *CurLoop;
+
+ // A map of blocks in the loop to the block their instructions will be hoisted
+ // to.
+ DenseMap<BasicBlock *, BasicBlock *> HoistDestinationMap;
+
+ // The branches that we can hoist, mapped to the block that marks a
+ // convergence point of their control flow.
+ DenseMap<BranchInst *, BasicBlock *> HoistableBranches;
+
+public:
+ ControlFlowHoister(LoopInfo *LI, DominatorTree *DT, Loop *CurLoop)
+ : LI(LI), DT(DT), CurLoop(CurLoop) {}
+
+ void registerPossiblyHoistableBranch(BranchInst *BI) {
+ // We can only hoist conditional branches with loop invariant operands.
+ if (!ControlFlowHoisting || !BI->isConditional() ||
+ !CurLoop->hasLoopInvariantOperands(BI))
+ return;
+
+ // The branch destinations need to be in the loop, and we don't gain
+ // anything by duplicating conditional branches with duplicate successors,
+ // as it's essentially the same as an unconditional branch.
+ BasicBlock *TrueDest = BI->getSuccessor(0);
+ BasicBlock *FalseDest = BI->getSuccessor(1);
+ if (!CurLoop->contains(TrueDest) || !CurLoop->contains(FalseDest) ||
+ TrueDest == FalseDest)
+ return;
+
+ // We can hoist BI if one branch destination is the successor of the other,
+ // or both have common successor which we check by seeing if the
+ // intersection of their successors is non-empty.
+ // TODO: This could be expanded to allowing branches where both ends
+ // eventually converge to a single block.
+ SmallPtrSet<BasicBlock *, 4> TrueDestSucc, FalseDestSucc;
+ TrueDestSucc.insert(succ_begin(TrueDest), succ_end(TrueDest));
+ FalseDestSucc.insert(succ_begin(FalseDest), succ_end(FalseDest));
+ BasicBlock *CommonSucc = nullptr;
+ if (TrueDestSucc.count(FalseDest)) {
+ CommonSucc = FalseDest;
+ } else if (FalseDestSucc.count(TrueDest)) {
+ CommonSucc = TrueDest;
+ } else {
+ set_intersect(TrueDestSucc, FalseDestSucc);
+ // If there's one common successor use that.
+ if (TrueDestSucc.size() == 1)
+ CommonSucc = *TrueDestSucc.begin();
+ // If there's more than one pick whichever appears first in the block list
+ // (we can't use the value returned by TrueDestSucc.begin() as it's
+ // unpredicatable which element gets returned).
+ else if (!TrueDestSucc.empty()) {
+ Function *F = TrueDest->getParent();
+ auto IsSucc = [&](BasicBlock &BB) { return TrueDestSucc.count(&BB); };
+ auto It = std::find_if(F->begin(), F->end(), IsSucc);
+ assert(It != F->end() && "Could not find successor in function");
+ CommonSucc = &*It;
+ }
+ }
+ // The common successor has to be dominated by the branch, as otherwise
+ // there will be some other path to the successor that will not be
+ // controlled by this branch so any phi we hoist would be controlled by the
+ // wrong condition. This also takes care of avoiding hoisting of loop back
+ // edges.
+ // TODO: In some cases this could be relaxed if the successor is dominated
+ // by another block that's been hoisted and we can guarantee that the
+ // control flow has been replicated exactly.
+ if (CommonSucc && DT->dominates(BI, CommonSucc))
+ HoistableBranches[BI] = CommonSucc;
+ }
+
+ bool canHoistPHI(PHINode *PN) {
+ // The phi must have loop invariant operands.
+ if (!ControlFlowHoisting || !CurLoop->hasLoopInvariantOperands(PN))
+ return false;
+ // We can hoist phis if the block they are in is the target of hoistable
+ // branches which cover all of the predecessors of the block.
+ SmallPtrSet<BasicBlock *, 8> PredecessorBlocks;
+ BasicBlock *BB = PN->getParent();
+ for (BasicBlock *PredBB : predecessors(BB))
+ PredecessorBlocks.insert(PredBB);
+ // If we have less predecessor blocks than predecessors then the phi will
+ // have more than one incoming value for the same block which we can't
+ // handle.
+ // TODO: This could be handled be erasing some of the duplicate incoming
+ // values.
+ if (PredecessorBlocks.size() != pred_size(BB))
+ return false;
+ for (auto &Pair : HoistableBranches) {
+ if (Pair.second == BB) {
+ // Which blocks are predecessors via this branch depends on if the
+ // branch is triangle-like or diamond-like.
+ if (Pair.first->getSuccessor(0) == BB) {
+ PredecessorBlocks.erase(Pair.first->getParent());
+ PredecessorBlocks.erase(Pair.first->getSuccessor(1));
+ } else if (Pair.first->getSuccessor(1) == BB) {
+ PredecessorBlocks.erase(Pair.first->getParent());
+ PredecessorBlocks.erase(Pair.first->getSuccessor(0));
+ } else {
+ PredecessorBlocks.erase(Pair.first->getSuccessor(0));
+ PredecessorBlocks.erase(Pair.first->getSuccessor(1));
+ }
+ }
+ }
+ // PredecessorBlocks will now be empty if for every predecessor of BB we
+ // found a hoistable branch source.
+ return PredecessorBlocks.empty();
+ }
+
+ BasicBlock *getOrCreateHoistedBlock(BasicBlock *BB) {
+ // If BB has already been hoisted, return that
+ if (HoistDestinationMap.count(BB))
+ return HoistDestinationMap[BB];
+
+ // Check if this block is conditional based on a pending branch
+ auto HasBBAsSuccessor =
+ [&](DenseMap<BranchInst *, BasicBlock *>::value_type &Pair) {
+ return BB != Pair.second && (Pair.first->getSuccessor(0) == BB ||
+ Pair.first->getSuccessor(1) == BB);
+ };
+ auto It = std::find_if(HoistableBranches.begin(), HoistableBranches.end(),
+ HasBBAsSuccessor);
+
+ // If not involved in a pending branch, hoist to preheader
+ BasicBlock *InitialPreheader = CurLoop->getLoopPreheader();
+ if (It == HoistableBranches.end()) {
+ LLVM_DEBUG(dbgs() << "LICM using " << InitialPreheader->getName()
+ << " as hoist destination for " << BB->getName()
+ << "\n");
+ HoistDestinationMap[BB] = InitialPreheader;
+ return InitialPreheader;
+ }
+ BranchInst *BI = It->first;
+ assert(std::find_if(++It, HoistableBranches.end(), HasBBAsSuccessor) ==
+ HoistableBranches.end() &&
+ "BB is expected to be the target of at most one branch");
+
+ LLVMContext &C = BB->getContext();
+ BasicBlock *TrueDest = BI->getSuccessor(0);
+ BasicBlock *FalseDest = BI->getSuccessor(1);
+ BasicBlock *CommonSucc = HoistableBranches[BI];
+ BasicBlock *HoistTarget = getOrCreateHoistedBlock(BI->getParent());
+
+ // Create hoisted versions of blocks that currently don't have them
+ auto CreateHoistedBlock = [&](BasicBlock *Orig) {
+ if (HoistDestinationMap.count(Orig))
+ return HoistDestinationMap[Orig];
+ BasicBlock *New =
+ BasicBlock::Create(C, Orig->getName() + ".licm", Orig->getParent());
+ HoistDestinationMap[Orig] = New;
+ DT->addNewBlock(New, HoistTarget);
+ if (CurLoop->getParentLoop())
+ CurLoop->getParentLoop()->addBasicBlockToLoop(New, *LI);
+ ++NumCreatedBlocks;
+ LLVM_DEBUG(dbgs() << "LICM created " << New->getName()
+ << " as hoist destination for " << Orig->getName()
+ << "\n");
+ return New;
+ };
+ BasicBlock *HoistTrueDest = CreateHoistedBlock(TrueDest);
+ BasicBlock *HoistFalseDest = CreateHoistedBlock(FalseDest);
+ BasicBlock *HoistCommonSucc = CreateHoistedBlock(CommonSucc);
+
+ // Link up these blocks with branches.
+ if (!HoistCommonSucc->getTerminator()) {
+ // The new common successor we've generated will branch to whatever that
+ // hoist target branched to.
+ BasicBlock *TargetSucc = HoistTarget->getSingleSuccessor();
+ assert(TargetSucc && "Expected hoist target to have a single successor");
+ HoistCommonSucc->moveBefore(TargetSucc);
+ BranchInst::Create(TargetSucc, HoistCommonSucc);
+ }
+ if (!HoistTrueDest->getTerminator()) {
+ HoistTrueDest->moveBefore(HoistCommonSucc);
+ BranchInst::Create(HoistCommonSucc, HoistTrueDest);
+ }
+ if (!HoistFalseDest->getTerminator()) {
+ HoistFalseDest->moveBefore(HoistCommonSucc);
+ BranchInst::Create(HoistCommonSucc, HoistFalseDest);
+ }
+
+ // If BI is being cloned to what was originally the preheader then
+ // HoistCommonSucc will now be the new preheader.
+ if (HoistTarget == InitialPreheader) {
+ // Phis in the loop header now need to use the new preheader.
+ InitialPreheader->replaceSuccessorsPhiUsesWith(HoistCommonSucc);
+ // The new preheader dominates the loop header.
+ DomTreeNode *PreheaderNode = DT->getNode(HoistCommonSucc);
+ DomTreeNode *HeaderNode = DT->getNode(CurLoop->getHeader());
+ DT->changeImmediateDominator(HeaderNode, PreheaderNode);
+ // The preheader hoist destination is now the new preheader, with the
+ // exception of the hoist destination of this branch.
+ for (auto &Pair : HoistDestinationMap)
+ if (Pair.second == InitialPreheader && Pair.first != BI->getParent())
+ Pair.second = HoistCommonSucc;
+ }
+
+ // Now finally clone BI.
+ ReplaceInstWithInst(
+ HoistTarget->getTerminator(),
+ BranchInst::Create(HoistTrueDest, HoistFalseDest, BI->getCondition()));
+ ++NumClonedBranches;
+
+ assert(CurLoop->getLoopPreheader() &&
+ "Hoisting blocks should not have destroyed preheader");
+ return HoistDestinationMap[BB];
+ }
+};
+
/// Walk the specified region of the CFG (defined by all blocks dominated by
/// the specified block, and that are in the current loop) in depth first
/// order w.r.t the DominatorTree. This allows us to visit definitions before
CurLoop != nullptr && CurAST != nullptr && SafetyInfo != nullptr &&
"Unexpected input to hoistRegion");
- // We want to visit parents before children. We will enque all the parents
- // before their children in the worklist and process the worklist in order.
- SmallVector<DomTreeNode *, 16> Worklist = collectChildrenInLoop(N, CurLoop);
+ ControlFlowHoister CFH(LI, DT, CurLoop);
+
+ // Keep track of instructions that have been hoisted, as they may need to be
+ // re-hoisted if they end up not dominating all of their uses.
+ SmallVector<Instruction *, 16> HoistedInstructions;
+ // For PHI hoisting to work we need to hoist blocks before their successors.
+ // We can do this by iterating through the blocks in the loop in reverse
+ // post-order.
+ LoopBlocksRPO Worklist(CurLoop);
+ Worklist.perform(LI);
bool Changed = false;
- for (DomTreeNode *DTN : Worklist) {
- BasicBlock *BB = DTN->getBlock();
+ for (BasicBlock *BB : Worklist) {
// Only need to process the contents of this block if it is not part of a
// subloop (which would already have been processed).
if (inSubLoop(BB, CurLoop, LI))
// Try hoisting the instruction out to the preheader. We can only do
// this if all of the operands of the instruction are loop invariant and
// if it is safe to hoist the instruction.
- //
+ // TODO: It may be safe to hoist if we are hoisting to a conditional block
+ // and we have accurately duplicated the control flow from the loop header
+ // to that block.
if (CurLoop->hasLoopInvariantOperands(&I) &&
canSinkOrHoistInst(I, AA, DT, CurLoop, CurAST, true, ORE) &&
isSafeToExecuteUnconditionally(
I, DT, CurLoop, SafetyInfo, ORE,
CurLoop->getLoopPreheader()->getTerminator())) {
- hoist(I, DT, CurLoop, SafetyInfo, ORE);
+ hoist(I, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB), SafetyInfo, ORE);
+ HoistedInstructions.push_back(&I);
Changed = true;
continue;
}
I.replaceAllUsesWith(Product);
eraseInstruction(I, *SafetyInfo, CurAST);
- hoist(*ReciprocalDivisor, DT, CurLoop, SafetyInfo, ORE);
+ hoist(*ReciprocalDivisor, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB),
+ SafetyInfo, ORE);
+ HoistedInstructions.push_back(ReciprocalDivisor);
Changed = true;
continue;
}
CurLoop->hasLoopInvariantOperands(&I) &&
SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop) &&
SafetyInfo->doesNotWriteMemoryBefore(I, CurLoop)) {
- hoist(I, DT, CurLoop, SafetyInfo, ORE);
+ hoist(I, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB), SafetyInfo, ORE);
+ HoistedInstructions.push_back(&I);
Changed = true;
continue;
}
+
+ if (PHINode *PN = dyn_cast<PHINode>(&I)) {
+ if (CFH.canHoistPHI(PN)) {
+ // Redirect incoming blocks first to ensure that we create hoisted
+ // versions of those blocks before we hoist the phi.
+ for (unsigned int i = 0; i < PN->getNumIncomingValues(); ++i)
+ PN->setIncomingBlock(
+ i, CFH.getOrCreateHoistedBlock(PN->getIncomingBlock(i)));
+ hoist(*PN, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB), SafetyInfo,
+ ORE);
+ assert(DT->dominates(PN, BB) && "Conditional PHIs not expected");
+ Changed = true;
+ continue;
+ }
+ }
+
+ // Remember possibly hoistable branches so we can actually hoist them
+ // later if needed.
+ if (BranchInst *BI = dyn_cast<BranchInst>(&I))
+ CFH.registerPossiblyHoistableBranch(BI);
+ }
+ }
+
+ // If we hoisted instructions to a conditional block they may not dominate
+ // their uses that weren't hoisted (such as phis where some operands are not
+ // loop invariant). If so make them unconditional by moving them to their
+ // immediate dominator. We iterate through the instructions in reverse order
+ // which ensures that when we rehoist an instruction we rehoist its operands,
+ // and also keep track of where in the block we are rehoisting to to make sure
+ // that we rehoist instructions before the instructions that use them.
+ Instruction *HoistPoint = nullptr;
+ for (Instruction *I : reverse(HoistedInstructions)) {
+ if (!llvm::all_of(I->uses(), [&](Use &U) { return DT->dominates(I, U); })) {
+ BasicBlock *Dominator =
+ DT->getNode(I->getParent())->getIDom()->getBlock();
+ LLVM_DEBUG(dbgs() << "LICM rehoisting to " << Dominator->getName() << ": "
+ << *I << "\n");
+ if (!HoistPoint || HoistPoint->getParent() != Dominator) {
+ if (HoistPoint)
+ assert(DT->dominates(Dominator, HoistPoint->getParent()) &&
+ "New hoist point expected to dominate old hoist point");
+ HoistPoint = Dominator->getTerminator();
+ }
+ moveInstructionBefore(*I, *HoistPoint, *SafetyInfo);
+ HoistPoint = I;
+ Changed = true;
}
}
+ // Now that we've finished hoisting make sure that LI and DT are still valid.
+#ifndef NDEBUG
+ if (Changed) {
+ assert(DT->verify(DominatorTree::VerificationLevel::Fast) &&
+ "Dominator tree verification failed");
+ LI->verify(*DT);
+ }
+#endif
+
return Changed;
}
/// is safe to hoist, this instruction is called to do the dirty work.
///
static void hoist(Instruction &I, const DominatorTree *DT, const Loop *CurLoop,
- ICFLoopSafetyInfo *SafetyInfo, OptimizationRemarkEmitter *ORE) {
- auto *Preheader = CurLoop->getLoopPreheader();
- LLVM_DEBUG(dbgs() << "LICM hoisting to " << Preheader->getName() << ": " << I
+ BasicBlock *Dest, ICFLoopSafetyInfo *SafetyInfo,
+ OptimizationRemarkEmitter *ORE) {
+ LLVM_DEBUG(dbgs() << "LICM hoisting to " << Dest->getName() << ": " << I
<< "\n");
ORE->emit([&]() {
return OptimizationRemark(DEBUG_TYPE, "Hoisted", &I) << "hoisting "
!SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop))
I.dropUnknownNonDebugMetadata();
- // Move the new node to the Preheader, before its terminator.
- moveInstructionBefore(I, *Preheader->getTerminator(), *SafetyInfo);
+ if (isa<PHINode>(I))
+ // Move the new node to the end of the phi list in the destination block.
+ moveInstructionBefore(I, *Dest->getFirstNonPHI(), *SafetyInfo);
+ else
+ // Move the new node to the destination block, before its terminator.
+ moveInstructionBefore(I, *Dest->getTerminator(), *SafetyInfo);
// Do not retain debug locations when we are moving instructions to different
// basic blocks, because we want to avoid jumpy line tables. Calls, however,
--- /dev/null
+; RUN: opt -S -licm < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED
+; RUN: opt -S -licm -licm-control-flow-hoisting=1 < %s | FileCheck %s -check-prefixes=CHECK,CHECK-ENABLED
+; RUN: opt -S -licm -licm-control-flow-hoisting=0 < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED
+; RUN: opt -passes='require<opt-remark-emit>,loop(licm)' -S < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED
+; RUN: opt -passes='require<opt-remark-emit>,loop(licm)' -licm-control-flow-hoisting=1 -S < %s | FileCheck %s -check-prefixes=CHECK,CHECK-ENABLED
+; RUN: opt -passes='require<opt-remark-emit>,loop(licm)' -licm-control-flow-hoisting=0 -S < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED
+
+; CHECK-LABEL: @triangle_phi
+define void @triangle_phi(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK: %add = add i32 %x, 1
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: phi i32 [ %add, %[[IF_LICM]] ], [ %x, %entry ]
+; CHECK-ENABLED: store i32 %phi, i32* %p
+; CHECK-ENABLED: %cmp2 = icmp ne i32 %phi, 0
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %then
+
+if:
+ %add = add i32 %x, 1
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %x, %loop ]
+; CHECK-DISABLED: %cmp2 = icmp ne i32 %phi, 0
+then:
+ %phi = phi i32 [ %add, %if ], [ %x, %loop ]
+ store i32 %phi, i32* %p
+ %cmp2 = icmp ne i32 %phi, 0
+ br i1 %cmp2, label %loop, label %end
+
+; CHECK-LABEL: end:
+; CHECK-DISABLED: %[[PHI_LCSSA:.*]] = phi i32 [ %phi, %then ]
+; CHECK-DISABLED: store i32 %[[PHI_LCSSA]], i32* %p
+end:
+ ret void
+}
+
+; CHECK-LABEL: @diamond_phi
+define void @diamond_phi(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_LICM]]:
+; CHECK-DAG: %sub = sub i32 %x, 1
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]
+; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %sub, %[[ELSE_LICM]] ]
+; CHECK-ENABLED: store i32 %phi, i32* %p
+; CHECK-ENABLED: %cmp2 = icmp ne i32 %phi, 0
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ %add = add i32 %x, 1
+ br label %then
+
+else:
+ %sub = sub i32 %x, 1
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %sub, %else ]
+; CHECK-DISABLED: %cmp2 = icmp ne i32 %phi, 0
+then:
+ %phi = phi i32 [ %add, %if ], [ %sub, %else ]
+ store i32 %phi, i32* %p
+ %cmp2 = icmp ne i32 %phi, 0
+ br i1 %cmp2, label %loop, label %end
+
+; CHECK-LABEL: end:
+; CHECK-DISABLED: %[[PHI_LCSSA:.*]] = phi i32 [ %phi, %then ]
+; CHECK-DISABLED: store i32 %[[PHI_LCSSA]], i32* %p
+end:
+ ret void
+}
+
+; TODO: This is currently too complicated for us to be able to hoist the phi.
+; CHECK-LABEL: @three_way_phi
+define void @three_way_phi(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-DAG: %cmp2 = icmp sgt i32 %add, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK: %sub = sub i32 %x, 1
+; CHECK: br label %loop
+
+entry:
+ br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %then
+
+if:
+ %add = add i32 %x, 1
+ %cmp2 = icmp sgt i32 %add, 0
+ br i1 %cmp2, label %if.if, label %then
+
+if.if:
+ %sub = sub i32 %x, 1
+ br label %then
+
+then:
+ %phi = phi i32 [ 0, %loop ], [ %add, %if ], [ %sub, %if.if ]
+ store i32 %phi, i32* %p
+ %cmp3 = icmp ne i32 %phi, 0
+ br i1 %cmp3, label %loop, label %end
+
+end:
+ ret void
+}
+
+; TODO: This is currently too complicated for us to be able to hoist the phi.
+; CHECK-LABEL: @tree_phi
+define void @tree_phi(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-DAG: %cmp2 = icmp sgt i32 %add, 0
+; CHECK-DAG: %sub = sub i32 %x, 1
+; CHECK: br label %loop
+
+entry:
+ br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ %add = add i32 %x, 1
+ %cmp2 = icmp sgt i32 %add, 0
+ br i1 %cmp2, label %if.if, label %if.else
+
+if.if:
+ br label %then
+
+if.else:
+ br label %then
+
+else:
+ %sub = sub i32 %x, 1
+ br label %then
+
+then:
+ %phi = phi i32 [ %add, %if.if ], [ 0, %if.else ], [ %sub, %else ]
+ store i32 %phi, i32* %p
+ %cmp3 = icmp ne i32 %phi, 0
+ br i1 %cmp3, label %loop, label %end
+
+end:
+ ret void
+}
+
+; TODO: We can hoist the first phi, but not the second.
+; CHECK-LABEL: @phi_phi
+define void @phi_phi(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-DAG: %cmp2 = icmp sgt i32 %add, 0
+; CHECK-DAG: %sub = sub i32 %x, 1
+; CHECK-ENABLED: br i1 %cmp2, label %[[IF_IF_LICM:.*]], label %[[IF_ELSE_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_IF_LICM]]:
+; CHECK-ENABLED: br label %[[IF_THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[IF_THEN_LICM]]
+
+; CHECK-ENABLED: [[IF_THEN_LICM]]:
+; CHECK-ENABLED: %phi1 = phi i32 [ %add, %[[IF_IF_LICM]] ], [ 0, %[[IF_ELSE_LICM]] ]
+; CHECK: br label %loop
+
+entry:
+ br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ %add = add i32 %x, 1
+ %cmp2 = icmp sgt i32 %add, 0
+ br i1 %cmp2, label %if.if, label %if.else
+
+if.if:
+ br label %if.then
+
+if.else:
+ br label %if.then
+
+; CHECK-LABEL: if.then:
+; CHECK-DISABLED: %phi1 = phi i32 [ %add, %if.if ], [ 0, %if.else ]
+if.then:
+ %phi1 = phi i32 [ %add, %if.if ], [ 0, %if.else ]
+ br label %then
+
+else:
+ %sub = sub i32 %x, 1
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK: %phi2 = phi i32 [ %phi1, %if.then ], [ %sub, %else ]
+then:
+ %phi2 = phi i32 [ %phi1, %if.then ], [ %sub, %else ]
+ store i32 %phi2, i32* %p
+ %cmp3 = icmp ne i32 %phi2, 0
+ br i1 %cmp3, label %loop, label %end
+
+end:
+ ret void
+}
+
+; Check that we correctly duplicate empty control flow.
+; CHECK-LABEL: @empty_triangle_phi
+define i8 @empty_triangle_phi(i32 %x, i32 %y) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp1 = icmp eq i32 %x, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %entry ]
+; CHECK: %cmp2 = icmp eq i32 %y, 0
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp eq i32 %x, 0
+ br i1 %cmp1, label %if, label %then
+
+if:
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %loop ]
+then:
+ %phi = phi i8 [ 0, %if ], [ 1, %loop ]
+ %cmp2 = icmp eq i32 %y, 0
+ br i1 %cmp2, label %end, label %loop
+
+end:
+ ret i8 %phi
+}
+
+; CHECK-LABEL: @empty_diamond_phi
+define i8 @empty_diamond_phi(i32 %x, i32 %y) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp1 = icmp eq i32 %x, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %[[ELSE_LICM]] ]
+; CHECK: %cmp2 = icmp eq i32 %y, 0
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp eq i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ br label %then
+
+else:
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %else ]
+then:
+ %phi = phi i8 [ 0, %if ], [ 1, %else ]
+ %cmp2 = icmp eq i32 %y, 0
+ br i1 %cmp2, label %end, label %loop
+
+end:
+ ret i8 %phi
+}
+
+; Check that we correctly handle the case that the first thing we try to hoist is a phi.
+; CHECK-LABEL: @empty_triangle_phi_first
+define i8 @empty_triangle_phi_first(i32 %x, i1 %cond) {
+; CHECK-LABEL: entry:
+; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %entry ]
+; CHECK: %cmp = icmp eq i32 %x, 0
+; CHECK: br label %loop
+
+loop:
+ br i1 %cond, label %if, label %then
+
+if:
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %loop ]
+then:
+ %phi = phi i8 [ 0, %if ], [ 1, %loop ]
+ %cmp = icmp eq i32 %x, 0
+ br i1 %cmp, label %end, label %loop
+
+end:
+ ret i8 %phi
+}
+
+; CHECK-LABEL: @empty_diamond_phi
+define i8 @empty_diamond_phi_first(i32 %x, i1 %cond) {
+; CHECK-LABEL: entry:
+; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %[[ELSE_LICM]] ]
+; CHECK: %cmp = icmp eq i32 %x, 0
+; CHECK: br label %loop
+
+loop:
+ br i1 %cond, label %if, label %else
+
+if:
+ br label %then
+
+else:
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %else ]
+then:
+ %phi = phi i8 [ 0, %if ], [ 1, %else ]
+ %cmp = icmp eq i32 %x, 0
+ br i1 %cmp, label %end, label %loop
+
+end:
+ ret i8 %phi
+}
+
+; CHECK-LABEL: @empty_triangle_phi_first
+define i8 @empty_triangle_phi_first_empty_loop_head(i32 %x, i1 %cond) {
+; CHECK-LABEL: entry:
+; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %entry ]
+; CHECK: %cmp = icmp eq i32 %x, 0
+; CHECK: br label %loop
+
+loop:
+ br label %test
+
+test:
+ br i1 %cond, label %if, label %then
+
+if:
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %test ]
+then:
+ %phi = phi i8 [ 0, %if ], [ 1, %test ]
+ %cmp = icmp eq i32 %x, 0
+ br i1 %cmp, label %end, label %loop
+
+end:
+ ret i8 %phi
+}
+
+; CHECK-LABEL: @empty_diamond_phi_first_empty_loop_head
+define i8 @empty_diamond_phi_first_empty_loop_head(i32 %x, i1 %cond) {
+; CHECK-LABEL: entry:
+; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %[[ELSE_LICM]] ]
+; CHECK: %cmp = icmp eq i32 %x, 0
+; CHECK: br label %loop
+
+loop:
+ br label %test
+
+test:
+ br i1 %cond, label %if, label %else
+
+if:
+ br label %then
+
+else:
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %else ]
+then:
+ %phi = phi i8 [ 0, %if ], [ 1, %else ]
+ %cmp = icmp eq i32 %x, 0
+ br i1 %cmp, label %end, label %loop
+
+end:
+ ret i8 %phi
+}
+
+; The phi is on one branch of a diamond while simultaneously at the end of a
+; triangle. Check that we duplicate the triangle and not the diamond.
+; CHECK-LABEL: @triangle_diamond
+define void @triangle_diamond(i32* %ptr, i32 %x, i32 %y) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp ne i32 %x, 0
+; CHECK-DAG: %cmp2 = icmp ne i32 %y, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i32 [ 0, %[[IF_LICM]] ], [ 127, %entry ]
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp ne i32 %x, 0
+ br i1 %cmp1, label %if, label %then
+
+if:
+ %cmp2 = icmp ne i32 %y, 0
+ br i1 %cmp2, label %if.then, label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ 0, %if ], [ 127, %loop ]
+then:
+ %phi = phi i32 [ 0, %if ], [ 127, %loop ]
+ store i32 %phi, i32* %ptr
+ br label %end
+
+if.then:
+ br label %end
+
+end:
+ br label %loop
+}
+
+; As the previous, but the end of the diamond is the head of the loop.
+; CHECK-LABEL: @triangle_diamond_backedge
+define void @triangle_diamond_backedge(i32* %ptr, i32 %x, i32 %y) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp ne i32 %x, 0
+; CHECK-DAG: %cmp2 = icmp ne i32 %y, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i32 [ 0, %[[IF_LICM]] ], [ 127, %entry ]
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp ne i32 %x, 0
+ br i1 %cmp1, label %if, label %then
+
+if:
+ %cmp2 = icmp ne i32 %y, 0
+ br i1 %cmp2, label %backedge, label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ 0, %if ], [ 127, %loop ]
+then:
+ %phi = phi i32 [ 0, %if ], [ 127, %loop ]
+ store i32 %phi, i32* %ptr
+ br label %loop
+
+backedge:
+ br label %loop
+}
+
+; TODO: The inner diamonds can be hoisted, but not currently the outer diamond
+; CHECK-LABEL: @diamonds_inside_diamond
+define void @diamonds_inside_diamond(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %cmp3 = icmp slt i32 %x, -10
+; CHECK-ENABLED: br i1 %cmp3, label %[[ELSE_IF_LICM:.*]], label %[[ELSE_ELSE_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[ELSE_IF_LICM]]:
+; CHECK-ENABLED: br label %[[ELSE_THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[ELSE_THEN_LICM]]
+
+; CHECK-ENABLED: [[ELSE_THEN_LICM]]:
+; CHECK-ENABLED: %phi2 = phi i32 [ 2, %[[ELSE_IF_LICM]] ], [ 3, %[[ELSE_ELSE_LICM]] ]
+; CHECK: %cmp2 = icmp sgt i32 %x, 10
+; CHECK-ENABLED: br i1 %cmp2, label %[[IF_IF_LICM:.*]], label %[[IF_ELSE_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_IF_LICM]]:
+; CHECK-ENABLED: br label %[[IF_THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[IF_THEN_LICM]]
+
+; CHECK-ENABLED: [[IF_THEN_LICM]]:
+; CHECK-ENABLED: %phi1 = phi i32 [ 0, %[[IF_IF_LICM]] ], [ 1, %[[IF_ELSE_LICM]] ]
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ %cmp2 = icmp sgt i32 %x, 10
+ br i1 %cmp2, label %if.if, label %if.else
+
+if.if:
+ br label %if.then
+
+if.else:
+ br label %if.then
+
+; CHECK-LABEL: if.then:
+; CHECK-DISABLED: %phi1 = phi i32 [ 0, %if.if ], [ 1, %if.else ]
+if.then:
+ %phi1 = phi i32 [ 0, %if.if ], [ 1, %if.else ]
+ br label %then
+
+else:
+ %cmp3 = icmp slt i32 %x, -10
+ br i1 %cmp3, label %else.if, label %else.else
+
+else.if:
+ br label %else.then
+
+else.else:
+ br label %else.then
+
+; CHECK-LABEL: else.then:
+; CHECK-DISABLED: %phi2 = phi i32 [ 2, %else.if ], [ 3, %else.else ]
+else.then:
+ %phi2 = phi i32 [ 2, %else.if ], [ 3, %else.else ]
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK: %phi3 = phi i32 [ %phi1, %if.then ], [ %phi2, %else.then ]
+; CHECK: %cmp4 = icmp ne i32 %phi3, 0
+then:
+ %phi3 = phi i32 [ %phi1, %if.then ], [ %phi2, %else.then ]
+ store i32 %phi3, i32* %p
+ %cmp4 = icmp ne i32 %phi3, 0
+ br i1 %cmp4, label %loop, label %end
+
+end:
+ ret void
+}
+
+; We can hoist blocks that contain an edge that exits the loop by ignoring that
+; edge in the hoisted block.
+; CHECK-LABEL: @triangle_phi_loopexit
+define void @triangle_phi_loopexit(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %cmp2 = icmp sgt i32 10, %add
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %x, %entry ]
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %then
+
+if:
+ %add = add i32 %x, 1
+ %cmp2 = icmp sgt i32 10, %add
+ br i1 %cmp2, label %then, label %end
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %x, %loop ]
+then:
+ %phi = phi i32 [ %add, %if ], [ %x, %loop ]
+ store i32 %phi, i32* %p
+ %cmp3 = icmp ne i32 %phi, 0
+ br i1 %cmp3, label %loop, label %end
+
+end:
+ ret void
+}
+
+; CHECK-LABEL: @diamond_phi_oneloopexit
+define void @diamond_phi_oneloopexit(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %cmp2 = icmp sgt i32 10, %add
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_LICM]]:
+; CHECK-DAG: %sub = sub i32 %x, 1
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]
+; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %sub, %[[ELSE_LICM]] ]
+; CHECK-ENABLED: %cmp3 = icmp ne i32 %phi, 0
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ %add = add i32 %x, 1
+ %cmp2 = icmp sgt i32 10, %add
+ br i1 %cmp2, label %then, label %end
+
+else:
+ %sub = sub i32 %x, 1
+ br label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %sub, %else ]
+then:
+ %phi = phi i32 [ %add, %if ], [ %sub, %else ]
+ store i32 %phi, i32* %p
+ %cmp3 = icmp ne i32 %phi, 0
+ br i1 %cmp3, label %loop, label %end
+
+end:
+ ret void
+}
+
+; CHECK-LABEL: @diamond_phi_twoloopexit
+define void @diamond_phi_twoloopexit(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %sub = sub i32 %x, 1
+; CHECK-DAG: %add = add i32 %x, 1
+; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0
+; CHECK-DAG: %cmp2 = icmp sgt i32 10, %add
+; CHECK-DAG: %cmp3 = icmp sgt i32 10, %sub
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM:.*]]
+
+; CHECK-ENABLED: [[ELSE_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]
+; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %sub, %[[ELSE_LICM]] ]
+; CHECK-ENABLED: %cmp4 = icmp ne i32 %phi, 0
+; CHECK: br label %loop
+
+loop:
+ %cmp1 = icmp sgt i32 %x, 0
+ br i1 %cmp1, label %if, label %else
+
+if:
+ %add = add i32 %x, 1
+ %cmp2 = icmp sgt i32 10, %add
+ br i1 %cmp2, label %then, label %end
+
+else:
+ %sub = sub i32 %x, 1
+ %cmp3 = icmp sgt i32 10, %sub
+ br i1 %cmp3, label %then, label %end
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %sub, %else ]
+; CHECK-DISABLED: %cmp4 = icmp ne i32 %phi, 0
+then:
+ %phi = phi i32 [ %add, %if ], [ %sub, %else ]
+ store i32 %phi, i32* %p
+ %cmp4 = icmp ne i32 %phi, 0
+ br i1 %cmp4, label %loop, label %end
+
+end:
+ ret void
+}
+
+; The store cannot be hoisted, so add and shr cannot be hoisted into a
+; conditional block.
+; CHECK-LABEL: @conditional_use
+define void @conditional_use(i32 %x, i32* %p) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cond = icmp ugt i32 %x, 0
+; CHECK-DAG: %add = add i32 %x, 5
+; CHECK-DAG: %shr = ashr i32 %add, 1
+; CHECK: br label %loop
+entry:
+ br label %loop
+
+loop:
+ %cond = icmp ugt i32 %x, 0
+ br i1 %cond, label %if, label %else
+
+; CHECK-LABEL: if:
+; CHECK: store i32 %shr, i32* %p, align 4
+if:
+ %add = add i32 %x, 5
+ %shr = ashr i32 %add, 1
+ store i32 %shr, i32* %p, align 4
+ br label %then
+
+else:
+ br label %then
+
+then:
+ br label %loop
+}
+
+; A diamond with two triangles on the left and one on the right. This test is
+; to check that we have a unique loop preheader when we hoist the store (and so
+; don't fail an assertion).
+; CHECK-LABEL: @triangles_in_diamond
+define void @triangles_in_diamond(i32* %ptr) {
+; CHECK-LABEL: entry:
+; CHECK: store i32 0, i32* %ptr, align 4
+; CHECK: br label %loop
+entry:
+ br label %loop
+
+loop:
+ br i1 undef, label %left_triangle_1, label %right_triangle
+
+left_triangle_1:
+ br i1 undef, label %left_triangle_1_if, label %left_triangle_2
+
+left_triangle_1_if:
+ br label %left_triangle_2
+
+left_triangle_2:
+ br i1 undef, label %left_triangle_2_if, label %left_triangle_2_then
+
+left_triangle_2_if:
+ br label %left_triangle_2_then
+
+left_triangle_2_then:
+ br label %loop.end
+
+right_triangle:
+ br i1 undef, label %right_triangle.if, label %right_triangle.then
+
+right_triangle.if:
+ br label %right_triangle.then
+
+right_triangle.then:
+ br label %loop.end
+
+loop.end:
+ store i32 0, i32* %ptr, align 4
+ br label %loop
+}
+
+; %cmp dominates its used after being hoisted, but not after %brmerge is rehoisted
+; CHECK-LABEL: @rehoist
+define void @rehoist(i8* %this, i32 %x) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %sub = add nsw i32 %x, -1
+; CHECK-DAG: %fptr = bitcast i8* %this to void (i8*)*
+; CHECK-DAG: %cmp = icmp eq i32 0, %sub
+; CHECK-DAG: %brmerge = or i1 %cmp, true
+entry:
+ %sub = add nsw i32 %x, -1
+ br label %loop
+
+loop:
+ br i1 undef, label %if1, label %else1
+
+if1:
+ %fptr = bitcast i8* %this to void (i8*)*
+ call void %fptr(i8* %this)
+ br label %then1
+
+else1:
+ br label %then1
+
+then1:
+ %cmp = icmp eq i32 0, %sub
+ br i1 %cmp, label %end, label %else2
+
+else2:
+ %brmerge = or i1 %cmp, true
+ br i1 %brmerge, label %if3, label %end
+
+if3:
+ br label %end
+
+end:
+ br label %loop
+}
+
+; A test case that uses empty blocks in a way that can cause control flow
+; hoisting to get confused.
+; CHECK-LABEL: @empty_blocks_multiple_conditional_branches
+define void @empty_blocks_multiple_conditional_branches(float %arg, float* %ptr) {
+; CHECK-LABEL: entry
+; CHECK-DAG: %div1 = fmul float %arg, 4.000000e+00
+; CHECK-DAG: %div2 = fmul float %arg, 2.000000e+00
+entry:
+ br label %loop
+
+; The exact path to the phi isn't checked here, because it depends on whether
+; cond2 or cond3 is hoisted first
+; CHECK-ENABLED: %phi = phi float [ 0.000000e+00, %{{.*}} ], [ %div1, %{{.*}} ]
+; CHECK: br label %loop
+
+loop:
+ br i1 undef, label %backedge2, label %cond1
+
+cond1:
+ br i1 undef, label %cond1.if, label %cond1.else
+
+cond1.else:
+ br label %cond3
+
+cond1.if:
+ br label %cond1.if.next
+
+cond1.if.next:
+ br label %cond2
+
+cond2:
+ %div1 = fmul float %arg, 4.000000e+00
+ br i1 undef, label %cond2.if, label %cond2.then
+
+cond2.if:
+ br label %cond2.then
+
+; CHECK-LABEL: cond2.then:
+; CHECK-DISABLED: %phi = phi float [ 0.000000e+00, %cond2 ], [ %div1, %cond2.if ]
+cond2.then:
+ %phi = phi float [ 0.000000e+00, %cond2 ], [ %div1, %cond2.if ]
+ store float %phi, float* %ptr
+ br label %backedge2
+
+cond3:
+ br i1 undef, label %cond3.then, label %cond3.if
+
+cond3.if:
+ %div2 = fmul float %arg, 2.000000e+00
+ store float %div2, float* %ptr
+ br label %cond3.then
+
+cond3.then:
+ br label %loop
+
+backedge2:
+ br label %loop
+}
+
+; We can't do much here, so mainly just check that we don't crash.
+; CHECK-LABEL: @many_path_phi
+define void @many_path_phi(i32* %ptr1, i32* %ptr2) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %gep3 = getelementptr inbounds i32, i32* %ptr2, i32 2
+; CHECK-DAG: %gep2 = getelementptr inbounds i32, i32* %ptr2, i32 2
+; CHECK: br label %loop
+entry:
+ br label %loop
+
+loop:
+ %phi1 = phi i32 [ 0, %entry ], [ %phi2, %end ]
+ %cmp1 = icmp ugt i32 %phi1, 3
+ br i1 %cmp1, label %cond2, label %cond1
+
+cond1:
+ br i1 undef, label %end, label %cond1.else
+
+cond1.else:
+ %gep2 = getelementptr inbounds i32, i32* %ptr2, i32 2
+ %val2 = load i32, i32* %gep2, align 4
+ %cmp2 = icmp eq i32 %val2, 13
+ br i1 %cmp2, label %cond1.end, label %end
+
+cond1.end:
+ br label %end
+
+cond2:
+ br i1 undef, label %end, label %cond2.else
+
+cond2.else:
+ %gep3 = getelementptr inbounds i32, i32* %ptr2, i32 2
+ %val3 = load i32, i32* %gep3, align 4
+ %cmp3 = icmp eq i32 %val3, 13
+ br i1 %cmp3, label %cond2.end, label %end
+
+cond2.end:
+ br label %end
+
+end:
+ %phi2 = phi i32 [ 1, %cond1 ], [ 2, %cond1.else ], [ 3, %cond1.end ], [ 4, %cond2 ], [ 5, %cond2.else ], [ 6, %cond2.end ]
+ br label %loop
+}
+
+; Check that we correctly handle the hoisting of %gep when theres a critical
+; edge that branches to the preheader.
+; CHECK-LABEL: @crit_edge
+define void @crit_edge(i32* %ptr, i32 %idx, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: entry:
+; CHECK: %gep = getelementptr inbounds i32, i32* %ptr, i32 %idx
+; CHECK: br label %preheader
+entry:
+ br label %preheader
+
+preheader:
+ br label %loop
+
+loop:
+ br i1 %cond1, label %then, label %if
+
+if:
+ %gep = getelementptr inbounds i32, i32* %ptr, i32 %idx
+ %val = load i32, i32* %gep
+ br label %then
+
+then:
+ %phi = phi i32 [ %val, %if ], [ 0, %loop ]
+ store i32 %phi, i32* %ptr
+ br i1 %cond2, label %loop, label %crit_edge
+
+crit_edge:
+ br label %preheader
+}
+
+; Check that the conditional sub is correctly hoisted from the inner loop to the
+; preheader of the outer loop.
+; CHECK-LABEL: @hoist_from_innermost_loop
+define void @hoist_from_innermost_loop(i32 %nx, i32* %ptr) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %sub = sub nsw i32 0, %nx
+; CHECK: br label %outer_loop
+entry:
+ br label %outer_loop
+
+outer_loop:
+ br label %middle_loop
+
+middle_loop:
+ br label %inner_loop
+
+inner_loop:
+ br i1 undef, label %inner_loop_end, label %if
+
+if:
+ %sub = sub nsw i32 0, %nx
+ store i32 %sub, i32* %ptr, align 4
+ br label %inner_loop_end
+
+inner_loop_end:
+ br i1 undef, label %inner_loop, label %middle_loop_end
+
+middle_loop_end:
+ br i1 undef, label %middle_loop, label %outer_loop_end
+
+outer_loop_end:
+ br label %outer_loop
+}
+
+; We have a diamond starting from %if, but %if.if is also reachable from %loop,
+; so %gep should not be conditionally hoisted.
+; CHECK-LABEL: @diamond_with_extra_in_edge
+define void @diamond_with_extra_in_edge(i32* %ptr1, i32* %ptr2, i32 %arg) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp2 = icmp ne i32 0, %arg
+; CHECK-DAG: %gep = getelementptr i32, i32* %ptr1, i32 4
+; CHECK: br label %loop
+entry:
+ br label %loop
+
+loop:
+ %phi1 = phi i32 [ 0, %entry ], [ %phi2, %then ]
+ %cmp1 = icmp ugt i32 16, %phi1
+ br i1 %cmp1, label %if, label %if.if
+
+if:
+ %cmp2 = icmp ne i32 0, %arg
+ br i1 %cmp2, label %if.if, label %if.else
+
+if.if:
+ %gep = getelementptr i32, i32* %ptr1, i32 4
+ %val = load i32, i32* %gep, align 4
+ br label %then
+
+if.else:
+ br label %then
+
+then:
+ %phi2 = phi i32 [ %val, %if.if ], [ %phi1, %if.else ]
+ store i32 %phi2, i32* %ptr2, align 4
+ br label %loop
+}
+
+; %loop/%if/%then form a triangle, but %loop/%if/%then/%end also form a diamond.
+; The triangle should be picked for conditional hoisting.
+; CHECK-LABEL: @both_triangle_and_diamond
+define void @both_triangle_and_diamond(i32* %ptr1, i32* %ptr2, i32 %arg) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp ne i32 0, %arg
+; CHECK-DAG: %gep = getelementptr i32, i32* %ptr1, i32 4
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[IF_LICM]]:
+; CHECK-ENABLED: br label %[[THEN_LICM]]
+
+; CHECK-ENABLED: [[THEN_LICM]]:
+; CHECK-ENABLED: %phi2 = phi i32 [ 0, %[[IF_LICM]] ], [ 1, %entry ]
+; CHECK: br label %loop
+
+loop:
+ %phi1 = phi i32 [ 0, %entry ], [ %phi3, %end ]
+ %cmp1 = icmp ne i32 0, %arg
+ br i1 %cmp1, label %if, label %then
+
+if:
+ %gep = getelementptr i32, i32* %ptr1, i32 4
+ %val = load i32, i32* %gep, align 4
+ %cmp2 = icmp ugt i32 16, %phi1
+ br i1 %cmp2, label %end, label %then
+
+; CHECK-LABEL: then:
+; CHECK-DISABLED: %phi2 = phi i32 [ 0, %if ], [ 1, %loop ]
+then:
+ %phi2 = phi i32 [ 0, %if ], [ 1, %loop ]
+ br label %end
+
+end:
+ %phi3 = phi i32 [ %phi2, %then ], [ %val, %if ]
+ store i32 %phi3, i32* %ptr2, align 4
+ br label %loop
+}
+
+; We shouldn't duplicate the branch at the end of %loop and should instead hoist
+; %val to %entry.
+; CHECK-LABEL: @same_destination_branch
+define i32 @same_destination_branch(i32 %arg1, i32 %arg2) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp ne i32 %arg2, 0
+; CHECK-DAG: %val = add i32 %arg1, 1
+; CHECK: br label %loop
+entry:
+ br label %loop
+
+; CHECK-LABEL: loop:
+; CHECK: %phi = phi i32 [ 0, %entry ], [ %add, %then ]
+loop:
+ %phi = phi i32 [ 0, %entry ], [ %add, %then ]
+ %add = add i32 %phi, 1
+ %cmp1 = icmp ne i32 %arg2, 0
+ br i1 %cmp1, label %if, label %if
+
+if:
+ %val = add i32 %arg1, 1
+ br label %then
+
+then:
+ %cmp2 = icmp ne i32 %val, %phi
+ br i1 %cmp2, label %loop, label %end
+
+end:
+ ret i32 %val
+}
+
+; Diamond-like control flow but the left/right blocks actually have the same
+; destinations.
+; TODO: We could potentially hoist all of phi2-4, but currently only hoist phi2.
+; CHECK-LABEL: @diamond_like_same_destinations
+define i32 @diamond_like_same_destinations(i32 %arg1, i32 %arg2) {
+; CHECK-LABEL: entry:
+; CHECK-DAG: %cmp1 = icmp ne i32 %arg1, 0
+; CHECK-DAG: %cmp2 = icmp ugt i32 %arg2, 1
+; CHECK-DAG: %cmp3 = icmp ugt i32 %arg2, 2
+; CHECK-ENABLED: br i1 %cmp1, label %[[LEFT1_LICM:.*]], label %[[RIGHT1_LICM:.*]]
+entry:
+ br label %loop
+
+; CHECK-ENABLED: [[LEFT1_LICM]]:
+; CHECK-ENABLED: br label %[[LEFT2_LICM:.*]]
+
+; CHECK-ENABLED: [[RIGHT1_LICM]]:
+; CHECK-ENABLED: br label %[[LEFT2_LICM]]
+
+; CHECK-ENABLED: [[LEFT2_LICM]]:
+; CHECK-ENABLED: %phi2 = phi i32 [ 0, %[[LEFT1_LICM]] ], [ 1, %[[RIGHT1_LICM]] ]
+; CHECK: br label %loop
+
+loop:
+ %phi1 = phi i32 [ 0, %entry ], [ %add, %loopend ]
+ %add = add i32 %phi1, 1
+ %cmp1 = icmp ne i32 %arg1, 0
+ br i1 %cmp1, label %left1, label %right1
+
+left1:
+ %cmp2 = icmp ugt i32 %arg2, 1
+ br i1 %cmp2, label %left2, label %right2
+
+right1:
+ %cmp3 = icmp ugt i32 %arg2, 2
+ br i1 %cmp3, label %left2, label %right2
+
+; CHECK-LABEL: left2:
+; CHECK-DISABLED: %phi2 = phi i32 [ 0, %left1 ], [ 1, %right1 ]
+left2:
+ %phi2 = phi i32 [ 0, %left1 ], [ 1, %right1 ]
+ br label %loopend
+
+; CHECK-LABEL: right2:
+; CHECK: %phi3 = phi i32 [ 2, %left1 ], [ 3, %right1 ]
+right2:
+ %phi3 = phi i32 [ 2, %left1 ], [ 3, %right1 ]
+ br label %loopend
+
+; CHECK-LABEL: loopend:
+; CHECK: %phi4 = phi i32 [ %phi2, %left2 ], [ %phi3, %right2 ]
+loopend:
+ %phi4 = phi i32 [ %phi2, %left2 ], [ %phi3, %right2 ]
+ %cmp4 = icmp ne i32 %phi1, 32
+ br i1 %cmp4, label %loop, label %end
+
+end:
+ ret i32 %phi4
+}
+
+; A phi with multiple incoming values for the same block due to a branch with
+; two destinations that are actually the same. We can't hoist this.
+; TODO: This could be hoisted by erasing one of the incoming values.
+; CHECK-LABEL: @phi_multiple_values_same_block
+define i32 @phi_multiple_values_same_block(i32 %arg) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp = icmp sgt i32 %arg, 4
+; CHECK-NOT: phi
+; CHECK: br label %loop
+entry:
+ br label %loop
+
+loop:
+ %cmp = icmp sgt i32 %arg, 4
+ br i1 %cmp, label %if, label %then
+
+if:
+ br i1 undef, label %then, label %then
+
+then:
+ %phi = phi i32 [ %arg, %loop ], [ 1, %if ], [ 1, %if ]
+ br i1 undef, label %exit, label %loop
+
+exit:
+ ret i32 %phi
+}
+
+; %phi is conditionally used in %d, and the store that %d is used in cannot be
+; hoisted. This means that we have to rehoist %d, but have to make sure to
+; rehoist it after %phi.
+; CHECK-LABEL: @phi_conditional_use
+define i64 @phi_conditional_use(i32 %f, i32* %g) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp1 = icmp eq i32 %f, 1
+; CHECK: %cmp2 = icmp eq i32 %f, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_END_LICM:.*]], label %[[IF_THEN_LICM:.*]]
+entry:
+ %cmp1 = icmp eq i32 %f, 1
+ %cmp2 = icmp eq i32 %f, 0
+ br label %loop
+
+; CHECK-ENABLED: [[IF_THEN_LICM]]:
+; CHECK-ENABLED: br label %[[IF_END_LICM]]
+
+; CHECK-ENABLED: [[IF_END_LICM]]:
+; CHECK-ENABLED: %phi = phi i64 [ 0, %entry ], [ 1, %[[IF_THEN_LICM]] ]
+; CHECK-ENABLED: %d = getelementptr inbounds i32, i32* %g, i64 %phi
+; CHECK-ENABLED: i1 %cmp2, label %[[LOOP_BACKEDGE_LICM:.*]], label %[[IF_THEN2_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_THEN2_LICM]]:
+; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM]]
+
+; CHECK-ENABLED: [[LOOP_BACKEDGE_LICM]]:
+; CHECK: br label %loop
+
+loop:
+ br i1 %cmp1, label %if.end, label %if.then
+
+if.then:
+ br label %if.end
+
+; CHECK-LABEL: if.end:
+; CHECK-DISABLED: %phi = phi i64 [ 0, %loop ], [ 1, %if.then ]
+if.end:
+ %phi = phi i64 [ 0, %loop ], [ 1, %if.then ]
+ br i1 %cmp2, label %loop.backedge, label %if.then2
+
+; CHECK-LABEL: if.then2:
+; CHECK-DISABLED: %d = getelementptr inbounds i32, i32* %g, i64 %phi
+if.then2:
+ %d = getelementptr inbounds i32, i32* %g, i64 %phi
+ store i32 1, i32* %d, align 4
+ br label %loop.backedge
+
+loop.backedge:
+ br label %loop
+}
+
+; As above, but we have two such phis
+; CHECK-LABEL: @phi_conditional_use_twice
+define i64 @phi_conditional_use_twice(i32 %f, i32* %g) {
+; CHECK-LABEL: entry:
+; CHECK: %cmp1 = icmp eq i32 %f, 1
+; CHECK: %cmp2 = icmp eq i32 %f, 0
+; CHECK-ENABLED: br i1 %cmp1, label %[[IF_END_LICM:.*]], label %[[IF_THEN_LICM:.*]]
+entry:
+ %cmp1 = icmp eq i32 %f, 1
+ %cmp2 = icmp eq i32 %f, 0
+ %cmp3 = icmp sgt i32 %f, 0
+ br label %loop
+
+; CHECK-ENABLED: [[IF_THEN_LICM]]:
+; CHECK-ENABLED: br label %[[IF_END_LICM]]
+
+; CHECK-ENABLED: [[IF_END_LICM]]:
+; CHECK-ENABLED: %phi1 = phi i64 [ 0, %entry ], [ 1, %[[IF_THEN_LICM]] ]
+; CHECK-ENABLED: %d = getelementptr inbounds i32, i32* %g, i64 %phi1
+; CHECK-ENABLED: i1 %cmp2, label %[[IF_END2_LICM:.*]], label %[[IF_THEN2_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_THEN2_LICM]]:
+; CHECK-ENABLED: br label %[[IF_END2_LICM]]
+
+; CHECK-ENABLED: [[IF_END2_LICM]]:
+; CHECK-ENABLED: %phi2 = phi i64 [ 2, %[[IF_END_LICM]] ], [ 3, %[[IF_THEN2_LICM]] ]
+; CHECK-ENABLED: %e = getelementptr inbounds i32, i32* %g, i64 %phi2
+; CHECK-ENABLED: i1 %cmp3, label %[[LOOP_BACKEDGE_LICM:.*]], label %[[IF_THEN3_LICM:.*]]
+
+; CHECK-ENABLED: [[IF_THEN3_LICM]]:
+; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM]]
+
+; CHECK-ENABLED: [[LOOP_BACKEDGE_LICM]]:
+; CHECK: br label %loop
+
+loop:
+ br i1 %cmp1, label %if.end, label %if.then
+
+if.then:
+ br label %if.end
+
+; CHECK-LABEL: if.end:
+; CHECK-DISABLED: %phi1 = phi i64 [ 0, %loop ], [ 1, %if.then ]
+if.end:
+ %phi1 = phi i64 [ 0, %loop ], [ 1, %if.then ]
+ br i1 %cmp2, label %if.end2, label %if.then2
+
+; CHECK-LABEL: if.then2:
+; CHECK-DISABLED: %d = getelementptr inbounds i32, i32* %g, i64 %phi1
+if.then2:
+ %d = getelementptr inbounds i32, i32* %g, i64 %phi1
+ store i32 1, i32* %d, align 4
+ br label %if.end2
+
+; CHECK-LABEL: if.end2:
+; CHECK-DISABLED: %phi2 = phi i64 [ 2, %if.end ], [ 3, %if.then2 ]
+if.end2:
+ %phi2 = phi i64 [ 2, %if.end ], [ 3, %if.then2 ]
+ br i1 %cmp3, label %loop.backedge, label %if.then3
+
+; CHECK-LABEL: if.then3:
+; CHECK-DISABLED: %e = getelementptr inbounds i32, i32* %g, i64 %phi2
+if.then3:
+ %e = getelementptr inbounds i32, i32* %g, i64 %phi2
+ store i32 1, i32* %e, align 4
+ br label %loop.backedge
+
+loop.backedge:
+ br label %loop
+}