[llvm] [FixIrreducible][UnifyLoopExits] Support switch statements (PR #206567)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 19 11:43:08 PDT 2026
https://github.com/hertelukas updated https://github.com/llvm/llvm-project/pull/206567
>From 49815f928db5f523b2381f0f85e47093b3c7ef35 Mon Sep 17 00:00:00 2001
From: Lukas <dev at lukas-hertel.de>
Date: Sun, 28 Jun 2026 17:43:33 +0200
Subject: [PATCH 1/5] [FixIrreducible] Support switch statements
Reuse the approach for callbr terminators for switches. Branches,
entering irreducible loops through switches, are split and replaced
with unconditional branches into the loop.
Assisted-by: LLM (Adapted callbr test structure to switch)
---
.../llvm/Transforms/Utils/BasicBlockUtils.h | 27 +-
llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 60 +-
llvm/lib/Transforms/Utils/FixIrreducible.cpp | 62 +-
llvm/lib/Transforms/Utils/UnifyLoopExits.cpp | 58 +-
llvm/test/Transforms/FixIrreducible/switch.ll | 960 +++++++++++++++++-
.../FixIrreducible/unsupported-terminator.ll | 25 +-
6 files changed, 1052 insertions(+), 140 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
index 3088b96bf7a32..be0567a1b1db2 100644
--- a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
@@ -268,19 +268,20 @@ LLVM_ABI BasicBlock *SplitEdge(BasicBlock *From, BasicBlock *To,
MemorySSAUpdater *MSSAU = nullptr,
const Twine &BBName = "");
-/// \brief Create a new intermediate target block for a callbr edge.
+/// \brief Create a new intermediate target block for a callbr and switch edges.
///
-/// Create a new basic block between a callbr instruction and one of its
-/// successors. The new block replaces the original successor in the callbr
-/// instruction and unconditionally branches to the original successor. This
-/// is useful for normalizing control flow, e.g., when transforming
-/// irreducible loops.
+/// Create a new basic block between a callbr or switch instruction and one of
+/// its successors. The new block replaces the original successor in the
+/// callbr/switch instruction and unconditionally branches to the original
+/// successor. This is useful for normalizing control flow, e.g., when
+/// transforming irreducible loops.
///
-/// \param CallBrBlock block containing the callbr instruction
+/// \param MultiBrBlock block containing the callbr or switch instruction
/// \param Succ original successor block
-/// \param SuccIdx index of the original successor in the callbr
+/// \param SuccIdx index of the original successor in the terminator
/// instruction
-/// \param CallBrTarget optional \p BasicBlock generated by \c SplitCallBrEdge
+/// \param BrTarget optional \p BasicBlock generated by \c
+/// SplitMultiBrEdge
/// to reuse for the split
/// \param DTU optional \p DomTreeUpdater for updating the
/// dominator tree
@@ -294,10 +295,10 @@ LLVM_ABI BasicBlock *SplitEdge(BasicBlock *From, BasicBlock *To,
/// \note This function updates PHI nodes, dominator tree, loop info, and
/// cycle info as needed.
LLVM_ABI BasicBlock *
-SplitCallBrEdge(BasicBlock *CallBrBlock, BasicBlock *Succ, unsigned SuccIdx,
- BasicBlock *CallBrTarget = nullptr,
- DomTreeUpdater *DTU = nullptr, CycleInfo *CI = nullptr,
- LoopInfo *LI = nullptr, bool *UpdatedLI = nullptr);
+SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ, unsigned SuccIdx,
+ BasicBlock *BrTarget = nullptr, DomTreeUpdater *DTU = nullptr,
+ CycleInfo *CI = nullptr, LoopInfo *LI = nullptr,
+ bool *UpdatedLI = nullptr);
/// Sets the unwind edge of an instruction to a particular successor.
LLVM_ABI void setUnwindEdgeTo(Instruction *TI, BasicBlock *Succ);
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index 5d1faec67bddd..e12d1919e2f4f 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -733,67 +733,67 @@ static bool updateCycleLoopInfo(TI *LCI, BasicBlock *CallBrBlock,
return true;
}
-BasicBlock *llvm::SplitCallBrEdge(BasicBlock *CallBrBlock, BasicBlock *Succ,
- unsigned SuccIdx, BasicBlock *CallBrTarget,
- DomTreeUpdater *DTU, CycleInfo *CI,
- LoopInfo *LI, bool *UpdatedLI) {
- CallBrInst *CallBr = dyn_cast<CallBrInst>(CallBrBlock->getTerminator());
- assert(CallBr && "expected callbr terminator");
- assert(SuccIdx < CallBr->getNumSuccessors() &&
- Succ == CallBr->getSuccessor(SuccIdx) && "invalid successor index");
+BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
+ unsigned SuccIdx, BasicBlock *BrTarget,
+ DomTreeUpdater *DTU, CycleInfo *CI,
+ LoopInfo *LI, bool *UpdatedLI) {
+ Instruction *Term = MultiBrBlock->getTerminator();
+ assert(Term && "expected terminator");
+ assert(SuccIdx < Term->getNumSuccessors() &&
+ Succ == Term->getSuccessor(SuccIdx) && "invalid successor index");
if (UpdatedLI)
*UpdatedLI = false;
- bool ReusesCallBrTarget = CallBrTarget;
- // Create a new block between callbr and the specified successor.
+ bool ReusesCallBrTarget = BrTarget;
+
+ // Create a new block between terminator and the specified successor.
// splitBlockBefore cannot be re-used here since it cannot split if the split
// point is a PHI node (because BasicBlock::splitBasicBlockBefore cannot
// handle that). But we don't need to rewire every part of a potential PHI
- // node. We only care about the edge between CallBrBlock and the original
+ // node. We only care about the edge between MultiBrBlock and the original
// successor.
if (!ReusesCallBrTarget) {
- CallBrTarget = BasicBlock::Create(CallBrBlock->getContext(),
- CallBrBlock->getName() + ".target." +
- Succ->getName(),
- CallBrBlock->getParent());
+ BrTarget = BasicBlock::Create(MultiBrBlock->getContext(),
+ MultiBrBlock->getName() + ".target." +
+ Succ->getName(),
+ MultiBrBlock->getParent());
// Jump from the new target block to the original successor.
- UncondBrInst::Create(Succ, CallBrTarget);
+ UncondBrInst::Create(Succ, BrTarget);
// Replace a single incoming value with the callbr target block. We cannot
// use replacePhiUsesWith, as this would replace the value for every edge
// from the callbr block to succ.
for (PHINode &PN : Succ->phis()) {
- int BBIdx = PN.getBasicBlockIndex(CallBrBlock);
+ int BBIdx = PN.getBasicBlockIndex(MultiBrBlock);
assert(BBIdx != -1 && "expected incoming value form callbr block");
- PN.setIncomingBlock(BBIdx, CallBrTarget);
+ PN.setIncomingBlock(BBIdx, BrTarget);
}
- bool Updated = updateCycleLoopInfo<LoopInfo, Loop>(LI, CallBrBlock,
- CallBrTarget, Succ);
+ bool Updated =
+ updateCycleLoopInfo<LoopInfo, Loop>(LI, MultiBrBlock, BrTarget, Succ);
if (UpdatedLI)
*UpdatedLI = Updated;
- updateCycleLoopInfo<CycleInfo, CycleRef>(CI, CallBrBlock, CallBrTarget,
- Succ);
+ updateCycleLoopInfo<CycleInfo, CycleRef>(CI, MultiBrBlock, BrTarget, Succ);
} else {
for (PHINode &PN : Succ->phis())
- PN.removeIncomingValue(CallBrBlock, false);
+ PN.removeIncomingValue(MultiBrBlock, false);
}
// Rewire control flow from callbr to the new target block.
- CallBr->setSuccessor(SuccIdx, CallBrTarget);
+ Term->setSuccessor(SuccIdx, BrTarget);
if (DTU) {
if (!ReusesCallBrTarget)
- DTU->applyUpdates({{DominatorTree::Insert, CallBrBlock, CallBrTarget}});
- if (DTU->getDomTree().dominates(CallBrBlock, Succ)) {
- if (!is_contained(successors(CallBrBlock), Succ))
- DTU->applyUpdates({{DominatorTree::Delete, CallBrBlock, Succ}});
+ DTU->applyUpdates({{DominatorTree::Insert, MultiBrBlock, BrTarget}});
+ if (DTU->getDomTree().dominates(MultiBrBlock, Succ)) {
+ if (!is_contained(successors(MultiBrBlock), Succ))
+ DTU->applyUpdates({{DominatorTree::Delete, MultiBrBlock, Succ}});
if (!ReusesCallBrTarget)
- DTU->applyUpdates({{DominatorTree::Insert, CallBrTarget, Succ}});
+ DTU->applyUpdates({{DominatorTree::Insert, BrTarget, Succ}});
}
}
- return CallBrTarget;
+ return BrTarget;
}
void llvm::setUnwindEdgeTo(Instruction *TI, BasicBlock *Succ) {
diff --git a/llvm/lib/Transforms/Utils/FixIrreducible.cpp b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
index 77e8cb838ca75..c4f7460ef0c98 100644
--- a/llvm/lib/Transforms/Utils/FixIrreducible.cpp
+++ b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
@@ -76,16 +76,15 @@
// a PHINode in a header. Hence the above handling of PHINodes is sufficient and
// no further processing is required to restore SSA.
//
-// Limitation: The pass cannot handle switch statements and indirect
-// branches. Both must be lowered to plain branches first.
+// Limitation: The pass cannot handle indirect branches. They must be lowered to
+// plain branches first.
//
-// CallBr support: CallBr is handled as a more general branch instruction which
-// can have multiple successors. The pass redirects the edges to intermediate
-// target blocks that unconditionally branch to the original callbr target
-// blocks. This allows the control flow hub to know to which of the original
-// target blocks to jump to.
-// Example input CFG:
-// Entry (callbr)
+// CallBr and Switch support: CallBr and Switch terminators are handled as a
+// more general branch instruction which can have multiple successors. The pass
+// redirects the edges to intermediate target blocks that unconditionally branch
+// to the original target blocks. This allows the control flow hub to know to
+// which of the original target blocks to jump to. Example input CFG:
+// Entry (callbr/switch)
// / \
// v v
// H ----> B
@@ -95,7 +94,7 @@
// Exit
//
// becomes:
-// Entry (callbr)
+// Entry (callbr/switch)
// / \
// v v
// target.H target.B
@@ -110,7 +109,7 @@
// Note
// OUTPUT CFG: Converted to a natural loop with a new header N.
//
-// Entry (callbr)
+// Entry (callbr/switch)
// / \
// v v
// target.H target.B
@@ -133,6 +132,7 @@
#include "llvm/Analysis/CycleAnalysis.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/Instructions.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/ErrorHandling.h"
@@ -308,13 +308,15 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
<< " -> " << printBasicBlock(Succ0)
<< (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
<< '\n');
- } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(P->getTerminator())) {
+ } else if (isa<CallBrInst>(P->getTerminator()) ||
+ isa<SwitchInst>(P->getTerminator())) {
BasicBlock *NewSucc = nullptr;
- for (unsigned I = 0; I < CallBr->getNumSuccessors(); ++I) {
- BasicBlock *Succ = CallBr->getSuccessor(I);
+ Instruction *Term = P->getTerminator();
+ for (unsigned I = 0; I < Term->getNumSuccessors(); ++I) {
+ BasicBlock *Succ = Term->getSuccessor(I);
if (Succ != Header)
continue;
- NewSucc = SplitCallBrEdge(P, Succ, I, NewSucc, &DTU, &CI, LI);
+ NewSucc = SplitMultiBrEdge(P, Succ, I, NewSucc, &DTU, &CI, LI);
LLVM_DEBUG(dbgs() << "Added internal branch: "
<< printBasicBlock(NewSucc) << " -> "
<< printBasicBlock(Succ) << '\n');
@@ -322,8 +324,9 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
if (NewSucc)
CHub.addBranch(NewSucc, Header);
} else {
- reportFatalUsageError("unsupported block terminator: fix-irreducible "
- "only supports br and callbr instructions");
+ reportFatalUsageError(
+ "unsupported block terminator: fix-irreducible "
+ "only supports br, callbr, and switch instructions");
}
}
@@ -355,31 +358,32 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
<< " -> " << printBasicBlock(Succ0)
<< (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
<< '\n');
- } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(P->getTerminator())) {
- SmallDenseMap<BasicBlock *, BasicBlock *> CallBrTargets;
- for (unsigned I = 0; I < CallBr->getNumSuccessors(); ++I) {
- BasicBlock *Succ = CallBr->getSuccessor(I);
+ } else if (isa<CallBrInst>(P->getTerminator()) ||
+ isa<SwitchInst>(P->getTerminator())) {
+ SmallDenseMap<BasicBlock *, BasicBlock *> MultiBrTargets;
+ Instruction *Term = P->getTerminator();
+ for (unsigned I = 0; I < Term->getNumSuccessors(); ++I) {
+ BasicBlock *Succ = Term->getSuccessor(I);
if (!CI.contains(C, Succ))
continue;
- auto It = CallBrTargets.find(Succ);
+ auto It = MultiBrTargets.find(Succ);
BasicBlock *ExistingTarget =
- (It != CallBrTargets.end()) ? It->second : nullptr;
+ (It != MultiBrTargets.end()) ? It->second : nullptr;
BasicBlock *NewSucc =
- SplitCallBrEdge(P, Succ, I, ExistingTarget, &DTU, &CI, LI);
-
+ SplitMultiBrEdge(P, Succ, I, ExistingTarget, &DTU, &CI, LI);
if (!ExistingTarget) {
CHub.addBranch(NewSucc, Succ);
- CallBrTargets[Succ] = NewSucc;
+ MultiBrTargets[Succ] = NewSucc;
}
-
LLVM_DEBUG(dbgs() << "Added external branch: "
<< printBasicBlock(NewSucc) << " -> "
<< printBasicBlock(Succ) << '\n');
}
} else {
- reportFatalUsageError("unsupported block terminator: fix-irreducible "
- "only supports br and callbr instructions");
+ reportFatalUsageError(
+ "unsupported block terminator: fix-irreducible "
+ "only supports br, callbr, and switch instructions");
}
}
diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
index c31d1dd13e1ea..11a7d233d1bc0 100644
--- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
+++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
@@ -13,10 +13,10 @@
// Limitation: This assumes that all terminators in the CFG are direct branches
// (the "br" instruction). The presence of any other control flow
// such as indirectbr or switch will cause an assert.
-// The callbr terminator is supported by creating intermediate
-// target blocks that unconditionally branch to the original target
-// blocks. These intermediate target blocks can then be redirected
-// through the ControlFlowHub as usual.
+// The callbr and switch terminators are supported by creating
+// intermediate target blocks that unconditionally branch to the
+// original target blocks. These intermediate target blocks can then
+// be redirected through the ControlFlowHub as usual.
//
//===----------------------------------------------------------------------===//
@@ -27,6 +27,7 @@
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Instructions.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Utils.h"
@@ -160,7 +161,7 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
return false;
DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
- SmallVector<BasicBlock *, 8> CallBrTargetBlocksToFix;
+ SmallVector<BasicBlock *, 8> MultiBrTargetBlocksToFix;
// Redirect exiting edges through a control flow hub.
ControlFlowHub CHub;
@@ -188,41 +189,44 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
<< " -> " << printBasicBlock(Succ0)
<< (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
<< '\n');
- } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(BB->getTerminator())) {
- SmallDenseMap<BasicBlock *, BasicBlock *> CallBrTargets;
- for (unsigned J = 0; J < CallBr->getNumSuccessors(); ++J) {
- BasicBlock *Succ = CallBr->getSuccessor(J);
+ } else if (isa<CallBrInst>(BB->getTerminator()) ||
+ isa<SwitchInst>(BB->getTerminator())) {
+ SmallDenseMap<BasicBlock *, BasicBlock *> BrTargets;
+ Instruction *Term = BB->getTerminator();
+ for (unsigned J = 0; J < Term->getNumSuccessors(); ++J) {
+ BasicBlock *Succ = Term->getSuccessor(J);
if (L->contains(Succ))
continue;
bool UpdatedLI;
- auto It = CallBrTargets.find(Succ);
+ auto It = BrTargets.find(Succ);
BasicBlock *ExistingTarget =
- (It != CallBrTargets.end()) ? It->second : nullptr;
- BasicBlock *NewSucc = SplitCallBrEdge(BB, Succ, J, ExistingTarget, &DTU,
- nullptr, &LI, &UpdatedLI);
+ (It != BrTargets.end()) ? It->second : nullptr;
+ BasicBlock *NewSucc = SplitMultiBrEdge(BB, Succ, J, ExistingTarget,
+ &DTU, nullptr, &LI, &UpdatedLI);
if (!ExistingTarget) {
- // SplitCallBrEdge modifies the CFG because it creates an intermediate
- // block. So we need to set the changed flag no matter what the
- // ControlFlowHub is going to do later.
+ // SplitMutliBrEdge modifies the CFG because it creates an
+ // intermediate block. So we need to set the changed flag no matter
+ // what the ControlFlowHub is going to do later.
Changed = true;
- // Even if CallBr and Succ do not have a common parent loop, we need
- // to add the new target block to the parent loop of the current loop.
+ // Even if the terminator and Succ do not have a common parent loop,
+ // we need to add the new target block to the parent loop of the
+ // current loop.
if (!UpdatedLI)
- CallBrTargetBlocksToFix.push_back(NewSucc);
+ MultiBrTargetBlocksToFix.push_back(NewSucc);
// ExitingBlocks is later used to restore SSA, so we need to make sure
// that the blocks used for phi nodes in the guard blocks match the
- // predecessors of the guard blocks, which, in the case of callbr, are
- // the new intermediate target blocks instead of the callbr blocks
- // themselves. If only one exiting block is generated, the callbr
- // block itself is overwritten, while further blocks are appended as
- // additional exiting blocks.
- if (CallBrTargets.empty())
+ // predecessors of the guard blocks, which, in the case of callbr or
+ // switch terminator, are the new intermediate target blocks instead
+ // of themselves. If only one exiting block is generated, the
+ // branchning block itself is overwritten, while further blocks are
+ // appended as additional exiting blocks.
+ if (BrTargets.empty())
ExitingBlocks[I] = NewSucc;
else
ExitingBlocks.push_back(NewSucc);
CHub.addBranch(NewSucc, Succ);
- CallBrTargets[Succ] = NewSucc;
+ BrTargets[Succ] = NewSucc;
}
LLVM_DEBUG(dbgs() << "Added exiting branch: "
<< printBasicBlock(NewSucc) << " -> "
@@ -263,7 +267,7 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
for (auto *G : GuardBlocks) {
ParentLoop->addBasicBlockToLoop(G, LI);
}
- for (auto *C : CallBrTargetBlocksToFix) {
+ for (auto *C : MultiBrTargetBlocksToFix) {
ParentLoop->addBasicBlockToLoop(C, LI);
}
ParentLoop->verifyLoop();
diff --git a/llvm/test/Transforms/FixIrreducible/switch.ll b/llvm/test/Transforms/FixIrreducible/switch.ll
index 4d3d9498b284d..0da0a0d28735c 100644
--- a/llvm/test/Transforms/FixIrreducible/switch.ll
+++ b/llvm/test/Transforms/FixIrreducible/switch.ll
@@ -1,31 +1,145 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -lowerswitch -fix-irreducible --verify-loop-info -S | FileCheck %s
-; RUN: opt < %s -passes='lower-switch,fix-irreducible,verify<loops>' -S | FileCheck %s
-; RUN: opt < %s -passes='lower-switch,verify<loops>,fix-irreducible,verify<loops>' -S | FileCheck %s
-
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes='fix-irreducible,verify<loops>' -S | FileCheck %s
+; RUN: opt < %s -passes='verify<loops>,fix-irreducible,verify<loops>' -S | FileCheck %s
+; RUN: opt < %s -passes='print<cycles>' -disable-output 2>&1 | FileCheck %s --check-prefix CYCLES-BEFORE
+; RUN: opt < %s -passes='fix-irreducible,print<cycles>' -disable-output 2>&1 | FileCheck %s --check-prefix CYCLES-AFTER
+; CYCLES-BEFORE: CycleInfo for function: loop_1
+; CYCLES-BEFORE-NEXT: depth=1: entries(B A)
+; CYCLES-AFTER: CycleInfo for function: loop_1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) B A B.target.B
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_entry
+; CYCLES-BEFORE-NEXT: depth=1: entries(indirect fallthrough)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_entry
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect fallthrough
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_entry_targets_with_phi_nodes
+; CYCLES-BEFORE-NEXT: depth=1: entries(block1 block)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_entry_targets_with_phi_nodes
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) block1 block
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_entry_multiple_targets
+; CYCLES-BEFORE-NEXT: depth=1: entries(indirect fallthrough)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_entry_multiple_targets
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect fallthrough
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_entry_multiple_targets1
+; CYCLES-BEFORE-NEXT: depth=1: entries(indirect1 indirect fallthrough)
+; CYCLES-BEFORE-NEXT: depth=2: entries(indirect fallthrough)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_entry_multiple_targets1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect1 indirect fallthrough irr.guard1 irr.guard2
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) indirect fallthrough
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_header_no_indirect
+; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough switch_bb)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_header_no_indirect
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough switch_bb switch_bb.target.fallthrough
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_header
+; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough switch_bb)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_header
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough switch_bb switch_bb.target.fallthrough
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_header_multiple_targets
+; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough switch_bb) indirect1
+; CYCLES-BEFORE-NEXT: depth=2: entries(switch_bb) indirect1
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_header_multiple_targets
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough switch_bb indirect1 switch_bb.target.fallthrough
+; CYCLES-AFTER-NEXT: depth=2: entries(switch_bb) indirect1
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_regular
+; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough2 fallthrough1)
+; CYCLES-BEFORE-NEXT: depth=1: entries(indirect2 indirect1)
+; CYCLES-BEFORE-NEXT: depth=1: entries(noswitch2 noswitch1)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_regular
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough2 fallthrough1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard1) indirect2 indirect1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard2) noswitch2 noswitch1
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_regular1
+; CYCLES-BEFORE-NEXT: depth=1: entries(switch_bb noswitch)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_regular1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) switch_bb noswitch
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_header_and_regular
+; CYCLES-BEFORE-NEXT: depth=1: entries(switch_header) switch_regular mid
+; CYCLES-BEFORE-NEXT: depth=2: entries(switch_regular mid)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_header_and_regular
+; CYCLES-AFTER-NEXT: depth=1: entries(switch_header) switch_regular mid switch_header.target.mid switch_header.target.switch_regular irr.guard
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard) switch_regular mid
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_only
+; CYCLES-BEFORE-NEXT: depth=1: entries(switch_block switch_header)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_only
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) switch_block switch_header switch_header.target.switch_block
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: entry_multiple_switch
+; CYCLES-BEFORE-NEXT: depth=1: entries(sw2 block block1)
+; CYCLES-BEFORE-NEXT: depth=2: entries(block block1)
+; CYCLES-AFTER-NEXT: CycleInfo for function: entry_multiple_switch
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) sw2 block block1 irr.guard1 sw2.target.block1 sw2.target.block irr.guard2
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) block block1
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_exit_with_separate_entries
+; CYCLES-BEFORE-NEXT: depth=1: entries(l2 l1) sw
+; CYCLES-BEFORE-NEXT: depth=2: entries(l1 sw)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_exit_with_separate_entries
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l2 l1 sw sw.target.l1 irr.guard1
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard1) l1 sw sw.target.l1
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_exit_with_separate_entries1
+; CYCLES-BEFORE-NEXT: depth=1: entries(loop2 loop1) sw
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_exit_with_separate_entries1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) loop2 loop1 sw sw.target.loop2
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_only_multiple
+; CYCLES-BEFORE-NEXT: depth=1: entries(sw3 sw1 sw2)
+; CYCLES-BEFORE-NEXT: depth=2: entries(sw1 sw2)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_only_multiple
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) sw3 sw1 sw2 sw2.target.sw3 sw1.target.sw3 irr.guard1 sw2.target.sw1 sw3.target.sw1 irr.guard2
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) sw1 sw2 sw2.target.sw1
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_bypass
+; CYCLES-BEFORE-NEXT: depth=1: entries(l1 sw) l2
+; CYCLES-BEFORE-NEXT: depth=2: entries(sw l2)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_bypass
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l1 sw l2 sw.target.l1 irr.guard1
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard1) sw l2
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_multiple_with_exit
+; CYCLES-BEFORE-NEXT: depth=1: entries(l3 l1 l2)
+; CYCLES-BEFORE-NEXT: depth=2: entries(l1 l2)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_multiple_with_exit
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l3 l1 l2 irr.guard1 irr.guard2
+; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) l1 l2
+;
+; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_nested
+; CYCLES-BEFORE-NEXT: depth=1: entries(bb bh)
+; CYCLES-BEFORE-NEXT: depth=1: entries(b h)
+; CYCLES-AFTER-NEXT: CycleInfo for function: switch_nested
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) bb bh
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard1) b h
+;
define void @loop_1(i32 %Value, i1 %PredEntry, i1 %PredD) {
-; CHECK-LABEL: @loop_1(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[PREDENTRY_INV:%.*]] = xor i1 [[PREDENTRY:%.*]], true
-; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
-; CHECK: A:
-; CHECK-NEXT: br label [[IRR_GUARD]]
-; CHECK: B:
-; CHECK-NEXT: br label [[NODEBLOCK:%.*]]
-; CHECK: NodeBlock:
-; CHECK-NEXT: [[PIVOT:%.*]] = icmp slt i32 [[VALUE:%.*]], 1
-; CHECK-NEXT: br i1 [[PIVOT]], label [[LEAFBLOCK:%.*]], label [[LEAFBLOCK1:%.*]]
-; CHECK: LeafBlock1:
-; CHECK-NEXT: [[SWITCHLEAF2:%.*]] = icmp eq i32 [[VALUE]], 1
-; CHECK-NEXT: br i1 [[SWITCHLEAF2]], label [[IRR_GUARD]], label [[EXIT:%.*]]
-; CHECK: LeafBlock:
-; CHECK-NEXT: [[SWITCHLEAF:%.*]] = icmp eq i32 [[VALUE]], 0
-; CHECK-NEXT: br i1 [[SWITCHLEAF]], label [[A:%.*]], label [[EXIT]]
-; CHECK: exit:
-; CHECK-NEXT: ret void
-; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[LEAFBLOCK1]] ], [ true, [[A]] ], [ [[PREDENTRY_INV]], [[ENTRY:%.*]] ]
-; CHECK-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[A]]
+; CHECK-LABEL: define void @loop_1(
+; CHECK-SAME: i32 [[VALUE:%.*]], i1 [[PREDENTRY:%.*]], i1 [[PREDD:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PREDENTRY_INV:%.*]] = xor i1 [[PREDENTRY]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[A:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[B:.*]]:
+; CHECK-NEXT: switch i32 [[VALUE]], label %[[EXIT:.*]] [
+; CHECK-NEXT: i32 0, label %[[A]]
+; CHECK-NEXT: i32 1, label %[[B_TARGET_B:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[B_TARGET_B]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, %[[B_TARGET_B]] ], [ true, %[[A]] ], [ [[PREDENTRY_INV]], %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[A]]
;
entry:
br i1 %PredEntry, label %A, label %B
@@ -42,3 +156,795 @@ B:
exit:
ret void
}
+
+define void @switch_entry(i32 %val, i1 %c) {
+; CHECK-LABEL: define void @switch_entry(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_FALLTHROUGH:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_INDIRECT:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[FALLTHROUGH:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD:.*]], label %[[RET:.*]]
+; CHECK: [[INDIRECT:.*]]:
+; CHECK-NEXT: br label %[[FALLTHROUGH]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_TARGET_FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_INDIRECT]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_INDIRECT:%.*]] = phi i1 [ true, %[[FALLTHROUGH]] ], [ false, %[[ENTRY_TARGET_FALLTHROUGH]] ], [ true, %[[ENTRY_TARGET_INDIRECT]] ]
+; CHECK-NEXT: br i1 [[GUARD_INDIRECT]], label %[[INDIRECT]], label %[[FALLTHROUGH]]
+;
+entry:
+ switch i32 %val, label %fallthrough [
+ i32 0, label %indirect
+ ]
+fallthrough:
+ br i1 %c, label %indirect, label %ret
+indirect:
+ br label %fallthrough
+ret:
+ ret void
+}
+
+define i32 @switch_entry_targets_with_phi_nodes(i32 %val, i1 %c) {
+; CHECK-LABEL: define i32 @switch_entry_targets_with_phi_nodes(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_BLOCK:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_BLOCK1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[BLOCK:.*]]:
+; CHECK-NEXT: [[A:%.*]] = phi i32 [ 1, %[[BLOCK1:.*]] ], [ [[A_MOVED:%.*]], %[[IRR_GUARD:.*]] ]
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[BLOCK1]]:
+; CHECK-NEXT: br i1 [[C]], label %[[BLOCK]], label %[[RET:.*]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret i32 [[B_MOVED:%.*]]
+; CHECK: [[ENTRY_TARGET_BLOCK]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_BLOCK1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[A_MOVED]] = phi i32 [ poison, %[[BLOCK]] ], [ 42, %[[ENTRY_TARGET_BLOCK]] ], [ poison, %[[ENTRY_TARGET_BLOCK1]] ]
+; CHECK-NEXT: [[B_MOVED]] = phi i32 [ [[A]], %[[BLOCK]] ], [ poison, %[[ENTRY_TARGET_BLOCK]] ], [ 43, %[[ENTRY_TARGET_BLOCK1]] ]
+; CHECK-NEXT: [[GUARD_BLOCK1:%.*]] = phi i1 [ true, %[[BLOCK]] ], [ false, %[[ENTRY_TARGET_BLOCK]] ], [ true, %[[ENTRY_TARGET_BLOCK1]] ]
+; CHECK-NEXT: br i1 [[GUARD_BLOCK1]], label %[[BLOCK1]], label %[[BLOCK]]
+;
+entry:
+ switch i32 %val, label %block [
+ i32 0, label %block1
+ ]
+block:
+ %a = phi i32 [42, %entry], [1, %block1]
+ br label %block1
+block1:
+ %b = phi i32 [43, %entry], [%a, %block]
+ br i1 %c, label %block, label %ret
+ret:
+ ret i32 %b
+}
+
+define void @switch_entry_multiple_targets(i32 %val, i1 %c) {
+; CHECK-LABEL: define void @switch_entry_multiple_targets(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_FALLTHROUGH:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_INDIRECT:.*]]
+; CHECK-NEXT: i32 1, label %[[INDIRECT1:.*]]
+; CHECK-NEXT: i32 2, label %[[INDIRECT2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[FALLTHROUGH:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD:.*]], label %[[RET:.*]]
+; CHECK: [[INDIRECT:.*]]:
+; CHECK-NEXT: br label %[[FALLTHROUGH]]
+; CHECK: [[INDIRECT1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[INDIRECT2]]:
+; CHECK-NEXT: br label %[[RET]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_TARGET_FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_INDIRECT]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_INDIRECT:%.*]] = phi i1 [ true, %[[FALLTHROUGH]] ], [ true, %[[INDIRECT1]] ], [ false, %[[ENTRY_TARGET_FALLTHROUGH]] ], [ true, %[[ENTRY_TARGET_INDIRECT]] ]
+; CHECK-NEXT: br i1 [[GUARD_INDIRECT]], label %[[INDIRECT]], label %[[FALLTHROUGH]]
+;
+entry:
+ switch i32 %val, label %fallthrough [
+ i32 0, label %indirect
+ i32 1, label %indirect1
+ i32 2, label %indirect2
+ ]
+fallthrough:
+ br i1 %c, label %indirect, label %ret
+indirect:
+ br label %fallthrough
+indirect1:
+ br label %indirect
+indirect2:
+ br label %ret
+ret:
+ ret void
+}
+
+define void @switch_entry_multiple_targets1(i32 %val, i1 %c, i1 %d) {
+; CHECK-LABEL: define void @switch_entry_multiple_targets1(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]], i1 [[D:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_FALLTHROUGH:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_INDIRECT:.*]]
+; CHECK-NEXT: i32 1, label %[[ENTRY_TARGET_INDIRECT1:.*]]
+; CHECK-NEXT: i32 2, label %[[INDIRECT2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[FALLTHROUGH:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD2:.*]], label %[[RET:.*]]
+; CHECK: [[INDIRECT:.*]]:
+; CHECK-NEXT: br i1 [[D]], label %[[FALLTHROUGH]], label %[[IRR_GUARD:.*]]
+; CHECK: [[INDIRECT1:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[INDIRECT2]]:
+; CHECK-NEXT: br label %[[RET]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_TARGET_FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_INDIRECT]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_INDIRECT1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_INDIRECT1:%.*]] = phi i1 [ true, %[[INDIRECT]] ], [ false, %[[ENTRY_TARGET_FALLTHROUGH]] ], [ false, %[[ENTRY_TARGET_INDIRECT]] ], [ true, %[[ENTRY_TARGET_INDIRECT1]] ]
+; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ false, %[[INDIRECT]] ], [ true, %[[ENTRY_TARGET_FALLTHROUGH]] ], [ false, %[[ENTRY_TARGET_INDIRECT]] ], [ false, %[[ENTRY_TARGET_INDIRECT1]] ]
+; CHECK-NEXT: [[GUARD_FALLTHROUGH_INV:%.*]] = xor i1 [[GUARD_FALLTHROUGH]], true
+; CHECK-NEXT: br i1 [[GUARD_INDIRECT1]], label %[[INDIRECT1]], label %[[IRR_GUARD1:.*]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[IRR_GUARD2]]:
+; CHECK-NEXT: [[GUARD_INDIRECT:%.*]] = phi i1 [ true, %[[FALLTHROUGH]] ], [ [[GUARD_FALLTHROUGH_INV]], %[[IRR_GUARD1]] ], [ true, %[[INDIRECT1]] ]
+; CHECK-NEXT: br i1 [[GUARD_INDIRECT]], label %[[INDIRECT]], label %[[FALLTHROUGH]]
+;
+entry:
+ switch i32 %val, label %fallthrough [
+ i32 0, label %indirect
+ i32 1, label %indirect1
+ i32 2, label %indirect2
+ ]
+fallthrough:
+ br i1 %c, label %indirect, label %ret
+indirect:
+ br i1 %d, label %fallthrough, label %indirect1
+indirect1:
+ br label %indirect
+indirect2:
+ br label %ret
+ret:
+ ret void
+}
+
+define void @switch_header_no_indirect(i32 %val, i1 %c, i1 %d) {
+; CHECK-LABEL: define void @switch_header_no_indirect(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]], i1 [[D:%.*]]) {
+; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[SWITCH_BB:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_BB_TARGET_FALLTHROUGH:.*]] [
+; CHECK-NEXT: ]
+; CHECK: [[FALLTHROUGH:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[SWITCH_BB]], label %[[RET:.*]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SWITCH_BB_TARGET_FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ true, %[[SWITCH_BB_TARGET_FALLTHROUGH]] ], [ [[D_INV]], [[TMP0:%.*]] ]
+; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH]], label %[[FALLTHROUGH]], label %[[SWITCH_BB]]
+;
+ br i1 %d, label %switch_bb, label %fallthrough
+switch_bb:
+ switch i32 %val, label %fallthrough []
+fallthrough:
+ br i1 %c, label %switch_bb, label %ret
+ret:
+ ret void
+}
+
+define void @switch_header(i32 %val, i1 %c, i1 %d) {
+; CHECK-LABEL: define void @switch_header(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]], i1 [[D:%.*]]) {
+; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[SWITCH_BB:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_BB_TARGET_FALLTHROUGH:.*]] [
+; CHECK-NEXT: i32 0, label %[[INDIRECT:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[INDIRECT]]:
+; CHECK-NEXT: br label %[[RET:.*]]
+; CHECK: [[FALLTHROUGH:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[SWITCH_BB]], label %[[RET]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SWITCH_BB_TARGET_FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ true, %[[SWITCH_BB_TARGET_FALLTHROUGH]] ], [ [[D_INV]], [[TMP0:%.*]] ]
+; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH]], label %[[FALLTHROUGH]], label %[[SWITCH_BB]]
+;
+ br i1 %d, label %switch_bb, label %fallthrough
+switch_bb:
+ switch i32 %val, label %fallthrough [
+ i32 0, label %indirect
+ ]
+indirect:
+ br label %ret
+fallthrough:
+ br i1 %c, label %switch_bb, label %ret
+ret:
+ ret void
+}
+
+define void @switch_header_multiple_targets(i32 %val, i1 %c, i1 %d) {
+; CHECK-LABEL: define void @switch_header_multiple_targets(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]], i1 [[D:%.*]]) {
+; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[SWITCH_BB:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_BB_TARGET_FALLTHROUGH:.*]] [
+; CHECK-NEXT: i32 0, label %[[INDIRECT:.*]]
+; CHECK-NEXT: i32 1, label %[[INDIRECT1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[INDIRECT]]:
+; CHECK-NEXT: br label %[[RET:.*]]
+; CHECK: [[INDIRECT1]]:
+; CHECK-NEXT: br label %[[SWITCH_BB]]
+; CHECK: [[FALLTHROUGH:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[SWITCH_BB]], label %[[RET]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SWITCH_BB_TARGET_FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ true, %[[SWITCH_BB_TARGET_FALLTHROUGH]] ], [ [[D_INV]], [[TMP0:%.*]] ]
+; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH]], label %[[FALLTHROUGH]], label %[[SWITCH_BB]]
+;
+ br i1 %d, label %switch_bb, label %fallthrough
+switch_bb:
+ switch i32 %val, label %fallthrough [
+ i32 0, label %indirect
+ i32 1, label %indirect1
+ ]
+indirect:
+ br label %ret
+indirect1:
+ br label %switch_bb
+fallthrough:
+ br i1 %c, label %switch_bb, label %ret
+ret:
+ ret void
+}
+
+define void @switch_regular(i32 %val, i1 %c, i1 %d) {
+; CHECK-LABEL: define void @switch_regular(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]], i1 [[D:%.*]]) {
+; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
+; CHECK-NEXT: br i1 [[D]], label %[[SWITCH_BB:.*]], label %[[NOSWITCH:.*]]
+; CHECK: [[SWITCH_BB]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[FALLTHROUGH:.*]] [
+; CHECK-NEXT: i32 0, label %[[INDIRECT:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[FALLTHROUGH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[FALLTHROUGH1:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[FALLTHROUGH2:.*]]:
+; CHECK-NEXT: br i1 [[D]], label %[[FALLTHROUGH1]], label %[[RET:.*]]
+; CHECK: [[INDIRECT]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1:.*]]
+; CHECK: [[INDIRECT1:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1]]
+; CHECK: [[INDIRECT2:.*]]:
+; CHECK-NEXT: br i1 [[D]], label %[[INDIRECT1]], label %[[RET]]
+; CHECK: [[NOSWITCH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2:.*]]
+; CHECK: [[NOSWITCH1:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[NOSWITCH2:.*]]:
+; CHECK-NEXT: br i1 [[D]], label %[[NOSWITCH1]], label %[[RET]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_FALLTHROUGH2:%.*]] = phi i1 [ true, %[[FALLTHROUGH1]] ], [ [[C_INV]], %[[FALLTHROUGH]] ]
+; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH2]], label %[[FALLTHROUGH2]], label %[[FALLTHROUGH1]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: [[GUARD_INDIRECT2:%.*]] = phi i1 [ true, %[[INDIRECT1]] ], [ [[C_INV]], %[[INDIRECT]] ]
+; CHECK-NEXT: br i1 [[GUARD_INDIRECT2]], label %[[INDIRECT2]], label %[[INDIRECT1]]
+; CHECK: [[IRR_GUARD2]]:
+; CHECK-NEXT: [[GUARD_NOSWITCH2:%.*]] = phi i1 [ true, %[[NOSWITCH1]] ], [ [[C_INV]], %[[NOSWITCH]] ]
+; CHECK-NEXT: br i1 [[GUARD_NOSWITCH2]], label %[[NOSWITCH2]], label %[[NOSWITCH1]]
+;
+ br i1 %d, label %switch_bb, label %noswitch
+switch_bb:
+ switch i32 %val, label %fallthrough [
+ i32 0, label %indirect
+ ]
+fallthrough:
+ br i1 %c, label %fallthrough1, label %fallthrough2
+fallthrough1:
+ br label %fallthrough2
+fallthrough2:
+ br i1 %d, label %fallthrough1, label %ret
+indirect:
+ br i1 %c, label %indirect1, label %indirect2
+indirect1:
+ br label %indirect2
+indirect2:
+ br i1 %d, label %indirect1, label %ret
+noswitch:
+ br i1 %c, label %noswitch1, label %noswitch2
+noswitch1:
+ br label %noswitch2
+noswitch2:
+ br i1 %d, label %noswitch1, label %ret
+ret:
+ ret void
+}
+
+define void @switch_regular1(i32 %val, i1 %c) {
+; CHECK-LABEL: define void @switch_regular1(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[NOSWITCH:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[SWITCH_BB:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[RET:.*]] [
+; CHECK-NEXT: i32 0, label %[[NOSWITCH]]
+; CHECK-NEXT: ]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_SWITCH_BB:%.*]] = phi i1 [ true, %[[NOSWITCH]] ], [ [[C_INV]], [[TMP0:%.*]] ]
+; CHECK-NEXT: br i1 [[GUARD_SWITCH_BB]], label %[[SWITCH_BB]], label %[[NOSWITCH]]
+;
+ br i1 %c, label %noswitch, label %switch_bb
+noswitch:
+ br label %switch_bb
+switch_bb:
+ switch i32 %val, label %ret [
+ i32 0, label %noswitch
+ ]
+ret:
+ ret void
+}
+
+define void @switch_header_and_regular(i32 %val, i1 %c) {
+; CHECK-LABEL: define void @switch_header_and_regular(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: br label %[[SWITCH_HEADER:.*]]
+; CHECK: [[SWITCH_HEADER]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_HEADER_TARGET_MID:.*]] [
+; CHECK-NEXT: i32 0, label %[[SWITCH_HEADER_TARGET_SWITCH_REGULAR:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[MID:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD:.*]], label %[[RET:.*]]
+; CHECK: [[SWITCH_REGULAR:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_HEADER]] [
+; CHECK-NEXT: i32 0, label %[[MID]]
+; CHECK-NEXT: ]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SWITCH_HEADER_TARGET_MID]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[SWITCH_HEADER_TARGET_SWITCH_REGULAR]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_SWITCH_REGULAR:%.*]] = phi i1 [ true, %[[MID]] ], [ false, %[[SWITCH_HEADER_TARGET_MID]] ], [ true, %[[SWITCH_HEADER_TARGET_SWITCH_REGULAR]] ]
+; CHECK-NEXT: br i1 [[GUARD_SWITCH_REGULAR]], label %[[SWITCH_REGULAR]], label %[[MID]]
+;
+ br label %switch_header
+switch_header:
+ switch i32 %val, label %mid [
+ i32 0, label %switch_regular
+ ]
+mid:
+ br i1 %c, label %switch_regular, label %ret
+switch_regular:
+ switch i32 %val, label %switch_header [
+ i32 0, label %mid
+ ]
+ret:
+ ret void
+}
+
+define void @switch_only(i32 %val, i1 %c) {
+; CHECK-LABEL: define void @switch_only(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[SWITCH_ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_ENTRY_TARGET_SWITCH_HEADER:.*]] [
+; CHECK-NEXT: i32 0, label %[[SWITCH_ENTRY_TARGET_SWITCH_BLOCK:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[SWITCH_HEADER:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_HEADER_TARGET_SWITCH_BLOCK:.*]] [
+; CHECK-NEXT: ]
+; CHECK: [[SWITCH_BLOCK:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SWITCH_HEADER]] [
+; CHECK-NEXT: i32 0, label %[[RET:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SWITCH_HEADER_TARGET_SWITCH_BLOCK]]:
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[SWITCH_ENTRY_TARGET_SWITCH_HEADER]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[SWITCH_ENTRY_TARGET_SWITCH_BLOCK]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_SWITCH_BLOCK:%.*]] = phi i1 [ true, %[[SWITCH_HEADER_TARGET_SWITCH_BLOCK]] ], [ false, %[[SWITCH_ENTRY_TARGET_SWITCH_HEADER]] ], [ true, %[[SWITCH_ENTRY_TARGET_SWITCH_BLOCK]] ]
+; CHECK-NEXT: br i1 [[GUARD_SWITCH_BLOCK]], label %[[SWITCH_BLOCK]], label %[[SWITCH_HEADER]]
+;
+switch_entry:
+ switch i32 %val, label %switch_header [
+ i32 0, label %switch_block
+ ]
+switch_header:
+ switch i32 %val, label %switch_block []
+switch_block:
+ switch i32 %val, label %switch_header [
+ i32 0, label %ret
+ ]
+ret:
+ ret void
+}
+
+define void @entry_multiple_switch(i32 %val, i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define void @entry_multiple_switch(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br i1 [[A]], label %[[SW1:.*]], label %[[IRR_GUARD:.*]]
+; CHECK: [[SW1]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SW1_TARGET_BLOCK:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW1_TARGET_SW2:.*]]
+; CHECK-NEXT: i32 1, label %[[SW1_TARGET_BLOCK1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[BLOCK:.*]]:
+; CHECK-NEXT: br i1 [[B]], label %[[IRR_GUARD]], label %[[BLOCK1:.*]]
+; CHECK: [[SW2:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SW2_TARGET_BLOCK1:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW2_TARGET_BLOCK:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[BLOCK1]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD2:.*]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SW1_TARGET_BLOCK]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[SW1_TARGET_SW2]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[SW1_TARGET_BLOCK1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_SW2:%.*]] = phi i1 [ true, %[[BLOCK]] ], [ false, %[[SW1_TARGET_BLOCK]] ], [ true, %[[SW1_TARGET_SW2]] ], [ false, %[[SW1_TARGET_BLOCK1]] ], [ true, %[[ENTRY]] ]
+; CHECK-NEXT: [[GUARD_BLOCK:%.*]] = phi i1 [ false, %[[BLOCK]] ], [ true, %[[SW1_TARGET_BLOCK]] ], [ false, %[[SW1_TARGET_SW2]] ], [ false, %[[SW1_TARGET_BLOCK1]] ], [ false, %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_SW2]], label %[[SW2]], label %[[IRR_GUARD1:.*]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[SW2_TARGET_BLOCK1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[SW2_TARGET_BLOCK]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[IRR_GUARD2]]:
+; CHECK-NEXT: [[GUARD_BLOCK3:%.*]] = phi i1 [ true, %[[BLOCK1]] ], [ [[GUARD_BLOCK]], %[[IRR_GUARD1]] ], [ false, %[[SW2_TARGET_BLOCK1]] ], [ true, %[[SW2_TARGET_BLOCK]] ]
+; CHECK-NEXT: br i1 [[GUARD_BLOCK3]], label %[[BLOCK]], label %[[BLOCK1]]
+;
+entry:
+ br i1 %a, label %sw1, label %sw2
+sw1:
+ switch i32 %val, label %block [
+ i32 0, label %sw2
+ i32 1, label %block1
+ ]
+block:
+ br i1 %b, label %sw2, label %block1
+sw2:
+ switch i32 %val, label %block1 [
+ i32 0, label %block
+ ]
+block1:
+ br i1 %c, label %block, label %exit
+exit:
+ ret void
+}
+
+define void @switch_exit_with_separate_entries(i32 %val, i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define void @switch_exit_with_separate_entries(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
+; CHECK-NEXT: [[A_INV:%.*]] = xor i1 [[A]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[L1:.*]]:
+; CHECK-NEXT: br i1 [[B]], label %[[SW:.*]], label %[[IRR_GUARD]]
+; CHECK: [[L2:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1:.*]]
+; CHECK: [[SW]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[EXIT:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW_TARGET_L1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_L2:%.*]] = phi i1 [ true, %[[L1]] ], [ [[A_INV]], %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_L2]], label %[[L2]], label %[[IRR_GUARD1]]
+; CHECK: [[SW_TARGET_L1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: [[GUARD_L1:%.*]] = phi i1 [ true, %[[SW_TARGET_L1]] ], [ true, %[[IRR_GUARD]] ], [ [[C_INV]], %[[L2]] ]
+; CHECK-NEXT: br i1 [[GUARD_L1]], label %[[L1]], label %[[SW]]
+;
+entry:
+ br i1 %a, label %l1, label %l2
+l1:
+ br i1 %b, label %sw, label %l2
+l2:
+ br i1 %c, label %sw, label %l1
+sw:
+ switch i32 %val, label %exit [
+ i32 0, label %l1
+ ]
+exit:
+ ret void
+}
+
+define void @switch_exit_with_separate_entries1(i32 %val, i1 %a, i1 %b) {
+; CHECK-LABEL: define void @switch_exit_with_separate_entries1(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[A:%.*]], i1 [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[A_INV:%.*]] = xor i1 [[A]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[LOOP1:.*]]:
+; CHECK-NEXT: br i1 [[B]], label %[[SW:.*]], label %[[IRR_GUARD]]
+; CHECK: [[LOOP2:.*]]:
+; CHECK-NEXT: br label %[[LOOP1]]
+; CHECK: [[SW]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[EXIT:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW_TARGET_LOOP2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SW_TARGET_LOOP2]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_LOOP2:%.*]] = phi i1 [ true, %[[SW_TARGET_LOOP2]] ], [ true, %[[LOOP1]] ], [ [[A_INV]], %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_LOOP2]], label %[[LOOP2]], label %[[LOOP1]]
+;
+entry:
+ br i1 %a, label %loop1, label %loop2
+loop1:
+ br i1 %b, label %sw, label %loop2
+loop2:
+ br label %loop1
+sw:
+ switch i32 %val, label %exit [
+ i32 0, label %loop2
+ ]
+exit:
+ ret void
+}
+
+define void @switch_only_multiple(i32 %val, i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define void @switch_only_multiple(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_SW1:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_SW2:.*]]
+; CHECK-NEXT: i32 1, label %[[ENTRY_TARGET_SW3:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[SW1:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SW2:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW1_TARGET_SW3:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[SW2]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SW2_TARGET_SW3:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW2_TARGET_SW1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[SW3:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[SW3_TARGET_SW1:.*]] [
+; CHECK-NEXT: i32 0, label %[[EXIT:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SW2_TARGET_SW3]]:
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[SW1_TARGET_SW3]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_SW1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_SW2]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_SW3]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_SW3:%.*]] = phi i1 [ true, %[[SW2_TARGET_SW3]] ], [ true, %[[SW1_TARGET_SW3]] ], [ false, %[[ENTRY_TARGET_SW1]] ], [ false, %[[ENTRY_TARGET_SW2]] ], [ true, %[[ENTRY_TARGET_SW3]] ]
+; CHECK-NEXT: [[GUARD_SW1:%.*]] = phi i1 [ false, %[[SW2_TARGET_SW3]] ], [ false, %[[SW1_TARGET_SW3]] ], [ true, %[[ENTRY_TARGET_SW1]] ], [ false, %[[ENTRY_TARGET_SW2]] ], [ false, %[[ENTRY_TARGET_SW3]] ]
+; CHECK-NEXT: br i1 [[GUARD_SW3]], label %[[SW3]], label %[[IRR_GUARD1:.*]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2:.*]]
+; CHECK: [[SW2_TARGET_SW1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[SW3_TARGET_SW1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[IRR_GUARD2]]:
+; CHECK-NEXT: [[GUARD_SW13:%.*]] = phi i1 [ true, %[[SW2_TARGET_SW1]] ], [ [[GUARD_SW1]], %[[IRR_GUARD1]] ], [ true, %[[SW3_TARGET_SW1]] ]
+; CHECK-NEXT: br i1 [[GUARD_SW13]], label %[[SW1]], label %[[SW2]]
+;
+entry:
+ switch i32 %val, label %sw1 [
+ i32 0, label %sw2
+ i32 1, label %sw3
+ ]
+sw1:
+ switch i32 %val, label %sw2 [
+ i32 0, label %sw3
+ ]
+sw2:
+ switch i32 %val, label %sw3 [
+ i32 0, label %sw1
+ ]
+sw3:
+ switch i32 %val, label %sw1 [
+ i32 0, label %exit
+ ]
+exit:
+ ret void
+}
+
+define void @switch_bypass(i32 %val, i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define void @switch_bypass(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[B_INV:%.*]] = xor i1 [[B]], true
+; CHECK-NEXT: [[A_INV:%.*]] = xor i1 [[A]], true
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[SW:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[L2:.*]] [
+; CHECK-NEXT: i32 0, label %[[SW_TARGET_L1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[L1:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1:.*]]
+; CHECK: [[L2]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD1]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[SW_TARGET_L1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_L1:%.*]] = phi i1 [ true, %[[SW_TARGET_L1]] ], [ [[A_INV]], %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_L1]], label %[[L1]], label %[[IRR_GUARD1]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: [[GUARD_SW:%.*]] = phi i1 [ true, %[[L2]] ], [ true, %[[IRR_GUARD]] ], [ [[B_INV]], %[[L1]] ]
+; CHECK-NEXT: br i1 [[GUARD_SW]], label %[[SW]], label %[[L2]]
+;
+entry:
+ br i1 %a, label %sw, label %l1
+sw:
+ switch i32 %val, label %l2 [
+ i32 0, label %l1
+ ]
+l1:
+ br i1 %b, label %l2, label %sw
+l2:
+ br i1 %c, label %sw, label %exit
+exit:
+ ret void
+}
+
+define void @switch_multiple_with_exit(i32 %val, i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define void @switch_multiple_with_exit(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_L1:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_L2:.*]]
+; CHECK-NEXT: i32 1, label %[[EXIT:.*]]
+; CHECK-NEXT: i32 2, label %[[ENTRY_TARGET_L3:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[L1:.*]]:
+; CHECK-NEXT: br i1 [[A]], label %[[L2:.*]], label %[[IRR_GUARD:.*]]
+; CHECK: [[L2]]:
+; CHECK-NEXT: br i1 [[B]], label %[[IRR_GUARD2:.*]], label %[[EXIT]]
+; CHECK: [[L3:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD2]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_TARGET_L1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_L2]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_L3]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_L3:%.*]] = phi i1 [ true, %[[L1]] ], [ false, %[[ENTRY_TARGET_L1]] ], [ false, %[[ENTRY_TARGET_L2]] ], [ true, %[[ENTRY_TARGET_L3]] ]
+; CHECK-NEXT: [[GUARD_L1:%.*]] = phi i1 [ false, %[[L1]] ], [ true, %[[ENTRY_TARGET_L1]] ], [ false, %[[ENTRY_TARGET_L2]] ], [ false, %[[ENTRY_TARGET_L3]] ]
+; CHECK-NEXT: br i1 [[GUARD_L3]], label %[[L3]], label %[[IRR_GUARD1:.*]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: br label %[[IRR_GUARD2]]
+; CHECK: [[IRR_GUARD2]]:
+; CHECK-NEXT: [[GUARD_L13:%.*]] = phi i1 [ true, %[[L2]] ], [ [[GUARD_L1]], %[[IRR_GUARD1]] ], [ true, %[[L3]] ]
+; CHECK-NEXT: br i1 [[GUARD_L13]], label %[[L1]], label %[[L2]]
+;
+entry:
+ switch i32 %val, label %l1 [
+ i32 0, label %l2
+ i32 1, label %exit
+ i32 2, label %l3
+ ]
+l1:
+ br i1 %a, label %l2, label %l3
+l2:
+ br i1 %b, label %l1, label %exit
+l3:
+ br i1 %c, label %l1, label %exit
+exit:
+ ret void
+}
+
+define void @switch_nested(i32 %val, i1 %c, i1 %d) {
+; CHECK-LABEL: define void @switch_nested(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C:%.*]], i1 [[D:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[VAL]], label %[[ENTRY_TARGET_H:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_B:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[H:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1:.*]]
+; CHECK: [[B:.*]]:
+; CHECK-NEXT: switch i32 [[VAL]], label %[[H]] [
+; CHECK-NEXT: i32 0, label %[[B_TARGET_BH:.*]]
+; CHECK-NEXT: i32 1, label %[[B_TARGET_BB:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[BH:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[BB:.*]]:
+; CHECK-NEXT: br i1 [[C]], label %[[BH]], label %[[RET:.*]]
+; CHECK: [[RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[B_TARGET_BH]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[B_TARGET_BB]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[GUARD_BB:%.*]] = phi i1 [ true, %[[BH]] ], [ false, %[[B_TARGET_BH]] ], [ true, %[[B_TARGET_BB]] ]
+; CHECK-NEXT: br i1 [[GUARD_BB]], label %[[BB]], label %[[BH]]
+; CHECK: [[ENTRY_TARGET_H]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1]]
+; CHECK: [[ENTRY_TARGET_B]]:
+; CHECK-NEXT: br label %[[IRR_GUARD1]]
+; CHECK: [[IRR_GUARD1]]:
+; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, %[[H]] ], [ false, %[[ENTRY_TARGET_H]] ], [ true, %[[ENTRY_TARGET_B]] ]
+; CHECK-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[H]]
+;
+entry:
+ switch i32 %val, label %h [
+ i32 0, label %b
+ ]
+h:
+ br label %b
+b:
+ switch i32 %val, label %h [
+ i32 0, label %bh
+ i32 1, label %bb
+ ]
+bh:
+ br label %bb
+bb:
+ br i1 %c, label %bh, label %ret
+ret:
+ ret void
+}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CYCLES-AFTER: {{.*}}
+; CYCLES-BEFORE: {{.*}}
diff --git a/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll b/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll
index f1be342c3d324..02a4ec802cd33 100644
--- a/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll
+++ b/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll
@@ -1,19 +1,16 @@
; RUN: not opt < %s -passes=fix-irreducible -S 2>&1 | FileCheck %s
-; CHECK: LLVM ERROR: unsupported block terminator: fix-irreducible only supports br and callbr instructions
+; CHECK: LLVM ERROR: unsupported block terminator: fix-irreducible only supports br, callbr, and switch instructions
-define void @loop_1(i32 %Value, i1 %PredEntry) {
+define i32 @test(i1 %cond) {
entry:
- br i1 %PredEntry, label %A, label %B
+ %target = select i1 %cond,
+ ptr blockaddress(@test, %then),
+ ptr blockaddress(@test, %else)
+ indirectbr ptr %target, [label %then, label %else]
-A:
- br label %B
+then:
+ br label %else
-B:
- switch i32 %Value, label %exit [
- i32 0, label %A
- i32 1, label %B
- ]
-
-exit:
- ret void
-}
+else:
+ br label %then
+}
\ No newline at end of file
>From a46d89d031b0d86fee5906bdc885e1d85e6387d3 Mon Sep 17 00:00:00 2001
From: Lukas <dev at lukas-hertel.de>
Date: Sat, 4 Jul 2026 10:29:28 +0200
Subject: [PATCH 2/5] Address review feedback and add switch tests
---
.../llvm/Transforms/Utils/BasicBlockUtils.h | 2 +-
.../Transforms/FixIrreducible/bug45623.ll | 131 ++-
llvm/test/Transforms/FixIrreducible/nested.ll | 869 ++++++++++++++++--
.../Transforms/FixIrreducible/unreachable.ll | 23 +
.../FixIrreducible/unsupported-terminator.ll | 2 +-
llvm/test/Transforms/UnifyLoopExits/basic.ll | 170 +++-
.../UnifyLoopExits/integer_guards.ll | 503 +++++++++-
llvm/test/Transforms/UnifyLoopExits/nested.ll | 168 +++-
.../Transforms/UnifyLoopExits/restore-ssa.ll | 275 +++++-
llvm/test/Transforms/UnifyLoopExits/switch.ll | 29 +
.../Transforms/UnifyLoopExits/undef-phis.ll | 81 +-
11 files changed, 2109 insertions(+), 144 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
index be0567a1b1db2..7503248046395 100644
--- a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
@@ -268,7 +268,7 @@ LLVM_ABI BasicBlock *SplitEdge(BasicBlock *From, BasicBlock *To,
MemorySSAUpdater *MSSAU = nullptr,
const Twine &BBName = "");
-/// \brief Create a new intermediate target block for a callbr and switch edges.
+/// \brief Create a new intermediate target block for a callbr or switch edge.
///
/// Create a new basic block between a callbr or switch instruction and one of
/// its successors. The new block replaces the original successor in the
diff --git a/llvm/test/Transforms/FixIrreducible/bug45623.ll b/llvm/test/Transforms/FixIrreducible/bug45623.ll
index b6dd6fb9e6fcb..d03d1a1aaf9a9 100644
--- a/llvm/test/Transforms/FixIrreducible/bug45623.ll
+++ b/llvm/test/Transforms/FixIrreducible/bug45623.ll
@@ -98,16 +98,16 @@ define void @tre_tnfa_run_backtrack_callbr(i1 %arg) {
; CHECK-NEXT: to label [[RETRY:%.*]] []
; CHECK: retry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[ARG:%.*]])
-; CHECK-NEXT: to label [[RETRY_TARGET_BACKTRACK:%.*]] [label %retry.target.while.body248]
+; CHECK-NEXT: to label [[RETRY_TARGET_BACKTRACK:%.*]] [label [[RETRY_TARGET_WHILE_BODY248:%.*]]]
; CHECK: while.body248:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[ARG]])
-; CHECK-NEXT: to label [[IF_THEN250:%.*]] [label %if.end275]
+; CHECK-NEXT: to label [[IF_THEN250:%.*]] [label [[IF_END275:%.*]]]
; CHECK: if.then250:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[FOR_COND264:%.*]] []
; CHECK: for.cond264:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[ARG]])
-; CHECK-NEXT: to label [[FOR_BODY267:%.*]] [label %backtrack]
+; CHECK-NEXT: to label [[FOR_BODY267:%.*]] [label [[BACKTRACK:%.*]]]
; CHECK: for.body267:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[FOR_COND264]] []
@@ -116,22 +116,22 @@ define void @tre_tnfa_run_backtrack_callbr(i1 %arg) {
; CHECK-NEXT: to label [[FOR_COND342:%.*]] []
; CHECK: for.cond342:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[ARG]])
-; CHECK-NEXT: to label [[FOR_BODY345:%.*]] [label %for.end580]
+; CHECK-NEXT: to label [[FOR_BODY345:%.*]] [label [[FOR_END580:%.*]]]
; CHECK: for.body345:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[FOR_COND342]] []
; CHECK: for.end580:
; CHECK-NEXT: callbr void asm "", ""()
-; CHECK-NEXT: to label [[BACKTRACK:%.*]] []
+; CHECK-NEXT: to label [[BACKTRACK]] []
; CHECK: backtrack:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[ARG]])
-; CHECK-NEXT: to label [[IF_THEN595:%.*]] [label %if.else629]
+; CHECK-NEXT: to label [[IF_THEN595:%.*]] [label [[IF_ELSE629:%.*]]]
; CHECK: if.then595:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[FOR_COND616:%.*]] []
; CHECK: for.cond616:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[ARG]])
-; CHECK-NEXT: to label [[FOR_BODY619:%.*]] [label %for.end626]
+; CHECK-NEXT: to label [[FOR_BODY619:%.*]] [label [[FOR_END626:%.*]]]
; CHECK: for.body619:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[FOR_COND616]] []
@@ -148,7 +148,7 @@ define void @tre_tnfa_run_backtrack_callbr(i1 %arg) {
; CHECK: retry.target.while.body248:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_WHILE_BODY248:%.*]] = phi i1 [ true, [[FOR_END626_TARGET_WHILE_BODY248]] ], [ false, [[RETRY_TARGET_BACKTRACK]] ], [ true, [[RETRY_TARGET_WHILE_BODY248:%.*]] ]
+; CHECK-NEXT: [[GUARD_WHILE_BODY248:%.*]] = phi i1 [ true, [[FOR_END626_TARGET_WHILE_BODY248]] ], [ false, [[RETRY_TARGET_BACKTRACK]] ], [ true, [[RETRY_TARGET_WHILE_BODY248]] ]
; CHECK-NEXT: br i1 [[GUARD_WHILE_BODY248]], label [[WHILE_BODY248:%.*]], label [[BACKTRACK]]
;
entry:
@@ -199,3 +199,118 @@ for.end626: ; preds = %for.cond616
if.else629: ; preds = %backtrack
callbr void asm "", ""() to label %retry []
}
+
+define void @tre_tnfa_run_backtrack_switch(i32 %arg) {
+; CHECK-LABEL: @tre_tnfa_run_backtrack_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[ARG:%.*]], label [[RETRY:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: retry:
+; CHECK-NEXT: switch i32 [[ARG]], label [[RETRY_TARGET_BACKTRACK:%.*]] [
+; CHECK-NEXT: i32 0, label [[RETRY_TARGET_WHILE_BODY248:%.*]]
+; CHECK-NEXT: ]
+; CHECK: while.body248:
+; CHECK-NEXT: switch i32 [[ARG]], label [[IF_THEN250:%.*]] [
+; CHECK-NEXT: i32 0, label [[IF_END275:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.then250:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_COND264:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: for.cond264:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_BODY267:%.*]] [
+; CHECK-NEXT: i32 0, label [[BACKTRACK:%.*]]
+; CHECK-NEXT: ]
+; CHECK: for.body267:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_COND264]] [
+; CHECK-NEXT: ]
+; CHECK: if.end275:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_COND342:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: for.cond342:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_BODY345:%.*]] [
+; CHECK-NEXT: i32 0, label [[FOR_END580:%.*]]
+; CHECK-NEXT: ]
+; CHECK: for.body345:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_COND342]] [
+; CHECK-NEXT: ]
+; CHECK: for.end580:
+; CHECK-NEXT: switch i32 [[ARG]], label [[BACKTRACK]] [
+; CHECK-NEXT: ]
+; CHECK: backtrack:
+; CHECK-NEXT: switch i32 [[ARG]], label [[IF_THEN595:%.*]] [
+; CHECK-NEXT: i32 0, label [[IF_ELSE629:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.then595:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_COND616:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: for.cond616:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_BODY619:%.*]] [
+; CHECK-NEXT: i32 0, label [[FOR_END626:%.*]]
+; CHECK-NEXT: ]
+; CHECK: for.body619:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_COND616]] [
+; CHECK-NEXT: ]
+; CHECK: for.end626:
+; CHECK-NEXT: switch i32 [[ARG]], label [[FOR_END626_TARGET_WHILE_BODY248:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: if.else629:
+; CHECK-NEXT: switch i32 [[ARG]], label [[RETRY]] [
+; CHECK-NEXT: ]
+; CHECK: for.end626.target.while.body248:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: retry.target.backtrack:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: retry.target.while.body248:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_WHILE_BODY248:%.*]] = phi i1 [ true, [[FOR_END626_TARGET_WHILE_BODY248]] ], [ false, [[RETRY_TARGET_BACKTRACK]] ], [ true, [[RETRY_TARGET_WHILE_BODY248]] ]
+; CHECK-NEXT: br i1 [[GUARD_WHILE_BODY248]], label [[WHILE_BODY248:%.*]], label [[BACKTRACK]]
+;
+entry:
+ switch i32 %arg, label %retry []
+
+retry:
+ switch i32 %arg, label %backtrack [ i32 0, label %while.body248 ]
+
+while.body248: ; preds = %for.end626, %retry
+ switch i32 %arg, label %if.then250 [ i32 0, label %if.end275 ]
+
+if.then250: ; preds = %while.body248
+ switch i32 %arg, label %for.cond264 []
+
+for.cond264: ; preds = %for.body267, %if.then250
+ switch i32 %arg, label %for.body267 [ i32 0, label %backtrack ]
+
+for.body267: ; preds = %for.cond264
+ switch i32 %arg, label %for.cond264 []
+
+if.end275: ; preds = %while.body248
+ switch i32 %arg, label %for.cond342 []
+
+for.cond342: ; preds = %for.body345, %if.end275
+ switch i32 %arg, label %for.body345 [ i32 0, label %for.end580 ]
+
+for.body345: ; preds = %for.cond342
+ switch i32 %arg, label %for.cond342 []
+
+for.end580: ; preds = %for.cond342
+ switch i32 %arg, label %backtrack []
+
+backtrack: ; preds = %for.end580, %for.cond264, %retry
+ switch i32 %arg, label %if.then595 [ i32 0, label %if.else629 ]
+
+if.then595: ; preds = %backtrack
+ switch i32 %arg, label %for.cond616 []
+
+for.cond616: ; preds = %for.body619, %if.then595
+ switch i32 %arg, label %for.body619 [ i32 0, label %for.end626 ]
+
+for.body619: ; preds = %for.cond616
+ switch i32 %arg, label %for.cond616 []
+
+for.end626: ; preds = %for.cond616
+ switch i32 %arg, label %while.body248 []
+
+if.else629: ; preds = %backtrack
+ switch i32 %arg, label %retry []
+}
diff --git a/llvm/test/Transforms/FixIrreducible/nested.ll b/llvm/test/Transforms/FixIrreducible/nested.ll
index c9161cc14f208..a739d093f5583 100644
--- a/llvm/test/Transforms/FixIrreducible/nested.ll
+++ b/llvm/test/Transforms/FixIrreducible/nested.ll
@@ -54,22 +54,22 @@ define void @nested_irr_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pr
; CHECK-LABEL: @nested_irr_top_level_callbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[ENTRY_TARGET_A1:%.*]] [label %entry.target.A2]
+; CHECK-NEXT: to label [[ENTRY_TARGET_A1:%.*]] [label [[ENTRY_TARGET_A2:%.*]]]
; CHECK: A1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[A1_TARGET_B1:%.*]] [label %A1.target.B2]
+; CHECK-NEXT: to label [[A1_TARGET_B1:%.*]] [label [[A1_TARGET_B2:%.*]]]
; CHECK: B1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[B1_TARGET_B2:%.*]] [label %A3]
+; CHECK-NEXT: to label [[B1_TARGET_B2:%.*]] [label [[A3:%.*]]]
; CHECK: B2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED3:%.*]])
-; CHECK-NEXT: to label [[B1:%.*]] [label %A3]
+; CHECK-NEXT: to label [[B1:%.*]] [label [[A3]]]
; CHECK: A3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED4:%.*]])
-; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label %exit]
+; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label [[EXIT:%.*]]]
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED5:%.*]])
-; CHECK-NEXT: to label [[A1:%.*]] [label %exit]
+; CHECK-NEXT: to label [[A1:%.*]] [label [[EXIT]]]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: A3.target.A2:
@@ -79,7 +79,7 @@ define void @nested_irr_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pr
; CHECK: entry.target.A2:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_A1]] ], [ true, [[ENTRY_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_A1]] ], [ true, [[ENTRY_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
; CHECK: B1.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
@@ -88,7 +88,7 @@ define void @nested_irr_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pr
; CHECK: A1.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1]]
; CHECK: irr.guard1:
-; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[B1_TARGET_B2]] ], [ false, [[A1_TARGET_B1]] ], [ true, [[A1_TARGET_B2:%.*]] ]
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[B1_TARGET_B2]] ], [ false, [[A1_TARGET_B1]] ], [ true, [[A1_TARGET_B2]] ]
; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
;
entry:
@@ -113,6 +113,75 @@ exit:
ret void
}
+define void @nested_irr_top_level_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5) {
+; CHECK-LABEL: @nested_irr_top_level_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[ENTRY_TARGET_A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[ENTRY_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[A1_TARGET_B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[A1_TARGET_B2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B1:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[B1_TARGET_B2:%.*]] [
+; CHECK-NEXT: i1 false, label [[A3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B2:
+; CHECK-NEXT: switch i1 [[PRED3:%.*]], label [[B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[A3]]
+; CHECK-NEXT: ]
+; CHECK: A3:
+; CHECK-NEXT: switch i1 [[PRED4:%.*]], label [[A3_TARGET_A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[EXIT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED5:%.*]], label [[A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[EXIT]]
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A3.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: entry.target.A1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: entry.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_A1]] ], [ true, [[ENTRY_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
+; CHECK: B1.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
+; CHECK: A1.target.B1:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: A1.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: irr.guard1:
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[B1_TARGET_B2]] ], [ false, [[A1_TARGET_B1]] ], [ true, [[A1_TARGET_B2]] ]
+; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
+;
+entry:
+ switch i1 %Pred0, label %A1 [ i1 0, label %A2 ]
+
+A1:
+ switch i1 %Pred1, label %B1 [ i1 0, label %B2 ]
+
+B1:
+ switch i1 %Pred2, label %B2 [ i1 0, label %A3 ]
+
+B2:
+ switch i1 %Pred3, label %B1 [ i1 0, label %A3 ]
+
+A3:
+ switch i1 %Pred4, label %A2 [ i1 0, label %exit ]
+
+A2:
+ switch i1 %Pred5, label %A1 [ i1 0, label %exit ]
+
+exit:
+ ret void
+}
+
define void @nested_irr_in_loop(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6) {
; CHECK-LABEL: @nested_irr_in_loop(
; CHECK-NEXT: entry:
@@ -176,25 +245,25 @@ define void @nested_irr_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK-NEXT: br label [[H1:%.*]]
; CHECK: H1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[H1_TARGET_A1:%.*]] [label %H1.target.A2]
+; CHECK-NEXT: to label [[H1_TARGET_A1:%.*]] [label [[H1_TARGET_A2:%.*]]]
; CHECK: A1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[A1_TARGET_B1:%.*]] [label %A1.target.B2]
+; CHECK-NEXT: to label [[A1_TARGET_B1:%.*]] [label [[A1_TARGET_B2:%.*]]]
; CHECK: B1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[B1_TARGET_B2:%.*]] [label %A3]
+; CHECK-NEXT: to label [[B1_TARGET_B2:%.*]] [label [[A3:%.*]]]
; CHECK: B2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED3:%.*]])
-; CHECK-NEXT: to label [[B1:%.*]] [label %A3]
+; CHECK-NEXT: to label [[B1:%.*]] [label [[A3]]]
; CHECK: A3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED4:%.*]])
-; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label %L1]
+; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label [[L1:%.*]]]
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED5:%.*]])
-; CHECK-NEXT: to label [[A1:%.*]] [label %L1]
+; CHECK-NEXT: to label [[A1:%.*]] [label [[L1]]]
; CHECK: L1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED6:%.*]])
-; CHECK-NEXT: to label [[EXIT:%.*]] [label %H1]
+; CHECK-NEXT: to label [[EXIT:%.*]] [label [[H1]]]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: A3.target.A2:
@@ -204,7 +273,7 @@ define void @nested_irr_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK: H1.target.A2:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
; CHECK: B1.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
@@ -213,7 +282,7 @@ define void @nested_irr_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK: A1.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1]]
; CHECK: irr.guard1:
-; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[B1_TARGET_B2]] ], [ false, [[A1_TARGET_B1]] ], [ true, [[A1_TARGET_B2:%.*]] ]
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[B1_TARGET_B2]] ], [ false, [[A1_TARGET_B1]] ], [ true, [[A1_TARGET_B2]] ]
; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
;
entry:
@@ -244,6 +313,87 @@ exit:
ret void
}
+define void @nested_irr_in_loop_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6) {
+; CHECK-LABEL: @nested_irr_in_loop_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[H1:%.*]]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[H1_TARGET_A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[H1_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[A1_TARGET_B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[A1_TARGET_B2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B1:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[B1_TARGET_B2:%.*]] [
+; CHECK-NEXT: i1 false, label [[A3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B2:
+; CHECK-NEXT: switch i1 [[PRED3:%.*]], label [[B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[A3]]
+; CHECK-NEXT: ]
+; CHECK: A3:
+; CHECK-NEXT: switch i1 [[PRED4:%.*]], label [[A3_TARGET_A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[L1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED5:%.*]], label [[A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[L1]]
+; CHECK-NEXT: ]
+; CHECK: L1:
+; CHECK-NEXT: switch i1 [[PRED6:%.*]], label [[EXIT:%.*]] [
+; CHECK-NEXT: i1 false, label [[H1]]
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A3.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: H1.target.A1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: H1.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
+; CHECK: B1.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
+; CHECK: A1.target.B1:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: A1.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: irr.guard1:
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[B1_TARGET_B2]] ], [ false, [[A1_TARGET_B1]] ], [ true, [[A1_TARGET_B2]] ]
+; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
+;
+entry:
+ br label %H1
+
+H1:
+ switch i1 %Pred0, label %A1 [ i1 0, label %A2 ]
+
+A1:
+ switch i1 %Pred1, label %B1 [ i1 0, label %B2 ]
+
+B1:
+ switch i1 %Pred2, label %B2 [ i1 0, label %A3 ]
+
+B2:
+ switch i1 %Pred3, label %B1 [ i1 0, label %A3 ]
+
+A3:
+ switch i1 %Pred4, label %A2 [ i1 0, label %L1 ]
+
+A2:
+ switch i1 %Pred5, label %A1 [ i1 0, label %L1 ]
+
+L1:
+ switch i1 %Pred6, label %exit [ i1 0, label %H1 ]
+
+exit:
+ ret void
+}
+
define void @loop_in_irr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-LABEL: @loop_in_irr(
; CHECK-NEXT: entry:
@@ -291,7 +441,7 @@ define void @loop_in_irr_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-LABEL: @loop_in_irr_callbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[ENTRY_TARGET_A1:%.*]] [label %entry.target.A2]
+; CHECK-NEXT: to label [[ENTRY_TARGET_A1:%.*]] [label [[ENTRY_TARGET_A2:%.*]]]
; CHECK: A1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[H1:%.*]] []
@@ -300,10 +450,10 @@ define void @loop_in_irr_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-NEXT: to label [[L1:%.*]] []
; CHECK: L1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[H1]] [label %A3]
+; CHECK-NEXT: to label [[H1]] [label [[A3:%.*]]]
; CHECK: A3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label %exit]
+; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label [[EXIT:%.*]]]
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[A1:%.*]] []
@@ -316,7 +466,7 @@ define void @loop_in_irr_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK: entry.target.A2:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_A1]] ], [ true, [[ENTRY_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_A1]] ], [ true, [[ENTRY_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
;
entry:
@@ -341,6 +491,63 @@ exit:
ret void
}
+define void @loop_in_irr_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
+; CHECK-LABEL: @loop_in_irr_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[ENTRY_TARGET_A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[ENTRY_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[H1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[L1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: L1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[H1]] [
+; CHECK-NEXT: i1 false, label [[A3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A3:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[A3_TARGET_A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[EXIT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[A1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A3.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: entry.target.A1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: entry.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_A1]] ], [ true, [[ENTRY_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
+;
+entry:
+ switch i1 %Pred0, label %A1 [ i1 0, label %A2 ]
+
+A1:
+ switch i1 %Pred0, label %H1 []
+
+H1:
+ switch i1 %Pred0, label %L1 []
+
+L1:
+ switch i1 %Pred1, label %H1 [ i1 0, label %A3 ]
+
+A3:
+ switch i1 %Pred2, label %A2 [ i1 0, label %exit ]
+
+A2:
+ switch i1 %Pred0, label %A1 []
+
+exit:
+ ret void
+}
+
define void @loop_in_irr_shared_entry(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-LABEL: @loop_in_irr_shared_entry(
; CHECK-NEXT: entry:
@@ -383,16 +590,16 @@ define void @loop_in_irr_shared_entry_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-LABEL: @loop_in_irr_shared_entry_callbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[ENTRY_TARGET_H1:%.*]] [label %entry.target.A2]
+; CHECK-NEXT: to label [[ENTRY_TARGET_H1:%.*]] [label [[ENTRY_TARGET_A2:%.*]]]
; CHECK: H1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[L1:%.*]] []
; CHECK: L1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[H1:%.*]] [label %A3]
+; CHECK-NEXT: to label [[H1:%.*]] [label [[A3:%.*]]]
; CHECK: A3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label %exit]
+; CHECK-NEXT: to label [[A3_TARGET_A2:%.*]] [label [[EXIT:%.*]]]
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[H1]] []
@@ -405,7 +612,7 @@ define void @loop_in_irr_shared_entry_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK: entry.target.A2:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_H1]] ], [ true, [[ENTRY_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_H1]] ], [ true, [[ENTRY_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[H1]]
;
entry:
@@ -427,6 +634,57 @@ exit:
ret void
}
+define void @loop_in_irr_shared_entry_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
+; CHECK-LABEL: @loop_in_irr_shared_entry_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[ENTRY_TARGET_H1:%.*]] [
+; CHECK-NEXT: i1 false, label [[ENTRY_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[L1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: L1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[H1:%.*]] [
+; CHECK-NEXT: i1 false, label [[A3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A3:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[A3_TARGET_A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[EXIT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[H1]] [
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A3.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: entry.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: entry.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A3_TARGET_A2]] ], [ false, [[ENTRY_TARGET_H1]] ], [ true, [[ENTRY_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[H1]]
+;
+entry:
+ switch i1 %Pred0, label %H1 [ i1 0, label %A2 ]
+
+H1:
+ switch i1 %Pred0, label %L1 []
+
+L1:
+ switch i1 %Pred1, label %H1 [ i1 0, label %A3 ]
+
+A3:
+ switch i1 %Pred2, label %A2 [ i1 0, label %exit ]
+
+A2:
+ switch i1 %Pred0, label %H1 []
+
+exit:
+ ret void
+}
+
define void @loop_in_irr_shared_header(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-LABEL: @loop_in_irr_shared_header(
; CHECK-NEXT: entry:
@@ -469,16 +727,16 @@ define void @loop_in_irr_shared_header_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK-LABEL: @loop_in_irr_shared_header_callbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[ENTRY_TARGET_A2:%.*]] [label %entry.target.H1]
+; CHECK-NEXT: to label [[ENTRY_TARGET_A2:%.*]] [label [[ENTRY_TARGET_H1:%.*]]]
; CHECK: H1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[L1:%.*]] []
; CHECK: L1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[L1_TARGET_H1:%.*]] [label %A3]
+; CHECK-NEXT: to label [[L1_TARGET_H1:%.*]] [label [[A3:%.*]]]
; CHECK: A3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[A2:%.*]] [label %exit]
+; CHECK-NEXT: to label [[A2:%.*]] [label [[EXIT:%.*]]]
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[A2_TARGET_H1:%.*]] []
@@ -493,7 +751,7 @@ define void @loop_in_irr_shared_header_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
; CHECK: entry.target.H1:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_H1:%.*]] = phi i1 [ true, [[A2_TARGET_H1]] ], [ true, [[L1_TARGET_H1]] ], [ false, [[ENTRY_TARGET_A2]] ], [ true, [[ENTRY_TARGET_H1:%.*]] ]
+; CHECK-NEXT: [[GUARD_H1:%.*]] = phi i1 [ true, [[A2_TARGET_H1]] ], [ true, [[L1_TARGET_H1]] ], [ false, [[ENTRY_TARGET_A2]] ], [ true, [[ENTRY_TARGET_H1]] ]
; CHECK-NEXT: br i1 [[GUARD_H1]], label [[H1:%.*]], label [[A2]]
;
entry:
@@ -515,6 +773,59 @@ exit:
ret void
}
+define void @loop_in_irr_shared_header_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2) {
+; CHECK-LABEL: @loop_in_irr_shared_header_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[ENTRY_TARGET_A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[ENTRY_TARGET_H1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[L1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: L1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[L1_TARGET_H1:%.*]] [
+; CHECK-NEXT: i1 false, label [[A3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A3:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[EXIT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[A2_TARGET_H1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A2.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: L1.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: entry.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: entry.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_H1:%.*]] = phi i1 [ true, [[A2_TARGET_H1]] ], [ true, [[L1_TARGET_H1]] ], [ false, [[ENTRY_TARGET_A2]] ], [ true, [[ENTRY_TARGET_H1]] ]
+; CHECK-NEXT: br i1 [[GUARD_H1]], label [[H1:%.*]], label [[A2]]
+;
+entry:
+ switch i1 %Pred0, label %A2 [ i1 0, label %H1 ]
+
+H1:
+ switch i1 %Pred0, label %L1 []
+
+L1:
+ switch i1 %Pred1, label %H1 [ i1 0, label %A3 ]
+
+A3:
+ switch i1 %Pred2, label %A2 [ i1 0, label %exit ]
+
+A2:
+ switch i1 %Pred0, label %H1 []
+
+exit:
+ ret void
+}
+
define void @loop_irr_loop_shared_header(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3) {
; CHECK-LABEL: @loop_irr_loop_shared_header(
; CHECK-NEXT: entry:
@@ -565,19 +876,19 @@ define void @loop_irr_loop_shared_header_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2,
; CHECK-NEXT: to label [[H2:%.*]] []
; CHECK: H2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[H2_TARGET_A2:%.*]] [label %H2.target.H1]
+; CHECK-NEXT: to label [[H2_TARGET_A2:%.*]] [label [[H2_TARGET_H1:%.*]]]
; CHECK: H1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[A3:%.*]] [label %H1.target.H1]
+; CHECK-NEXT: to label [[A3:%.*]] [label [[H1_TARGET_H1:%.*]]]
; CHECK: A3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[A2:%.*]] [label %L2]
+; CHECK-NEXT: to label [[A2:%.*]] [label [[L2:%.*]]]
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[A2_TARGET_H1:%.*]] []
; CHECK: L2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED3:%.*]])
-; CHECK-NEXT: to label [[H2]] [label %exit]
+; CHECK-NEXT: to label [[H2]] [label [[EXIT:%.*]]]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: A2.target.H1:
@@ -589,7 +900,7 @@ define void @loop_irr_loop_shared_header_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2,
; CHECK: H2.target.H1:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_H1:%.*]] = phi i1 [ true, [[A2_TARGET_H1]] ], [ true, [[H1_TARGET_H1:%.*]] ], [ false, [[H2_TARGET_A2]] ], [ true, [[H2_TARGET_H1:%.*]] ]
+; CHECK-NEXT: [[GUARD_H1:%.*]] = phi i1 [ true, [[A2_TARGET_H1]] ], [ true, [[H1_TARGET_H1]] ], [ false, [[H2_TARGET_A2]] ], [ true, [[H2_TARGET_H1]] ]
; CHECK-NEXT: br i1 [[GUARD_H1]], label [[H1:%.*]], label [[A2]]
;
entry:
@@ -614,6 +925,66 @@ exit:
ret void
}
+define void @loop_irr_loop_shared_header_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3) {
+; CHECK-LABEL: @loop_irr_loop_shared_header_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[H2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: H2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[H2_TARGET_A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[H2_TARGET_H1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[A3:%.*]] [
+; CHECK-NEXT: i1 false, label [[H1_TARGET_H1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A3:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[A2:%.*]] [
+; CHECK-NEXT: i1 false, label [[L2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[A2_TARGET_H1:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: L2:
+; CHECK-NEXT: switch i1 [[PRED3:%.*]], label [[H2]] [
+; CHECK-NEXT: i1 false, label [[EXIT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A2.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: H1.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: H2.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: H2.target.H1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_H1:%.*]] = phi i1 [ true, [[A2_TARGET_H1]] ], [ true, [[H1_TARGET_H1]] ], [ false, [[H2_TARGET_A2]] ], [ true, [[H2_TARGET_H1]] ]
+; CHECK-NEXT: br i1 [[GUARD_H1]], label [[H1:%.*]], label [[A2]]
+;
+entry:
+ switch i1 %Pred0, label %H2 []
+
+H2:
+ switch i1 %Pred0, label %A2 [ i1 0, label %H1 ]
+
+H1:
+ switch i1 %Pred1, label %A3 [ i1 0, label %H1 ]
+
+A3:
+ switch i1 %Pred2, label %A2 [ i1 0, label %L2 ]
+
+A2:
+ switch i1 %Pred0, label %H1 []
+
+L2:
+ switch i1 %Pred3, label %H2 [ i1 0, label %exit ]
+
+exit:
+ ret void
+}
+
define void @siblings_top_level(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6) {
; CHECK-LABEL: @siblings_top_level(
; CHECK-NEXT: entry:
@@ -685,22 +1056,22 @@ define void @siblings_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK-LABEL: @siblings_top_level_callbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[H1:%.*]] [label %fork1]
+; CHECK-NEXT: to label [[H1:%.*]] [label [[FORK1:%.*]]]
; CHECK: H1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[H1_TARGET_A1:%.*]] [label %H1.target.A2]
+; CHECK-NEXT: to label [[H1_TARGET_A1:%.*]] [label [[H1_TARGET_A2:%.*]]]
; CHECK: A1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[A1_TARGET_A2:%.*]] []
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[A1:%.*]] [label %L1]
+; CHECK-NEXT: to label [[A1:%.*]] [label [[L1:%.*]]]
; CHECK: L1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED3:%.*]])
-; CHECK-NEXT: to label [[H1]] [label %exit]
+; CHECK-NEXT: to label [[H1]] [label [[EXIT:%.*]]]
; CHECK: fork1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED4:%.*]])
-; CHECK-NEXT: to label [[FORK1_TARGET_B1:%.*]] [label %fork1.target.B2]
+; CHECK-NEXT: to label [[FORK1_TARGET_B1:%.*]] [label [[FORK1_TARGET_B2:%.*]]]
; CHECK: B1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[H2:%.*]] []
@@ -709,10 +1080,10 @@ define void @siblings_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK-NEXT: to label [[L2:%.*]] []
; CHECK: L2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED5:%.*]])
-; CHECK-NEXT: to label [[H2]] [label %L2.target.B2]
+; CHECK-NEXT: to label [[H2]] [label [[L2_TARGET_B2:%.*]]]
; CHECK: B2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED6:%.*]])
-; CHECK-NEXT: to label [[B1:%.*]] [label %exit]
+; CHECK-NEXT: to label [[B1:%.*]] [label [[EXIT]]]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: A1.target.A2:
@@ -722,7 +1093,7 @@ define void @siblings_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK: H1.target.A2:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A1_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A1_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
; CHECK: L2.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
@@ -731,7 +1102,7 @@ define void @siblings_top_level_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred
; CHECK: fork1.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1]]
; CHECK: irr.guard1:
-; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[L2_TARGET_B2:%.*]] ], [ false, [[FORK1_TARGET_B1]] ], [ true, [[FORK1_TARGET_B2:%.*]] ]
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[L2_TARGET_B2]] ], [ false, [[FORK1_TARGET_B1]] ], [ true, [[FORK1_TARGET_B2]] ]
; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
;
entry:
@@ -768,6 +1139,100 @@ exit:
ret void
}
+define void @siblings_top_level_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6) {
+; CHECK-LABEL: @siblings_top_level_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[H1:%.*]] [
+; CHECK-NEXT: i1 false, label [[FORK1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[H1_TARGET_A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[H1_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[A1_TARGET_A2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[L1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: L1:
+; CHECK-NEXT: switch i1 [[PRED3:%.*]], label [[H1]] [
+; CHECK-NEXT: i1 false, label [[EXIT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: fork1:
+; CHECK-NEXT: switch i1 [[PRED4:%.*]], label [[FORK1_TARGET_B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[FORK1_TARGET_B2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[H2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: H2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[L2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: L2:
+; CHECK-NEXT: switch i1 [[PRED5:%.*]], label [[H2]] [
+; CHECK-NEXT: i1 false, label [[L2_TARGET_B2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B2:
+; CHECK-NEXT: switch i1 [[PRED6:%.*]], label [[B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[EXIT]]
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A1.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: H1.target.A1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: H1.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A1_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
+; CHECK: L2.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
+; CHECK: fork1.target.B1:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: fork1.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: irr.guard1:
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[L2_TARGET_B2]] ], [ false, [[FORK1_TARGET_B1]] ], [ true, [[FORK1_TARGET_B2]] ]
+; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
+;
+entry:
+ switch i1 %Pred0, label %H1 [ i1 0, label %fork1 ]
+
+H1:
+ switch i1 %Pred1, label %A1 [ i1 0, label %A2 ]
+
+A1:
+ switch i1 %Pred0, label %A2 []
+
+A2:
+ switch i1 %Pred2, label %A1 [ i1 0, label %L1 ]
+
+L1:
+ switch i1 %Pred3, label %H1 [ i1 0, label %exit ]
+
+fork1:
+ switch i1 %Pred4, label %B1 [ i1 0, label %B2 ]
+
+B1:
+ switch i1 %Pred0, label %H2 []
+
+H2:
+ switch i1 %Pred0, label %L2 []
+
+L2:
+ switch i1 %Pred5, label %H2 [ i1 0, label %B2 ]
+
+B2:
+ switch i1 %Pred6, label %B1 [ i1 0, label %exit ]
+
+exit:
+ ret void
+}
+
define void @siblings_in_loop(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6, i1 %Pred7) {
; CHECK-LABEL: @siblings_in_loop(
; CHECK-NEXT: entry:
@@ -852,22 +1317,22 @@ define void @siblings_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3,
; CHECK-NEXT: to label [[H0:%.*]] []
; CHECK: H0:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[H1:%.*]] [label %fork1]
+; CHECK-NEXT: to label [[H1:%.*]] [label [[FORK1:%.*]]]
; CHECK: H1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[H1_TARGET_A1:%.*]] [label %H1.target.A2]
+; CHECK-NEXT: to label [[H1_TARGET_A1:%.*]] [label [[H1_TARGET_A2:%.*]]]
; CHECK: A1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[A1_TARGET_A2:%.*]] []
; CHECK: A2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[A1:%.*]] [label %L1]
+; CHECK-NEXT: to label [[A1:%.*]] [label [[L1:%.*]]]
; CHECK: L1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED3:%.*]])
-; CHECK-NEXT: to label [[H1]] [label %L0]
+; CHECK-NEXT: to label [[H1]] [label [[L0:%.*]]]
; CHECK: fork1:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED4:%.*]])
-; CHECK-NEXT: to label [[FORK1_TARGET_B1:%.*]] [label %fork1.target.B2]
+; CHECK-NEXT: to label [[FORK1_TARGET_B1:%.*]] [label [[FORK1_TARGET_B2:%.*]]]
; CHECK: B1:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[H2:%.*]] []
@@ -876,13 +1341,13 @@ define void @siblings_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3,
; CHECK-NEXT: to label [[L2:%.*]] []
; CHECK: L2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED5:%.*]])
-; CHECK-NEXT: to label [[H2]] [label %L2.target.B2]
+; CHECK-NEXT: to label [[H2]] [label [[L2_TARGET_B2:%.*]]]
; CHECK: B2:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED6:%.*]])
-; CHECK-NEXT: to label [[B1:%.*]] [label %L0]
+; CHECK-NEXT: to label [[B1:%.*]] [label [[L0]]]
; CHECK: L0:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED7:%.*]])
-; CHECK-NEXT: to label [[EXIT:%.*]] [label %H0]
+; CHECK-NEXT: to label [[EXIT:%.*]] [label [[H0]]]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: A1.target.A2:
@@ -892,7 +1357,7 @@ define void @siblings_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3,
; CHECK: H1.target.A2:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A1_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A1_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
; CHECK: L2.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
@@ -901,7 +1366,7 @@ define void @siblings_in_loop_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3,
; CHECK: fork1.target.B2:
; CHECK-NEXT: br label [[IRR_GUARD1]]
; CHECK: irr.guard1:
-; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[L2_TARGET_B2:%.*]] ], [ false, [[FORK1_TARGET_B1]] ], [ true, [[FORK1_TARGET_B2:%.*]] ]
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[L2_TARGET_B2]] ], [ false, [[FORK1_TARGET_B1]] ], [ true, [[FORK1_TARGET_B2]] ]
; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
;
entry:
@@ -944,6 +1409,113 @@ exit:
ret void
}
+define void @siblings_in_loop_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6, i1 %Pred7) {
+; CHECK-LABEL: @siblings_in_loop_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[H0:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: H0:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[H1:%.*]] [
+; CHECK-NEXT: i1 false, label [[FORK1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: H1:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[H1_TARGET_A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[H1_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[A1_TARGET_A2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[A1:%.*]] [
+; CHECK-NEXT: i1 false, label [[L1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: L1:
+; CHECK-NEXT: switch i1 [[PRED3:%.*]], label [[H1]] [
+; CHECK-NEXT: i1 false, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: fork1:
+; CHECK-NEXT: switch i1 [[PRED4:%.*]], label [[FORK1_TARGET_B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[FORK1_TARGET_B2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B1:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[H2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: H2:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[L2:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: L2:
+; CHECK-NEXT: switch i1 [[PRED5:%.*]], label [[H2]] [
+; CHECK-NEXT: i1 false, label [[L2_TARGET_B2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B2:
+; CHECK-NEXT: switch i1 [[PRED6:%.*]], label [[B1:%.*]] [
+; CHECK-NEXT: i1 false, label [[L0]]
+; CHECK-NEXT: ]
+; CHECK: L0:
+; CHECK-NEXT: switch i1 [[PRED7:%.*]], label [[EXIT:%.*]] [
+; CHECK-NEXT: i1 false, label [[H0]]
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A1.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: H1.target.A1:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: H1.target.A2:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_A2:%.*]] = phi i1 [ true, [[A1_TARGET_A2]] ], [ false, [[H1_TARGET_A1]] ], [ true, [[H1_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A2]], label [[A2:%.*]], label [[A1]]
+; CHECK: L2.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1:%.*]]
+; CHECK: fork1.target.B1:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: fork1.target.B2:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: irr.guard1:
+; CHECK-NEXT: [[GUARD_B2:%.*]] = phi i1 [ true, [[L2_TARGET_B2]] ], [ false, [[FORK1_TARGET_B1]] ], [ true, [[FORK1_TARGET_B2]] ]
+; CHECK-NEXT: br i1 [[GUARD_B2]], label [[B2:%.*]], label [[B1]]
+;
+entry:
+ switch i1 %Pred0, label %H0 []
+
+H0:
+ switch i1 %Pred0, label %H1 [ i1 0, label %fork1 ]
+
+H1:
+ switch i1 %Pred1, label %A1 [ i1 0, label %A2 ]
+
+A1:
+ switch i1 %Pred0, label %A2 []
+
+A2:
+ switch i1 %Pred2, label %A1 [ i1 0, label %L1 ]
+
+L1:
+ switch i1 %Pred3, label %H1 [ i1 0, label %L0 ]
+
+fork1:
+ switch i1 %Pred4, label %B1 [ i1 0, label %B2 ]
+
+B1:
+ switch i1 %Pred0, label %H2 []
+
+H2:
+ switch i1 %Pred0, label %L2 []
+
+L2:
+ switch i1 %Pred5, label %H2 [ i1 0, label %B2 ]
+
+B2:
+ switch i1 %Pred6, label %B1 [ i1 0, label %L0 ]
+
+L0:
+ switch i1 %Pred7, label %exit [ i1 0, label %H0 ]
+
+exit:
+ ret void
+}
+
define void @irr_in_irr_shared_entry(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6, i1 %Pred7, i1 %Pred8, i1 %Pred9, i1 %Pred10, i1 %Pred11, i1 %Pred12, i1 %Pred13) {
; CHECK-LABEL: @irr_in_irr_shared_entry(
; CHECK-NEXT: entry:
@@ -1063,10 +1635,10 @@ define void @irr_in_irr_shared_entry_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1
; CHECK-LABEL: @irr_in_irr_shared_entry_callbr(
; CHECK-NEXT: entry:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED0:%.*]])
-; CHECK-NEXT: to label [[IF_END:%.*]] [label %if.then]
+; CHECK-NEXT: to label [[IF_END:%.*]] [label [[IF_THEN:%.*]]]
; CHECK: if.end:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED1:%.*]])
-; CHECK-NEXT: to label [[IF_THEN7:%.*]] [label %if.else]
+; CHECK-NEXT: to label [[IF_THEN7:%.*]] [label [[IF_ELSE:%.*]]]
; CHECK: if.then7:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[IF_END16:%.*]] []
@@ -1075,49 +1647,49 @@ define void @irr_in_irr_shared_entry_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1
; CHECK-NEXT: to label [[IF_END16]] []
; CHECK: if.end16:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED2:%.*]])
-; CHECK-NEXT: to label [[WHILE_COND_PREHEADER:%.*]] [label %if.then39]
+; CHECK-NEXT: to label [[WHILE_COND_PREHEADER:%.*]] [label [[IF_THEN39:%.*]]]
; CHECK: while.cond.preheader:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[WHILE_COND:%.*]] []
; CHECK: while.cond:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED3:%.*]])
-; CHECK-NEXT: to label [[WHILE_COND_TARGET_COND_TRUE49:%.*]] [label %lor.rhs]
+; CHECK-NEXT: to label [[WHILE_COND_TARGET_COND_TRUE49:%.*]] [label [[LOR_RHS:%.*]]]
; CHECK: cond.true49:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED4:%.*]])
-; CHECK-NEXT: to label [[IF_THEN69:%.*]] [label %cond.true49.target.while.body63]
+; CHECK-NEXT: to label [[IF_THEN69:%.*]] [label [[COND_TRUE49_TARGET_WHILE_BODY63:%.*]]]
; CHECK: while.body63:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED5:%.*]])
-; CHECK-NEXT: to label [[EXIT:%.*]] [label %while.cond47]
+; CHECK-NEXT: to label [[EXIT:%.*]] [label [[WHILE_COND47:%.*]]]
; CHECK: while.cond47:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED6:%.*]])
-; CHECK-NEXT: to label [[COND_TRUE49:%.*]] [label %while.cond47.target.cond.end61]
+; CHECK-NEXT: to label [[COND_TRUE49:%.*]] [label [[WHILE_COND47_TARGET_COND_END61:%.*]]]
; CHECK: cond.end61:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED7:%.*]])
-; CHECK-NEXT: to label [[COND_END61_TARGET_WHILE_BODY63:%.*]] [label %while.cond]
+; CHECK-NEXT: to label [[COND_END61_TARGET_WHILE_BODY63:%.*]] [label [[WHILE_COND]]]
; CHECK: if.then69:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED8:%.*]])
-; CHECK-NEXT: to label [[EXIT]] [label %while.cond]
+; CHECK-NEXT: to label [[EXIT]] [label [[WHILE_COND]]]
; CHECK: lor.rhs:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED9:%.*]])
-; CHECK-NEXT: to label [[LOR_RHS_TARGET_COND_END61:%.*]] [label %while.end76]
+; CHECK-NEXT: to label [[LOR_RHS_TARGET_COND_END61:%.*]] [label [[WHILE_END76:%.*]]]
; CHECK: while.end76:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[EXIT]] []
; CHECK: if.then39:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED10:%.*]])
-; CHECK-NEXT: to label [[EXIT]] [label %if.end.i145]
+; CHECK-NEXT: to label [[EXIT]] [label [[IF_END_I145:%.*]]]
; CHECK: if.end.i145:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED11:%.*]])
-; CHECK-NEXT: to label [[EXIT]] [label %if.end8.i149]
+; CHECK-NEXT: to label [[EXIT]] [label [[IF_END8_I149:%.*]]]
; CHECK: if.end8.i149:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[EXIT]] []
; CHECK: if.then:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED12:%.*]])
-; CHECK-NEXT: to label [[EXIT]] [label %if.end.i]
+; CHECK-NEXT: to label [[EXIT]] [label [[IF_END_I:%.*]]]
; CHECK: if.end.i:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED13:%.*]])
-; CHECK-NEXT: to label [[EXIT]] [label %if.end8.i]
+; CHECK-NEXT: to label [[EXIT]] [label [[IF_END8_I:%.*]]]
; CHECK: if.end8.i:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[EXIT]] []
@@ -1130,14 +1702,14 @@ define void @irr_in_irr_shared_entry_callbr(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1
; CHECK: while.cond.target.cond.true49:
; CHECK-NEXT: br label [[IRR_GUARD]]
; CHECK: irr.guard:
-; CHECK-NEXT: [[GUARD_COND_END61:%.*]] = phi i1 [ true, [[WHILE_COND47_TARGET_COND_END61:%.*]] ], [ true, [[LOR_RHS_TARGET_COND_END61]] ], [ false, [[WHILE_COND_TARGET_COND_TRUE49]] ]
+; CHECK-NEXT: [[GUARD_COND_END61:%.*]] = phi i1 [ true, [[WHILE_COND47_TARGET_COND_END61]] ], [ true, [[LOR_RHS_TARGET_COND_END61]] ], [ false, [[WHILE_COND_TARGET_COND_TRUE49]] ]
; CHECK-NEXT: br i1 [[GUARD_COND_END61]], label [[COND_END61:%.*]], label [[IRR_GUARD1:%.*]]
; CHECK: cond.true49.target.while.body63:
; CHECK-NEXT: br label [[IRR_GUARD1]]
; CHECK: cond.end61.target.while.body63:
; CHECK-NEXT: br label [[IRR_GUARD1]]
; CHECK: irr.guard1:
-; CHECK-NEXT: [[GUARD_WHILE_BODY63:%.*]] = phi i1 [ true, [[COND_TRUE49_TARGET_WHILE_BODY63:%.*]] ], [ true, [[COND_END61_TARGET_WHILE_BODY63]] ], [ false, [[IRR_GUARD]] ]
+; CHECK-NEXT: [[GUARD_WHILE_BODY63:%.*]] = phi i1 [ true, [[COND_TRUE49_TARGET_WHILE_BODY63]] ], [ true, [[COND_END61_TARGET_WHILE_BODY63]] ], [ false, [[IRR_GUARD]] ]
; CHECK-NEXT: br i1 [[GUARD_WHILE_BODY63]], label [[WHILE_BODY63:%.*]], label [[COND_TRUE49]]
;
entry:
@@ -1203,3 +1775,162 @@ if.end8.i:
exit:
ret void
}
+
+define void @irr_in_irr_shared_entry_switch(i1 %Pred0, i1 %Pred1, i1 %Pred2, i1 %Pred3, i1 %Pred4, i1 %Pred5, i1 %Pred6, i1 %Pred7, i1 %Pred8, i1 %Pred9, i1 %Pred10, i1 %Pred11, i1 %Pred12, i1 %Pred13) {
+; CHECK-LABEL: @irr_in_irr_shared_entry_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED0:%.*]], label [[IF_END:%.*]] [
+; CHECK-NEXT: i1 false, label [[IF_THEN:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.end:
+; CHECK-NEXT: switch i1 [[PRED1:%.*]], label [[IF_THEN7:%.*]] [
+; CHECK-NEXT: i1 false, label [[IF_ELSE:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.then7:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[IF_END16:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: if.else:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[IF_END16]] [
+; CHECK-NEXT: ]
+; CHECK: if.end16:
+; CHECK-NEXT: switch i1 [[PRED2:%.*]], label [[WHILE_COND_PREHEADER:%.*]] [
+; CHECK-NEXT: i1 false, label [[IF_THEN39:%.*]]
+; CHECK-NEXT: ]
+; CHECK: while.cond.preheader:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[WHILE_COND:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: while.cond:
+; CHECK-NEXT: switch i1 [[PRED3:%.*]], label [[WHILE_COND_TARGET_COND_TRUE49:%.*]] [
+; CHECK-NEXT: i1 false, label [[LOR_RHS:%.*]]
+; CHECK-NEXT: ]
+; CHECK: cond.true49:
+; CHECK-NEXT: switch i1 [[PRED4:%.*]], label [[IF_THEN69:%.*]] [
+; CHECK-NEXT: i1 false, label [[COND_TRUE49_TARGET_WHILE_BODY63:%.*]]
+; CHECK-NEXT: ]
+; CHECK: while.body63:
+; CHECK-NEXT: switch i1 [[PRED5:%.*]], label [[EXIT:%.*]] [
+; CHECK-NEXT: i1 false, label [[WHILE_COND47:%.*]]
+; CHECK-NEXT: ]
+; CHECK: while.cond47:
+; CHECK-NEXT: switch i1 [[PRED6:%.*]], label [[COND_TRUE49:%.*]] [
+; CHECK-NEXT: i1 false, label [[WHILE_COND47_TARGET_COND_END61:%.*]]
+; CHECK-NEXT: ]
+; CHECK: cond.end61:
+; CHECK-NEXT: switch i1 [[PRED7:%.*]], label [[COND_END61_TARGET_WHILE_BODY63:%.*]] [
+; CHECK-NEXT: i1 false, label [[WHILE_COND]]
+; CHECK-NEXT: ]
+; CHECK: if.then69:
+; CHECK-NEXT: switch i1 [[PRED8:%.*]], label [[EXIT]] [
+; CHECK-NEXT: i1 false, label [[WHILE_COND]]
+; CHECK-NEXT: ]
+; CHECK: lor.rhs:
+; CHECK-NEXT: switch i1 [[PRED9:%.*]], label [[LOR_RHS_TARGET_COND_END61:%.*]] [
+; CHECK-NEXT: i1 false, label [[WHILE_END76:%.*]]
+; CHECK-NEXT: ]
+; CHECK: while.end76:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[EXIT]] [
+; CHECK-NEXT: ]
+; CHECK: if.then39:
+; CHECK-NEXT: switch i1 [[PRED10:%.*]], label [[EXIT]] [
+; CHECK-NEXT: i1 false, label [[IF_END_I145:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.end.i145:
+; CHECK-NEXT: switch i1 [[PRED11:%.*]], label [[EXIT]] [
+; CHECK-NEXT: i1 false, label [[IF_END8_I149:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.end8.i149:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[EXIT]] [
+; CHECK-NEXT: ]
+; CHECK: if.then:
+; CHECK-NEXT: switch i1 [[PRED12:%.*]], label [[EXIT]] [
+; CHECK-NEXT: i1 false, label [[IF_END_I:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.end.i:
+; CHECK-NEXT: switch i1 [[PRED13:%.*]], label [[EXIT]] [
+; CHECK-NEXT: i1 false, label [[IF_END8_I:%.*]]
+; CHECK-NEXT: ]
+; CHECK: if.end8.i:
+; CHECK-NEXT: switch i1 [[PRED0]], label [[EXIT]] [
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: while.cond47.target.cond.end61:
+; CHECK-NEXT: br label [[IRR_GUARD:%.*]]
+; CHECK: lor.rhs.target.cond.end61:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: while.cond.target.cond.true49:
+; CHECK-NEXT: br label [[IRR_GUARD]]
+; CHECK: irr.guard:
+; CHECK-NEXT: [[GUARD_COND_END61:%.*]] = phi i1 [ true, [[WHILE_COND47_TARGET_COND_END61]] ], [ true, [[LOR_RHS_TARGET_COND_END61]] ], [ false, [[WHILE_COND_TARGET_COND_TRUE49]] ]
+; CHECK-NEXT: br i1 [[GUARD_COND_END61]], label [[COND_END61:%.*]], label [[IRR_GUARD1:%.*]]
+; CHECK: cond.true49.target.while.body63:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: cond.end61.target.while.body63:
+; CHECK-NEXT: br label [[IRR_GUARD1]]
+; CHECK: irr.guard1:
+; CHECK-NEXT: [[GUARD_WHILE_BODY63:%.*]] = phi i1 [ true, [[COND_TRUE49_TARGET_WHILE_BODY63]] ], [ true, [[COND_END61_TARGET_WHILE_BODY63]] ], [ false, [[IRR_GUARD]] ]
+; CHECK-NEXT: br i1 [[GUARD_WHILE_BODY63]], label [[WHILE_BODY63:%.*]], label [[COND_TRUE49]]
+;
+entry:
+ switch i1 %Pred0, label %if.end [ i1 0, label %if.then ]
+
+if.end:
+ switch i1 %Pred1, label %if.then7 [ i1 0, label %if.else ]
+
+if.then7:
+ switch i1 %Pred0, label %if.end16 []
+
+if.else:
+ switch i1 %Pred0, label %if.end16 []
+
+if.end16:
+ switch i1 %Pred2, label %while.cond.preheader [ i1 0, label %if.then39 ]
+
+while.cond.preheader:
+ switch i1 %Pred0, label %while.cond []
+
+while.cond:
+ switch i1 %Pred3, label %cond.true49 [ i1 0, label %lor.rhs ]
+
+cond.true49:
+ switch i1 %Pred4, label %if.then69 [ i1 0, label %while.body63 ]
+
+while.body63:
+ switch i1 %Pred5, label %exit [ i1 0, label %while.cond47 ]
+
+while.cond47:
+ switch i1 %Pred6, label %cond.true49 [ i1 0, label %cond.end61 ]
+
+cond.end61:
+ switch i1 %Pred7, label %while.body63 [ i1 0, label %while.cond ]
+
+if.then69:
+ switch i1 %Pred8, label %exit [ i1 0, label %while.cond ]
+
+lor.rhs:
+ switch i1 %Pred9, label %cond.end61 [ i1 0, label %while.end76 ]
+
+while.end76:
+ switch i1 %Pred0, label %exit []
+
+if.then39:
+ switch i1 %Pred10, label %exit [ i1 0, label %if.end.i145 ]
+
+if.end.i145:
+ switch i1 %Pred11, label %exit [ i1 0, label %if.end8.i149 ]
+
+if.end8.i149:
+ switch i1 %Pred0, label %exit []
+
+if.then:
+ switch i1 %Pred12, label %exit [ i1 0, label %if.end.i ]
+
+if.end.i:
+ switch i1 %Pred13, label %exit [ i1 0, label %if.end8.i ]
+
+if.end8.i:
+ switch i1 %Pred0, label %exit []
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/FixIrreducible/unreachable.ll b/llvm/test/Transforms/FixIrreducible/unreachable.ll
index 845cf507c7fc0..02a23a97380f8 100644
--- a/llvm/test/Transforms/FixIrreducible/unreachable.ll
+++ b/llvm/test/Transforms/FixIrreducible/unreachable.ll
@@ -48,3 +48,26 @@ loop.latch:
loop.exit:
ret void
}
+
+; CHECK-LABEL: @unreachable_switch(
+; CHECK: entry:
+; CHECK-NOT: irr.guard:
+define void @unreachable_switch(i32 %n, i1 %arg) {
+entry:
+ switch i32 %n, label %loop.body []
+
+loop.body:
+ switch i32 %n, label %inner.block []
+
+unreachable.block:
+ switch i32 %n, label %inner.block []
+
+inner.block:
+ switch i1 %arg, label %loop.exit [ i1 0, label %loop.latch ]
+
+loop.latch:
+ switch i32 %n, label %loop.body []
+
+loop.exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll b/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll
index 02a4ec802cd33..80711a8ddaedf 100644
--- a/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll
+++ b/llvm/test/Transforms/FixIrreducible/unsupported-terminator.ll
@@ -13,4 +13,4 @@ then:
else:
br label %then
-}
\ No newline at end of file
+}
diff --git a/llvm/test/Transforms/UnifyLoopExits/basic.ll b/llvm/test/Transforms/UnifyLoopExits/basic.ll
index 5771453574626..461bd3ee9c23d 100644
--- a/llvm/test/Transforms/UnifyLoopExits/basic.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/basic.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes='lower-switch,unify-loop-exits' -S | FileCheck %s
+; RUN: opt < %s -passes='unify-loop-exits' -S | FileCheck %s
define void @loop_1(i1 %PredEntry, i1 %PredB, i1 %PredC, i1 %PredD) {
; CHECK-LABEL: @loop_1(
@@ -73,7 +73,7 @@ define void @loop_1_callbr(i1 %PredEntry, i1 %PredB, i1 %PredC, i1 %PredD) {
; CHECK: F:
; CHECK-NEXT: br label [[EXIT]]
; CHECK: G:
-; CHECK-NEXT: br label [[Y:%.*]]
+; CHECK-NEXT: br label [[F:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: B.target.E:
@@ -83,8 +83,8 @@ define void @loop_1_callbr(i1 %PredEntry, i1 %PredB, i1 %PredC, i1 %PredD) {
; CHECK: D.target.F:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[GUARD_X:%.*]] = phi i1 [ true, [[B_TARGET_E]] ], [ false, [[C_TARGET_F]] ], [ false, [[D_TARGET_F]] ]
-; CHECK-NEXT: br i1 [[GUARD_X]], label [[X:%.*]], label [[Y]]
+; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[B_TARGET_E]] ], [ false, [[C_TARGET_F]] ], [ false, [[D_TARGET_F]] ]
+; CHECK-NEXT: br i1 [[GUARD_E]], label [[E:%.*]], label [[F]]
;
entry:
br i1 %PredEntry, label %A, label %G
@@ -114,6 +114,72 @@ exit:
ret void
}
+define void @loop_1_switch(i1 %PredEntry, i1 %PredB, i1 %PredC, i1 %PredD) {
+; CHECK-LABEL: @loop_1_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: i1 false, label [[G:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: br label [[B:%.*]]
+; CHECK: B:
+; CHECK-NEXT: switch i1 [[PREDB:%.*]], label [[C:%.*]] [
+; CHECK-NEXT: i1 false, label [[B_TARGET_E:%.*]]
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: switch i1 [[PREDC:%.*]], label [[D:%.*]] [
+; CHECK-NEXT: i1 false, label [[C_TARGET_F:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: switch i1 [[PREDD:%.*]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[D_TARGET_F:%.*]]
+; CHECK-NEXT: ]
+; CHECK: E:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: F:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: G:
+; CHECK-NEXT: br label [[F:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: B.target.E:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: C.target.F:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: D.target.F:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[B_TARGET_E]] ], [ false, [[C_TARGET_F]] ], [ false, [[D_TARGET_F]] ]
+; CHECK-NEXT: br i1 [[GUARD_E]], label [[E:%.*]], label [[F]]
+;
+entry:
+ switch i1 %PredEntry, label %A [ i1 0, label %G ]
+
+A:
+ br label %B
+
+B:
+ switch i1 %PredB, label %C [ i1 0, label %E ]
+
+C:
+ switch i1 %PredC, label %D [ i1 0, label %F ]
+
+D:
+ switch i1 %PredD, label %A [ i1 0, label %F ]
+
+E:
+ br label %exit
+
+F:
+ br label %exit
+
+G:
+ br label %F
+
+exit:
+ ret void
+}
+
define void @loop_2(i1 %PredA, i1 %PredB, i1 %PredC) {
; CHECK-LABEL: @loop_2(
; CHECK-NEXT: entry:
@@ -233,6 +299,73 @@ exit:
ret void
}
+define void @loop_2_switch(i1 %PredA, i1 %PredB, i1 %PredC) {
+; CHECK-LABEL: @loop_2_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[A:%.*]]
+; CHECK: A:
+; CHECK-NEXT: switch i1 [[PREDA:%.*]], label [[B:%.*]] [
+; CHECK-NEXT: i1 false, label [[A_TARGET_X:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: switch i1 [[PREDB:%.*]], label [[C:%.*]] [
+; CHECK-NEXT: i1 false, label [[B_TARGET_Y:%.*]]
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: switch i1 [[PREDC:%.*]], label [[D:%.*]] [
+; CHECK-NEXT: i1 false, label [[C_TARGET_Z:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: br label [[A]]
+; CHECK: X:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: Y:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: Z:
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: A.target.X:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: B.target.Y:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: C.target.Z:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[GUARD_X:%.*]] = phi i1 [ true, [[A_TARGET_X]] ], [ false, [[B_TARGET_Y]] ], [ false, [[C_TARGET_Z]] ]
+; CHECK-NEXT: [[GUARD_Y:%.*]] = phi i1 [ false, [[A_TARGET_X]] ], [ true, [[B_TARGET_Y]] ], [ false, [[C_TARGET_Z]] ]
+; CHECK-NEXT: br i1 [[GUARD_X]], label [[X:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
+; CHECK: loop.exit.guard1:
+; CHECK-NEXT: br i1 [[GUARD_Y]], label [[Y:%.*]], label [[Z:%.*]]
+;
+entry:
+ br label %A
+
+A:
+ switch i1 %PredA, label %B [ i1 0, label %X ]
+
+B:
+ switch i1 %PredB, label %C [ i1 0, label %Y ]
+
+C:
+ switch i1 %PredC, label %D [ i1 0, label %Z ]
+
+D:
+ br label %A
+
+X:
+ br label %exit
+
+Y:
+ br label %exit
+
+Z:
+ br label %exit
+
+exit:
+ ret void
+}
+
; Test that UnifyLoopExits handles callbr with duplicate successors correctly.
; The exit block appears twice as a successor of the callbr instruction.
define void @callbr_duplicate_successors() {
@@ -257,3 +390,32 @@ loop:
exit:
ret void
}
+
+define void @switch_duplicate_successors(i1 %Pred) {
+; CHECK-LABEL: @switch_duplicate_successors(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: switch i1 [[PRED:%.*]], label [[LOOP_TARGET_EXIT:%.*]] [
+; CHECK-NEXT: i1 false, label [[LOOP]]
+; CHECK-NEXT: i1 true, label [[LOOP_TARGET_EXIT1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+; CHECK: loop.target.exit:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: loop.target.exit1:
+; CHECK-NEXT: br label [[EXIT]]
+;
+entry:
+ br label %loop
+
+loop:
+ switch i1 %Pred, label %exit [
+ i1 0, label %loop
+ i1 1, label %exit
+ ]
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/UnifyLoopExits/integer_guards.ll b/llvm/test/Transforms/UnifyLoopExits/integer_guards.ll
index be982d5d043f9..5cded19c06d97 100644
--- a/llvm/test/Transforms/UnifyLoopExits/integer_guards.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/integer_guards.ll
@@ -78,7 +78,7 @@ define void @loop_two_exits_callbr(i1 %PredEntry, i1 %PredA) {
; CHECK: A:
; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA:%.*]])
-; CHECK-NEXT: to label [[A_TARGET_B:%.*]] [label %C]
+; CHECK-NEXT: to label [[A_TARGET_B:%.*]] [label [[C]]]
; CHECK: B:
; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
; CHECK-NEXT: br label [[D:%.*]]
@@ -86,7 +86,7 @@ define void @loop_two_exits_callbr(i1 %PredEntry, i1 %PredA) {
; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; CHECK-NEXT: to label [[A]] [label %C.target.E]
+; CHECK-NEXT: to label [[A]] [label [[C_TARGET_E:%.*]]]
; CHECK: D:
; CHECK-NEXT: unreachable
; CHECK: E:
@@ -96,7 +96,7 @@ define void @loop_two_exits_callbr(i1 %PredEntry, i1 %PredA) {
; CHECK: C.target.E:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A_TARGET_B]] ], [ 1, [[C_TARGET_E:%.*]] ]
+; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A_TARGET_B]] ], [ 1, [[C_TARGET_E]] ]
; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[E]]
;
@@ -106,7 +106,7 @@ define void @loop_two_exits_callbr(i1 %PredEntry, i1 %PredA) {
; BOOLEAN: A:
; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA:%.*]])
-; BOOLEAN-NEXT: to label [[A_TARGET_B:%.*]] [label %C]
+; BOOLEAN-NEXT: to label [[A_TARGET_B:%.*]] [label [[C]]]
; BOOLEAN: B:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[D:%.*]]
@@ -114,7 +114,7 @@ define void @loop_two_exits_callbr(i1 %PredEntry, i1 %PredA) {
; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1
; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; BOOLEAN-NEXT: to label [[A]] [label %C.target.E]
+; BOOLEAN-NEXT: to label [[A]] [label [[C_TARGET_E:%.*]]]
; BOOLEAN: D:
; BOOLEAN-NEXT: unreachable
; BOOLEAN: E:
@@ -124,7 +124,7 @@ define void @loop_two_exits_callbr(i1 %PredEntry, i1 %PredA) {
; BOOLEAN: C.target.E:
; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
; BOOLEAN: loop.exit.guard:
-; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A_TARGET_B]] ], [ false, [[C_TARGET_E:%.*]] ]
+; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A_TARGET_B]] ], [ false, [[C_TARGET_E]] ]
; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[E]]
;
entry:
@@ -150,6 +150,93 @@ E:
ret void
}
+define void @loop_two_exits_switch(i1 %PredEntry, i1 %PredA) {
+; CHECK-LABEL: @loop_two_exits_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: i1 false, label [[E:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
+; CHECK-NEXT: switch i1 [[PREDA:%.*]], label [[A_TARGET_B:%.*]] [
+; CHECK-NEXT: i1 false, label [[C]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
+; CHECK-NEXT: br label [[D:%.*]]
+; CHECK: C:
+; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
+; CHECK-NEXT: switch i1 [[CMP]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[C_TARGET_E:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: unreachable
+; CHECK: E:
+; CHECK-NEXT: ret void
+; CHECK: A.target.B:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: C.target.E:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A_TARGET_B]] ], [ 1, [[C_TARGET_E]] ]
+; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
+; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[E]]
+;
+; BOOLEAN-LABEL: @loop_two_exits_switch(
+; BOOLEAN-NEXT: entry:
+; BOOLEAN-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[E:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: A:
+; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
+; BOOLEAN-NEXT: switch i1 [[PREDA:%.*]], label [[A_TARGET_B:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[C]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: B:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[D:%.*]]
+; BOOLEAN: C:
+; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1
+; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
+; BOOLEAN-NEXT: switch i1 [[CMP]], label [[A]] [
+; BOOLEAN-NEXT: i1 false, label [[C_TARGET_E:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: D:
+; BOOLEAN-NEXT: unreachable
+; BOOLEAN: E:
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: A.target.B:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; BOOLEAN: C.target.E:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: loop.exit.guard:
+; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A_TARGET_B]] ], [ false, [[C_TARGET_E]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[E]]
+;
+entry:
+ switch i1 %PredEntry, label %A [ i1 0, label %E ]
+
+A:
+ %inc1 = phi i32 [ 0, %entry ], [ %inc2, %C ]
+ switch i1 %PredA, label %B [ i1 0, label %C ]
+
+B:
+ tail call fastcc void @check(i32 1) #0
+ br label %D
+
+C:
+ %inc2 = add i32 %inc1, 1
+ %cmp = icmp ult i32 %inc2, 10
+ switch i1 %cmp, label %A [ i1 0, label %E ]
+
+D:
+ unreachable
+
+E:
+ ret void
+}
+
; The loop exit blocks appear in an inner loop.
define void @inner_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) {
@@ -285,13 +372,13 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; CHECK: B:
; CHECK-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA:%.*]])
-; CHECK-NEXT: to label [[D:%.*]] [label %B.target.B.target.C]
+; CHECK-NEXT: to label [[D:%.*]] [label [[B_TARGET_B_TARGET_C:%.*]]]
; CHECK: C:
; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
; CHECK-NEXT: br label [[H:%.*]]
; CHECK: D:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDB:%.*]])
-; CHECK-NEXT: to label [[D_TARGET_D_TARGET_E:%.*]] [label %F]
+; CHECK-NEXT: to label [[D_TARGET_D_TARGET_E:%.*]] [label [[F]]]
; CHECK: E:
; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
; CHECK-NEXT: br label [[H]]
@@ -299,12 +386,12 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; CHECK-NEXT: [[INNER2]] = add i32 [[INNER1]], 1
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP1]])
-; CHECK-NEXT: to label [[B]] [label %F.target.G]
+; CHECK-NEXT: to label [[B]] [label [[F_TARGET_G:%.*]]]
; CHECK: G:
; CHECK-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP2]])
-; CHECK-NEXT: to label [[A]] [label %G.target.I]
+; CHECK-NEXT: to label [[A]] [label [[G_TARGET_I:%.*]]]
; CHECK: H:
; CHECK-NEXT: unreachable
; CHECK: I:
@@ -316,7 +403,7 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; CHECK: G.target.I:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[B_TARGET_C:%.*]] ], [ 1, [[D_TARGET_E:%.*]] ], [ 2, [[G_TARGET_I:%.*]] ]
+; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[B_TARGET_C:%.*]] ], [ 1, [[D_TARGET_E:%.*]] ], [ 2, [[G_TARGET_I]] ]
; CHECK-NEXT: [[C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
; CHECK-NEXT: br i1 [[C_PREDICATE]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
; CHECK: loop.exit.guard1:
@@ -329,7 +416,7 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; CHECK: F.target.G:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD2]]
; CHECK: loop.exit.guard2:
-; CHECK-NEXT: [[MERGED_BB_IDX4:%.*]] = phi i32 [ 0, [[B_TARGET_B_TARGET_C:%.*]] ], [ 1, [[D_TARGET_D_TARGET_E]] ], [ 2, [[F_TARGET_G:%.*]] ]
+; CHECK-NEXT: [[MERGED_BB_IDX4:%.*]] = phi i32 [ 0, [[B_TARGET_B_TARGET_C]] ], [ 1, [[D_TARGET_D_TARGET_E]] ], [ 2, [[F_TARGET_G]] ]
; CHECK-NEXT: [[B_TARGET_C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX4]], 0
; CHECK-NEXT: br i1 [[B_TARGET_C_PREDICATE]], label [[B_TARGET_C]], label [[LOOP_EXIT_GUARD3:%.*]]
; CHECK: loop.exit.guard3:
@@ -345,13 +432,13 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; BOOLEAN: B:
; BOOLEAN-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA:%.*]])
-; BOOLEAN-NEXT: to label [[D:%.*]] [label %B.target.B.target.C]
+; BOOLEAN-NEXT: to label [[D:%.*]] [label [[B_TARGET_B_TARGET_C:%.*]]]
; BOOLEAN: C:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[H:%.*]]
; BOOLEAN: D:
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDB:%.*]])
-; BOOLEAN-NEXT: to label [[D_TARGET_D_TARGET_E:%.*]] [label %F]
+; BOOLEAN-NEXT: to label [[D_TARGET_D_TARGET_E:%.*]] [label [[F]]]
; BOOLEAN: E:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[H]]
@@ -359,12 +446,12 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; BOOLEAN-NEXT: [[INNER2]] = add i32 [[INNER1]], 1
; BOOLEAN-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[CMP1]])
-; BOOLEAN-NEXT: to label [[B]] [label %F.target.G]
+; BOOLEAN-NEXT: to label [[B]] [label [[F_TARGET_G:%.*]]]
; BOOLEAN: G:
; BOOLEAN-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1
; BOOLEAN-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[CMP2]])
-; BOOLEAN-NEXT: to label [[A]] [label %G.target.I]
+; BOOLEAN-NEXT: to label [[A]] [label [[G_TARGET_I:%.*]]]
; BOOLEAN: H:
; BOOLEAN-NEXT: unreachable
; BOOLEAN: I:
@@ -376,7 +463,7 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; BOOLEAN: G.target.I:
; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
; BOOLEAN: loop.exit.guard:
-; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, [[B_TARGET_C:%.*]] ], [ false, [[D_TARGET_E:%.*]] ], [ false, [[G_TARGET_I:%.*]] ]
+; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, [[B_TARGET_C:%.*]] ], [ false, [[D_TARGET_E:%.*]] ], [ false, [[G_TARGET_I]] ]
; BOOLEAN-NEXT: [[GUARD_E:%.*]] = phi i1 [ false, [[B_TARGET_C]] ], [ true, [[D_TARGET_E]] ], [ false, [[G_TARGET_I]] ]
; BOOLEAN-NEXT: br i1 [[GUARD_C]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
; BOOLEAN: loop.exit.guard1:
@@ -388,7 +475,7 @@ define void @inner_loop_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB) {
; BOOLEAN: F.target.G:
; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD2]]
; BOOLEAN: loop.exit.guard2:
-; BOOLEAN-NEXT: [[GUARD_B_TARGET_C:%.*]] = phi i1 [ true, [[B_TARGET_B_TARGET_C:%.*]] ], [ false, [[D_TARGET_D_TARGET_E]] ], [ false, [[F_TARGET_G:%.*]] ]
+; BOOLEAN-NEXT: [[GUARD_B_TARGET_C:%.*]] = phi i1 [ true, [[B_TARGET_B_TARGET_C]] ], [ false, [[D_TARGET_D_TARGET_E]] ], [ false, [[F_TARGET_G]] ]
; BOOLEAN-NEXT: [[GUARD_D_TARGET_E:%.*]] = phi i1 [ false, [[B_TARGET_B_TARGET_C]] ], [ true, [[D_TARGET_D_TARGET_E]] ], [ false, [[F_TARGET_G]] ]
; BOOLEAN-NEXT: br i1 [[GUARD_B_TARGET_C]], label [[B_TARGET_C]], label [[LOOP_EXIT_GUARD3:%.*]]
; BOOLEAN: loop.exit.guard3:
@@ -433,6 +520,176 @@ I:
ret void
}
+define void @inner_loop_switch(i1 %PredEntry, i1 %PredA, i1 %PredB) {
+; CHECK-LABEL: @inner_loop_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: i1 false, label [[I:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ]
+; CHECK-NEXT: br label [[B:%.*]]
+; CHECK: B:
+; CHECK-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
+; CHECK-NEXT: switch i1 [[PREDA:%.*]], label [[D:%.*]] [
+; CHECK-NEXT: i1 false, label [[B_TARGET_B_TARGET_C:%.*]]
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
+; CHECK-NEXT: br label [[H:%.*]]
+; CHECK: D:
+; CHECK-NEXT: switch i1 [[PREDB:%.*]], label [[D_TARGET_D_TARGET_E:%.*]] [
+; CHECK-NEXT: i1 false, label [[F]]
+; CHECK-NEXT: ]
+; CHECK: E:
+; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
+; CHECK-NEXT: br label [[H]]
+; CHECK: F:
+; CHECK-NEXT: [[INNER2]] = add i32 [[INNER1]], 1
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20
+; CHECK-NEXT: switch i1 [[CMP1]], label [[B]] [
+; CHECK-NEXT: i1 false, label [[F_TARGET_G:%.*]]
+; CHECK-NEXT: ]
+; CHECK: G:
+; CHECK-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10
+; CHECK-NEXT: switch i1 [[CMP2]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[G_TARGET_I:%.*]]
+; CHECK-NEXT: ]
+; CHECK: H:
+; CHECK-NEXT: unreachable
+; CHECK: I:
+; CHECK-NEXT: ret void
+; CHECK: B.target.C:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: D.target.E:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: G.target.I:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[B_TARGET_C:%.*]] ], [ 1, [[D_TARGET_E:%.*]] ], [ 2, [[G_TARGET_I]] ]
+; CHECK-NEXT: [[C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
+; CHECK-NEXT: br i1 [[C_PREDICATE]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
+; CHECK: loop.exit.guard1:
+; CHECK-NEXT: [[E_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1
+; CHECK-NEXT: br i1 [[E_PREDICATE]], label [[E:%.*]], label [[I]]
+; CHECK: B.target.B.target.C:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD2:%.*]]
+; CHECK: D.target.D.target.E:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD2]]
+; CHECK: F.target.G:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD2]]
+; CHECK: loop.exit.guard2:
+; CHECK-NEXT: [[MERGED_BB_IDX4:%.*]] = phi i32 [ 0, [[B_TARGET_B_TARGET_C]] ], [ 1, [[D_TARGET_D_TARGET_E]] ], [ 2, [[F_TARGET_G]] ]
+; CHECK-NEXT: [[B_TARGET_C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX4]], 0
+; CHECK-NEXT: br i1 [[B_TARGET_C_PREDICATE]], label [[B_TARGET_C]], label [[LOOP_EXIT_GUARD3:%.*]]
+; CHECK: loop.exit.guard3:
+; CHECK-NEXT: [[D_TARGET_E_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX4]], 1
+; CHECK-NEXT: br i1 [[D_TARGET_E_PREDICATE]], label [[D_TARGET_E]], label [[G]]
+;
+; BOOLEAN-LABEL: @inner_loop_switch(
+; BOOLEAN-NEXT: entry:
+; BOOLEAN-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[I:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: A:
+; BOOLEAN-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ]
+; BOOLEAN-NEXT: br label [[B:%.*]]
+; BOOLEAN: B:
+; BOOLEAN-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
+; BOOLEAN-NEXT: switch i1 [[PREDA:%.*]], label [[D:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[B_TARGET_B_TARGET_C:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: C:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[H:%.*]]
+; BOOLEAN: D:
+; BOOLEAN-NEXT: switch i1 [[PREDB:%.*]], label [[D_TARGET_D_TARGET_E:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[F]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: E:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[H]]
+; BOOLEAN: F:
+; BOOLEAN-NEXT: [[INNER2]] = add i32 [[INNER1]], 1
+; BOOLEAN-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20
+; BOOLEAN-NEXT: switch i1 [[CMP1]], label [[B]] [
+; BOOLEAN-NEXT: i1 false, label [[F_TARGET_G:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: G:
+; BOOLEAN-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1
+; BOOLEAN-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10
+; BOOLEAN-NEXT: switch i1 [[CMP2]], label [[A]] [
+; BOOLEAN-NEXT: i1 false, label [[G_TARGET_I:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: H:
+; BOOLEAN-NEXT: unreachable
+; BOOLEAN: I:
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: B.target.C:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; BOOLEAN: D.target.E:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: G.target.I:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: loop.exit.guard:
+; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, [[B_TARGET_C:%.*]] ], [ false, [[D_TARGET_E:%.*]] ], [ false, [[G_TARGET_I]] ]
+; BOOLEAN-NEXT: [[GUARD_E:%.*]] = phi i1 [ false, [[B_TARGET_C]] ], [ true, [[D_TARGET_E]] ], [ false, [[G_TARGET_I]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_C]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
+; BOOLEAN: loop.exit.guard1:
+; BOOLEAN-NEXT: br i1 [[GUARD_E]], label [[E:%.*]], label [[I]]
+; BOOLEAN: B.target.B.target.C:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD2:%.*]]
+; BOOLEAN: D.target.D.target.E:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD2]]
+; BOOLEAN: F.target.G:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD2]]
+; BOOLEAN: loop.exit.guard2:
+; BOOLEAN-NEXT: [[GUARD_B_TARGET_C:%.*]] = phi i1 [ true, [[B_TARGET_B_TARGET_C]] ], [ false, [[D_TARGET_D_TARGET_E]] ], [ false, [[F_TARGET_G]] ]
+; BOOLEAN-NEXT: [[GUARD_D_TARGET_E:%.*]] = phi i1 [ false, [[B_TARGET_B_TARGET_C]] ], [ true, [[D_TARGET_D_TARGET_E]] ], [ false, [[F_TARGET_G]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_B_TARGET_C]], label [[B_TARGET_C]], label [[LOOP_EXIT_GUARD3:%.*]]
+; BOOLEAN: loop.exit.guard3:
+; BOOLEAN-NEXT: br i1 [[GUARD_D_TARGET_E]], label [[D_TARGET_E]], label [[G]]
+;
+entry:
+ switch i1 %PredEntry, label %A [ i1 0, label %I ]
+
+A:
+ %outer1 = phi i32 [ 0, %entry ], [ %outer2, %G ]
+ br label %B
+
+B:
+ %inner1 = phi i32 [ 0, %A ], [ %inner2, %F ]
+ switch i1 %PredA, label %D [ i1 0, label %C ]
+
+C:
+ tail call fastcc void @check(i32 1) #0
+ br label %H
+
+D:
+ switch i1 %PredB, label %E [ i1 0, label %F ]
+
+E:
+ tail call fastcc void @check(i32 2) #0
+ br label %H
+
+F:
+ %inner2 = add i32 %inner1, 1
+ %cmp1 = icmp ult i32 %inner2, 20
+ switch i1 %cmp1, label %B [ i1 0, label %G ]
+
+G:
+ %outer2 = add i32 %outer1, 1
+ %cmp2 = icmp ult i32 %outer2, 10
+ switch i1 %cmp2, label %A [ i1 0, label %I ]
+
+H:
+ unreachable
+
+I:
+ ret void
+}
+
; A loop with more exit blocks.
define void @loop_five_exits(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) {
@@ -585,25 +842,25 @@ define void @loop_five_exits_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %Pre
; CHECK: A:
; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA:%.*]])
-; CHECK-NEXT: to label [[A_TARGET_B:%.*]] [label %C]
+; CHECK-NEXT: to label [[A_TARGET_B:%.*]] [label [[C:%.*]]]
; CHECK: B:
; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
; CHECK-NEXT: br label [[J:%.*]]
; CHECK: C:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDB:%.*]])
-; CHECK-NEXT: to label [[C_TARGET_D:%.*]] [label %E]
+; CHECK-NEXT: to label [[C_TARGET_D:%.*]] [label [[E:%.*]]]
; CHECK: D:
; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
; CHECK-NEXT: br label [[J]]
; CHECK: E:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDC:%.*]])
-; CHECK-NEXT: to label [[E_TARGET_F:%.*]] [label %G]
+; CHECK-NEXT: to label [[E_TARGET_F:%.*]] [label [[G:%.*]]]
; CHECK: F:
; CHECK-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]]
; CHECK-NEXT: br label [[K:%.*]]
; CHECK: G:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDD:%.*]])
-; CHECK-NEXT: to label [[G_TARGET_H:%.*]] [label %I]
+; CHECK-NEXT: to label [[G_TARGET_H:%.*]] [label [[I]]]
; CHECK: H:
; CHECK-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]]
; CHECK-NEXT: br label [[K]]
@@ -611,7 +868,7 @@ define void @loop_five_exits_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %Pre
; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; CHECK-NEXT: to label [[A]] [label %I.target.L]
+; CHECK-NEXT: to label [[A]] [label [[I_TARGET_L:%.*]]]
; CHECK: J:
; CHECK-NEXT: br label [[L]]
; CHECK: K:
@@ -629,7 +886,7 @@ define void @loop_five_exits_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %Pre
; CHECK: I.target.L:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A_TARGET_B]] ], [ 1, [[C_TARGET_D]] ], [ 2, [[E_TARGET_F]] ], [ 3, [[G_TARGET_H]] ], [ 4, [[I_TARGET_L:%.*]] ]
+; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A_TARGET_B]] ], [ 1, [[C_TARGET_D]] ], [ 2, [[E_TARGET_F]] ], [ 3, [[G_TARGET_H]] ], [ 4, [[I_TARGET_L]] ]
; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
; CHECK: loop.exit.guard1:
@@ -648,25 +905,25 @@ define void @loop_five_exits_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %Pre
; BOOLEAN: A:
; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA:%.*]])
-; BOOLEAN-NEXT: to label [[A_TARGET_B:%.*]] [label %C]
+; BOOLEAN-NEXT: to label [[A_TARGET_B:%.*]] [label [[C:%.*]]]
; BOOLEAN: B:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[J:%.*]]
; BOOLEAN: C:
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDB:%.*]])
-; BOOLEAN-NEXT: to label [[C_TARGET_D:%.*]] [label %E]
+; BOOLEAN-NEXT: to label [[C_TARGET_D:%.*]] [label [[E:%.*]]]
; BOOLEAN: D:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[J]]
; BOOLEAN: E:
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDC:%.*]])
-; BOOLEAN-NEXT: to label [[E_TARGET_F:%.*]] [label %G]
+; BOOLEAN-NEXT: to label [[E_TARGET_F:%.*]] [label [[G:%.*]]]
; BOOLEAN: F:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[K:%.*]]
; BOOLEAN: G:
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[PREDD:%.*]])
-; BOOLEAN-NEXT: to label [[G_TARGET_H:%.*]] [label %I]
+; BOOLEAN-NEXT: to label [[G_TARGET_H:%.*]] [label [[I]]]
; BOOLEAN: H:
; BOOLEAN-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]]
; BOOLEAN-NEXT: br label [[K]]
@@ -674,7 +931,7 @@ define void @loop_five_exits_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %Pre
; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1
; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
; BOOLEAN-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; BOOLEAN-NEXT: to label [[A]] [label %I.target.L]
+; BOOLEAN-NEXT: to label [[A]] [label [[I_TARGET_L:%.*]]]
; BOOLEAN: J:
; BOOLEAN-NEXT: br label [[L]]
; BOOLEAN: K:
@@ -692,7 +949,7 @@ define void @loop_five_exits_callbr(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %Pre
; BOOLEAN: I.target.L:
; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
; BOOLEAN: loop.exit.guard:
-; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L:%.*]] ]
+; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
; BOOLEAN-NEXT: [[GUARD_D:%.*]] = phi i1 [ false, [[A_TARGET_B]] ], [ true, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
; BOOLEAN-NEXT: [[GUARD_F:%.*]] = phi i1 [ false, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ true, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
; BOOLEAN-NEXT: [[GUARD_H:%.*]] = phi i1 [ false, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ true, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
@@ -751,6 +1008,192 @@ L:
ret void
}
+define void @loop_five_exits_switch(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) {
+; CHECK-LABEL: @loop_five_exits_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: i1 false, label [[L:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
+; CHECK-NEXT: switch i1 [[PREDA:%.*]], label [[A_TARGET_B:%.*]] [
+; CHECK-NEXT: i1 false, label [[C:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
+; CHECK-NEXT: br label [[J:%.*]]
+; CHECK: C:
+; CHECK-NEXT: switch i1 [[PREDB:%.*]], label [[C_TARGET_D:%.*]] [
+; CHECK-NEXT: i1 false, label [[E:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
+; CHECK-NEXT: br label [[J]]
+; CHECK: E:
+; CHECK-NEXT: switch i1 [[PREDC:%.*]], label [[E_TARGET_F:%.*]] [
+; CHECK-NEXT: i1 false, label [[G:%.*]]
+; CHECK-NEXT: ]
+; CHECK: F:
+; CHECK-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]]
+; CHECK-NEXT: br label [[K:%.*]]
+; CHECK: G:
+; CHECK-NEXT: switch i1 [[PREDD:%.*]], label [[G_TARGET_H:%.*]] [
+; CHECK-NEXT: i1 false, label [[I]]
+; CHECK-NEXT: ]
+; CHECK: H:
+; CHECK-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]]
+; CHECK-NEXT: br label [[K]]
+; CHECK: I:
+; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
+; CHECK-NEXT: switch i1 [[CMP]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[I_TARGET_L:%.*]]
+; CHECK-NEXT: ]
+; CHECK: J:
+; CHECK-NEXT: br label [[L]]
+; CHECK: K:
+; CHECK-NEXT: br label [[L]]
+; CHECK: L:
+; CHECK-NEXT: ret void
+; CHECK: A.target.B:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: C.target.D:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: E.target.F:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: G.target.H:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: I.target.L:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A_TARGET_B]] ], [ 1, [[C_TARGET_D]] ], [ 2, [[E_TARGET_F]] ], [ 3, [[G_TARGET_H]] ], [ 4, [[I_TARGET_L]] ]
+; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
+; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
+; CHECK: loop.exit.guard1:
+; CHECK-NEXT: [[D_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1
+; CHECK-NEXT: br i1 [[D_PREDICATE]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
+; CHECK: loop.exit.guard2:
+; CHECK-NEXT: [[F_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 2
+; CHECK-NEXT: br i1 [[F_PREDICATE]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]]
+; CHECK: loop.exit.guard3:
+; CHECK-NEXT: [[H_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 3
+; CHECK-NEXT: br i1 [[H_PREDICATE]], label [[H:%.*]], label [[L]]
+;
+; BOOLEAN-LABEL: @loop_five_exits_switch(
+; BOOLEAN-NEXT: entry:
+; BOOLEAN-NEXT: switch i1 [[PREDENTRY:%.*]], label [[A:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[L:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: A:
+; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
+; BOOLEAN-NEXT: switch i1 [[PREDA:%.*]], label [[A_TARGET_B:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[C:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: B:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[J:%.*]]
+; BOOLEAN: C:
+; BOOLEAN-NEXT: switch i1 [[PREDB:%.*]], label [[C_TARGET_D:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[E:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: D:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[J]]
+; BOOLEAN: E:
+; BOOLEAN-NEXT: switch i1 [[PREDC:%.*]], label [[E_TARGET_F:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[G:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: F:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[K:%.*]]
+; BOOLEAN: G:
+; BOOLEAN-NEXT: switch i1 [[PREDD:%.*]], label [[G_TARGET_H:%.*]] [
+; BOOLEAN-NEXT: i1 false, label [[I]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: H:
+; BOOLEAN-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]]
+; BOOLEAN-NEXT: br label [[K]]
+; BOOLEAN: I:
+; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1
+; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
+; BOOLEAN-NEXT: switch i1 [[CMP]], label [[A]] [
+; BOOLEAN-NEXT: i1 false, label [[I_TARGET_L:%.*]]
+; BOOLEAN-NEXT: ]
+; BOOLEAN: J:
+; BOOLEAN-NEXT: br label [[L]]
+; BOOLEAN: K:
+; BOOLEAN-NEXT: br label [[L]]
+; BOOLEAN: L:
+; BOOLEAN-NEXT: ret void
+; BOOLEAN: A.target.B:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; BOOLEAN: C.target.D:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: E.target.F:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: G.target.H:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: I.target.L:
+; BOOLEAN-NEXT: br label [[LOOP_EXIT_GUARD]]
+; BOOLEAN: loop.exit.guard:
+; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
+; BOOLEAN-NEXT: [[GUARD_D:%.*]] = phi i1 [ false, [[A_TARGET_B]] ], [ true, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
+; BOOLEAN-NEXT: [[GUARD_F:%.*]] = phi i1 [ false, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ true, [[E_TARGET_F]] ], [ false, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
+; BOOLEAN-NEXT: [[GUARD_H:%.*]] = phi i1 [ false, [[A_TARGET_B]] ], [ false, [[C_TARGET_D]] ], [ false, [[E_TARGET_F]] ], [ true, [[G_TARGET_H]] ], [ false, [[I_TARGET_L]] ]
+; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
+; BOOLEAN: loop.exit.guard1:
+; BOOLEAN-NEXT: br i1 [[GUARD_D]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
+; BOOLEAN: loop.exit.guard2:
+; BOOLEAN-NEXT: br i1 [[GUARD_F]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]]
+; BOOLEAN: loop.exit.guard3:
+; BOOLEAN-NEXT: br i1 [[GUARD_H]], label [[H:%.*]], label [[L]]
+;
+entry:
+ switch i1 %PredEntry, label %A [ i1 0, label %L ]
+
+A:
+ %inc1 = phi i32 [ 0, %entry ], [ %inc2, %I ]
+ switch i1 %PredA, label %B [ i1 0, label %C ]
+
+B:
+ tail call fastcc void @check(i32 1) #0
+ br label %J
+
+C:
+ switch i1 %PredB, label %D [ i1 0, label %E ]
+
+D:
+ tail call fastcc void @check(i32 2) #0
+ br label %J
+
+E:
+ switch i1 %PredC, label %F [ i1 0, label %G ]
+
+F:
+ tail call fastcc void @check(i32 3) #0
+ br label %K
+
+G:
+ switch i1 %PredD, label %H [ i1 0, label %I ]
+
+H:
+ tail call fastcc void @check(i32 4) #0
+ br label %K
+
+I:
+ %inc2 = add i32 %inc1, 1
+ %cmp = icmp ult i32 %inc2, 10
+ switch i1 %cmp, label %A [ i1 0, label %L ]
+
+J:
+ br label %L
+
+K:
+ br label %L
+
+L:
+ ret void
+}
declare void @check(i32 noundef %i) #0
diff --git a/llvm/test/Transforms/UnifyLoopExits/nested.ll b/llvm/test/Transforms/UnifyLoopExits/nested.ll
index 2ec576a2efa89..2383667b434a0 100644
--- a/llvm/test/Transforms/UnifyLoopExits/nested.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/nested.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes='lower-switch,unify-loop-exits' -S | FileCheck %s
+; RUN: opt < %s -passes='unify-loop-exits' -S | FileCheck %s
define void @nested(i1 %PredB3, i1 %PredB4, i1 %PredA4, i1 %PredA3, i32 %X, i32 %Y, i32 %Z) {
; CHECK-LABEL: @nested(
@@ -92,10 +92,10 @@ define void @nested_callbr(i1 %PredB3, i1 %PredB4, i1 %PredA4, i1 %PredA3, i32 %
; CHECK-NEXT: br label [[B3:%.*]]
; CHECK: B3:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDB3:%.*]])
-; CHECK-NEXT: to label [[B4:%.*]] [label %B3.target.A3]
+; CHECK-NEXT: to label [[B4:%.*]] [label [[B3_TARGET_A3:%.*]]]
; CHECK: B4:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDB4:%.*]])
-; CHECK-NEXT: to label [[B1]] [label %B4.target.A2]
+; CHECK-NEXT: to label [[B1]] [label [[B4_TARGET_A2:%.*]]]
; CHECK: A2:
; CHECK-NEXT: br label [[A4:%.*]]
; CHECK: A3:
@@ -103,10 +103,10 @@ define void @nested_callbr(i1 %PredB3, i1 %PredB4, i1 %PredA4, i1 %PredA3, i32 %
; CHECK: A4:
; CHECK-NEXT: [[A4_PHI:%.*]] = phi i32 [ [[Y:%.*]], [[A3:%.*]] ], [ [[X_INC_MOVED:%.*]], [[A2:%.*]] ]
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA4:%.*]])
-; CHECK-NEXT: to label [[A4_TARGET_C:%.*]] [label %A5]
+; CHECK-NEXT: to label [[A4_TARGET_C:%.*]] [label [[A5:%.*]]]
; CHECK: A5:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDA3:%.*]])
-; CHECK-NEXT: to label [[A5_TARGET_EXIT:%.*]] [label %A1]
+; CHECK-NEXT: to label [[A5_TARGET_EXIT:%.*]] [label [[A1]]]
; CHECK: C:
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
@@ -125,7 +125,7 @@ define void @nested_callbr(i1 %PredB3, i1 %PredB4, i1 %PredA4, i1 %PredA3, i32 %
; CHECK: B4.target.A2:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1]]
; CHECK: loop.exit.guard1:
-; CHECK-NEXT: [[X_INC_MOVED]] = phi i32 [ [[X_INC]], [[B3_TARGET_A3:%.*]] ], [ [[X_INC]], [[B4_TARGET_A2:%.*]] ]
+; CHECK-NEXT: [[X_INC_MOVED]] = phi i32 [ [[X_INC]], [[B3_TARGET_A3]] ], [ [[X_INC]], [[B4_TARGET_A2]] ]
; CHECK-NEXT: [[GUARD_A3:%.*]] = phi i1 [ true, [[B3_TARGET_A3]] ], [ false, [[B4_TARGET_A2]] ]
; CHECK-NEXT: br i1 [[GUARD_A3]], label [[A3]], label [[A2]]
;
@@ -169,6 +169,100 @@ exit:
ret void
}
+define void @nested_switch(i1 %PredB3, i1 %PredB4, i1 %PredA4, i1 %PredA3, i32 %X, i32 %Y, i32 %Z) {
+; CHECK-LABEL: @nested_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[A1:%.*]]
+; CHECK: A1:
+; CHECK-NEXT: br label [[B1:%.*]]
+; CHECK: B1:
+; CHECK-NEXT: br label [[B2:%.*]]
+; CHECK: B2:
+; CHECK-NEXT: [[X_INC:%.*]] = add i32 [[X:%.*]], 1
+; CHECK-NEXT: br label [[B3:%.*]]
+; CHECK: B3:
+; CHECK-NEXT: switch i1 [[PREDB3:%.*]], label [[B4:%.*]] [
+; CHECK-NEXT: i1 false, label [[B3_TARGET_A3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B4:
+; CHECK-NEXT: switch i1 [[PREDB4:%.*]], label [[B1]] [
+; CHECK-NEXT: i1 false, label [[B4_TARGET_A2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A2:
+; CHECK-NEXT: br label [[A4:%.*]]
+; CHECK: A3:
+; CHECK-NEXT: br label [[A4]]
+; CHECK: A4:
+; CHECK-NEXT: [[A4_PHI:%.*]] = phi i32 [ [[Y:%.*]], [[A3:%.*]] ], [ [[X_INC_MOVED:%.*]], [[A2:%.*]] ]
+; CHECK-NEXT: switch i1 [[PREDA4:%.*]], label [[A4_TARGET_C:%.*]] [
+; CHECK-NEXT: i1 false, label [[A5:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A5:
+; CHECK-NEXT: switch i1 [[PREDA3:%.*]], label [[A5_TARGET_EXIT:%.*]] [
+; CHECK-NEXT: i1 false, label [[A1]]
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: br label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: [[EXIT_PHI:%.*]] = phi i32 [ [[Z:%.*]], [[C:%.*]] ], [ [[EXIT_PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD:%.*]] ]
+; CHECK-NEXT: ret void
+; CHECK: A4.target.C:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: A5.target.exit:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[EXIT_PHI_MOVED]] = phi i32 [ poison, [[A4_TARGET_C]] ], [ [[A4_PHI]], [[A5_TARGET_EXIT]] ]
+; CHECK-NEXT: [[GUARD_C:%.*]] = phi i1 [ true, [[A4_TARGET_C]] ], [ false, [[A5_TARGET_EXIT]] ]
+; CHECK-NEXT: br i1 [[GUARD_C]], label [[C]], label [[EXIT]]
+; CHECK: B3.target.A3:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1:%.*]]
+; CHECK: B4.target.A2:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1]]
+; CHECK: loop.exit.guard1:
+; CHECK-NEXT: [[X_INC_MOVED]] = phi i32 [ [[X_INC]], [[B3_TARGET_A3]] ], [ [[X_INC]], [[B4_TARGET_A2]] ]
+; CHECK-NEXT: [[GUARD_A3:%.*]] = phi i1 [ true, [[B3_TARGET_A3]] ], [ false, [[B4_TARGET_A2]] ]
+; CHECK-NEXT: br i1 [[GUARD_A3]], label [[A3]], label [[A2]]
+;
+entry:
+ br label %A1
+
+A1:
+ br label %B1
+
+B1:
+ br label %B2
+
+B2:
+ %X.inc = add i32 %X, 1
+ br label %B3
+
+B3:
+ switch i1 %PredB3, label %B4 [ i1 0, label %A3 ]
+
+B4:
+ switch i1 %PredB4, label %B1 [ i1 0, label %A2 ]
+
+A2:
+ br label %A4
+
+A3:
+ br label %A4
+
+A4:
+ %A4.phi = phi i32 [%Y, %A3], [%X.inc, %A2]
+ switch i1 %PredA4, label %C [ i1 0, label %A5 ]
+
+A5:
+ switch i1 %PredA3, label %exit [ i1 0, label %A1 ]
+
+C:
+ br label %exit
+
+exit:
+ %exit.phi = phi i32 [%A4.phi, %A5], [%Z, %C]
+ ret void
+}
+
; Here, the newly created target loop that connects b to r1 needs to be part of
; the parent loop (the outer loop b participates in). Otherwise, it will be
; regarded as an additional loop entry point to this outer loop.
@@ -180,13 +274,13 @@ define void @nested_callbr_multiple_exits() {
; CHECK-NEXT: to label [[B:%.*]] []
; CHECK: b:
; CHECK-NEXT: callbr void asm "", "!i"()
-; CHECK-NEXT: to label [[C:%.*]] [label %b.target.b.target.r1]
+; CHECK-NEXT: to label [[C:%.*]] [label [[B_TARGET_B_TARGET_R1:%.*]]]
; CHECK: c:
; CHECK-NEXT: callbr void asm "", "!i"()
-; CHECK-NEXT: to label [[C_TARGET_E:%.*]] [label %b]
+; CHECK-NEXT: to label [[C_TARGET_E:%.*]] [label [[B]]]
; CHECK: e:
; CHECK-NEXT: callbr void asm "", "!i"()
-; CHECK-NEXT: to label [[A]] [label %e.target.r2]
+; CHECK-NEXT: to label [[A]] [label [[E_TARGET_R2:%.*]]]
; CHECK: r1:
; CHECK-NEXT: ret void
; CHECK: r2:
@@ -196,14 +290,14 @@ define void @nested_callbr_multiple_exits() {
; CHECK: e.target.r2:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[GUARD_R1:%.*]] = phi i1 [ true, [[B_TARGET_R1:%.*]] ], [ false, [[E_TARGET_R2:%.*]] ]
+; CHECK-NEXT: [[GUARD_R1:%.*]] = phi i1 [ true, [[B_TARGET_R1:%.*]] ], [ false, [[E_TARGET_R2]] ]
; CHECK-NEXT: br i1 [[GUARD_R1]], label [[R1:%.*]], label [[R2:%.*]]
; CHECK: b.target.b.target.r1:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1:%.*]]
; CHECK: c.target.e:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1]]
; CHECK: loop.exit.guard1:
-; CHECK-NEXT: [[GUARD_B_TARGET_R1:%.*]] = phi i1 [ true, [[B_TARGET_B_TARGET_R1:%.*]] ], [ false, [[C_TARGET_E]] ]
+; CHECK-NEXT: [[GUARD_B_TARGET_R1:%.*]] = phi i1 [ true, [[B_TARGET_B_TARGET_R1]] ], [ false, [[C_TARGET_E]] ]
; CHECK-NEXT: br i1 [[GUARD_B_TARGET_R1]], label [[B_TARGET_R1]], label [[E:%.*]]
;
br label %a
@@ -220,3 +314,55 @@ r1:
r2:
ret void
}
+
+define void @nested_switch_multiple_exits(i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredE) {
+; CHECK-LABEL: @nested_switch_multiple_exits(
+; CHECK-NEXT: br label [[A:%.*]]
+; CHECK: a:
+; CHECK-NEXT: switch i1 [[PREDA:%.*]], label [[B:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: b:
+; CHECK-NEXT: switch i1 [[PREDB:%.*]], label [[C:%.*]] [
+; CHECK-NEXT: i1 false, label [[B_TARGET_B_TARGET_R1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: c:
+; CHECK-NEXT: switch i1 [[PREDC:%.*]], label [[C_TARGET_E:%.*]] [
+; CHECK-NEXT: i1 false, label [[B]]
+; CHECK-NEXT: ]
+; CHECK: e:
+; CHECK-NEXT: switch i1 [[PREDE:%.*]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[E_TARGET_R2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: r1:
+; CHECK-NEXT: ret void
+; CHECK: r2:
+; CHECK-NEXT: ret void
+; CHECK: b.target.r1:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: e.target.r2:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[GUARD_R1:%.*]] = phi i1 [ true, [[B_TARGET_R1:%.*]] ], [ false, [[E_TARGET_R2]] ]
+; CHECK-NEXT: br i1 [[GUARD_R1]], label [[R1:%.*]], label [[R2:%.*]]
+; CHECK: b.target.b.target.r1:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1:%.*]]
+; CHECK: c.target.e:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1]]
+; CHECK: loop.exit.guard1:
+; CHECK-NEXT: [[GUARD_B_TARGET_R1:%.*]] = phi i1 [ true, [[B_TARGET_B_TARGET_R1]] ], [ false, [[C_TARGET_E]] ]
+; CHECK-NEXT: br i1 [[GUARD_B_TARGET_R1]], label [[B_TARGET_R1]], label [[E:%.*]]
+;
+ br label %a
+a:
+ switch i1 %PredA, label %b []
+b:
+ switch i1 %PredB, label %c [ i1 0, label %r1 ]
+c:
+ switch i1 %PredC, label %e [ i1 0, label %b ]
+e:
+ switch i1 %PredE, label %a [ i1 0, label %r2 ]
+r1:
+ ret void
+r2:
+ ret void
+}
diff --git a/llvm/test/Transforms/UnifyLoopExits/restore-ssa.ll b/llvm/test/Transforms/UnifyLoopExits/restore-ssa.ll
index ffe8026a535c0..5e8202eca56f3 100644
--- a/llvm/test/Transforms/UnifyLoopExits/restore-ssa.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/restore-ssa.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes='lower-switch,unify-loop-exits' -S | FileCheck %s
+; RUN: opt < %s -passes='unify-loop-exits' -S | FileCheck %s
; Loop consists of A and B:
; - A is the header
@@ -66,12 +66,12 @@ define i32 @exiting-used-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_addr
; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, ptr [[ARG1:%.*]], align 4
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP1]])
-; CHECK-NEXT: to label [[B:%.*]] [label %A.target.return]
+; CHECK-NEXT: to label [[B:%.*]] [label [[A_TARGET_RETURN:%.*]]]
; CHECK: B:
; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP41]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; CHECK-NEXT: to label [[A]] [label %B.target.C]
+; CHECK-NEXT: to label [[A]] [label [[B_TARGET_C:%.*]]]
; CHECK: C:
; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1
; CHECK-NEXT: callbr void asm "", ""()
@@ -84,7 +84,7 @@ define i32 @exiting-used-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_addr
; CHECK: B.target.C:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN:%.*]] ], [ [[MYTMP41]], [[B_TARGET_C:%.*]] ]
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN]] ], [ [[MYTMP41]], [[B_TARGET_C]] ]
; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP42]], [[A_TARGET_RETURN]] ], [ poison, [[B_TARGET_C]] ]
; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A_TARGET_RETURN]] ], [ false, [[B_TARGET_C]] ]
; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[C]]
@@ -111,6 +111,62 @@ return:
ret i32 %phi
}
+define i32 @exiting-used-in-exit_switch(ptr %arg1, ptr %arg2, i1 %Pred) local_unnamed_addr align 2 {
+; CHECK-LABEL: @exiting-used-in-exit_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i1 [[PRED:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, ptr [[ARG1:%.*]], align 4
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP1]], label [[B:%.*]] [
+; CHECK-NEXT: i1 false, label [[A_TARGET_RETURN:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP41]], 0
+; CHECK-NEXT: switch i1 [[CMP]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[B_TARGET_C:%.*]]
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1
+; CHECK-NEXT: switch i1 [[PRED]], label [[RETURN:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: return:
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[INC]], [[C:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD:%.*]] ]
+; CHECK-NEXT: ret i32 [[PHI]]
+; CHECK: A.target.return:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: B.target.C:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN]] ], [ [[MYTMP41]], [[B_TARGET_C]] ]
+; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP42]], [[A_TARGET_RETURN]] ], [ poison, [[B_TARGET_C]] ]
+; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A_TARGET_RETURN]] ], [ false, [[B_TARGET_C]] ]
+; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[C]]
+;
+entry:
+ switch i1 %Pred, label %A []
+
+A:
+ %mytmp42 = load i32, ptr %arg1, align 4
+ %cmp1 = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp1, label %B [ i1 0, label %return ]
+
+B:
+ %mytmp41 = load i32, ptr %arg2, align 4
+ %cmp = icmp slt i32 %mytmp41, 0
+ switch i1 %cmp, label %A [ i1 0, label %C ]
+
+C:
+ %inc = add i32 %mytmp41, 1
+ switch i1 %Pred, label %return []
+
+return:
+ %phi = phi i32 [ %inc, %C ], [ %mytmp42, %A ]
+ ret i32 %phi
+}
+
; Loop consists of A, B and C:
; - A is the header
; - A and C are exiting blocks
@@ -175,7 +231,7 @@ define i32 @internal-used-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_add
; CHECK: A:
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP1]])
-; CHECK-NEXT: to label [[B:%.*]] [label %A.target.return]
+; CHECK-NEXT: to label [[B:%.*]] [label [[A_TARGET_RETURN:%.*]]]
; CHECK: B:
; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
; CHECK-NEXT: callbr void asm "", ""()
@@ -183,7 +239,7 @@ define i32 @internal-used-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_add
; CHECK: C:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; CHECK-NEXT: to label [[A]] [label %C.target.D]
+; CHECK-NEXT: to label [[A]] [label [[C_TARGET_D:%.*]]]
; CHECK: D:
; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1
; CHECK-NEXT: callbr void asm "", ""()
@@ -195,7 +251,7 @@ define i32 @internal-used-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_add
; CHECK: C.target.D:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN:%.*]] ], [ [[MYTMP41]], [[C_TARGET_D:%.*]] ]
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN]] ], [ [[MYTMP41]], [[C_TARGET_D]] ]
; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A_TARGET_RETURN]] ], [ false, [[C_TARGET_D]] ]
; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D:%.*]]
;
@@ -223,6 +279,65 @@ return:
ret i32 0
}
+define i32 @internal-used-in-exit_switch(ptr %arg1, ptr %arg2, i1 %Pred) local_unnamed_addr align 2 {
+; CHECK-LABEL: @internal-used-in-exit_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, ptr [[ARG1:%.*]], align 4
+; CHECK-NEXT: switch i1 [[PRED:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP1]], label [[B:%.*]] [
+; CHECK-NEXT: i1 false, label [[A_TARGET_RETURN:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
+; CHECK-NEXT: switch i1 [[PRED]], label [[C:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[C_TARGET_D:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1
+; CHECK-NEXT: switch i1 [[PRED]], label [[RETURN:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: return:
+; CHECK-NEXT: ret i32 0
+; CHECK: A.target.return:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: C.target.D:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN]] ], [ [[MYTMP41]], [[C_TARGET_D]] ]
+; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A_TARGET_RETURN]] ], [ false, [[C_TARGET_D]] ]
+; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D:%.*]]
+;
+entry:
+ %mytmp42 = load i32, ptr %arg1, align 4
+ switch i1 %Pred, label %A []
+
+A:
+ %cmp1 = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp1, label %B [ i1 0, label %return ]
+
+B:
+ %mytmp41 = load i32, ptr %arg2, align 4
+ switch i1 %Pred, label %C []
+
+C:
+ %cmp = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp, label %A [ i1 0, label %D ]
+
+D:
+ %inc = add i32 %mytmp41, 1
+ switch i1 %Pred, label %return []
+
+return:
+ ret i32 0
+}
+
; Loop consists of A, B and C:
; - A is the header
; - A and C are exiting blocks
@@ -289,12 +404,12 @@ define i32 @mixed-use-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_addr al
; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, ptr [[ARG1:%.*]], align 4
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP2]])
-; CHECK-NEXT: to label [[A:%.*]] [label %return]
+; CHECK-NEXT: to label [[A:%.*]] [label [[RETURN:%.*]]]
; CHECK: A:
; CHECK-NEXT: [[MYTMP43:%.*]] = add i32 [[MYTMP42]], 1
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP1]])
-; CHECK-NEXT: to label [[B:%.*]] [label %A.target.return]
+; CHECK-NEXT: to label [[B:%.*]] [label [[A_TARGET_RETURN:%.*]]]
; CHECK: B:
; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
; CHECK-NEXT: callbr void asm "", ""()
@@ -302,10 +417,10 @@ define i32 @mixed-use-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_addr al
; CHECK: C:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; CHECK-NEXT: to label [[A]] [label %C.target.D]
+; CHECK-NEXT: to label [[A]] [label [[C_TARGET_D:%.*]]]
; CHECK: D:
; CHECK-NEXT: callbr void asm "", ""()
-; CHECK-NEXT: to label [[RETURN:%.*]] []
+; CHECK-NEXT: to label [[RETURN]] []
; CHECK: return:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[ENTRY:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD:%.*]] ]
; CHECK-NEXT: ret i32 [[PHI]]
@@ -314,7 +429,7 @@ define i32 @mixed-use-in-exit_callbr(ptr %arg1, ptr %arg2) local_unnamed_addr al
; CHECK: C.target.D:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN:%.*]] ], [ [[MYTMP41]], [[C_TARGET_D:%.*]] ]
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN]] ], [ [[MYTMP41]], [[C_TARGET_D]] ]
; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP43]], [[A_TARGET_RETURN]] ], [ poison, [[C_TARGET_D]] ]
; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A_TARGET_RETURN]] ], [ false, [[C_TARGET_D]] ]
; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D]]
@@ -345,6 +460,71 @@ return:
ret i32 %phi
}
+define i32 @mixed-use-in-exit_switch(ptr %arg1, ptr %arg2, i1 %Pred) local_unnamed_addr align 2 {
+; CHECK-LABEL: @mixed-use-in-exit_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, ptr [[ARG1:%.*]], align 4
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP2]], label [[A:%.*]] [
+; CHECK-NEXT: i1 false, label [[RETURN:%.*]]
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[MYTMP43:%.*]] = add i32 [[MYTMP42]], 1
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP1]], label [[B:%.*]] [
+; CHECK-NEXT: i1 false, label [[A_TARGET_RETURN:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
+; CHECK-NEXT: switch i1 [[PRED:%.*]], label [[C:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[C_TARGET_D:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: switch i1 [[PRED]], label [[RETURN]] [
+; CHECK-NEXT: ]
+; CHECK: return:
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[ENTRY:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD:%.*]] ]
+; CHECK-NEXT: ret i32 [[PHI]]
+; CHECK: A.target.return:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: C.target.D:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_RETURN]] ], [ [[MYTMP41]], [[C_TARGET_D]] ]
+; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP43]], [[A_TARGET_RETURN]] ], [ poison, [[C_TARGET_D]] ]
+; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A_TARGET_RETURN]] ], [ false, [[C_TARGET_D]] ]
+; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D]]
+;
+entry:
+ %mytmp42 = load i32, ptr %arg1, align 4
+ %cmp2 = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp2, label %A [ i1 0, label %return ]
+
+A:
+ %mytmp43 = add i32 %mytmp42, 1
+ %cmp1 = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp1, label %B [ i1 0, label %return ]
+
+B:
+ %mytmp41 = load i32, ptr %arg2, align 4
+ switch i1 %Pred, label %C []
+
+C:
+ %cmp = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp, label %A [ i1 0, label %D ]
+
+D:
+ switch i1 %Pred, label %return []
+
+return:
+ %phi = phi i32 [ %mytmp41, %D ], [ %mytmp43, %A ], [%mytmp42, %entry]
+ ret i32 %phi
+}
+
; Loop consists of A, B and C:
; - A is the header
; - A and C are exiting blocks
@@ -419,7 +599,7 @@ define i32 @phi-via-external-block_callbr(ptr %arg1, ptr %arg2) local_unnamed_ad
; CHECK: A:
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP1]])
-; CHECK-NEXT: to label [[B:%.*]] [label %A.target.E]
+; CHECK-NEXT: to label [[B:%.*]] [label [[A_TARGET_E:%.*]]]
; CHECK: B:
; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
; CHECK-NEXT: callbr void asm "", ""()
@@ -427,7 +607,7 @@ define i32 @phi-via-external-block_callbr(ptr %arg1, ptr %arg2) local_unnamed_ad
; CHECK: C:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[CMP]])
-; CHECK-NEXT: to label [[A]] [label %C.target.D]
+; CHECK-NEXT: to label [[A]] [label [[C_TARGET_D:%.*]]]
; CHECK: D:
; CHECK-NEXT: callbr void asm "", ""()
; CHECK-NEXT: to label [[RETURN:%.*]] []
@@ -442,7 +622,7 @@ define i32 @phi-via-external-block_callbr(ptr %arg1, ptr %arg2) local_unnamed_ad
; CHECK: C.target.D:
; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
; CHECK: loop.exit.guard:
-; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_E:%.*]] ], [ [[MYTMP41]], [[C_TARGET_D:%.*]] ]
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_E]] ], [ [[MYTMP41]], [[C_TARGET_D]] ]
; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[A_TARGET_E]] ], [ false, [[C_TARGET_D]] ]
; CHECK-NEXT: br i1 [[GUARD_E]], label [[E]], label [[D]]
;
@@ -472,3 +652,68 @@ return:
%phi = phi i32 [ %mytmp41, %D ], [ %mytmp42, %E ]
ret i32 %phi
}
+
+define i32 @phi-via-external-block_switch(ptr %arg1, ptr %arg2, i1 %Pred) local_unnamed_addr align 2 {
+; CHECK-LABEL: @phi-via-external-block_switch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, ptr [[ARG1:%.*]], align 4
+; CHECK-NEXT: switch i1 [[PRED:%.*]], label [[A:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: A:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP1]], label [[B:%.*]] [
+; CHECK-NEXT: i1 false, label [[A_TARGET_E:%.*]]
+; CHECK-NEXT: ]
+; CHECK: B:
+; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, ptr [[ARG2:%.*]], align 4
+; CHECK-NEXT: switch i1 [[PRED]], label [[C:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: C:
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
+; CHECK-NEXT: switch i1 [[CMP]], label [[A]] [
+; CHECK-NEXT: i1 false, label [[C_TARGET_D:%.*]]
+; CHECK-NEXT: ]
+; CHECK: D:
+; CHECK-NEXT: switch i1 [[PRED]], label [[RETURN:%.*]] [
+; CHECK-NEXT: ]
+; CHECK: E:
+; CHECK-NEXT: switch i1 [[PRED]], label [[RETURN]] [
+; CHECK-NEXT: ]
+; CHECK: return:
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[E:%.*]] ]
+; CHECK-NEXT: ret i32 [[PHI]]
+; CHECK: A.target.E:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]]
+; CHECK: C.target.D:
+; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]]
+; CHECK: loop.exit.guard:
+; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ poison, [[A_TARGET_E]] ], [ [[MYTMP41]], [[C_TARGET_D]] ]
+; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[A_TARGET_E]] ], [ false, [[C_TARGET_D]] ]
+; CHECK-NEXT: br i1 [[GUARD_E]], label [[E]], label [[D]]
+;
+entry:
+ %mytmp42 = load i32, ptr %arg1, align 4
+ switch i1 %Pred, label %A []
+
+A:
+ %cmp1 = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp1, label %B [ i1 0, label %E ]
+
+B:
+ %mytmp41 = load i32, ptr %arg2, align 4
+ switch i1 %Pred, label %C []
+
+C:
+ %cmp = icmp slt i32 %mytmp42, 0
+ switch i1 %cmp, label %A [ i1 0, label %D ]
+
+D:
+ switch i1 %Pred, label %return []
+
+E:
+ switch i1 %Pred, label %return []
+
+return:
+ %phi = phi i32 [ %mytmp41, %D ], [ %mytmp42, %E ]
+ ret i32 %phi
+}
diff --git a/llvm/test/Transforms/UnifyLoopExits/switch.ll b/llvm/test/Transforms/UnifyLoopExits/switch.ll
index 887a6f5d08e9b..7ab6de7651bd9 100644
--- a/llvm/test/Transforms/UnifyLoopExits/switch.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/switch.ll
@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes='lower-switch,unify-loop-exits' -S | FileCheck %s
+; RUN: opt < %s -passes='unify-loop-exits' -S | FileCheck %s --check-prefix=NOLOWER
define void @loop_1(i32 %Value, i1 %PredEntry, i1 %PredD) {
; CHECK-LABEL: @loop_1(
@@ -34,6 +35,34 @@ define void @loop_1(i32 %Value, i1 %PredEntry, i1 %PredD) {
; CHECK-NEXT: [[GUARD_X:%.*]] = phi i1 [ true, [[LEAFBLOCK1]] ], [ true, [[LEAFBLOCK]] ], [ false, [[D]] ]
; CHECK-NEXT: br i1 [[GUARD_X]], label [[X:%.*]], label [[Y:%.*]]
;
+; NOLOWER-LABEL: @loop_1(
+; NOLOWER-NEXT: entry:
+; NOLOWER-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[G:%.*]]
+; NOLOWER: A:
+; NOLOWER-NEXT: br label [[B:%.*]]
+; NOLOWER: B:
+; NOLOWER-NEXT: switch i32 [[VALUE:%.*]], label [[B_TARGET_X:%.*]] [
+; NOLOWER-NEXT: i32 0, label [[C:%.*]]
+; NOLOWER-NEXT: i32 1, label [[D:%.*]]
+; NOLOWER-NEXT: ]
+; NOLOWER: C:
+; NOLOWER-NEXT: br label [[D]]
+; NOLOWER: D:
+; NOLOWER-NEXT: br i1 [[PREDD:%.*]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]]
+; NOLOWER: X:
+; NOLOWER-NEXT: br label [[EXIT:%.*]]
+; NOLOWER: Y:
+; NOLOWER-NEXT: br label [[EXIT]]
+; NOLOWER: G:
+; NOLOWER-NEXT: br label [[EXIT]]
+; NOLOWER: exit:
+; NOLOWER-NEXT: ret void
+; NOLOWER: B.target.X:
+; NOLOWER-NEXT: br label [[LOOP_EXIT_GUARD]]
+; NOLOWER: loop.exit.guard:
+; NOLOWER-NEXT: [[GUARD_X:%.*]] = phi i1 [ true, [[B_TARGET_X]] ], [ false, [[D]] ]
+; NOLOWER-NEXT: br i1 [[GUARD_X]], label [[X:%.*]], label [[Y:%.*]]
+;
entry:
br i1 %PredEntry, label %A, label %G
diff --git a/llvm/test/Transforms/UnifyLoopExits/undef-phis.ll b/llvm/test/Transforms/UnifyLoopExits/undef-phis.ll
index e65e2549a21c8..af7d1a912be70 100644
--- a/llvm/test/Transforms/UnifyLoopExits/undef-phis.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/undef-phis.ll
@@ -70,28 +70,28 @@ define fastcc void @undef_phi_callbr(i64 %i5247, i1 %i4530, i1 %i4936.not) {
; CHECK-NEXT: [[TMP0:%.*]] = trunc i64 [[I5247]] to i32
; CHECK-NEXT: [[I5290:%.*]] = icmp eq i32 [[TMP0]], 0
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[I5290]])
-; CHECK-NEXT: to label %[[MBB3932]] [label %mbb4321.target.mbb5291]
+; CHECK-NEXT: to label %[[MBB3932]] [label %[[MBB4321_TARGET_MBB5291:.*]]]
; CHECK: [[MBB4454]]:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[I4530]])
-; CHECK-NEXT: to label %[[MBB4535:.*]] [label %mbb4454.target.mbb4454.target.mbb4531]
+; CHECK-NEXT: to label %[[MBB4535:.*]] [label %[[MBB4454_TARGET_MBB4454_TARGET_MBB4531:.*]]]
; CHECK: [[MBB4531:.*]]:
; CHECK-NEXT: ret void
; CHECK: [[MBB4535]]:
; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[I4936_NOT]])
-; CHECK-NEXT: to label %[[MBB4535_TARGET_MBB4321:.*]] [label %mbb4454]
+; CHECK-NEXT: to label %[[MBB4535_TARGET_MBB4321:.*]] [label %[[MBB4454]]]
; CHECK: [[MBB5291:.*]]:
; CHECK-NEXT: [[I5293:%.*]] = insertvalue [2 x i32] zeroinitializer, i32 [[DOTMOVED:%.*]], 1
; CHECK-NEXT: store volatile [2 x i32] [[I5293]], ptr addrspace(5) null, align 4
; CHECK-NEXT: ret void
; CHECK: [[MBB4454_TARGET_MBB4531:.*]]:
; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]]
-; CHECK: [[MBB4321_TARGET_MBB5291:.*]]:
+; CHECK: [[MBB4321_TARGET_MBB5291]]:
; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
; CHECK: [[LOOP_EXIT_GUARD]]:
; CHECK-NEXT: [[DOTMOVED]] = phi i32 [ poison, %[[MBB4454_TARGET_MBB4531]] ], [ [[TMP0]], %[[MBB4321_TARGET_MBB5291]] ]
; CHECK-NEXT: [[GUARD_MBB4531:%.*]] = phi i1 [ true, %[[MBB4454_TARGET_MBB4531]] ], [ false, %[[MBB4321_TARGET_MBB5291]] ]
; CHECK-NEXT: br i1 [[GUARD_MBB4531]], label %[[MBB4531]], label %[[MBB5291]]
-; CHECK: [[MBB4454_TARGET_MBB4454_TARGET_MBB4531:.*]]:
+; CHECK: [[MBB4454_TARGET_MBB4454_TARGET_MBB4531]]:
; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD1:.*]]
; CHECK: [[MBB4535_TARGET_MBB4321]]:
; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD1]]
@@ -124,3 +124,74 @@ mbb5291: ; preds = %mbb4321
store volatile [2 x i32] %i5293, ptr addrspace(5) null, align 4
ret void
}
+
+define fastcc void @undef_phi_switch(i64 %i5247, i1 %i4530, i1 %i4936.not, i1 %Pred) {
+; CHECK-LABEL: define fastcc void @undef_phi_switch(
+; CHECK-SAME: i64 [[I5247:%.*]], i1 [[I4530:%.*]], i1 [[I4936_NOT:%.*]], i1 [[PRED:%.*]]) {
+; CHECK-NEXT: [[MBB:.*:]]
+; CHECK-NEXT: switch i1 [[PRED]], label %[[MBB3932:.*]] [
+; CHECK-NEXT: ]
+; CHECK: [[MBB3932]]:
+; CHECK-NEXT: switch i1 [[PRED]], label %[[MBB4454:.*]] [
+; CHECK-NEXT: ]
+; CHECK: [[MBB4321:.*]]:
+; CHECK-NEXT: [[TMP0:%.*]] = trunc i64 [[I5247]] to i32
+; CHECK-NEXT: [[I5290:%.*]] = icmp eq i32 [[TMP0]], 0
+; CHECK-NEXT: switch i1 [[I5290]], label %[[MBB3932]] [
+; CHECK-NEXT: i1 false, label %[[MBB4321_TARGET_MBB5291:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[MBB4454]]:
+; CHECK-NEXT: switch i1 [[I4530]], label %[[MBB4535:.*]] [
+; CHECK-NEXT: i1 false, label %[[MBB4454_TARGET_MBB4454_TARGET_MBB4531:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[MBB4531:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[MBB4535]]:
+; CHECK-NEXT: switch i1 [[I4936_NOT]], label %[[MBB4535_TARGET_MBB4321:.*]] [
+; CHECK-NEXT: i1 false, label %[[MBB4454]]
+; CHECK-NEXT: ]
+; CHECK: [[MBB5291:.*]]:
+; CHECK-NEXT: [[I5293:%.*]] = insertvalue [2 x i32] zeroinitializer, i32 [[DOTMOVED:%.*]], 1
+; CHECK-NEXT: store volatile [2 x i32] [[I5293]], ptr addrspace(5) null, align 4
+; CHECK-NEXT: ret void
+; CHECK: [[MBB4454_TARGET_MBB4531:.*]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]]
+; CHECK: [[MBB4321_TARGET_MBB5291]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; CHECK: [[LOOP_EXIT_GUARD]]:
+; CHECK-NEXT: [[DOTMOVED]] = phi i32 [ poison, %[[MBB4454_TARGET_MBB4531]] ], [ [[TMP0]], %[[MBB4321_TARGET_MBB5291]] ]
+; CHECK-NEXT: [[GUARD_MBB4531:%.*]] = phi i1 [ true, %[[MBB4454_TARGET_MBB4531]] ], [ false, %[[MBB4321_TARGET_MBB5291]] ]
+; CHECK-NEXT: br i1 [[GUARD_MBB4531]], label %[[MBB4531]], label %[[MBB5291]]
+; CHECK: [[MBB4454_TARGET_MBB4454_TARGET_MBB4531]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD1:.*]]
+; CHECK: [[MBB4535_TARGET_MBB4321]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD1]]
+; CHECK: [[LOOP_EXIT_GUARD1]]:
+; CHECK-NEXT: [[GUARD_MBB4454_TARGET_MBB4531:%.*]] = phi i1 [ true, %[[MBB4454_TARGET_MBB4454_TARGET_MBB4531]] ], [ false, %[[MBB4535_TARGET_MBB4321]] ]
+; CHECK-NEXT: br i1 [[GUARD_MBB4454_TARGET_MBB4531]], label %[[MBB4454_TARGET_MBB4531]], label %[[MBB4321]]
+;
+mbb:
+ switch i1 %Pred, label %mbb3932 []
+
+mbb3932: ; preds = %mbb4321, %mbb
+ switch i1 %Pred, label %mbb4454 []
+
+mbb4321: ; preds = %mbb4535
+ %0 = trunc i64 %i5247 to i32
+ %i5290 = icmp eq i32 %0, 0
+ switch i1 %i5290, label %mbb3932 [ i1 0, label %mbb5291 ]
+
+mbb4454: ; preds = %mbb4535, %mbb3932
+ switch i1 %i4530, label %mbb4535 [ i1 0, label %mbb4531 ]
+
+mbb4531: ; preds = %mbb4454
+ ret void
+
+mbb4535: ; preds = %mbb4454
+ switch i1 %i4936.not, label %mbb4321 [ i1 0, label %mbb4454 ]
+
+mbb5291: ; preds = %mbb4321
+ %i5293 = insertvalue [2 x i32] zeroinitializer, i32 %0, 1
+ store volatile [2 x i32] %i5293, ptr addrspace(5) null, align 4
+ ret void
+}
>From 7d9078d3783a81ae3012001db59316f0327677ef Mon Sep 17 00:00:00 2001
From: Lukas <dev at lukas-hertel.de>
Date: Sun, 12 Jul 2026 20:17:41 +0200
Subject: [PATCH 3/5] Added tests for multiedges and fixed some naming
---
llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 40 ++--
llvm/lib/Transforms/Utils/UnifyLoopExits.cpp | 12 +-
.../Transforms/FixIrreducible/multiedge.ll | 109 +++++++++++
.../Transforms/UnifyLoopExits/multiedge.ll | 171 ++++++++++++++++++
4 files changed, 306 insertions(+), 26 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index e12d1919e2f4f..5cbd7945cab11 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -703,31 +703,31 @@ BasicBlock *llvm::SplitEdge(BasicBlock *BB, BasicBlock *Succ, DominatorTree *DT,
}
/// Helper function to update the cycle or loop information after inserting a
-/// new block between a callbr instruction and one of its target blocks. Adds
-/// the new block to the innermost cycle or loop that the callbr instruction and
-/// the original target block share.
+/// new block between a callbr or switch instruction and one of its target
+/// blocks. Adds the new block to the innermost cycle or loop that the
+/// callbr/switch instruction and the original target block share.
/// \p LCI cycle or loop information to update
-/// \p CallBrBlock block containing the callbr instruction
-/// \p CallBrTarget new target block of the callbr instruction
-/// \p Succ original target block of the callbr instruction
+/// \p MultiBrBlock block containing the callbr/switch instruction
+/// \p BrTarget new target block of the callbr/switch instruction
+/// \p Succ original target block of the callbr/switch instruction
template <typename TI, typename T>
-static bool updateCycleLoopInfo(TI *LCI, BasicBlock *CallBrBlock,
- BasicBlock *CallBrTarget, BasicBlock *Succ) {
+static bool updateCycleLoopInfo(TI *LCI, BasicBlock *MultiBrBlock,
+ BasicBlock *BrTarget, BasicBlock *Succ) {
static_assert(std::is_same_v<TI, CycleInfo> || std::is_same_v<TI, LoopInfo>,
"type must be CycleInfo or LoopInfo");
if (!LCI)
return false;
if constexpr (std::is_same_v<TI, CycleInfo>) {
- T LC = LCI->getSmallestCommonCycle(CallBrBlock, Succ);
+ T LC = LCI->getSmallestCommonCycle(MultiBrBlock, Succ);
if (!LC)
return false;
- LCI->addBlockToCycle(CallBrTarget, LC);
+ LCI->addBlockToCycle(BrTarget, LC);
} else {
- T *LC = LCI->getSmallestCommonLoop(CallBrBlock, Succ);
+ T *LC = LCI->getSmallestCommonLoop(MultiBrBlock, Succ);
if (!LC)
return false;
- LC->addBasicBlockToLoop(CallBrTarget, *LCI);
+ LC->addBasicBlockToLoop(BrTarget, *LCI);
}
return true;
@@ -745,7 +745,7 @@ BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
if (UpdatedLI)
*UpdatedLI = false;
- bool ReusesCallBrTarget = BrTarget;
+ bool ReusesBrTarget = BrTarget;
// Create a new block between terminator and the specified successor.
// splitBlockBefore cannot be re-used here since it cannot split if the split
@@ -753,19 +753,19 @@ BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
// handle that). But we don't need to rewire every part of a potential PHI
// node. We only care about the edge between MultiBrBlock and the original
// successor.
- if (!ReusesCallBrTarget) {
+ if (!ReusesBrTarget) {
BrTarget = BasicBlock::Create(MultiBrBlock->getContext(),
MultiBrBlock->getName() + ".target." +
Succ->getName(),
MultiBrBlock->getParent());
// Jump from the new target block to the original successor.
UncondBrInst::Create(Succ, BrTarget);
- // Replace a single incoming value with the callbr target block. We cannot
+ // Replace a single incoming value with the target block. We cannot
// use replacePhiUsesWith, as this would replace the value for every edge
- // from the callbr block to succ.
+ // from the MultiBrBlock to the successor.
for (PHINode &PN : Succ->phis()) {
int BBIdx = PN.getBasicBlockIndex(MultiBrBlock);
- assert(BBIdx != -1 && "expected incoming value form callbr block");
+ assert(BBIdx != -1 && "expected incoming value form MultiBrBlock");
PN.setIncomingBlock(BBIdx, BrTarget);
}
@@ -779,16 +779,16 @@ BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
PN.removeIncomingValue(MultiBrBlock, false);
}
- // Rewire control flow from callbr to the new target block.
+ // Rewire control flow from the MultiBrBlock to the new target block.
Term->setSuccessor(SuccIdx, BrTarget);
if (DTU) {
- if (!ReusesCallBrTarget)
+ if (!ReusesBrTarget)
DTU->applyUpdates({{DominatorTree::Insert, MultiBrBlock, BrTarget}});
if (DTU->getDomTree().dominates(MultiBrBlock, Succ)) {
if (!is_contained(successors(MultiBrBlock), Succ))
DTU->applyUpdates({{DominatorTree::Delete, MultiBrBlock, Succ}});
- if (!ReusesCallBrTarget)
+ if (!ReusesBrTarget)
DTU->applyUpdates({{DominatorTree::Insert, BrTarget, Succ}});
}
}
diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
index 11a7d233d1bc0..81591423c1b76 100644
--- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
+++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
@@ -257,12 +257,12 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
// The guard blocks were created outside the loop, so they need to become
// members of the parent loop.
- // Same goes for the callbr target blocks. Although we try to add them to the
- // smallest common parent loop of the callbr block and the corresponding
- // original target block, there might not have been such a loop, in which case
- // the newly created callbr target blocks are not part of any loop. For nested
- // loops, this might result in them leading to a loop with multiple entry
- // points.
+ // Same goes for the callbr/switch target blocks. Although we try to add them
+ // to the smallest common parent loop of the branching block and the
+ // corresponding original target block, there might not have been such a loop,
+ // in which case the newly created target blocks are not part of any
+ // loop. For nested loops, this might result in them leading to a loop with
+ // multiple entry points.
if (auto *ParentLoop = L->getParentLoop()) {
for (auto *G : GuardBlocks) {
ParentLoop->addBasicBlockToLoop(G, LI);
diff --git a/llvm/test/Transforms/FixIrreducible/multiedge.ll b/llvm/test/Transforms/FixIrreducible/multiedge.ll
index b61bfbdf814d2..6e92b4e7ad420 100644
--- a/llvm/test/Transforms/FixIrreducible/multiedge.ll
+++ b/llvm/test/Transforms/FixIrreducible/multiedge.ll
@@ -32,6 +32,42 @@ b:
br label %a
}
+define void @multiedge_external_switch(i32 %Pred) {
+; CHECK-LABEL: define void @multiedge_external_switch(
+; CHECK-SAME: i32 [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 [[PRED]], label %[[ENTRY_TARGET_A:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_A]]
+; CHECK-NEXT: i32 1, label %[[ENTRY_TARGET_B:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[A:.*]]:
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, %[[B:.*]] ], [ [[PHI_MOVED:%.*]], %[[IRR_GUARD:.*]] ]
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[B]]:
+; CHECK-NEXT: br label %[[A]]
+; CHECK: [[ENTRY_TARGET_A]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_B]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ poison, %[[A]] ], [ 0, %[[ENTRY_TARGET_A]] ], [ poison, %[[ENTRY_TARGET_B]] ]
+; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, %[[A]] ], [ false, %[[ENTRY_TARGET_A]] ], [ true, %[[ENTRY_TARGET_B]] ]
+; CHECK-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[A]]
+;
+entry:
+ switch i32 %Pred, label %a [
+ i32 0, label %a
+ i32 1, label %b
+ ]
+
+a:
+ %phi = phi i32 [ 0, %entry ], [ 0, %entry ], [ 1, %b ]
+ br label %b
+
+b:
+ br label %a
+}
+
define void @multiedge_internal_callbr() {
; CHECK-LABEL: define void @multiedge_internal_callbr() {
; CHECK-NEXT: [[ENTRY:.*]]:
@@ -59,6 +95,36 @@ b:
br label %a
}
+define void @multiedge_internal_switch() {
+; CHECK-LABEL: define void @multiedge_internal_switch() {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[IRR_GUARD:.*]]
+; CHECK: [[A:.*]]:
+; CHECK-NEXT: switch i32 0, label %[[A_TARGET_B:.*]] [
+; CHECK-NEXT: i32 0, label %[[A_TARGET_B]]
+; CHECK-NEXT: ]
+; CHECK: [[B:.*]]:
+; CHECK-NEXT: br label %[[A]]
+; CHECK: [[A_TARGET_B]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[PHI_MOVED:%.*]] = phi i1 [ true, %[[A_TARGET_B]] ], [ false, %[[ENTRY]] ]
+; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, %[[A_TARGET_B]] ], [ true, %[[ENTRY]] ]
+; CHECK-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[A]]
+;
+entry:
+ br i1 false, label %a, label %b
+
+a:
+ switch i32 0, label %b [
+ i32 0, label %b
+ ]
+
+b:
+ %phi = phi i1 [ false, %entry ], [ true, %a ], [ true, %a ]
+ br label %a
+}
+
define void @multiedge_both_callbr() {
; CHECK-LABEL: define void @multiedge_both_callbr() {
; CHECK-NEXT: [[ENTRY:.*:]]
@@ -93,3 +159,46 @@ b:
%phi.b = phi i32 [ 2, %entry ], [ 3, %a ], [ 3, %a ]
br label %a
}
+
+define void @multiedge_both_switch() {
+; CHECK-LABEL: define void @multiedge_both_switch() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: switch i32 0, label %[[ENTRY_TARGET_A:.*]] [
+; CHECK-NEXT: i32 0, label %[[ENTRY_TARGET_A]]
+; CHECK-NEXT: i32 1, label %[[ENTRY_TARGET_B:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[A:.*]]:
+; CHECK-NEXT: [[PHI_A:%.*]] = phi i32 [ 1, %[[B:.*]] ], [ [[PHI_A_MOVED:%.*]], %[[IRR_GUARD:.*]] ]
+; CHECK-NEXT: switch i32 0, label %[[A_TARGET_B:.*]] [
+; CHECK-NEXT: i32 0, label %[[A_TARGET_B]]
+; CHECK-NEXT: ]
+; CHECK: [[B]]:
+; CHECK-NEXT: br label %[[A]]
+; CHECK: [[A_TARGET_B]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_A]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[ENTRY_TARGET_B]]:
+; CHECK-NEXT: br label %[[IRR_GUARD]]
+; CHECK: [[IRR_GUARD]]:
+; CHECK-NEXT: [[PHI_A_MOVED]] = phi i32 [ poison, %[[A_TARGET_B]] ], [ 0, %[[ENTRY_TARGET_A]] ], [ poison, %[[ENTRY_TARGET_B]] ]
+; CHECK-NEXT: [[PHI_B_MOVED:%.*]] = phi i32 [ 3, %[[A_TARGET_B]] ], [ poison, %[[ENTRY_TARGET_A]] ], [ 2, %[[ENTRY_TARGET_B]] ]
+; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, %[[A_TARGET_B]] ], [ false, %[[ENTRY_TARGET_A]] ], [ true, %[[ENTRY_TARGET_B]] ]
+; CHECK-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[A]]
+;
+entry:
+ switch i32 0, label %a [
+ i32 0, label %a
+ i32 1, label %b
+ ]
+
+a:
+ %phi.a = phi i32 [ 0, %entry ], [ 0, %entry ], [ 1, %b ]
+ switch i32 0, label %b [
+ i32 0, label %b
+ ]
+
+b:
+ %phi.b = phi i32 [ 2, %entry ], [ 3, %a ], [ 3, %a ]
+ br label %a
+}
diff --git a/llvm/test/Transforms/UnifyLoopExits/multiedge.ll b/llvm/test/Transforms/UnifyLoopExits/multiedge.ll
index 2061a9aac042e..8b854ff769981 100644
--- a/llvm/test/Transforms/UnifyLoopExits/multiedge.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/multiedge.ll
@@ -63,6 +63,79 @@ exit:
ret void
}
+define void @loop_1_switch(i1 %PredEntry, i32 %PredB, i32 %PredC, i32 %PredD) {
+; CHECK-LABEL: define void @loop_1_switch(
+; CHECK-SAME: i1 [[PREDENTRY:%.*]], i32 [[PREDB:%.*]], i32 [[PREDC:%.*]], i32 [[PREDD:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[PREDENTRY]], label %[[A:.*]], label %[[G:.*]]
+; CHECK: [[A]]:
+; CHECK-NEXT: br label %[[B:.*]]
+; CHECK: [[B]]:
+; CHECK-NEXT: switch i32 [[PREDB]], label %[[C:.*]] [
+; CHECK-NEXT: i32 0, label %[[B_TARGET_E:.*]]
+; CHECK-NEXT: i32 1, label %[[B_TARGET_E]]
+; CHECK-NEXT: ]
+; CHECK: [[C]]:
+; CHECK-NEXT: switch i32 [[PREDC]], label %[[D:.*]] [
+; CHECK-NEXT: i32 0, label %[[C_TARGET_F:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[D]]:
+; CHECK-NEXT: switch i32 [[PREDD]], label %[[A]] [
+; CHECK-NEXT: i32 0, label %[[D_TARGET_F:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[E:.*]]:
+; CHECK-NEXT: br label %[[EXIT:.*]]
+; CHECK: [[F:.*]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[G]]:
+; CHECK-NEXT: br label %[[F]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[B_TARGET_E]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]]
+; CHECK: [[C_TARGET_F]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; CHECK: [[D_TARGET_F]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; CHECK: [[LOOP_EXIT_GUARD]]:
+; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, %[[B_TARGET_E]] ], [ false, %[[C_TARGET_F]] ], [ false, %[[D_TARGET_F]] ]
+; CHECK-NEXT: br i1 [[GUARD_E]], label %[[E]], label %[[F]]
+;
+entry:
+ br i1 %PredEntry, label %A, label %G
+
+A:
+ br label %B
+
+B:
+ switch i32 %PredB, label %C [
+ i32 0, label %E
+ i32 1, label %E
+ ]
+
+C:
+ switch i32 %PredC, label %D [
+ i32 0, label %F
+ ]
+
+D:
+ switch i32 %PredD, label %A [
+ i32 0, label %F
+ ]
+
+E:
+ br label %exit
+
+F:
+ br label %exit
+
+G:
+ br label %F
+
+exit:
+ ret void
+}
+
define void @multiedge_both_callbr(i1 %Pred) {
; CHECK-LABEL: define void @multiedge_both_callbr(
; CHECK-SAME: i1 [[PRED:%.*]]) {
@@ -96,6 +169,47 @@ exit:
ret void
}
+define void @multiedge_both_switch(i32 %Pred) {
+; CHECK-LABEL: define void @multiedge_both_switch(
+; CHECK-SAME: i32 [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: switch i32 [[PRED]], label %[[LOOP_NEXT:.*]] [
+; CHECK-NEXT: i32 0, label %[[LOOP_NEXT]]
+; CHECK-NEXT: ]
+; CHECK: [[LOOP_NEXT]]:
+; CHECK-NEXT: [[PHI_INTERNAL:%.*]] = phi i32 [ 1, %[[LOOP]] ], [ 1, %[[LOOP]] ]
+; CHECK-NEXT: switch i32 [[PRED]], label %[[LOOP]] [
+; CHECK-NEXT: i32 0, label %[[LOOP_NEXT_TARGET_EXIT:.*]]
+; CHECK-NEXT: i32 1, label %[[LOOP_NEXT_TARGET_EXIT]]
+; CHECK-NEXT: ]
+; CHECK: [[EXIT:.*]]:
+; CHECK-NEXT: [[PHI_EXTERNAL:%.*]] = phi i32 [ 2, %[[LOOP_NEXT_TARGET_EXIT]] ]
+; CHECK-NEXT: ret void
+; CHECK: [[LOOP_NEXT_TARGET_EXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+;
+entry:
+ br label %loop
+
+loop:
+ switch i32 %Pred, label %loop.next [
+ i32 0, label %loop.next
+ ]
+
+loop.next:
+ %phi.internal = phi i32 [ 1, %loop ], [ 1, %loop ]
+ switch i32 %Pred, label %loop [
+ i32 0, label %exit
+ i32 1, label %exit
+ ]
+
+exit:
+ %phi.external = phi i32 [ 2, %loop.next ], [ 2, %loop.next ]
+ ret void
+}
+
define i32 @multiedge_restore_ssa_callbr() {
; CHECK-LABEL: define i32 @multiedge_restore_ssa_callbr() {
; CHECK-NEXT: [[ENTRY:.*:]]
@@ -142,3 +256,60 @@ exit1: ; preds = %second_exiting, %he
%use1 = or i32 %val, 0
ret i32 0
}
+
+define i32 @multiedge_restore_ssa_switch() {
+; CHECK-LABEL: define i32 @multiedge_restore_ssa_switch() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[HEADER:.*]]
+; CHECK: [[HEADER]]:
+; CHECK-NEXT: [[VAL:%.*]] = add i32 0, 0
+; CHECK-NEXT: switch i32 0, label %[[HEADER_TARGET_EXIT1:.*]] [
+; CHECK-NEXT: i32 0, label %[[HEADER_TARGET_COMMON_RET:.*]]
+; CHECK-NEXT: i32 1, label %[[SECOND_EXITING:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[SECOND_EXITING]]:
+; CHECK-NEXT: switch i32 0, label %[[HEADER]] [
+; CHECK-NEXT: i32 0, label %[[SECOND_EXITING_TARGET_EXIT1:.*]]
+; CHECK-NEXT: i32 1, label %[[SECOND_EXITING_TARGET_COMMON_RET:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret i32 0
+; CHECK: [[EXIT1:.*]]:
+; CHECK-NEXT: [[USE1:%.*]] = or i32 [[VAL_MOVED:%.*]], 0
+; CHECK-NEXT: ret i32 0
+; CHECK: [[HEADER_TARGET_EXIT1]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]]
+; CHECK: [[HEADER_TARGET_COMMON_RET]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; CHECK: [[SECOND_EXITING_TARGET_EXIT1]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; CHECK: [[SECOND_EXITING_TARGET_COMMON_RET]]:
+; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]]
+; CHECK: [[LOOP_EXIT_GUARD]]:
+; CHECK-NEXT: [[VAL_MOVED]] = phi i32 [ [[VAL]], %[[HEADER_TARGET_EXIT1]] ], [ [[VAL]], %[[SECOND_EXITING_TARGET_EXIT1]] ], [ [[VAL]], %[[HEADER_TARGET_COMMON_RET]] ], [ [[VAL]], %[[SECOND_EXITING_TARGET_COMMON_RET]] ]
+; CHECK-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[HEADER_TARGET_EXIT1]] ], [ false, %[[HEADER_TARGET_COMMON_RET]] ], [ true, %[[SECOND_EXITING_TARGET_EXIT1]] ], [ false, %[[SECOND_EXITING_TARGET_COMMON_RET]] ]
+; CHECK-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[COMMON_RET]]
+;
+entry:
+ br label %header
+
+header: ; preds = %second_exiting, %entry
+ %val = add i32 0, 0
+ switch i32 0, label %exit1 [
+ i32 0, label %common.ret
+ i32 1, label %second_exiting
+ ]
+
+second_exiting: ; preds = %header
+ switch i32 0, label %header [
+ i32 0, label %exit1
+ i32 1, label %common.ret
+ ]
+
+common.ret: ; preds = %second_exiting, %header
+ ret i32 0
+
+exit1: ; preds = %second_exiting, %header
+ %use1 = or i32 %val, 0
+ ret i32 0
+}
>From 26db602c25505427be581ce38659069b9dacad1c Mon Sep 17 00:00:00 2001
From: Lukas <dev at lukas-hertel.de>
Date: Sun, 12 Jul 2026 21:46:25 +0200
Subject: [PATCH 4/5] Remove multiedge in unify loop exits test
---
llvm/test/Transforms/UnifyLoopExits/basic.ll | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/llvm/test/Transforms/UnifyLoopExits/basic.ll b/llvm/test/Transforms/UnifyLoopExits/basic.ll
index 461bd3ee9c23d..a6f8c5635439f 100644
--- a/llvm/test/Transforms/UnifyLoopExits/basic.ll
+++ b/llvm/test/Transforms/UnifyLoopExits/basic.ll
@@ -398,14 +398,12 @@ define void @switch_duplicate_successors(i1 %Pred) {
; CHECK: loop:
; CHECK-NEXT: switch i1 [[PRED:%.*]], label [[LOOP_TARGET_EXIT:%.*]] [
; CHECK-NEXT: i1 false, label [[LOOP]]
-; CHECK-NEXT: i1 true, label [[LOOP_TARGET_EXIT1:%.*]]
+; CHECK-NEXT: i1 true, label [[LOOP_TARGET_EXIT]]
; CHECK-NEXT: ]
; CHECK: exit:
; CHECK-NEXT: ret void
; CHECK: loop.target.exit:
; CHECK-NEXT: br label [[EXIT:%.*]]
-; CHECK: loop.target.exit1:
-; CHECK-NEXT: br label [[EXIT]]
;
entry:
br label %loop
>From 9704bf275793e87d52d394c91565172ed671ef6c Mon Sep 17 00:00:00 2001
From: Lukas <dev at lukas-hertel.de>
Date: Wed, 15 Jul 2026 21:59:19 +0200
Subject: [PATCH 5/5] address feedback: typos, hoist term initialization,
llvm_unreachable
---
llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 6 ++++--
llvm/lib/Transforms/Utils/FixIrreducible.cpp | 20 ++++++++----------
llvm/lib/Transforms/Utils/UnifyLoopExits.cpp | 21 ++++++++++---------
llvm/test/Transforms/FixIrreducible/switch.ll | 14 ++++++-------
4 files changed, 31 insertions(+), 30 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index 5cbd7945cab11..5ea4e21e5919c 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -738,7 +738,9 @@ BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
DomTreeUpdater *DTU, CycleInfo *CI,
LoopInfo *LI, bool *UpdatedLI) {
Instruction *Term = MultiBrBlock->getTerminator();
- assert(Term && "expected terminator");
+ assert((isa_and_present<SwitchInst>(Term) ||
+ isa_and_present<CallBrInst>(Term)) &&
+ "expected callbr or switch terminator");
assert(SuccIdx < Term->getNumSuccessors() &&
Succ == Term->getSuccessor(SuccIdx) && "invalid successor index");
@@ -765,7 +767,7 @@ BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
// from the MultiBrBlock to the successor.
for (PHINode &PN : Succ->phis()) {
int BBIdx = PN.getBasicBlockIndex(MultiBrBlock);
- assert(BBIdx != -1 && "expected incoming value form MultiBrBlock");
+ assert(BBIdx != -1 && "expected incoming value from MultiBrBlock");
PN.setIncomingBlock(BBIdx, BrTarget);
}
diff --git a/llvm/lib/Transforms/Utils/FixIrreducible.cpp b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
index c4f7460ef0c98..23f458c4a8991 100644
--- a/llvm/lib/Transforms/Utils/FixIrreducible.cpp
+++ b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
@@ -292,13 +292,14 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
}
for (BasicBlock *P : Predecessors) {
- if (isa<UncondBrInst>(P->getTerminator())) {
- assert(P->getTerminator()->getSuccessor(0) == Header);
+ Instruction *Term = P->getTerminator();
+ if (isa<UncondBrInst>(Term)) {
+ assert(Term->getSuccessor(0) == Header);
CHub.addBranch(P, Header);
LLVM_DEBUG(dbgs() << "Added internal branch: " << printBasicBlock(P)
<< " -> " << printBasicBlock(Header) << '\n');
- } else if (CondBrInst *Branch = dyn_cast<CondBrInst>(P->getTerminator())) {
+ } else if (CondBrInst *Branch = dyn_cast<CondBrInst>(Term)) {
BasicBlock *Succ0 = Branch->getSuccessor(0) == Header ? Header : nullptr;
BasicBlock *Succ1 = Branch->getSuccessor(1) == Header ? Header : nullptr;
assert(Succ0 || Succ1);
@@ -308,10 +309,8 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
<< " -> " << printBasicBlock(Succ0)
<< (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
<< '\n');
- } else if (isa<CallBrInst>(P->getTerminator()) ||
- isa<SwitchInst>(P->getTerminator())) {
+ } else if (isa<CallBrInst>(Term) || isa<SwitchInst>(Term)) {
BasicBlock *NewSucc = nullptr;
- Instruction *Term = P->getTerminator();
for (unsigned I = 0; I < Term->getNumSuccessors(); ++I) {
BasicBlock *Succ = Term->getSuccessor(I);
if (Succ != Header)
@@ -340,14 +339,15 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
}
for (BasicBlock *P : Predecessors) {
- if (UncondBrInst *Branch = dyn_cast<UncondBrInst>(P->getTerminator())) {
+ Instruction *Term = P->getTerminator();
+ if (UncondBrInst *Branch = dyn_cast<UncondBrInst>(Term)) {
BasicBlock *Succ0 = Branch->getSuccessor();
Succ0 = CI.contains(C, Succ0) ? Succ0 : nullptr;
CHub.addBranch(P, Succ0);
LLVM_DEBUG(dbgs() << "Added external branch: " << printBasicBlock(P)
<< " -> " << printBasicBlock(Succ0) << '\n');
- } else if (CondBrInst *Branch = dyn_cast<CondBrInst>(P->getTerminator())) {
+ } else if (CondBrInst *Branch = dyn_cast<CondBrInst>(Term)) {
BasicBlock *Succ0 = Branch->getSuccessor(0);
Succ0 = CI.contains(C, Succ0) ? Succ0 : nullptr;
BasicBlock *Succ1 = Branch->getSuccessor(1);
@@ -358,10 +358,8 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
<< " -> " << printBasicBlock(Succ0)
<< (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
<< '\n');
- } else if (isa<CallBrInst>(P->getTerminator()) ||
- isa<SwitchInst>(P->getTerminator())) {
+ } else if (isa<CallBrInst>(Term) || isa<SwitchInst>(Term)) {
SmallDenseMap<BasicBlock *, BasicBlock *> MultiBrTargets;
- Instruction *Term = P->getTerminator();
for (unsigned I = 0; I < Term->getNumSuccessors(); ++I) {
BasicBlock *Succ = Term->getSuccessor(I);
if (!CI.contains(C, Succ))
diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
index 81591423c1b76..ad90480a1136c 100644
--- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
+++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
@@ -170,14 +170,15 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
unsigned NumExitingBlocks = ExitingBlocks.size();
for (unsigned I = 0; I < NumExitingBlocks; ++I) {
BasicBlock *BB = ExitingBlocks[I];
- if (UncondBrInst *Branch = dyn_cast<UncondBrInst>(BB->getTerminator())) {
+ Instruction *Term = BB->getTerminator();
+ if (UncondBrInst *Branch = dyn_cast<UncondBrInst>(Term)) {
BasicBlock *Succ0 = Branch->getSuccessor(0);
Succ0 = L->contains(Succ0) ? nullptr : Succ0;
CHub.addBranch(BB, Succ0);
- LLVM_DEBUG(dbgs() << "Added extiting branch: " << printBasicBlock(BB)
+ LLVM_DEBUG(dbgs() << "Added exiting branch: " << printBasicBlock(BB)
<< " -> " << printBasicBlock(Succ0) << '\n');
- } else if (CondBrInst *Branch = dyn_cast<CondBrInst>(BB->getTerminator())) {
+ } else if (CondBrInst *Branch = dyn_cast<CondBrInst>(Term)) {
BasicBlock *Succ0 = Branch->getSuccessor(0);
Succ0 = L->contains(Succ0) ? nullptr : Succ0;
@@ -185,14 +186,12 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
Succ1 = L->contains(Succ1) ? nullptr : Succ1;
CHub.addBranch(BB, Succ0, Succ1);
- LLVM_DEBUG(dbgs() << "Added extiting branch: " << printBasicBlock(BB)
+ LLVM_DEBUG(dbgs() << "Added exiting branch: " << printBasicBlock(BB)
<< " -> " << printBasicBlock(Succ0)
<< (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
<< '\n');
- } else if (isa<CallBrInst>(BB->getTerminator()) ||
- isa<SwitchInst>(BB->getTerminator())) {
+ } else if (isa<CallBrInst>(Term) || isa<SwitchInst>(Term)) {
SmallDenseMap<BasicBlock *, BasicBlock *> BrTargets;
- Instruction *Term = BB->getTerminator();
for (unsigned J = 0; J < Term->getNumSuccessors(); ++J) {
BasicBlock *Succ = Term->getSuccessor(J);
if (L->contains(Succ))
@@ -205,7 +204,7 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
&DTU, nullptr, &LI, &UpdatedLI);
if (!ExistingTarget) {
- // SplitMutliBrEdge modifies the CFG because it creates an
+ // SplitMultiBrEdge modifies the CFG because it creates an
// intermediate block. So we need to set the changed flag no matter
// what the ControlFlowHub is going to do later.
Changed = true;
@@ -219,7 +218,7 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
// predecessors of the guard blocks, which, in the case of callbr or
// switch terminator, are the new intermediate target blocks instead
// of themselves. If only one exiting block is generated, the
- // branchning block itself is overwritten, while further blocks are
+ // branching block itself is overwritten, while further blocks are
// appended as additional exiting blocks.
if (BrTargets.empty())
ExitingBlocks[I] = NewSucc;
@@ -233,7 +232,9 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
<< printBasicBlock(Succ) << '\n');
}
} else {
- llvm_unreachable("unsupported block terminator");
+ reportFatalUsageError(
+ "unsupported block terminator: unify-loop-exits "
+ "only supports br, callbr, and switch instructions");
}
}
diff --git a/llvm/test/Transforms/FixIrreducible/switch.ll b/llvm/test/Transforms/FixIrreducible/switch.ll
index 0da0a0d28735c..39c80e3171bb7 100644
--- a/llvm/test/Transforms/FixIrreducible/switch.ll
+++ b/llvm/test/Transforms/FixIrreducible/switch.ll
@@ -27,7 +27,7 @@
; CYCLES-BEFORE-NEXT: depth=1: entries(indirect1 indirect fallthrough)
; CYCLES-BEFORE-NEXT: depth=2: entries(indirect fallthrough)
; CYCLES-AFTER-NEXT: CycleInfo for function: switch_entry_multiple_targets1
-; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect1 indirect fallthrough irr.guard1 irr.guard2
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect1 indirect fallthrough irr.guard2 irr.guard1
; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) indirect fallthrough
;
; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_header_no_indirect
@@ -65,7 +65,7 @@
; CYCLES-BEFORE-NEXT: depth=1: entries(switch_header) switch_regular mid
; CYCLES-BEFORE-NEXT: depth=2: entries(switch_regular mid)
; CYCLES-AFTER-NEXT: CycleInfo for function: switch_header_and_regular
-; CYCLES-AFTER-NEXT: depth=1: entries(switch_header) switch_regular mid switch_header.target.mid switch_header.target.switch_regular irr.guard
+; CYCLES-AFTER-NEXT: depth=1: entries(switch_header) switch_regular mid irr.guard switch_header.target.mid switch_header.target.switch_regular
; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard) switch_regular mid
;
; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_only
@@ -77,7 +77,7 @@
; CYCLES-BEFORE-NEXT: depth=1: entries(sw2 block block1)
; CYCLES-BEFORE-NEXT: depth=2: entries(block block1)
; CYCLES-AFTER-NEXT: CycleInfo for function: entry_multiple_switch
-; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) sw2 block block1 irr.guard1 sw2.target.block1 sw2.target.block irr.guard2
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) sw2 block block1 irr.guard2 irr.guard1 sw2.target.block1 sw2.target.block
; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) block block1
;
; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_exit_with_separate_entries
@@ -96,21 +96,21 @@
; CYCLES-BEFORE-NEXT: depth=1: entries(sw3 sw1 sw2)
; CYCLES-BEFORE-NEXT: depth=2: entries(sw1 sw2)
; CYCLES-AFTER-NEXT: CycleInfo for function: switch_only_multiple
-; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) sw3 sw1 sw2 sw2.target.sw3 sw1.target.sw3 irr.guard1 sw2.target.sw1 sw3.target.sw1 irr.guard2
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) sw3 sw1 sw2 sw2.target.sw1 irr.guard2 sw2.target.sw3 sw1.target.sw3 irr.guard1 sw3.target.sw1
; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) sw1 sw2 sw2.target.sw1
;
; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_bypass
; CYCLES-BEFORE-NEXT: depth=1: entries(l1 sw) l2
; CYCLES-BEFORE-NEXT: depth=2: entries(sw l2)
; CYCLES-AFTER-NEXT: CycleInfo for function: switch_bypass
-; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l1 sw l2 sw.target.l1 irr.guard1
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l1 sw l2 irr.guard1 sw.target.l1
; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard1) sw l2
;
; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_multiple_with_exit
; CYCLES-BEFORE-NEXT: depth=1: entries(l3 l1 l2)
; CYCLES-BEFORE-NEXT: depth=2: entries(l1 l2)
; CYCLES-AFTER-NEXT: CycleInfo for function: switch_multiple_with_exit
-; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l3 l1 l2 irr.guard1 irr.guard2
+; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l3 l1 l2 irr.guard2 irr.guard1
; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) l1 l2
;
; CYCLES-BEFORE-NEXT: CycleInfo for function: switch_nested
@@ -119,7 +119,7 @@
; CYCLES-AFTER-NEXT: CycleInfo for function: switch_nested
; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) bb bh
; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard1) b h
-;
+
define void @loop_1(i32 %Value, i1 %PredEntry, i1 %PredD) {
; CHECK-LABEL: define void @loop_1(
; CHECK-SAME: i32 [[VALUE:%.*]], i1 [[PREDENTRY:%.*]], i1 [[PREDD:%.*]]) {
More information about the llvm-commits
mailing list