[llvm] [ControlFlowHub] Add support for switch-based guards (PR #213549)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 2 10:32:05 PDT 2026
https://github.com/hertelukas updated https://github.com/llvm/llvm-project/pull/213549
>From 5c7ab18598de05cccd002d915a674497577c39b7 Mon Sep 17 00:00:00 2001
From: Lukas <dev at lukas-hertel.de>
Date: Sat, 1 Aug 2026 19:57:37 +0200
Subject: [PATCH] [ControlFlowHub] Add support for switch-based guards
Introduce `finalizeAsSwitch` in `ControlFlowHub` to allow generating a
single switch instruction with a PHI node instead of a cascaded branch
sled when routing control flow to multiple outgoing targets.
Expose a `switch-guards` option in the new pass manager for both
`FixIrreducible` and `UnifyLoopExits` passes (defaulting to false).
Assisted by: Gemini 3.1 Pro for code review
---
.../llvm/Transforms/Utils/ControlFlowUtils.h | 37 +-
.../llvm/Transforms/Utils/FixIrreducible.h | 4 +
.../llvm/Transforms/Utils/UnifyLoopExits.h | 3 +
llvm/lib/Passes/PassBuilder.cpp | 10 +
llvm/lib/Passes/PassRegistry.def | 10 +-
.../lib/Transforms/Utils/ControlFlowUtils.cpp | 88 +++-
llvm/lib/Transforms/Utils/FixIrreducible.cpp | 12 +-
llvm/lib/Transforms/Utils/UnifyLoopExits.cpp | 16 +-
.../FixIrreducible/switch_guards.ll | 344 ++++++++++++++
.../UnifyLoopExits/switch_guards.ll | 428 ++++++++++++++++++
10 files changed, 924 insertions(+), 28 deletions(-)
create mode 100644 llvm/test/Transforms/FixIrreducible/switch_guards.ll
create mode 100644 llvm/test/Transforms/UnifyLoopExits/switch_guards.ll
diff --git a/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h b/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h
index e4dab90031e81..8b1ae7de17d0c 100644
--- a/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h
@@ -13,6 +13,7 @@
#ifndef LLVM_TRANSFORMS_UTILS_CONTROLFLOWUTILS_H
#define LLVM_TRANSFORMS_UTILS_CONTROLFLOWUTILS_H
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -25,11 +26,19 @@ class DomTreeUpdater;
/// Given a set of branch descriptors [BB, Succ0, Succ1], create a "hub" such
/// that the control flow from each BB to a successor is now split into two
-/// edges, one from BB to the hub and another from the hub to the successor. The
-/// hub consists of a series of guard blocks, one for each outgoing block. Each
-/// guard block conditionally branches to the corresponding outgoing block, or
-/// the next guard block in the chain. These guard blocks are returned in the
-/// argument vector.
+/// edges, one from BB to the hub and another from the hub to the successor.
+/// There are two ways to configure the hub: either as a branch sled or as a
+/// single switch.
+///
+/// For the branch sled case, the hub consists of a series of guard blocks, one
+/// for each outgoing block. Each guard block conditionally branches to the
+/// corresponding outgoing block, or the next guard block in the chain. These
+/// guard blocks are returned in the argument vector `GuardBlocks`.
+///
+/// In the case of a switch guard, the hub consists only of a single guard block
+/// containing a switch instruction that branches to all outgoing blocks (with
+/// an unreachable default destination). `GuardBlocks` will contain this single
+/// guard block.
///
/// This also updates any PHINodes in the successor. For each such PHINode, the
/// operands corresponding to incoming blocks are moved to a new PHINode in the
@@ -113,14 +122,26 @@ struct ControlFlowHub {
Branches.emplace_back(BB, Succ0, Succ1);
}
- /// Return the unified loop exit block and a flag indicating if the CFG was
- /// changed at all.
+ /// Return the first guard block (or the single switch guard block) and a
+ /// flag indicating if the CFG was changed at all.
LLVM_ABI std::pair<BasicBlock *, bool>
finalize(DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
const StringRef Prefix,
- std::optional<unsigned> MaxControlFlowBooleans = std::nullopt);
+ std::optional<unsigned> MaxControlFlowBooleans = std::nullopt,
+ bool SwitchGuards = false);
SmallVector<BranchDescriptor> Branches;
+
+private:
+ BasicBlock *finalizeAsBrSled(DomTreeUpdater *DTU,
+ SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix,
+ std::optional<unsigned> MaxControlFlowBooleans,
+ SetVector<BasicBlock *> &Outgoing);
+ BasicBlock *finalizeAsSwitch(DomTreeUpdater *DTU,
+ SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix,
+ SetVector<BasicBlock *> &Outgoing);
};
} // end namespace llvm
diff --git a/llvm/include/llvm/Transforms/Utils/FixIrreducible.h b/llvm/include/llvm/Transforms/Utils/FixIrreducible.h
index b10ca1a590a37..84358f00b94b6 100644
--- a/llvm/include/llvm/Transforms/Utils/FixIrreducible.h
+++ b/llvm/include/llvm/Transforms/Utils/FixIrreducible.h
@@ -13,7 +13,11 @@
namespace llvm {
struct FixIrreduciblePass : OptionalPassInfoMixin<FixIrreduciblePass> {
+
+ FixIrreduciblePass(bool SwitchGuards = false) : SwitchGuards(SwitchGuards) {}
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+ bool SwitchGuards;
};
} // namespace llvm
diff --git a/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h b/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h
index c304ff912bb5e..627ff1563d972 100644
--- a/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h
+++ b/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h
@@ -15,7 +15,10 @@ namespace llvm {
class UnifyLoopExitsPass : public RequiredPassInfoMixin<UnifyLoopExitsPass> {
public:
+ UnifyLoopExitsPass(bool SwitchGuards = false) : SwitchGuards(SwitchGuards) {}
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+ bool SwitchGuards;
};
} // namespace llvm
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index f758469dd8ebb..8a222bb1f7d5a 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -968,6 +968,11 @@ Expected<bool> parseDropUnnecessaryAssumesPassOptions(StringRef Params) {
"DropUnnecessaryAssumes");
}
+Expected<bool> parseFixIrreduciblePassOptions(StringRef Params) {
+ return PassBuilder::parseSinglePassOption(Params, "switch-guards",
+ "FixIrreducible");
+}
+
Expected<bool> parseLoopExtractorPassOptions(StringRef Params) {
return PassBuilder::parseSinglePassOption(Params, "single", "LoopExtractor");
}
@@ -977,6 +982,11 @@ Expected<bool> parseLowerMatrixIntrinsicsPassOptions(StringRef Params) {
"LowerMatrixIntrinsics");
}
+Expected<bool> parseUnifyLoopExitsPassOptions(StringRef Params) {
+ return PassBuilder::parseSinglePassOption(Params, "switch-guards",
+ "UnifyLoopExits");
+}
+
Expected<IRNormalizerOptions> parseIRNormalizerPassOptions(StringRef Params) {
IRNormalizerOptions Result;
while (!Params.empty()) {
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 0955bfdb3246c..b94316424313d 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -452,7 +452,6 @@ FUNCTION_PASS("expand-memcmp", ExpandMemCmpPass())
FUNCTION_PASS("expand-reductions", ExpandReductionsPass())
FUNCTION_PASS("extra-vector-passes",
ExtraFunctionPassManager<ShouldRunExtraVectorPasses>())
-FUNCTION_PASS("fix-irreducible", FixIrreduciblePass())
FUNCTION_PASS("flatten-cfg", FlattenCFGPass())
FUNCTION_PASS("float2int", Float2IntPass())
FUNCTION_PASS("free-machine-function", FreeMachineFunctionPass())
@@ -565,7 +564,6 @@ FUNCTION_PASS("trigger-crash-function", TriggerCrashFunctionPass())
FUNCTION_PASS("trigger-verifier-error", TriggerVerifierErrorPass())
FUNCTION_PASS("tsan", ThreadSanitizerPass())
FUNCTION_PASS("typepromotion", TypePromotionPass(*TM))
-FUNCTION_PASS("unify-loop-exits", UnifyLoopExitsPass())
FUNCTION_PASS("unreachableblockelim", UnreachableBlockElimPass())
FUNCTION_PASS("vector-combine", VectorCombinePass())
FUNCTION_PASS("verify", VerifierPass())
@@ -600,6 +598,10 @@ FUNCTION_PASS_WITH_PARAMS(
"ee-instrument", "EntryExitInstrumenterPass",
[](bool PostInlining) { return EntryExitInstrumenterPass(PostInlining); },
parseEntryExitInstrumenterPassOptions, "post-inline")
+FUNCTION_PASS_WITH_PARAMS(
+ "fix-irreducible", "FixIrreduciblePass",
+ [](bool SwitchGuards) { return FixIrreduciblePass(SwitchGuards); },
+ parseFixIrreduciblePassOptions, "switch-guards")
FUNCTION_PASS_WITH_PARAMS(
"func-properties-stats", "FunctionPropertiesStatisticsPass",
[](bool IsPreOptimizations) {
@@ -737,6 +739,10 @@ FUNCTION_PASS_WITH_PARAMS(
return StructurizeCFGPass(SkipUniformRegions);
},
parseStructurizeCFGPassOptions, "skip-uniform-regions")
+FUNCTION_PASS_WITH_PARAMS(
+ "unify-loop-exits", "UnifyLoopExitsPass",
+ [](bool SwitchGuards) { return UnifyLoopExitsPass(SwitchGuards); },
+ parseUnifyLoopExitsPassOptions, "switch-guards")
FUNCTION_PASS_WITH_PARAMS(
"win-eh-prepare", "WinEHPreparePass",
[](bool DemoteCatchSwitchPHIOnly) {
diff --git a/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp b/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp
index cf3bacc08f5a5..d35c059ddfbb6 100644
--- a/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp
@@ -15,6 +15,7 @@
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Transforms/Utils/Local.h"
@@ -279,7 +280,8 @@ static void reconnectPhis(BasicBlock *Out, BasicBlock *GuardBlock,
std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
- const StringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans) {
+ const StringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans,
+ bool SwitchGuards) {
#ifndef NDEBUG
SmallPtrSet<BasicBlock *, 8> Incoming;
#endif
@@ -287,9 +289,10 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
for (auto [BB, Succ0, Succ1] : Branches) {
#ifndef NDEBUG
- assert(
- (Incoming.insert(BB).second || isa<CallBrInst>(BB->getTerminator())) &&
- "Duplicate entry for incoming block.");
+ assert((Incoming.insert(BB).second ||
+ isa<CallBrInst>(BB->getTerminator()) ||
+ isa<SwitchInst>(BB->getTerminator())) &&
+ "Duplicate entry for incoming block.");
#endif
if (Succ0)
Outgoing.insert(Succ0);
@@ -302,6 +305,18 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
if (Outgoing.size() < 2)
return {Outgoing.front(), false};
+ if (SwitchGuards && Outgoing.size() > 2)
+ return {finalizeAsSwitch(DTU, GuardBlocks, Prefix, Outgoing), true};
+
+ return {finalizeAsBrSled(DTU, GuardBlocks, Prefix, MaxControlFlowBooleans,
+ Outgoing),
+ true};
+}
+
+BasicBlock *ControlFlowHub::finalizeAsBrSled(
+ DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans,
+ SetVector<BasicBlock *> &Outgoing) {
SmallVector<DominatorTree::UpdateType, 16> Updates;
if (DTU) {
for (auto [BB, Succ0, Succ1] : Branches) {
@@ -350,5 +365,68 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
Inst->eraseFromParent();
}
- return {FirstGuardBlock, true};
+ return FirstGuardBlock;
+}
+
+BasicBlock *ControlFlowHub::finalizeAsSwitch(
+ DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix, SetVector<BasicBlock *> &Outgoing) {
+
+ SmallVector<DominatorTree::UpdateType, 16> Updates;
+ if (DTU) {
+ for (auto [BB, Succ0, Succ1] : Branches) {
+ if (Succ0)
+ Updates.push_back({DominatorTree::Delete, BB, Succ0});
+ // Only add Succ1 if it's different from Succ0 to avoid duplicate updates
+ if (Succ1 && Succ1 != Succ0)
+ Updates.push_back({DominatorTree::Delete, BB, Succ1});
+ }
+ }
+
+ Function *F = Outgoing.front()->getParent();
+ BasicBlock *Guard = BasicBlock::Create(F->getContext(), Prefix + ".guard", F);
+ GuardBlocks.push_back(Guard);
+ Type *Int32Ty = Type::getInt32Ty(F->getContext());
+ IRBuilder<> Builder(Guard);
+ auto *Phi = Builder.CreatePHI(Int32Ty, Branches.size(), "merged.bb.idx");
+ for (auto [BB, Succ0, Succ1] : Branches) {
+ Value *Condition = redirectToHub(BB, Succ0, Succ1, Guard);
+ Value *IncomingId = nullptr;
+
+ if (Succ0 && Succ1) {
+ Value *Id0 = ConstantInt::get(
+ Int32Ty, std::distance(Outgoing.begin(), find(Outgoing, Succ0)));
+ Value *Id1 = ConstantInt::get(
+ Int32Ty, std::distance(Outgoing.begin(), find(Outgoing, Succ1)));
+ IncomingId = SelectInst::Create(Condition, Id0, Id1, "target.bb.idx",
+ BB->getTerminator()->getIterator());
+ } else {
+ BasicBlock *Succ = Succ0 ? Succ0 : Succ1;
+ uint64_t Idx = std::distance(Outgoing.begin(), find(Outgoing, Succ));
+ IncomingId = ConstantInt::get(Int32Ty, Idx);
+ }
+
+ Phi->addIncoming(IncomingId, BB);
+ }
+
+ BasicBlock *DefaultDest =
+ BasicBlock::Create(F->getContext(), Prefix + ".guard.default", F);
+ IRBuilder<>(DefaultDest).CreateUnreachable();
+ SwitchInst *SI = Builder.CreateSwitch(Phi, DefaultDest, Outgoing.size());
+ for (unsigned I = 0, E = Outgoing.size(); I != E; ++I)
+ SI->addCase(Builder.getInt32(I), Outgoing[I]);
+
+ for (BasicBlock *Out : Outgoing)
+ reconnectPhis(Out, Guard, Branches, Guard);
+
+ if (DTU) {
+ for (auto [BB, Succ0, Succ1] : Branches)
+ Updates.push_back({DominatorTree::Insert, BB, Guard});
+
+ for (BasicBlock *Out : Outgoing)
+ Updates.push_back({DominatorTree::Insert, Guard, Out});
+ Updates.push_back({DominatorTree::Insert, Guard, DefaultDest});
+ DTU->applyUpdates(Updates);
+ }
+ return Guard;
}
diff --git a/llvm/lib/Transforms/Utils/FixIrreducible.cpp b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
index 09e9f426dced6..49a8247224e18 100644
--- a/llvm/lib/Transforms/Utils/FixIrreducible.cpp
+++ b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
@@ -272,7 +272,7 @@ static void updateLoopInfo(CycleInfo &CI, LoopInfo &LI, CycleRef C,
// natural loop. Also insert this new loop at its appropriate place in the
// hierarchy of loops.
static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
- LoopInfo *LI) {
+ LoopInfo *LI, bool SwitchGuards) {
if (CI.isReducible(C))
return false;
LLVM_DEBUG(dbgs() << "Processing cycle:\n" << CI.print(C) << "\n";);
@@ -395,7 +395,7 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
SetVector<BasicBlock *> Entries;
Entries.insert(CI.getEntries(C).rbegin(), CI.getEntries(C).rend());
- CHub.finalize(&DTU, GuardBlocks, "irr");
+ CHub.finalize(&DTU, GuardBlocks, "irr", std::nullopt, SwitchGuards);
#if defined(EXPENSIVE_CHECKS)
assert(DT.verify(DominatorTree::VerificationLevel::Full));
#else
@@ -423,13 +423,13 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
}
static bool FixIrreducibleImpl(Function &F, CycleInfo &CI, DominatorTree &DT,
- LoopInfo *LI) {
+ LoopInfo *LI, bool SwitchGuards) {
LLVM_DEBUG(dbgs() << "===== Fix irreducible control-flow in function: "
<< F.getName() << "\n");
bool Changed = false;
for (auto C : CI.cycles())
- Changed |= fixIrreducible(C, CI, DT, LI);
+ Changed |= fixIrreducible(C, CI, DT, LI, SwitchGuards);
if (!Changed)
return false;
@@ -448,7 +448,7 @@ bool FixIrreducible::runOnFunction(Function &F) {
LoopInfo *LI = LIWP ? &LIWP->getLoopInfo() : nullptr;
auto &CI = getAnalysis<CycleInfoWrapperPass>().getResult();
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
- return FixIrreducibleImpl(F, CI, DT, LI);
+ return FixIrreducibleImpl(F, CI, DT, LI, false);
}
PreservedAnalyses FixIrreduciblePass::run(Function &F,
@@ -457,7 +457,7 @@ PreservedAnalyses FixIrreduciblePass::run(Function &F,
auto &CI = AM.getResult<CycleAnalysis>(F);
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
- if (!FixIrreducibleImpl(F, CI, DT, LI))
+ if (!FixIrreducibleImpl(F, CI, DT, LI, SwitchGuards))
return PreservedAnalyses::all();
PreservedAnalyses PA;
diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
index b8aba85130368..56162d1de78af 100644
--- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
+++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
@@ -147,7 +147,8 @@ static void restoreSSA(const DominatorTree &DT, const Loop *L,
}
}
-static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
+static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L,
+ bool SwitchGuards) {
// To unify the loop exits, we need a list of the exiting blocks as
// well as exit blocks. The functions for locating these lists both
// traverse the entire loop body. It is more efficient to first
@@ -241,8 +242,9 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
SmallVector<BasicBlock *, 8> GuardBlocks;
BasicBlock *LoopExitBlock;
bool ChangedCFG;
- std::tie(LoopExitBlock, ChangedCFG) = CHub.finalize(
- &DTU, GuardBlocks, "loop.exit", MaxBooleansInControlFlowHub.getValue());
+ std::tie(LoopExitBlock, ChangedCFG) =
+ CHub.finalize(&DTU, GuardBlocks, "loop.exit",
+ MaxBooleansInControlFlowHub.getValue(), SwitchGuards);
ChangedCFG |= Changed;
if (!ChangedCFG)
return false;
@@ -281,13 +283,13 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
return true;
}
-static bool runImpl(LoopInfo &LI, DominatorTree &DT) {
+static bool runImpl(LoopInfo &LI, DominatorTree &DT, bool SwitchGuards) {
bool Changed = false;
auto Loops = LI.getLoopsInPreorder();
for (auto *L : Loops) {
LLVM_DEBUG(dbgs() << "Processing loop:\n"; L->print(dbgs()));
- Changed |= unifyLoopExits(DT, LI, L);
+ Changed |= unifyLoopExits(DT, LI, L, SwitchGuards);
}
return Changed;
}
@@ -298,7 +300,7 @@ bool UnifyLoopExitsLegacyPass::runOnFunction(Function &F) {
auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
- return runImpl(LI, DT);
+ return runImpl(LI, DT, false);
}
namespace llvm {
@@ -310,7 +312,7 @@ PreservedAnalyses UnifyLoopExitsPass::run(Function &F,
auto &LI = AM.getResult<LoopAnalysis>(F);
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
- if (!runImpl(LI, DT))
+ if (!runImpl(LI, DT, SwitchGuards))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<LoopAnalysis>();
diff --git a/llvm/test/Transforms/FixIrreducible/switch_guards.ll b/llvm/test/Transforms/FixIrreducible/switch_guards.ll
new file mode 100644
index 0000000000000..836aedfd0f119
--- /dev/null
+++ b/llvm/test/Transforms/FixIrreducible/switch_guards.ll
@@ -0,0 +1,344 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes='fix-irreducible<switch-guards>' -S | FileCheck %s --check-prefixes=CHECK,SWITCH
+; RUN: opt < %s -passes='fix-irreducible' -S | FileCheck %s --check-prefixes=CHECK,BOOLEAN
+; RUN: opt < %s -passes='verify<loops>,fix-irreducible<switch-guards>,verify<loops>' -S | FileCheck %s --check-prefixes=CHECK,SWITCH
+
+declare void @use(i32)
+
+define i32 @irreducible_three_entries(i1 %PredEntry, i1 %PredEntry2, i1 %PredA, i1 %PredB, i1 %PredC, i32 %X, i32 %Y, i32 %Z) {
+; SWITCH-LABEL: define i32 @irreducible_three_entries(
+; SWITCH-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*]]:
+; SWITCH-NEXT: br i1 [[PREDENTRY]], label %[[IRR_GUARD:.*]], label %[[ENTRY2:.*]]
+; SWITCH: [[ENTRY2]]:
+; SWITCH-NEXT: [[TARGET_BB_IDX:%.*]] = select i1 [[PREDENTRY2]], i32 1, i32 0
+; SWITCH-NEXT: br label %[[IRR_GUARD]]
+; SWITCH: [[A:.*]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; SWITCH: [[EXIT]]:
+; SWITCH-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; SWITCH-NEXT: ret i32 [[RES]]
+; SWITCH: [[IRR_GUARD]]:
+; SWITCH-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY2]] ], [ [[X]], %[[ENTRY]] ]
+; SWITCH-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[Y]], %[[ENTRY2]] ], [ poison, %[[ENTRY]] ]
+; SWITCH-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY2]] ], [ poison, %[[ENTRY]] ]
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[B]] ], [ [[TARGET_BB_IDX]], %[[ENTRY2]] ], [ 2, %[[ENTRY]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[IRR_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[C]]
+; SWITCH-NEXT: i32 1, label %[[B]]
+; SWITCH-NEXT: i32 2, label %[[A]]
+; SWITCH-NEXT: ]
+; SWITCH: [[IRR_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define i32 @irreducible_three_entries(
+; BOOLEAN-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*]]:
+; BOOLEAN-NEXT: [[PREDENTRY2_INV:%.*]] = xor i1 [[PREDENTRY2]], true
+; BOOLEAN-NEXT: br i1 [[PREDENTRY]], label %[[IRR_GUARD:.*]], label %[[ENTRY2:.*]]
+; BOOLEAN: [[ENTRY2]]:
+; BOOLEAN-NEXT: br label %[[IRR_GUARD]]
+; BOOLEAN: [[A:.*]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD1:.*]] ]
+; BOOLEAN-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; BOOLEAN: [[B]]:
+; BOOLEAN-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD1]] ]
+; BOOLEAN-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; BOOLEAN-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; BOOLEAN: [[C]]:
+; BOOLEAN-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; BOOLEAN-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; BOOLEAN: [[EXIT]]:
+; BOOLEAN-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; BOOLEAN-NEXT: ret i32 [[RES]]
+; BOOLEAN: [[IRR_GUARD]]:
+; BOOLEAN-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY2]] ], [ [[X]], %[[ENTRY]] ]
+; BOOLEAN-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[Y]], %[[ENTRY2]] ], [ poison, %[[ENTRY]] ]
+; BOOLEAN-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY2]] ], [ poison, %[[ENTRY]] ]
+; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, %[[B]] ], [ [[PREDENTRY2_INV]], %[[ENTRY2]] ], [ false, %[[ENTRY]] ]
+; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ false, %[[B]] ], [ true, %[[ENTRY2]] ], [ false, %[[ENTRY]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_C]], label %[[C]], label %[[IRR_GUARD1]]
+; BOOLEAN: [[IRR_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[A]]
+;
+entry:
+ br i1 %PredEntry, label %A, label %entry2
+
+entry2:
+ br i1 %PredEntry2, label %B, label %C
+
+A:
+ %valA = phi i32 [ %X, %entry ], [ %valC, %C ]
+ %incA = add i32 %valA, 1
+ br i1 %PredA, label %B, label %exit
+
+B:
+ %valB = phi i32 [ %Y, %entry2 ], [ %incA, %A ]
+ %incB = add i32 %valB, 2
+ br i1 %PredB, label %C, label %exit
+
+C:
+ %valC = phi i32 [ %Z, %entry2 ], [ %incB, %B ]
+ %incC = add i32 %valC, 3
+ br i1 %PredC, label %A, label %exit
+
+exit:
+ %res = phi i32 [ %incA, %A ], [ %incB, %B ], [ %incC, %C ]
+ ret i32 %res
+}
+
+; Irreducible cycle where one predecessor only enters on one branch arm.
+define i32 @irreducible_partial_condition_entry(i1 %PredEntry, i1 %PredEntry1, i1 %PredEntry2, i1 %PredA, i1 %PredB, i1 %PredC, i32 %X, i32 %Y, i32 %Z) {
+; SWITCH-LABEL: define i32 @irreducible_partial_condition_entry(
+; SWITCH-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDENTRY1:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*:]]
+; SWITCH-NEXT: br i1 [[PREDENTRY]], label %[[ENTRY1:.*]], label %[[ENTRY2:.*]]
+; SWITCH: [[ENTRY1]]:
+; SWITCH-NEXT: br i1 [[PREDENTRY1]], label %[[IRR_GUARD:.*]], label %[[OUTSIDE_EXIT:.*]]
+; SWITCH: [[ENTRY2]]:
+; SWITCH-NEXT: [[TARGET_BB_IDX:%.*]] = select i1 [[PREDENTRY2]], i32 1, i32 0
+; SWITCH-NEXT: br label %[[IRR_GUARD]]
+; SWITCH: [[A:.*]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; SWITCH: [[EXIT]]:
+; SWITCH-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; SWITCH-NEXT: ret i32 [[RES]]
+; SWITCH: [[OUTSIDE_EXIT]]:
+; SWITCH-NEXT: ret i32 0
+; SWITCH: [[IRR_GUARD]]:
+; SWITCH-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY2]] ], [ [[X]], %[[ENTRY1]] ]
+; SWITCH-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[Y]], %[[ENTRY2]] ], [ poison, %[[ENTRY1]] ]
+; SWITCH-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY2]] ], [ poison, %[[ENTRY1]] ]
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[B]] ], [ [[TARGET_BB_IDX]], %[[ENTRY2]] ], [ 2, %[[ENTRY1]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[IRR_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[C]]
+; SWITCH-NEXT: i32 1, label %[[B]]
+; SWITCH-NEXT: i32 2, label %[[A]]
+; SWITCH-NEXT: ]
+; SWITCH: [[IRR_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define i32 @irreducible_partial_condition_entry(
+; BOOLEAN-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDENTRY1:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*:]]
+; BOOLEAN-NEXT: [[PREDENTRY2_INV:%.*]] = xor i1 [[PREDENTRY2]], true
+; BOOLEAN-NEXT: br i1 [[PREDENTRY]], label %[[ENTRY1:.*]], label %[[ENTRY2:.*]]
+; BOOLEAN: [[ENTRY1]]:
+; BOOLEAN-NEXT: br i1 [[PREDENTRY1]], label %[[IRR_GUARD:.*]], label %[[OUTSIDE_EXIT:.*]]
+; BOOLEAN: [[ENTRY2]]:
+; BOOLEAN-NEXT: br label %[[IRR_GUARD]]
+; BOOLEAN: [[A:.*]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD1:.*]] ]
+; BOOLEAN-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; BOOLEAN: [[B]]:
+; BOOLEAN-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD1]] ]
+; BOOLEAN-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; BOOLEAN-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; BOOLEAN: [[C]]:
+; BOOLEAN-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; BOOLEAN-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; BOOLEAN: [[EXIT]]:
+; BOOLEAN-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; BOOLEAN-NEXT: ret i32 [[RES]]
+; BOOLEAN: [[OUTSIDE_EXIT]]:
+; BOOLEAN-NEXT: ret i32 0
+; BOOLEAN: [[IRR_GUARD]]:
+; BOOLEAN-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY2]] ], [ [[X]], %[[ENTRY1]] ]
+; BOOLEAN-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[Y]], %[[ENTRY2]] ], [ poison, %[[ENTRY1]] ]
+; BOOLEAN-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY2]] ], [ poison, %[[ENTRY1]] ]
+; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, %[[B]] ], [ [[PREDENTRY2_INV]], %[[ENTRY2]] ], [ false, %[[ENTRY1]] ]
+; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ false, %[[B]] ], [ true, %[[ENTRY2]] ], [ false, %[[ENTRY1]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_C]], label %[[C]], label %[[IRR_GUARD1]]
+; BOOLEAN: [[IRR_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[A]]
+;
+entry:
+ br i1 %PredEntry, label %entry1, label %entry2
+
+entry1:
+ br i1 %PredEntry1, label %A, label %outside_exit
+
+entry2:
+ br i1 %PredEntry2, label %B, label %C
+
+A:
+ %valA = phi i32 [ %X, %entry1 ], [ %valC, %C ]
+ %incA = add i32 %valA, 1
+ br i1 %PredA, label %B, label %exit
+
+B:
+ %valB = phi i32 [ %Y, %entry2 ], [ %incA, %A ]
+ %incB = add i32 %valB, 2
+ br i1 %PredB, label %C, label %exit
+
+C:
+ %valC = phi i32 [ %Z, %entry2 ], [ %incB, %B ]
+ %incC = add i32 %valC, 3
+ br i1 %PredC, label %A, label %exit
+
+exit:
+ %res = phi i32 [ %incA, %A ], [ %incB, %B ], [ %incC, %C ]
+ ret i32 %res
+
+outside_exit:
+ ret i32 0
+}
+
+define i32 @irreducible_multiple_preds_same_entry(i1 %PredEntry1, i1 %PredEntry2, i1 %PredEntry3, i1 %PredA, i1 %PredB, i1 %PredC, i32 %X1, i32 %X2, i32 %Y, i32 %Z) {
+; SWITCH-LABEL: define i32 @irreducible_multiple_preds_same_entry(
+; SWITCH-SAME: i1 [[PREDENTRY1:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDENTRY3:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X1:%.*]], i32 [[X2:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*:]]
+; SWITCH-NEXT: br i1 [[PREDENTRY1]], label %[[ENTRY2:.*]], label %[[ENTRY3:.*]]
+; SWITCH: [[ENTRY2]]:
+; SWITCH-NEXT: [[TARGET_BB_IDX1:%.*]] = select i1 [[PREDENTRY2]], i32 1, i32 2
+; SWITCH-NEXT: br label %[[IRR_GUARD:.*]]
+; SWITCH: [[ENTRY3]]:
+; SWITCH-NEXT: [[TARGET_BB_IDX:%.*]] = select i1 [[PREDENTRY3]], i32 1, i32 0
+; SWITCH-NEXT: br label %[[IRR_GUARD]]
+; SWITCH: [[A:.*]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; SWITCH: [[EXIT]]:
+; SWITCH-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; SWITCH-NEXT: ret i32 [[RES]]
+; SWITCH: [[IRR_GUARD]]:
+; SWITCH-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY3]] ], [ [[Y]], %[[ENTRY2]] ]
+; SWITCH-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[X2]], %[[ENTRY3]] ], [ [[X1]], %[[ENTRY2]] ]
+; SWITCH-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY3]] ], [ poison, %[[ENTRY2]] ]
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[B]] ], [ [[TARGET_BB_IDX]], %[[ENTRY3]] ], [ [[TARGET_BB_IDX1]], %[[ENTRY2]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[IRR_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[C]]
+; SWITCH-NEXT: i32 1, label %[[A]]
+; SWITCH-NEXT: i32 2, label %[[B]]
+; SWITCH-NEXT: ]
+; SWITCH: [[IRR_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define i32 @irreducible_multiple_preds_same_entry(
+; BOOLEAN-SAME: i1 [[PREDENTRY1:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDENTRY3:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X1:%.*]], i32 [[X2:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*:]]
+; BOOLEAN-NEXT: [[PREDENTRY3_INV:%.*]] = xor i1 [[PREDENTRY3]], true
+; BOOLEAN-NEXT: br i1 [[PREDENTRY1]], label %[[ENTRY2:.*]], label %[[ENTRY3:.*]]
+; BOOLEAN: [[ENTRY2]]:
+; BOOLEAN-NEXT: br label %[[IRR_GUARD:.*]]
+; BOOLEAN: [[ENTRY3]]:
+; BOOLEAN-NEXT: br label %[[IRR_GUARD]]
+; BOOLEAN: [[A:.*]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD1:.*]] ]
+; BOOLEAN-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; BOOLEAN: [[B]]:
+; BOOLEAN-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD1]] ]
+; BOOLEAN-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; BOOLEAN-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; BOOLEAN: [[C]]:
+; BOOLEAN-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; BOOLEAN-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; BOOLEAN: [[EXIT]]:
+; BOOLEAN-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; BOOLEAN-NEXT: ret i32 [[RES]]
+; BOOLEAN: [[IRR_GUARD]]:
+; BOOLEAN-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY3]] ], [ [[Y]], %[[ENTRY2]] ]
+; BOOLEAN-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[X2]], %[[ENTRY3]] ], [ [[X1]], %[[ENTRY2]] ]
+; BOOLEAN-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY3]] ], [ poison, %[[ENTRY2]] ]
+; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, %[[B]] ], [ [[PREDENTRY3_INV]], %[[ENTRY3]] ], [ false, %[[ENTRY2]] ]
+; BOOLEAN-NEXT: [[GUARD_A:%.*]] = phi i1 [ false, %[[B]] ], [ true, %[[ENTRY3]] ], [ [[PREDENTRY2]], %[[ENTRY2]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_C]], label %[[C]], label %[[IRR_GUARD1]]
+; BOOLEAN: [[IRR_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_A]], label %[[A]], label %[[B]]
+;
+entry:
+ br i1 %PredEntry1, label %entry2, label %entry3
+
+entry2:
+ br i1 %PredEntry2, label %A, label %B
+
+entry3:
+ br i1 %PredEntry3, label %A, label %C
+
+A:
+ %valA = phi i32 [ %X1, %entry2 ], [ %X2, %entry3 ], [ %valC, %C ]
+ %incA = add i32 %valA, 1
+ br i1 %PredA, label %B, label %exit
+
+B:
+ %valB = phi i32 [ %Y, %entry2 ], [ %incA, %A ]
+ %incB = add i32 %valB, 2
+ br i1 %PredB, label %C, label %exit
+
+C:
+ %valC = phi i32 [ %Z, %entry3 ], [ %incB, %B ]
+ %incC = add i32 %valC, 3
+ br i1 %PredC, label %A, label %exit
+
+exit:
+ %res = phi i32 [ %incA, %A ], [ %incB, %B ], [ %incC, %C ]
+ ret i32 %res
+}
+
+; Fallback to branch sled when cycle entries <= 2.
+define i32 @irreducible_two_entries_fallback(i1 %PredEntry, i1 %PredLeft, i1 %PredRight, i32 %X, i32 %Y) {
+; CHECK-LABEL: define i32 @irreducible_two_entries_fallback(
+; CHECK-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDLEFT:%.*]], i1 [[PREDRIGHT:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PREDENTRY_INV:%.*]] = xor i1 [[PREDENTRY]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[LEFT:.*]]:
+; CHECK-NEXT: [[L_PHI:%.*]] = phi i32 [ [[R_PHI_MOVED:%.*]], %[[RIGHT:.*]] ], [ [[L_PHI_MOVED:%.*]], %[[IRR_GUARD]] ]
+; CHECK-NEXT: [[L:%.*]] = add i32 [[L_PHI]], 1
+; CHECK-NEXT: br i1 [[PREDLEFT]], label %[[IRR_GUARD]], label %[[EXIT:.*]]
+; CHECK: [[RIGHT]]:
+; CHECK-NEXT: br i1 [[PREDRIGHT]], label %[[LEFT]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[Z:%.*]] = phi i32 [ [[L]], %[[LEFT]] ], [ [[R_PHI_MOVED]], %[[RIGHT]] ]
+; CHECK-NEXT: ret i32 [[Z]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[L_PHI_MOVED]] = phi i32 [ poison, %[[LEFT]] ], [ [[X]], %[[ENTRY]] ]
+; CHECK-NEXT: [[R_PHI_MOVED]] = phi i32 [ [[L]], %[[LEFT]] ], [ [[Y]], %[[ENTRY]] ]
+; CHECK-NEXT: [[GUARD_RIGHT:%.*]] = phi i1 [ true, %[[LEFT]] ], [ [[PREDENTRY_INV]], %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_RIGHT]], label %[[RIGHT]], label %[[LEFT]]
+;
+entry:
+ br i1 %PredEntry, label %left, label %right
+
+left:
+ %L.phi = phi i32 [ %X, %entry ], [ %R.phi, %right ]
+ %L = add i32 %L.phi, 1
+ br i1 %PredLeft, label %right, label %exit
+
+right:
+ %R.phi = phi i32 [ %Y, %entry ], [ %L, %left ]
+ br i1 %PredRight, label %left, label %exit
+
+exit:
+ %Z = phi i32 [ %L, %left ], [ %R.phi, %right ]
+ ret i32 %Z
+}
diff --git a/llvm/test/Transforms/UnifyLoopExits/switch_guards.ll b/llvm/test/Transforms/UnifyLoopExits/switch_guards.ll
new file mode 100644
index 0000000000000..41e1f17302462
--- /dev/null
+++ b/llvm/test/Transforms/UnifyLoopExits/switch_guards.ll
@@ -0,0 +1,428 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes='unify-loop-exits<switch-guards>' -S | FileCheck %s --check-prefixes=CHECK,SWITCH
+; RUN: opt < %s -passes='unify-loop-exits' -S | FileCheck %s --check-prefixes=CHECK,BOOLEAN
+; RUN: opt < %s -passes='verify<loops>,unify-loop-exits<switch-guards>,verify<loops>' -S | FileCheck %s --check-prefixes=CHECK,SWITCH
+
+declare void @use(i32)
+
+define void @loop_three_exits(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC) {
+; SWITCH-LABEL: define void @loop_three_exits(
+; SWITCH-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*]]:
+; SWITCH-NEXT: br i1 [[PREDENTRY]], label %[[A:.*]], label %[[EXIT1:.*]]
+; SWITCH: [[A]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[C:.*]] ]
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[C]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INC]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; SWITCH: [[EXIT1]]:
+; SWITCH-NEXT: call void @use(i32 1)
+; SWITCH-NEXT: ret void
+; SWITCH: [[EXIT2:.*]]:
+; SWITCH-NEXT: call void @use(i32 2)
+; SWITCH-NEXT: ret void
+; SWITCH: [[EXIT3:.*]]:
+; SWITCH-NEXT: call void @use(i32 3)
+; SWITCH-NEXT: ret void
+; SWITCH: [[LOOP_EXIT_GUARD]]:
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[A]] ], [ 1, %[[B]] ], [ 2, %[[C]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[LOOP_EXIT_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[EXIT1]]
+; SWITCH-NEXT: i32 1, label %[[EXIT2]]
+; SWITCH-NEXT: i32 2, label %[[EXIT3]]
+; SWITCH-NEXT: ]
+; SWITCH: [[LOOP_EXIT_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define void @loop_three_exits(
+; BOOLEAN-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*]]:
+; BOOLEAN-NEXT: br i1 [[PREDENTRY]], label %[[A:.*]], label %[[EXIT1:.*]]
+; BOOLEAN: [[A]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[C:.*]] ]
+; BOOLEAN-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B:.*]]
+; BOOLEAN: [[B]]:
+; BOOLEAN-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[C]]
+; BOOLEAN: [[C]]:
+; BOOLEAN-NEXT: [[INC]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDC]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; BOOLEAN: [[EXIT1]]:
+; BOOLEAN-NEXT: call void @use(i32 1)
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[EXIT2:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 2)
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[EXIT3:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 3)
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[LOOP_EXIT_GUARD]]:
+; BOOLEAN-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[A]] ], [ false, %[[B]] ], [ false, %[[C]] ]
+; BOOLEAN-NEXT: [[GUARD_EXIT2:%.*]] = phi i1 [ false, %[[A]] ], [ true, %[[B]] ], [ false, %[[C]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[LOOP_EXIT_GUARD1:.*]]
+; BOOLEAN: [[LOOP_EXIT_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT2]], label %[[EXIT2]], label %[[EXIT3]]
+;
+entry:
+ br i1 %PredEntry, label %A, label %Exit1
+
+A:
+ %valA = phi i32 [ 0, %entry ], [ %inc, %C ]
+ br i1 %PredA, label %Exit1, label %B
+
+B:
+ br i1 %PredB, label %Exit2, label %C
+
+C:
+ %inc = add i32 %valA, 1
+ br i1 %PredC, label %Exit3, label %A
+
+Exit1:
+ call void @use(i32 1)
+ ret void
+
+Exit2:
+ call void @use(i32 2)
+ ret void
+
+Exit3:
+ call void @use(i32 3)
+ ret void
+}
+
+define void @loop_switch_terminator(i32 %Val, i1 %PredLoop) {
+; SWITCH-LABEL: define void @loop_switch_terminator(
+; SWITCH-SAME: i32 [[VAL:%.*]], i1 [[PREDLOOP:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*]]:
+; SWITCH-NEXT: br label %[[A:.*]]
+; SWITCH: [[A]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOPBACK:.*]] ]
+; SWITCH-NEXT: switch i32 [[VAL]], label %[[LOOPBACK]] [
+; SWITCH-NEXT: i32 0, label %[[A_TARGET_EXIT1:.*]]
+; SWITCH-NEXT: i32 1, label %[[A_TARGET_EXIT2:.*]]
+; SWITCH-NEXT: i32 2, label %[[A_TARGET_EXIT3:.*]]
+; SWITCH-NEXT: ]
+; SWITCH: [[LOOPBACK]]:
+; SWITCH-NEXT: [[INC]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDLOOP]], label %[[A]], label %[[LOOP_EXIT_GUARD:.*]]
+; SWITCH: [[EXIT1:.*]]:
+; SWITCH-NEXT: call void @use(i32 10)
+; SWITCH-NEXT: ret void
+; SWITCH: [[EXIT2:.*]]:
+; SWITCH-NEXT: call void @use(i32 20)
+; SWITCH-NEXT: ret void
+; SWITCH: [[EXIT3:.*]]:
+; SWITCH-NEXT: call void @use(i32 30)
+; SWITCH-NEXT: ret void
+; SWITCH: [[A_TARGET_EXIT1]]:
+; SWITCH-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; SWITCH: [[A_TARGET_EXIT2]]:
+; SWITCH-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; SWITCH: [[A_TARGET_EXIT3]]:
+; SWITCH-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; SWITCH: [[LOOP_EXIT_GUARD]]:
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[A_TARGET_EXIT1]] ], [ 1, %[[A_TARGET_EXIT2]] ], [ 2, %[[A_TARGET_EXIT3]] ], [ 0, %[[LOOPBACK]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[LOOP_EXIT_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[EXIT1]]
+; SWITCH-NEXT: i32 1, label %[[EXIT2]]
+; SWITCH-NEXT: i32 2, label %[[EXIT3]]
+; SWITCH-NEXT: ]
+; SWITCH: [[LOOP_EXIT_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define void @loop_switch_terminator(
+; BOOLEAN-SAME: i32 [[VAL:%.*]], i1 [[PREDLOOP:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*]]:
+; BOOLEAN-NEXT: br label %[[A:.*]]
+; BOOLEAN: [[A]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOPBACK:.*]] ]
+; BOOLEAN-NEXT: switch i32 [[VAL]], label %[[LOOPBACK]] [
+; BOOLEAN-NEXT: i32 0, label %[[A_TARGET_EXIT1:.*]]
+; BOOLEAN-NEXT: i32 1, label %[[A_TARGET_EXIT2:.*]]
+; BOOLEAN-NEXT: i32 2, label %[[A_TARGET_EXIT3:.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: [[LOOPBACK]]:
+; BOOLEAN-NEXT: [[INC]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDLOOP]], label %[[A]], label %[[LOOP_EXIT_GUARD:.*]]
+; BOOLEAN: [[EXIT1:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 10)
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[EXIT2:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 20)
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[EXIT3:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 30)
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[A_TARGET_EXIT1]]:
+; BOOLEAN-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; BOOLEAN: [[A_TARGET_EXIT2]]:
+; BOOLEAN-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; BOOLEAN: [[A_TARGET_EXIT3]]:
+; BOOLEAN-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; BOOLEAN: [[LOOP_EXIT_GUARD]]:
+; BOOLEAN-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[A_TARGET_EXIT1]] ], [ false, %[[A_TARGET_EXIT2]] ], [ false, %[[A_TARGET_EXIT3]] ], [ true, %[[LOOPBACK]] ]
+; BOOLEAN-NEXT: [[GUARD_EXIT2:%.*]] = phi i1 [ false, %[[A_TARGET_EXIT1]] ], [ true, %[[A_TARGET_EXIT2]] ], [ false, %[[A_TARGET_EXIT3]] ], [ false, %[[LOOPBACK]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[LOOP_EXIT_GUARD1:.*]]
+; BOOLEAN: [[LOOP_EXIT_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT2]], label %[[EXIT2]], label %[[EXIT3]]
+;
+entry:
+ br label %A
+
+A:
+ %valA = phi i32 [ 0, %entry ], [ %inc, %LoopBack ]
+ switch i32 %Val, label %LoopBack [
+ i32 0, label %Exit1
+ i32 1, label %Exit2
+ i32 2, label %Exit3
+ ]
+
+LoopBack:
+ %inc = add i32 %valA, 1
+ br i1 %PredLoop, label %A, label %Exit1
+
+Exit1:
+ call void @use(i32 10)
+ ret void
+
+Exit2:
+ call void @use(i32 20)
+ ret void
+
+Exit3:
+ call void @use(i32 30)
+ ret void
+}
+
+define void @loop_external_predecessor_to_exit(i1 %EarlyExit, i1 %PredA, i1 %PredB, i1 %PredC) {
+; SWITCH-LABEL: define void @loop_external_predecessor_to_exit(
+; SWITCH-SAME: i1 [[EARLYEXIT:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*]]:
+; SWITCH-NEXT: br i1 [[EARLYEXIT]], label %[[EXIT1:.*]], label %[[A:.*]]
+; SWITCH: [[A]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ 100, %[[ENTRY]] ], [ [[INC:%.*]], %[[C:.*]] ]
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[C]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INC]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; SWITCH: [[EXIT1]]:
+; SWITCH-NEXT: [[PHI_EXIT1:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[PHI_EXIT1_MOVED:%.*]], %[[LOOP_EXIT_GUARD]] ]
+; SWITCH-NEXT: call void @use(i32 [[PHI_EXIT1]])
+; SWITCH-NEXT: ret void
+; SWITCH: [[EXIT2:.*]]:
+; SWITCH-NEXT: call void @use(i32 [[VALA_MOVED:%.*]])
+; SWITCH-NEXT: ret void
+; SWITCH: [[EXIT3:.*]]:
+; SWITCH-NEXT: call void @use(i32 [[INC_MOVED:%.*]])
+; SWITCH-NEXT: ret void
+; SWITCH: [[LOOP_EXIT_GUARD]]:
+; SWITCH-NEXT: [[INC_MOVED]] = phi i32 [ poison, %[[A]] ], [ poison, %[[B]] ], [ [[INC]], %[[C]] ]
+; SWITCH-NEXT: [[VALA_MOVED]] = phi i32 [ [[VALA]], %[[A]] ], [ [[VALA]], %[[B]] ], [ [[VALA]], %[[C]] ]
+; SWITCH-NEXT: [[PHI_EXIT1_MOVED]] = phi i32 [ [[VALA]], %[[A]] ], [ poison, %[[B]] ], [ poison, %[[C]] ]
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[A]] ], [ 1, %[[B]] ], [ 2, %[[C]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[LOOP_EXIT_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[EXIT1]]
+; SWITCH-NEXT: i32 1, label %[[EXIT2]]
+; SWITCH-NEXT: i32 2, label %[[EXIT3]]
+; SWITCH-NEXT: ]
+; SWITCH: [[LOOP_EXIT_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define void @loop_external_predecessor_to_exit(
+; BOOLEAN-SAME: i1 [[EARLYEXIT:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*]]:
+; BOOLEAN-NEXT: br i1 [[EARLYEXIT]], label %[[EXIT1:.*]], label %[[A:.*]]
+; BOOLEAN: [[A]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ 100, %[[ENTRY]] ], [ [[INC:%.*]], %[[C:.*]] ]
+; BOOLEAN-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B:.*]]
+; BOOLEAN: [[B]]:
+; BOOLEAN-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[C]]
+; BOOLEAN: [[C]]:
+; BOOLEAN-NEXT: [[INC]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDC]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; BOOLEAN: [[EXIT1]]:
+; BOOLEAN-NEXT: [[PHI_EXIT1:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[PHI_EXIT1_MOVED:%.*]], %[[LOOP_EXIT_GUARD]] ]
+; BOOLEAN-NEXT: call void @use(i32 [[PHI_EXIT1]])
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[EXIT2:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 [[VALA_MOVED:%.*]])
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[EXIT3:.*]]:
+; BOOLEAN-NEXT: call void @use(i32 [[INC_MOVED:%.*]])
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: [[LOOP_EXIT_GUARD]]:
+; BOOLEAN-NEXT: [[INC_MOVED]] = phi i32 [ poison, %[[A]] ], [ poison, %[[B]] ], [ [[INC]], %[[C]] ]
+; BOOLEAN-NEXT: [[VALA_MOVED]] = phi i32 [ [[VALA]], %[[A]] ], [ [[VALA]], %[[B]] ], [ [[VALA]], %[[C]] ]
+; BOOLEAN-NEXT: [[PHI_EXIT1_MOVED]] = phi i32 [ [[VALA]], %[[A]] ], [ poison, %[[B]] ], [ poison, %[[C]] ]
+; BOOLEAN-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[A]] ], [ false, %[[B]] ], [ false, %[[C]] ]
+; BOOLEAN-NEXT: [[GUARD_EXIT2:%.*]] = phi i1 [ false, %[[A]] ], [ true, %[[B]] ], [ false, %[[C]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[LOOP_EXIT_GUARD1:.*]]
+; BOOLEAN: [[LOOP_EXIT_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT2]], label %[[EXIT2]], label %[[EXIT3]]
+;
+entry:
+ br i1 %EarlyExit, label %Exit1, label %A
+
+A:
+ %valA = phi i32 [ 100, %entry ], [ %inc, %C ]
+ br i1 %PredA, label %Exit1, label %B
+
+B:
+ br i1 %PredB, label %Exit2, label %C
+
+C:
+ %inc = add i32 %valA, 1
+ br i1 %PredC, label %Exit3, label %A
+
+Exit1:
+ %phi.exit1 = phi i32 [ 1, %entry ], [ %valA, %A ]
+ call void @use(i32 %phi.exit1)
+ ret void
+
+Exit2:
+ call void @use(i32 %valA)
+ ret void
+
+Exit3:
+ call void @use(i32 %inc)
+ ret void
+}
+
+define i32 @loop_restore_ssa(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i32 %X) {
+; SWITCH-LABEL: define i32 @loop_restore_ssa(
+; SWITCH-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*]]:
+; SWITCH-NEXT: br label %[[A:.*]]
+; SWITCH: [[A]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ [[X]], %[[ENTRY]] ], [ [[INC:%.*]], %[[C:.*]] ]
+; SWITCH-NEXT: [[DEF1:%.*]] = add i32 [[VALA]], 5
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: [[DEF2:%.*]] = mul i32 [[DEF1]], 2
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[C]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INC]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; SWITCH: [[EXIT1:.*]]:
+; SWITCH-NEXT: ret i32 [[PHI1_MOVED:%.*]]
+; SWITCH: [[EXIT2:.*]]:
+; SWITCH-NEXT: ret i32 [[PHI2_MOVED:%.*]]
+; SWITCH: [[EXIT3:.*]]:
+; SWITCH-NEXT: ret i32 [[PHI3_MOVED:%.*]]
+; SWITCH: [[LOOP_EXIT_GUARD]]:
+; SWITCH-NEXT: [[PHI3_MOVED]] = phi i32 [ poison, %[[A]] ], [ poison, %[[B]] ], [ [[INC]], %[[C]] ]
+; SWITCH-NEXT: [[PHI2_MOVED]] = phi i32 [ poison, %[[A]] ], [ [[DEF2]], %[[B]] ], [ poison, %[[C]] ]
+; SWITCH-NEXT: [[PHI1_MOVED]] = phi i32 [ [[DEF1]], %[[A]] ], [ poison, %[[B]] ], [ poison, %[[C]] ]
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[A]] ], [ 1, %[[B]] ], [ 2, %[[C]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[LOOP_EXIT_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[EXIT1]]
+; SWITCH-NEXT: i32 1, label %[[EXIT2]]
+; SWITCH-NEXT: i32 2, label %[[EXIT3]]
+; SWITCH-NEXT: ]
+; SWITCH: [[LOOP_EXIT_GUARD_DEFAULT]]:
+; SWITCH-NEXT: unreachable
+;
+; BOOLEAN-LABEL: define i32 @loop_restore_ssa(
+; BOOLEAN-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]]) {
+; BOOLEAN-NEXT: [[ENTRY:.*]]:
+; BOOLEAN-NEXT: br label %[[A:.*]]
+; BOOLEAN: [[A]]:
+; BOOLEAN-NEXT: [[VALA:%.*]] = phi i32 [ [[X]], %[[ENTRY]] ], [ [[INC:%.*]], %[[C:.*]] ]
+; BOOLEAN-NEXT: [[DEF1:%.*]] = add i32 [[VALA]], 5
+; BOOLEAN-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B:.*]]
+; BOOLEAN: [[B]]:
+; BOOLEAN-NEXT: [[DEF2:%.*]] = mul i32 [[DEF1]], 2
+; BOOLEAN-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[C]]
+; BOOLEAN: [[C]]:
+; BOOLEAN-NEXT: [[INC]] = add i32 [[VALA]], 1
+; BOOLEAN-NEXT: br i1 [[PREDC]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; BOOLEAN: [[EXIT1:.*]]:
+; BOOLEAN-NEXT: ret i32 [[PHI1_MOVED:%.*]]
+; BOOLEAN: [[EXIT2:.*]]:
+; BOOLEAN-NEXT: ret i32 [[PHI2_MOVED:%.*]]
+; BOOLEAN: [[EXIT3:.*]]:
+; BOOLEAN-NEXT: ret i32 [[PHI3_MOVED:%.*]]
+; BOOLEAN: [[LOOP_EXIT_GUARD]]:
+; BOOLEAN-NEXT: [[PHI3_MOVED]] = phi i32 [ poison, %[[A]] ], [ poison, %[[B]] ], [ [[INC]], %[[C]] ]
+; BOOLEAN-NEXT: [[PHI2_MOVED]] = phi i32 [ poison, %[[A]] ], [ [[DEF2]], %[[B]] ], [ poison, %[[C]] ]
+; BOOLEAN-NEXT: [[PHI1_MOVED]] = phi i32 [ [[DEF1]], %[[A]] ], [ poison, %[[B]] ], [ poison, %[[C]] ]
+; BOOLEAN-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[A]] ], [ false, %[[B]] ], [ false, %[[C]] ]
+; BOOLEAN-NEXT: [[GUARD_EXIT2:%.*]] = phi i1 [ false, %[[A]] ], [ true, %[[B]] ], [ false, %[[C]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[LOOP_EXIT_GUARD1:.*]]
+; BOOLEAN: [[LOOP_EXIT_GUARD1]]:
+; BOOLEAN-NEXT: br i1 [[GUARD_EXIT2]], label %[[EXIT2]], label %[[EXIT3]]
+;
+entry:
+ br label %A
+
+A:
+ %valA = phi i32 [ %X, %entry ], [ %inc, %C ]
+ %def1 = add i32 %valA, 5
+ br i1 %PredA, label %Exit1, label %B
+
+B:
+ %def2 = mul i32 %def1, 2
+ br i1 %PredB, label %Exit2, label %C
+
+C:
+ %inc = add i32 %valA, 1
+ br i1 %PredC, label %Exit3, label %A
+
+Exit1:
+ %phi1 = phi i32 [ %def1, %A ]
+ ret i32 %phi1
+
+Exit2:
+ %phi2 = phi i32 [ %def2, %B ]
+ ret i32 %phi2
+
+Exit3:
+ %phi3 = phi i32 [ %inc, %C ]
+ ret i32 %phi3
+}
+
+; Fallback to branch sled when exit blocks <= 2.
+define void @loop_two_exits_fallback(i1 %PredEntry, i1 %PredA, i1 %PredB) {
+; CHECK-LABEL: define void @loop_two_exits_fallback(
+; CHECK-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[A:.*]]
+; CHECK: [[A]]:
+; CHECK-NEXT: [[VALA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[B:.*]] ]
+; CHECK-NEXT: br i1 [[PREDA]], label %[[LOOP_EXIT_GUARD:.*]], label %[[B]]
+; CHECK: [[B]]:
+; CHECK-NEXT: [[INC]] = add i32 [[VALA]], 1
+; CHECK-NEXT: br i1 [[PREDB]], label %[[LOOP_EXIT_GUARD]], label %[[A]]
+; CHECK: [[EXIT1:.*]]:
+; CHECK-NEXT: call void @use(i32 1)
+; CHECK-NEXT: ret void
+; CHECK: [[EXIT2:.*]]:
+; CHECK-NEXT: call void @use(i32 2)
+; CHECK-NEXT: ret void
+; CHECK: [[LOOP_EXIT_GUARD]]:
+; CHECK-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[A]] ], [ false, %[[B]] ]
+; CHECK-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[EXIT2]]
+;
+entry:
+ br label %A
+
+A:
+ %valA = phi i32 [ 0, %entry ], [ %inc, %B ]
+ br i1 %PredA, label %Exit1, label %B
+
+B:
+ %inc = add i32 %valA, 1
+ br i1 %PredB, label %Exit2, label %A
+
+Exit1:
+ call void @use(i32 1)
+ ret void
+
+Exit2:
+ call void @use(i32 2)
+ ret void
+}
More information about the llvm-commits
mailing list