[llvm] [ControlFlowHub] Add support for switch-based guards (PR #213549)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 2 07:59:40 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Lukas (hertelukas)
<details>
<summary>Changes</summary>
Follow-up on #<!-- -->206567.
Implements switch guards for the `ControlFlowHub`. To route correctly, every outgoing edge is numbered and selected based on the predecessor through a `merged.bb.idx` phi. In the case the incoming BB has two incident edges, the conditional branch is replaced with a `select` instruction, which is then used in the routing phi.
Added switch generation as an option for `UnifyLoopExits` and `FixIrreducible`.
cc @<!-- -->ro-i
Assisted by: Gemini 3.1 Pro for code review
---
Patch is 53.19 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/213549.diff
10 Files Affected:
- (modified) llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h (+28-8)
- (modified) llvm/include/llvm/Transforms/Utils/FixIrreducible.h (+4)
- (modified) llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h (+3)
- (modified) llvm/lib/Passes/PassBuilder.cpp (+10)
- (modified) llvm/lib/Passes/PassRegistry.def (+8-2)
- (modified) llvm/lib/Transforms/Utils/ControlFlowUtils.cpp (+85-5)
- (modified) llvm/lib/Transforms/Utils/FixIrreducible.cpp (+6-6)
- (modified) llvm/lib/Transforms/Utils/UnifyLoopExits.cpp (+8-7)
- (added) llvm/test/Transforms/FixIrreducible/switch_guards.ll (+344)
- (added) llvm/test/Transforms/UnifyLoopExits/switch_guards.ll (+428)
``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h b/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h
index e4dab90031e81..93558a1351102 100644
--- a/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/ControlFlowUtils.h
@@ -25,11 +25,19 @@ class DomTreeUpdater;
/// Given a set of branch descriptors [BB, Succ0, Succ1], create a "hub" such
/// that the control flow from each BB to a successor is now split into two
-/// edges, one from BB to the hub and another from the hub to the successor. The
-/// hub consists of a series of guard blocks, one for each outgoing block. Each
-/// guard block conditionally branches to the corresponding outgoing block, or
-/// the next guard block in the chain. These guard blocks are returned in the
-/// argument vector.
+/// edges, one from BB to the hub and another from the hub to the successor.
+/// There are two ways to configure the hub: either as a branch sled or as a
+/// single switch.
+///
+/// For the branch sled case, the hub consists of a series of guard blocks, one
+/// for each outgoing block. Each guard block conditionally branches to the
+/// corresponding outgoing block, or the next guard block in the chain. These
+/// guard blocks are returned in the argument vector `GuardBlocks`.
+///
+/// In the case of a switch guard, the hub consists only of a single guard block
+/// containing a switch instruction that branches to all outgoing blocks (with an
+/// unreachable default destination). `GuardBlocks` will contain this single
+/// guard block.
///
/// This also updates any PHINodes in the successor. For each such PHINode, the
/// operands corresponding to incoming blocks are moved to a new PHINode in the
@@ -113,14 +121,26 @@ struct ControlFlowHub {
Branches.emplace_back(BB, Succ0, Succ1);
}
- /// Return the unified loop exit block and a flag indicating if the CFG was
- /// changed at all.
+ /// Return the first guard block (or the single switch guard block) and a
+ /// flag indicating if the CFG was changed at all.
LLVM_ABI std::pair<BasicBlock *, bool>
finalize(DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
const StringRef Prefix,
- std::optional<unsigned> MaxControlFlowBooleans = std::nullopt);
+ std::optional<unsigned> MaxControlFlowBooleans = std::nullopt,
+ bool SwitchGuards = false);
SmallVector<BranchDescriptor> Branches;
+
+private:
+ BasicBlock *finalizeAsBrSled(DomTreeUpdater *DTU,
+ SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix,
+ std::optional<unsigned> MaxControlFlowBooleans,
+ SetVector<BasicBlock *> &Outgoing);
+ BasicBlock *finalizeAsSwitch(DomTreeUpdater *DTU,
+ SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix,
+ SetVector<BasicBlock *> &Outgoing);
};
} // end namespace llvm
diff --git a/llvm/include/llvm/Transforms/Utils/FixIrreducible.h b/llvm/include/llvm/Transforms/Utils/FixIrreducible.h
index b10ca1a590a37..84358f00b94b6 100644
--- a/llvm/include/llvm/Transforms/Utils/FixIrreducible.h
+++ b/llvm/include/llvm/Transforms/Utils/FixIrreducible.h
@@ -13,7 +13,11 @@
namespace llvm {
struct FixIrreduciblePass : OptionalPassInfoMixin<FixIrreduciblePass> {
+
+ FixIrreduciblePass(bool SwitchGuards = false) : SwitchGuards(SwitchGuards) {}
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+ bool SwitchGuards;
};
} // namespace llvm
diff --git a/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h b/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h
index c304ff912bb5e..627ff1563d972 100644
--- a/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h
+++ b/llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h
@@ -15,7 +15,10 @@ namespace llvm {
class UnifyLoopExitsPass : public RequiredPassInfoMixin<UnifyLoopExitsPass> {
public:
+ UnifyLoopExitsPass(bool SwitchGuards = false) : SwitchGuards(SwitchGuards) {}
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+ bool SwitchGuards;
};
} // namespace llvm
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index f758469dd8ebb..8a222bb1f7d5a 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -968,6 +968,11 @@ Expected<bool> parseDropUnnecessaryAssumesPassOptions(StringRef Params) {
"DropUnnecessaryAssumes");
}
+Expected<bool> parseFixIrreduciblePassOptions(StringRef Params) {
+ return PassBuilder::parseSinglePassOption(Params, "switch-guards",
+ "FixIrreducible");
+}
+
Expected<bool> parseLoopExtractorPassOptions(StringRef Params) {
return PassBuilder::parseSinglePassOption(Params, "single", "LoopExtractor");
}
@@ -977,6 +982,11 @@ Expected<bool> parseLowerMatrixIntrinsicsPassOptions(StringRef Params) {
"LowerMatrixIntrinsics");
}
+Expected<bool> parseUnifyLoopExitsPassOptions(StringRef Params) {
+ return PassBuilder::parseSinglePassOption(Params, "switch-guards",
+ "UnifyLoopExits");
+}
+
Expected<IRNormalizerOptions> parseIRNormalizerPassOptions(StringRef Params) {
IRNormalizerOptions Result;
while (!Params.empty()) {
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 0955bfdb3246c..b94316424313d 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -452,7 +452,6 @@ FUNCTION_PASS("expand-memcmp", ExpandMemCmpPass())
FUNCTION_PASS("expand-reductions", ExpandReductionsPass())
FUNCTION_PASS("extra-vector-passes",
ExtraFunctionPassManager<ShouldRunExtraVectorPasses>())
-FUNCTION_PASS("fix-irreducible", FixIrreduciblePass())
FUNCTION_PASS("flatten-cfg", FlattenCFGPass())
FUNCTION_PASS("float2int", Float2IntPass())
FUNCTION_PASS("free-machine-function", FreeMachineFunctionPass())
@@ -565,7 +564,6 @@ FUNCTION_PASS("trigger-crash-function", TriggerCrashFunctionPass())
FUNCTION_PASS("trigger-verifier-error", TriggerVerifierErrorPass())
FUNCTION_PASS("tsan", ThreadSanitizerPass())
FUNCTION_PASS("typepromotion", TypePromotionPass(*TM))
-FUNCTION_PASS("unify-loop-exits", UnifyLoopExitsPass())
FUNCTION_PASS("unreachableblockelim", UnreachableBlockElimPass())
FUNCTION_PASS("vector-combine", VectorCombinePass())
FUNCTION_PASS("verify", VerifierPass())
@@ -600,6 +598,10 @@ FUNCTION_PASS_WITH_PARAMS(
"ee-instrument", "EntryExitInstrumenterPass",
[](bool PostInlining) { return EntryExitInstrumenterPass(PostInlining); },
parseEntryExitInstrumenterPassOptions, "post-inline")
+FUNCTION_PASS_WITH_PARAMS(
+ "fix-irreducible", "FixIrreduciblePass",
+ [](bool SwitchGuards) { return FixIrreduciblePass(SwitchGuards); },
+ parseFixIrreduciblePassOptions, "switch-guards")
FUNCTION_PASS_WITH_PARAMS(
"func-properties-stats", "FunctionPropertiesStatisticsPass",
[](bool IsPreOptimizations) {
@@ -737,6 +739,10 @@ FUNCTION_PASS_WITH_PARAMS(
return StructurizeCFGPass(SkipUniformRegions);
},
parseStructurizeCFGPassOptions, "skip-uniform-regions")
+FUNCTION_PASS_WITH_PARAMS(
+ "unify-loop-exits", "UnifyLoopExitsPass",
+ [](bool SwitchGuards) { return UnifyLoopExitsPass(SwitchGuards); },
+ parseUnifyLoopExitsPassOptions, "switch-guards")
FUNCTION_PASS_WITH_PARAMS(
"win-eh-prepare", "WinEHPreparePass",
[](bool DemoteCatchSwitchPHIOnly) {
diff --git a/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp b/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp
index cf3bacc08f5a5..b458c3af44e93 100644
--- a/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp
@@ -15,6 +15,7 @@
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Transforms/Utils/Local.h"
@@ -279,7 +280,8 @@ static void reconnectPhis(BasicBlock *Out, BasicBlock *GuardBlock,
std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
- const StringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans) {
+ const StringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans,
+ bool SwitchGuards) {
#ifndef NDEBUG
SmallPtrSet<BasicBlock *, 8> Incoming;
#endif
@@ -287,9 +289,10 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
for (auto [BB, Succ0, Succ1] : Branches) {
#ifndef NDEBUG
- assert(
- (Incoming.insert(BB).second || isa<CallBrInst>(BB->getTerminator())) &&
- "Duplicate entry for incoming block.");
+ assert((Incoming.insert(BB).second ||
+ isa<CallBrInst>(BB->getTerminator()) ||
+ isa<SwitchInst>(BB->getTerminator())) &&
+ "Duplicate entry for incoming block.");
#endif
if (Succ0)
Outgoing.insert(Succ0);
@@ -302,6 +305,18 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
if (Outgoing.size() < 2)
return {Outgoing.front(), false};
+ if (SwitchGuards && Outgoing.size() > 2)
+ return {finalizeAsSwitch(DTU, GuardBlocks, Prefix, Outgoing), true};
+
+ return {finalizeAsBrSled(DTU, GuardBlocks, Prefix, MaxControlFlowBooleans,
+ Outgoing),
+ true};
+}
+
+BasicBlock *ControlFlowHub::finalizeAsBrSled(
+ DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans,
+ SetVector<BasicBlock *> &Outgoing) {
SmallVector<DominatorTree::UpdateType, 16> Updates;
if (DTU) {
for (auto [BB, Succ0, Succ1] : Branches) {
@@ -350,5 +365,70 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(
Inst->eraseFromParent();
}
- return {FirstGuardBlock, true};
+ return FirstGuardBlock;
+}
+
+BasicBlock *ControlFlowHub::finalizeAsSwitch(
+ DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
+ const StringRef Prefix, SetVector<BasicBlock *> &Outgoing) {
+
+ SmallVector<DominatorTree::UpdateType, 16> Updates;
+ if (DTU) {
+ for (auto [BB, Succ0, Succ1] : Branches) {
+ if (Succ0)
+ Updates.push_back({DominatorTree::Delete, BB, Succ0});
+ // Only add Succ1 if it's different from Succ0 to avoid duplicate updates
+ if (Succ1 && Succ1 != Succ0)
+ Updates.push_back({DominatorTree::Delete, BB, Succ1});
+ }
+ }
+
+ Function *F = Outgoing.front()->getParent();
+ BasicBlock *Guard = BasicBlock::Create(F->getContext(), Prefix + ".guard", F);
+ GuardBlocks.push_back(Guard);
+ Type *Int32Ty = Type::getInt32Ty(F->getContext());
+ IRBuilder<> Builder(Guard);
+ auto *Phi = Builder.CreatePHI(Int32Ty, Branches.size(), "merged.bb.idx");
+ for (auto [BB, Succ0, Succ1] : Branches) {
+ Value *Condition = redirectToHub(BB, Succ0, Succ1, Guard);
+ Value *IncomingId = nullptr;
+
+ if (Succ0 && Succ1) {
+ Value *Id0 = ConstantInt::get(
+ Int32Ty, std::distance(Outgoing.begin(), find(Outgoing, Succ0)));
+ Value *Id1 = ConstantInt::get(
+ Int32Ty, std::distance(Outgoing.begin(), find(Outgoing, Succ1)));
+ IncomingId = SelectInst::Create(Condition, Id0, Id1, "target.bb.idx",
+ BB->getTerminator()->getIterator());
+ } else {
+ auto *Succ = Succ0 ? Succ0 : Succ1;
+ uint64_t Idx = std::distance(Outgoing.begin(), find(Outgoing, Succ));
+ IncomingId = ConstantInt::get(Int32Ty, Idx);
+ }
+
+ Phi->addIncoming(IncomingId, BB);
+ }
+
+ BasicBlock *DefaultDest =
+ BasicBlock::Create(F->getContext(), Prefix + ".guard.default", F);
+ IRBuilder<>(DefaultDest).CreateUnreachable();
+ SwitchInst *SI = Builder.CreateSwitch(Phi, DefaultDest, Outgoing.size());
+ for (int I = 0, E = Outgoing.size(); I != E; ++I) {
+ SI->addCase(Builder.getInt32(I), Outgoing[I]);
+ }
+
+ for (BasicBlock *Out : Outgoing) {
+ reconnectPhis(Out, Guard, Branches, Guard);
+ }
+
+ if (DTU) {
+ for (auto [BB, Succ0, Succ1] : Branches)
+ Updates.push_back({DominatorTree::Insert, BB, Guard});
+
+ for (auto *Outgoing : Outgoing)
+ Updates.push_back({DominatorTree::Insert, Guard, Outgoing});
+ Updates.push_back({DominatorTree::Insert, Guard, DefaultDest});
+ DTU->applyUpdates(Updates);
+ }
+ return Guard;
}
diff --git a/llvm/lib/Transforms/Utils/FixIrreducible.cpp b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
index 09e9f426dced6..49a8247224e18 100644
--- a/llvm/lib/Transforms/Utils/FixIrreducible.cpp
+++ b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
@@ -272,7 +272,7 @@ static void updateLoopInfo(CycleInfo &CI, LoopInfo &LI, CycleRef C,
// natural loop. Also insert this new loop at its appropriate place in the
// hierarchy of loops.
static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
- LoopInfo *LI) {
+ LoopInfo *LI, bool SwitchGuards) {
if (CI.isReducible(C))
return false;
LLVM_DEBUG(dbgs() << "Processing cycle:\n" << CI.print(C) << "\n";);
@@ -395,7 +395,7 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
SetVector<BasicBlock *> Entries;
Entries.insert(CI.getEntries(C).rbegin(), CI.getEntries(C).rend());
- CHub.finalize(&DTU, GuardBlocks, "irr");
+ CHub.finalize(&DTU, GuardBlocks, "irr", std::nullopt, SwitchGuards);
#if defined(EXPENSIVE_CHECKS)
assert(DT.verify(DominatorTree::VerificationLevel::Full));
#else
@@ -423,13 +423,13 @@ static bool fixIrreducible(CycleRef C, CycleInfo &CI, DominatorTree &DT,
}
static bool FixIrreducibleImpl(Function &F, CycleInfo &CI, DominatorTree &DT,
- LoopInfo *LI) {
+ LoopInfo *LI, bool SwitchGuards) {
LLVM_DEBUG(dbgs() << "===== Fix irreducible control-flow in function: "
<< F.getName() << "\n");
bool Changed = false;
for (auto C : CI.cycles())
- Changed |= fixIrreducible(C, CI, DT, LI);
+ Changed |= fixIrreducible(C, CI, DT, LI, SwitchGuards);
if (!Changed)
return false;
@@ -448,7 +448,7 @@ bool FixIrreducible::runOnFunction(Function &F) {
LoopInfo *LI = LIWP ? &LIWP->getLoopInfo() : nullptr;
auto &CI = getAnalysis<CycleInfoWrapperPass>().getResult();
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
- return FixIrreducibleImpl(F, CI, DT, LI);
+ return FixIrreducibleImpl(F, CI, DT, LI, false);
}
PreservedAnalyses FixIrreduciblePass::run(Function &F,
@@ -457,7 +457,7 @@ PreservedAnalyses FixIrreduciblePass::run(Function &F,
auto &CI = AM.getResult<CycleAnalysis>(F);
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
- if (!FixIrreducibleImpl(F, CI, DT, LI))
+ if (!FixIrreducibleImpl(F, CI, DT, LI, SwitchGuards))
return PreservedAnalyses::all();
PreservedAnalyses PA;
diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
index b8aba85130368..a0f78a99bc7f7 100644
--- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
+++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp
@@ -147,7 +147,7 @@ static void restoreSSA(const DominatorTree &DT, const Loop *L,
}
}
-static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
+static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L, bool SwitchGuards) {
// To unify the loop exits, we need a list of the exiting blocks as
// well as exit blocks. The functions for locating these lists both
// traverse the entire loop body. It is more efficient to first
@@ -241,8 +241,9 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
SmallVector<BasicBlock *, 8> GuardBlocks;
BasicBlock *LoopExitBlock;
bool ChangedCFG;
- std::tie(LoopExitBlock, ChangedCFG) = CHub.finalize(
- &DTU, GuardBlocks, "loop.exit", MaxBooleansInControlFlowHub.getValue());
+ std::tie(LoopExitBlock, ChangedCFG) =
+ CHub.finalize(&DTU, GuardBlocks, "loop.exit",
+ MaxBooleansInControlFlowHub.getValue(), SwitchGuards);
ChangedCFG |= Changed;
if (!ChangedCFG)
return false;
@@ -281,13 +282,13 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {
return true;
}
-static bool runImpl(LoopInfo &LI, DominatorTree &DT) {
+static bool runImpl(LoopInfo &LI, DominatorTree &DT, bool SwitchGuards) {
bool Changed = false;
auto Loops = LI.getLoopsInPreorder();
for (auto *L : Loops) {
LLVM_DEBUG(dbgs() << "Processing loop:\n"; L->print(dbgs()));
- Changed |= unifyLoopExits(DT, LI, L);
+ Changed |= unifyLoopExits(DT, LI, L, SwitchGuards);
}
return Changed;
}
@@ -298,7 +299,7 @@ bool UnifyLoopExitsLegacyPass::runOnFunction(Function &F) {
auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
- return runImpl(LI, DT);
+ return runImpl(LI, DT, false);
}
namespace llvm {
@@ -310,7 +311,7 @@ PreservedAnalyses UnifyLoopExitsPass::run(Function &F,
auto &LI = AM.getResult<LoopAnalysis>(F);
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
- if (!runImpl(LI, DT))
+ if (!runImpl(LI, DT, SwitchGuards))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<LoopAnalysis>();
diff --git a/llvm/test/Transforms/FixIrreducible/switch_guards.ll b/llvm/test/Transforms/FixIrreducible/switch_guards.ll
new file mode 100644
index 0000000000000..836aedfd0f119
--- /dev/null
+++ b/llvm/test/Transforms/FixIrreducible/switch_guards.ll
@@ -0,0 +1,344 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes='fix-irreducible<switch-guards>' -S | FileCheck %s --check-prefixes=CHECK,SWITCH
+; RUN: opt < %s -passes='fix-irreducible' -S | FileCheck %s --check-prefixes=CHECK,BOOLEAN
+; RUN: opt < %s -passes='verify<loops>,fix-irreducible<switch-guards>,verify<loops>' -S | FileCheck %s --check-prefixes=CHECK,SWITCH
+
+declare void @use(i32)
+
+define i32 @irreducible_three_entries(i1 %PredEntry, i1 %PredEntry2, i1 %PredA, i1 %PredB, i1 %PredC, i32 %X, i32 %Y, i32 %Z) {
+; SWITCH-LABEL: define i32 @irreducible_three_entries(
+; SWITCH-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDENTRY2:%.*]], i1 [[PREDA:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; SWITCH-NEXT: [[ENTRY:.*]]:
+; SWITCH-NEXT: br i1 [[PREDENTRY]], label %[[IRR_GUARD:.*]], label %[[ENTRY2:.*]]
+; SWITCH: [[ENTRY2]]:
+; SWITCH-NEXT: [[TARGET_BB_IDX:%.*]] = select i1 [[PREDENTRY2]], i32 1, i32 0
+; SWITCH-NEXT: br label %[[IRR_GUARD]]
+; SWITCH: [[A:.*]]:
+; SWITCH-NEXT: [[VALA:%.*]] = phi i32 [ [[VALC_MOVED:%.*]], %[[C:.*]] ], [ [[VALA_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCA:%.*]] = add i32 [[VALA]], 1
+; SWITCH-NEXT: br i1 [[PREDA]], label %[[B:.*]], label %[[EXIT:.*]]
+; SWITCH: [[B]]:
+; SWITCH-NEXT: [[VALB:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[VALB_MOVED:%.*]], %[[IRR_GUARD]] ]
+; SWITCH-NEXT: [[INCB:%.*]] = add i32 [[VALB]], 2
+; SWITCH-NEXT: br i1 [[PREDB]], label %[[IRR_GUARD]], label %[[EXIT]]
+; SWITCH: [[C]]:
+; SWITCH-NEXT: [[INCC:%.*]] = add i32 [[VALC_MOVED]], 3
+; SWITCH-NEXT: br i1 [[PREDC]], label %[[A]], label %[[EXIT]]
+; SWITCH: [[EXIT]]:
+; SWITCH-NEXT: [[RES:%.*]] = phi i32 [ [[INCA]], %[[A]] ], [ [[INCB]], %[[B]] ], [ [[INCC]], %[[C]] ]
+; SWITCH-NEXT: ret i32 [[RES]]
+; SWITCH: [[IRR_GUARD]]:
+; SWITCH-NEXT: [[VALA_MOVED]] = phi i32 [ poison, %[[B]] ], [ poison, %[[ENTRY2]] ], [ [[X]], %[[ENTRY]] ]
+; SWITCH-NEXT: [[VALB_MOVED]] = phi i32 [ poison, %[[B]] ], [ [[Y]], %[[ENTRY2]] ], [ poison, %[[ENTRY]] ]
+; SWITCH-NEXT: [[VALC_MOVED]] = phi i32 [ [[INCB]], %[[B]] ], [ [[Z]], %[[ENTRY2]] ], [ poison, %[[ENTRY]] ]
+; SWITCH-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, %[[B]] ], [ [[TARGET_BB_IDX]], %[[ENTRY2]] ], [ 2, %[[ENTRY]] ]
+; SWITCH-NEXT: switch i32 [[MERGED_BB_IDX]], label %[[IRR_GUARD_DEFAULT:.*]] [
+; SWITCH-NEXT: i32 0, label %[[C]]
+...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/213549
More information about the llvm-commits
mailing list