[llvm] [FixIrreducible] Support switch statements (PR #206567)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 29 11:58:04 PDT 2026


https://github.com/hertelukas created https://github.com/llvm/llvm-project/pull/206567

This builds on #149308, which implemented `callbr` support.

The same approach of splitting multi-branch edges entering irreducible loops can be used for switches. It does not introduce any new switch statements, but alleviates the requirement of having to run the `LowerSwitch` pass first. This makes the pass usable for users other than AMDGPU, which can handle switch statements (e.g., WebAssembly, see [here](https://github.com/llvm/llvm-project/pull/181755#issuecomment-3936316307)).

I'm also implementing a flag into the `ControlFlowHub` to support switches as well, for which I will open a separate PR. Currently the `ControlFlowHub` creates a branch-sled, mimicking a `switch` (a series of blocks consisting only of conditional branches, selectively entering an irreducible loop at a certain point or continuing to the next potential entry point). If the target can support switches, that should be the clear goal here, as shown in the image below. This PR implements the step from (a) to (b), whereas extending the `ControlFlowHub` represents the step from (b) to (c). ((c) to (d) already exists through `SwitchToLookup` and `SwitchToArithmetic`).

<img width="1055" height="432" alt="image" src="https://github.com/user-attachments/assets/780b7894-1895-4137-8148-b2bbc7f73a8f" />

The test cases in `switch.ll` were adapted from the `callbr.ll` tests using an LLM to ensure equivalent coverage. I've manually reviewed the control flow and conditions for every case, and the final assertions were generated using `update_test_checks.py`.

>From 3a6b883bc87a2104ed1e03a19d4610d16cc30370 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] [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.ll test structure to switch.ll)
---
 .../llvm/Transforms/Utils/BasicBlockUtils.h   |  23 +-
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp |  54 +-
 llvm/lib/Transforms/Utils/FixIrreducible.cpp  |  52 +-
 llvm/lib/Transforms/Utils/UnifyLoopExits.cpp  |  38 +-
 llvm/test/Transforms/FixIrreducible/switch.ll | 960 +++++++++++++++++-
 .../FixIrreducible/unsupported-terminator.ll  |  25 +-
 6 files changed, 1032 insertions(+), 120 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
index f9bfff09673f0..abf19177bd7da 100644
--- a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h
@@ -267,18 +267,17 @@ 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
-///                       instruction
+/// \param SuccIdx        index of the original successor in the terminator
 /// \param DTU            optional \p DomTreeUpdater for updating the
 ///                       dominator tree
 /// \param CI             optional \p CycleInfo for updating cycle membership
@@ -291,9 +290,9 @@ 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,
-                DomTreeUpdater *DTU = nullptr, CycleInfo *CI = nullptr,
-                LoopInfo *LI = nullptr, bool *UpdatedLI = nullptr);
+SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ, unsigned SuccIdx,
+                 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 425faf57f8b63..fbc0ba71b37de 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -725,47 +725,47 @@ static bool updateCycleLoopInfo(TI *LCI, BasicBlock *CallBrBlock,
   return true;
 }
 
-BasicBlock *llvm::SplitCallBrEdge(BasicBlock *CallBrBlock, BasicBlock *Succ,
-                                  unsigned SuccIdx, 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");
-
-  // Create a new block between callbr and the specified successor.
+BasicBlock *llvm::SplitMultiBrEdge(BasicBlock *MultiBrBlock, BasicBlock *Succ,
+                                   unsigned SuccIdx, 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");
+
+  // 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.
-  BasicBlock *CallBrTarget =
-      BasicBlock::Create(CallBrBlock->getContext(),
-                         CallBrBlock->getName() + ".target." + Succ->getName(),
-                         CallBrBlock->getParent());
+  BasicBlock *BrTarget =
+      BasicBlock::Create(MultiBrBlock->getContext(),
+                         MultiBrBlock->getName() + ".target." + Succ->getName(),
+                         MultiBrBlock->getParent());
   // Rewire control flow from the new target block to the original successor.
-  Succ->replacePhiUsesWith(CallBrBlock, CallBrTarget);
-  // Rewire control flow from callbr to the new target block.
-  CallBr->setSuccessor(SuccIdx, CallBrTarget);
+  Succ->replacePhiUsesWith(MultiBrBlock, BrTarget);
+  // Rewire control flow from the MultiBrBlock to the new target block.
+  Term->setSuccessor(SuccIdx, BrTarget);
   // Jump from the new target block to the original successor.
-  UncondBrInst::Create(Succ, CallBrTarget);
+  UncondBrInst::Create(Succ, BrTarget);
 
   bool Updated =
-      updateCycleLoopInfo<LoopInfo, Loop>(LI, CallBrBlock, CallBrTarget, Succ);
+      updateCycleLoopInfo<LoopInfo, Loop>(LI, MultiBrBlock, BrTarget, Succ);
   if (UpdatedLI)
     *UpdatedLI = Updated;
-  updateCycleLoopInfo<CycleInfo, Cycle>(CI, CallBrBlock, CallBrTarget, Succ);
+  updateCycleLoopInfo<CycleInfo, Cycle>(CI, MultiBrBlock, BrTarget, Succ);
   if (DTU) {
-    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, CallBrTarget, 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}});
+      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 ead39f5991081..3b445492dc564 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
@@ -132,6 +131,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"
@@ -309,20 +309,23 @@ static bool fixIrreducible(Cycle &C, CycleInfo &CI, DominatorTree &DT,
                         << " -> " << printBasicBlock(Succ0)
                         << (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
                         << '\n');
-    } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(P->getTerminator())) {
-      for (unsigned I = 0; I < CallBr->getNumSuccessors(); ++I) {
-        BasicBlock *Succ = CallBr->getSuccessor(I);
+    } else if (isa<CallBrInst>(P->getTerminator()) ||
+               isa<SwitchInst>(P->getTerminator())) {
+      Instruction *Term = P->getTerminator();
+      for (unsigned I = 0; I < Term->getNumSuccessors(); ++I) {
+        BasicBlock *Succ = Term->getSuccessor(I);
         if (Succ != Header)
           continue;
-        BasicBlock *NewSucc = SplitCallBrEdge(P, Succ, I, &DTU, &CI, LI);
+        BasicBlock *NewSucc = SplitMultiBrEdge(P, Succ, I, &DTU, &CI, LI);
         CHub.addBranch(NewSucc, Succ);
         LLVM_DEBUG(dbgs() << "Added internal 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");
     }
   }
 
@@ -354,20 +357,23 @@ static bool fixIrreducible(Cycle &C, CycleInfo &CI, DominatorTree &DT,
                         << " -> " << printBasicBlock(Succ0)
                         << (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1)
                         << '\n');
-    } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(P->getTerminator())) {
-      for (unsigned I = 0; I < CallBr->getNumSuccessors(); ++I) {
-        BasicBlock *Succ = CallBr->getSuccessor(I);
+    } else if (isa<CallBrInst>(P->getTerminator()) ||
+               isa<SwitchInst>(P->getTerminator())) {
+      Instruction *Term = P->getTerminator();
+      for (unsigned I = 0; I < Term->getNumSuccessors(); ++I) {
+        BasicBlock *Succ = Term->getSuccessor(I);
         if (!C.contains(Succ))
           continue;
-        BasicBlock *NewSucc = SplitCallBrEdge(P, Succ, I, &DTU, &CI, LI);
+        BasicBlock *NewSucc = SplitMultiBrEdge(P, Succ, I, &DTU, &CI, LI);
         CHub.addBranch(NewSucc, Succ);
         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 6947cb4f92723..657f407c020a5 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.
 //
 //===----------------------------------------------------------------------===//
 
@@ -26,6 +26,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"
@@ -159,7 +160,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;
@@ -186,27 +187,30 @@ 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())) {
-      for (unsigned J = 0; J < CallBr->getNumSuccessors(); ++J) {
-        BasicBlock *Succ = CallBr->getSuccessor(J);
+    } else if (isa<CallBrInst>(BB->getTerminator()) ||
+               isa<SwitchInst>(BB->getTerminator())) {
+      Instruction *Term = BB->getTerminator();
+      for (unsigned J = 0; J < Term->getNumSuccessors(); ++J) {
+        BasicBlock *Succ = Term->getSuccessor(J);
         if (L->contains(Succ))
           continue;
         bool UpdatedLI = false;
         BasicBlock *NewSucc =
-            SplitCallBrEdge(BB, Succ, J, &DTU, nullptr, &LI, &UpdatedLI);
-        // SplitCallBrEdge modifies the CFG because it creates an intermediate
+            SplitMultiBrEdge(BB, Succ, J, &DTU, nullptr, &LI, &UpdatedLI);
+        // 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;
-        // 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.
+        // predecessors of the guard blocks, which, in the case of a callbr or
+        // switch terminator, are the new intermediate target blocks instead of
+        // the blocks themselves.
         ExitingBlocks[I] = NewSucc;
         CHub.addBranch(NewSucc, Succ);
         LLVM_DEBUG(dbgs() << "Added exiting branch: "
@@ -248,7 +252,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



More information about the llvm-commits mailing list