diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 2a957737697c3..fd27b08f2714a 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -319,6 +319,7 @@ class SimplifyCFGOpt { bool simplifySwitchOnSelect(SwitchInst *SI, SelectInst *Select); bool simplifyIndirectBrOnSelect(IndirectBrInst *IBI, SelectInst *SI); bool turnSwitchRangeIntoICmp(SwitchInst *SI, IRBuilder<> &Builder); + bool simplifyDuplicatePredecessors(BasicBlock *Succ, DomTreeUpdater *DTU); public: SimplifyCFGOpt(const TargetTransformInfo &TTI, DomTreeUpdater *DTU, @@ -7983,7 +7984,7 @@ static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder, return true; } -/// Checking whether two cases of SI are equal depends on the contents of the +/// Checking whether two BBs are equal depends on the contents of the /// BasicBlock and the incoming values of their successor PHINodes. /// PHINode::getIncomingValueForBlock is O(|Preds|), so we'd like to avoid /// calling this function on each BasicBlock every time isEqual is called, @@ -7991,56 +7992,90 @@ static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder, /// times. To do this, we can precompute a map of PHINode -> Pred BasicBlock -> /// IncomingValue and add it in the Wrapper so isEqual can do O(1) checking /// of the incoming values. -struct SwitchSuccWrapper { - BasicBlock *Dest; - DenseMap> *PhiPredIVs; +struct EqualBBWrapper { + BasicBlock *BB; + + // One Phi usually has < 8 incoming values. + using BB2ValueMap = SmallDenseMap; + using Phi2IVsMap = DenseMap; + Phi2IVsMap *PhiPredIVs; + + // We only merge the identical non-entry BBs with + // - terminator unconditional br to Succ (pending relaxation), + // - does not have address taken / weird control. + static bool canBeMerged(const BasicBlock *BB) { + assert(BB && "Expected non-null BB"); + // Entry block cannot be eliminated or have predecessors. + if (BB->isEntryBlock()) + return false; + + // Single successor and must be Succ. + // FIXME: Relax that the terminator is a BranchInst by checking for equality + // on other kinds of terminators. We decide to only support unconditional + // branches for now for compile time reasons. + auto *BI = dyn_cast(BB->getTerminator()); + if (!BI || !BI->isUnconditional()) + return false; + + // Avoid blocks that are "address-taken" (blockaddress) or have unusual + // uses. + if (BB->hasAddressTaken() || BB->isEHPad()) + return false; + + // TODO: relax this condition to merge equal blocks with >1 instructions? + // Here, we use a O(1) form of the O(n) comparison of `size() != 1`. + if (&BB->front() != &BB->back()) + return false; + + // The BB must have at least one predecessor. + if (pred_empty(BB)) + return false; + + return true; + } }; -template <> struct llvm::DenseMapInfo { - static const SwitchSuccWrapper *getEmptyKey() { - return static_cast( - DenseMapInfo::getEmptyKey()); +template <> struct llvm::DenseMapInfo { + static const EqualBBWrapper *getEmptyKey() { + return static_cast(DenseMapInfo::getEmptyKey()); } - static const SwitchSuccWrapper *getTombstoneKey() { - return static_cast( + static const EqualBBWrapper *getTombstoneKey() { + return static_cast( DenseMapInfo::getTombstoneKey()); } - static unsigned getHashValue(const SwitchSuccWrapper *SSW) { - BasicBlock *Succ = SSW->Dest; - BranchInst *BI = cast(Succ->getTerminator()); + static unsigned getHashValue(const EqualBBWrapper *EBW) { + BasicBlock *BB = EBW->BB; + BranchInst *BI = cast(BB->getTerminator()); assert(BI->isUnconditional() && "Only supporting unconditional branches for now"); assert(BI->getNumSuccessors() == 1 && "Expected unconditional branches to have one successor"); - assert(Succ->size() == 1 && "Expected just a single branch in the BB"); + assert(BB->size() == 1 && "Expected just a single branch in the BB"); // Since we assume the BB is just a single BranchInst with a single // successor, we hash as the BB and the incoming Values of its successor // PHIs. Initially, we tried to just use the successor BB as the hash, but // including the incoming PHI values leads to better performance. // We also tried to build a map from BB -> Succs.IncomingValues ahead of - // time and passing it in SwitchSuccWrapper, but this slowed down the - // average compile time without having any impact on the worst case compile - // time. - BasicBlock *BB = BI->getSuccessor(0); - SmallVector PhiValsForBB; - for (PHINode &Phi : BB->phis()) - PhiValsForBB.emplace_back((*SSW->PhiPredIVs)[&Phi][BB]); - - return hash_combine(BB, hash_combine_range(PhiValsForBB)); - } - static bool isEqual(const SwitchSuccWrapper *LHS, - const SwitchSuccWrapper *RHS) { - auto EKey = DenseMapInfo::getEmptyKey(); - auto TKey = DenseMapInfo::getTombstoneKey(); + // time and passing it in EqualBBWrapper, but this slowed down the average + // compile time without having any impact on the worst case compile time. + BasicBlock *Succ = BI->getSuccessor(0); + auto PhiValsForBB = map_range(Succ->phis(), [&](PHINode &Phi) { + return (*EBW->PhiPredIVs)[&Phi][BB]; + }); + return hash_combine(Succ, hash_combine_range(PhiValsForBB)); + } + static bool isEqual(const EqualBBWrapper *LHS, const EqualBBWrapper *RHS) { + auto *EKey = DenseMapInfo::getEmptyKey(); + auto *TKey = DenseMapInfo::getTombstoneKey(); if (LHS == EKey || RHS == EKey || LHS == TKey || RHS == TKey) return LHS == RHS; - BasicBlock *A = LHS->Dest; - BasicBlock *B = RHS->Dest; + BasicBlock *A = LHS->BB; + BasicBlock *B = RHS->BB; // FIXME: we checked that the size of A and B are both 1 in - // simplifyDuplicateSwitchArms to make the Case list smaller to + // mergeIdenticalUncondBBs to make the Case list smaller to // improve performance. If we decide to support BasicBlocks with more // than just a single instruction, we need to check that A.size() == // B.size() here, and we need to check more than just the BranchInsts @@ -8053,113 +8088,158 @@ template <> struct llvm::DenseMapInfo { if (ABI->getSuccessor(0) != BBI->getSuccessor(0)) return false; - // Need to check that PHIs in successor have matching values + // Need to check that PHIs in successor have matching values. BasicBlock *Succ = ABI->getSuccessor(0); - for (PHINode &Phi : Succ->phis()) { + auto IfPhiIVMatch = [&](PHINode &Phi) { + // Replace O(|Pred|) Phi.getIncomingValueForBlock with this O(1) hashmap + // query. auto &PredIVs = (*LHS->PhiPredIVs)[&Phi]; - if (PredIVs[A] != PredIVs[B]) - return false; - } - - return true; + return PredIVs[A] == PredIVs[B]; + }; + return all_of(Succ->phis(), IfPhiIVMatch); } }; -bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, - DomTreeUpdater *DTU) { +// Merge identical BBs into one of them. +static bool mergeIdenticalBBs(ArrayRef Candidates, + DomTreeUpdater *DTU) { + if (Candidates.size() < 2) + return false; + // Build Cases. Skip BBs that are not candidates for simplification. Mark // PHINodes which need to be processed into PhiPredIVs. We decide to process // an entire PHI at once after the loop, opposed to calling // getIncomingValueForBlock inside this loop, since each call to // getIncomingValueForBlock is O(|Preds|). - SmallPtrSet Phis; - SmallPtrSet Seen; - DenseMap> PhiPredIVs; - DenseMap> BBToSuccessorIndexes; - SmallVector Cases; - Cases.reserve(SI->getNumSuccessors()); - - for (unsigned I = 0; I < SI->getNumSuccessors(); ++I) { - BasicBlock *BB = SI->getSuccessor(I); - - // FIXME: Support more than just a single BranchInst. One way we could do - // this is by taking a hashing approach of all insts in BB. - if (BB->size() != 1) - continue; - - // FIXME: Relax that the terminator is a BranchInst by checking for equality - // on other kinds of terminators. We decide to only support unconditional - // branches for now for compile time reasons. - auto *BI = dyn_cast(BB->getTerminator()); - if (!BI || BI->isConditional()) - continue; - - if (!Seen.insert(BB).second) { - auto It = BBToSuccessorIndexes.find(BB); - if (It != BBToSuccessorIndexes.end()) - It->second.emplace_back(I); - continue; - } - - // FIXME: This case needs some extra care because the terminators other than - // SI need to be updated. For now, consider only backedges to the SI. - if (BB->getUniquePredecessor() != SI->getParent()) - continue; - - // Keep track of which PHIs we need as keys in PhiPredIVs below. - for (BasicBlock *Succ : BI->successors()) - Phis.insert_range(llvm::make_pointer_range(Succ->phis())); + EqualBBWrapper::Phi2IVsMap PhiPredIVs; + SmallVector BBs2Merge; + BBs2Merge.reserve(Candidates.size()); + SmallSetVector Phis; - // Add the successor only if not previously visited. - Cases.emplace_back(SwitchSuccWrapper{BB, &PhiPredIVs}); - BBToSuccessorIndexes[BB].emplace_back(I); + for (BasicBlock *BB : Candidates) { + BasicBlock *Succ = BB->getSingleSuccessor(); + assert(Succ && "Expected unconditional BB"); + BBs2Merge.emplace_back(EqualBBWrapper{BB, &PhiPredIVs}); + Phis.insert_range(make_pointer_range(Succ->phis())); } // Precompute a data structure to improve performance of isEqual for - // SwitchSuccWrapper. + // EqualBBWrapper. PhiPredIVs.reserve(Phis.size()); for (PHINode *Phi : Phis) { auto &IVs = PhiPredIVs.try_emplace(Phi, Phi->getNumIncomingValues()).first->second; + // Pre-fill all incoming for O(1) lookup as Phi.getIncomingValueForBlock is + // O(|Pred|). for (auto &IV : Phi->incoming_values()) IVs.insert({Phi->getIncomingBlock(IV), IV.get()}); } - // Build a set such that if the SwitchSuccWrapper exists in the set and - // another SwitchSuccWrapper isEqual, then the equivalent SwitchSuccWrapper - // which is not in the set should be replaced with the one in the set. If the - // SwitchSuccWrapper is not in the set, then it should be added to the set so - // other SwitchSuccWrappers can check against it in the same manner. We use - // SwitchSuccWrapper instead of just BasicBlock because we'd like to pass - // around information to isEquality, getHashValue, and when doing the - // replacement with better performance. - DenseSet ReplaceWith; - ReplaceWith.reserve(Cases.size()); + // Group duplicates using DenseSet with custom equality/hashing. + // Build a set such that if the EqualBBWrapper exists in the set and another + // EqualBBWrapper isEqual, then the equivalent EqualBBWrapper which is not in + // the set should be replaced with the one in the set. If the EqualBBWrapper + // is not in the set, then it should be added to the set so other + // EqualBBWrapper can check against it in the same manner. We use + // EqualBBWrapper instead of just BasicBlock because we'd like to pass around + // information to isEquality, getHashValue, and when doing the replacement + // with better performance. + DenseSet Keep; + Keep.reserve(BBs2Merge.size()); SmallVector Updates; - Updates.reserve(ReplaceWith.size()); + Updates.reserve(BBs2Merge.size() * 2); + bool MadeChange = false; - for (auto &SSW : Cases) { - // SSW is a candidate for simplification. If we find a duplicate BB, - // replace it. - const auto [It, Inserted] = ReplaceWith.insert(&SSW); - if (!Inserted) { - // We know that SI's parent BB no longer dominates the old case successor - // since we are making it dead. - Updates.push_back({DominatorTree::Delete, SI->getParent(), SSW.Dest}); - const auto &Successors = BBToSuccessorIndexes.at(SSW.Dest); - for (unsigned Idx : Successors) - SI->setSuccessor(Idx, (*It)->Dest); - MadeChange = true; + + // Helper: redirect all edges X -> DeadPred to X -> LivePred. + auto RedirectIncomingEdges = [&](BasicBlock *Dead, BasicBlock *Live) { + SmallSetVector DeadPreds(llvm::from_range, + predecessors(Dead)); + if (DTU) { + // All predecessors of DeadPred (except the common predecessor) will be + // moved to LivePred. + Updates.reserve(Updates.size() + DeadPreds.size() * 2); + SmallPtrSet LivePreds(llvm::from_range, + predecessors(Live)); + for (BasicBlock *PredOfDead : DeadPreds) { + // Do not modify those common predecessors of DeadPred and LivePred. + if (!LivePreds.contains(PredOfDead)) + Updates.push_back({DominatorTree::Insert, PredOfDead, Live}); + Updates.push_back({DominatorTree::Delete, PredOfDead, Dead}); + } + } + LLVM_DEBUG(dbgs() << "Replacing duplicate pred BB "; + Dead->printAsOperand(dbgs()); dbgs() << " with pred "; + Live->printAsOperand(dbgs()); dbgs() << " for "; + Live->getSingleSuccessor()->printAsOperand(dbgs()); + dbgs() << "\n"); + // Replace successors in all predecessors of DeadPred. + for (BasicBlock *PredOfDead : DeadPreds) { + Instruction *T = PredOfDead->getTerminator(); + T->replaceSuccessorWith(Dead, Live); } + }; + + // Try to eliminate duplicate predecessors. + for (const auto &EBW : BBs2Merge) { + // EBW is a candidate for simplification. If we find a duplicate BB, + // replace it. + const auto &[It, Inserted] = Keep.insert(&EBW); + if (Inserted) + continue; + + // Found duplicate: merge P into canonical predecessor It->Pred. + BasicBlock *KeepBB = (*It)->BB; + BasicBlock *DeadBB = EBW.BB; + + // Avoid merging a BB with itself. + if (KeepBB == DeadBB) + continue; + + // Redirect all edges into DeadPred to KeepPred. + RedirectIncomingEdges(DeadBB, KeepBB); + + // Now DeadBB should become unreachable; leave DCE to later, + // but we can try to simplify it if it only branches to Succ. + // (We won't erase here to keep the routine simple and DT-safe.) + assert(pred_empty(DeadBB) && "DeadBB should be unreachable."); + MadeChange = true; } - if (DTU) + if (DTU && !Updates.empty()) DTU->applyUpdates(Updates); return MadeChange; } +bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, + DomTreeUpdater *DTU) { + // Collect candidate switch-arms top-down. + SmallSetVector FilteredArms( + llvm::from_range, + make_filter_range(successors(SI), EqualBBWrapper::canBeMerged)); + return mergeIdenticalBBs(FilteredArms.getArrayRef(), DTU); +} + +bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, + DomTreeUpdater *DTU) { + // Need at least 2 predecessors to do anything. + if (!BB || !BB->hasNPredecessorsOrMore(2)) + return false; + + // Compilation time consideration: retain the canonical loop, otherwise, we + // require more time in the later loop canonicalization. + if (Options.NeedCanonicalLoop && is_contained(LoopHeaders, BB)) + return false; + + // Collect candidate predecessors bottom-up. + SmallSetVector FilteredPreds( + llvm::from_range, + make_filter_range(predecessors(BB), EqualBBWrapper::canBeMerged)); + return mergeIdenticalBBs(FilteredPreds.getArrayRef(), DTU); +} + bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { BasicBlock *BB = SI->getParent(); @@ -8219,6 +8299,8 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { hoistCommonCodeFromSuccessors(SI, !Options.HoistCommonInsts)) return requestResimplify(); + // We can merge identical switch arms early to enhance more aggressive + // optimization on switch. if (simplifyDuplicateSwitchArms(SI, DTU)) return requestResimplify(); @@ -8901,7 +8983,7 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { if (MergeBlockIntoPredecessor(BB, DTU)) return true; - if (SinkCommon && Options.SinkCommonInsts) + if (SinkCommon && Options.SinkCommonInsts) { if (sinkCommonCodeFromPredecessors(BB, DTU) || mergeCompatibleInvokes(BB, DTU)) { // sinkCommonCodeFromPredecessors() does not automatically CSE PHI's, @@ -8911,8 +8993,10 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { // after which we'd need a whole EarlyCSE pass run to cleanup them. return true; } - - IRBuilder<> Builder(BB); + // Merge identical predecessors of this block. + if (simplifyDuplicatePredecessors(BB, DTU)) + return true; + } if (Options.SpeculateBlocks && !BB->getParent()->hasFnAttribute(Attribute::OptForFuzzing)) { @@ -8925,6 +9009,7 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { return true; } + IRBuilder<> Builder(BB); Instruction *Terminator = BB->getTerminator(); Builder.SetInsertPoint(Terminator); switch (Terminator->getOpcode()) { diff --git a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll new file mode 100644 index 0000000000000..36a7c9f05d637 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll @@ -0,0 +1,108 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S | FileCheck %s -check-prefix=SIMPLIFY-CFG + +define i8 @test_duplicate_preds_merging(i8 %v1, i8 %v2) { +; SIMPLIFY-CFG-LABEL: @test_duplicate_preds_merging( +; SIMPLIFY-CFG-NEXT: entry: +; SIMPLIFY-CFG-NEXT: switch i8 [[V1:%.*]], label [[EXIT:%.*]] [ +; SIMPLIFY-CFG-NEXT: i8 0, label [[THEN:%.*]] +; SIMPLIFY-CFG-NEXT: i8 1, label [[ELSE:%.*]] +; SIMPLIFY-CFG-NEXT: ] +; SIMPLIFY-CFG: then: +; SIMPLIFY-CFG-NEXT: switch i8 [[V2:%.*]], label [[EXIT]] [ +; SIMPLIFY-CFG-NEXT: i8 0, label [[ELSE]] +; SIMPLIFY-CFG-NEXT: i8 1, label [[SWITCH_CASE_1:%.*]] +; SIMPLIFY-CFG-NEXT: i8 2, label [[SWITCH_CASE_1]] +; SIMPLIFY-CFG-NEXT: ] +; SIMPLIFY-CFG: switch.case.1: +; SIMPLIFY-CFG-NEXT: br label [[EXIT]] +; SIMPLIFY-CFG: else: +; SIMPLIFY-CFG-NEXT: br label [[EXIT]] +; SIMPLIFY-CFG: exit: +; SIMPLIFY-CFG-NEXT: [[RET:%.*]] = phi i8 [ 0, [[ELSE]] ], [ 2, [[THEN]] ], [ 1, [[SWITCH_CASE_1]] ], [ 3, [[ENTRY:%.*]] ] +; SIMPLIFY-CFG-NEXT: ret i8 [[RET]] +; +entry: + switch i8 %v1, label %exit [ + i8 0, label %then + i8 1, label %else + ] + +then: ; preds = %entry + switch i8 %v2, label %exit [ + i8 0, label %switch.case.0 + i8 1, label %switch.case.1 + i8 2, label %switch.case.2 + ] + +switch.case.0: ; preds = %then + br label %exit + +switch.case.1: ; preds = %then + br label %exit + +switch.case.2: ; preds = %then + br label %exit + +else: ; preds = %entry + br label %exit + +exit: ; preds = %else, %switch.case.2, %switch.case.1, %switch.case.0, %then, %entry + %ret = phi i8 [ 0, %else ], [ 0, %switch.case.0 ], [ 1, %switch.case.1 ], [ 1, %switch.case.2 ], [ 2, %then ], [ 3, %entry ] + ret i8 %ret +} + +define i8 @test_no_merge_address_taken(i8 %v1, i8 %v2, ptr %p) { +; SIMPLIFY-CFG-LABEL: @test_no_merge_address_taken( +; SIMPLIFY-CFG-NEXT: entry: +; SIMPLIFY-CFG-NEXT: store ptr blockaddress(@test_no_merge_address_taken, [[SWITCH_CASE_0:%.*]]), ptr [[P:%.*]], align 8 +; SIMPLIFY-CFG-NEXT: switch i8 [[V1:%.*]], label [[EXIT:%.*]] [ +; SIMPLIFY-CFG-NEXT: i8 0, label [[THEN:%.*]] +; SIMPLIFY-CFG-NEXT: i8 1, label [[ELSE:%.*]] +; SIMPLIFY-CFG-NEXT: ] +; SIMPLIFY-CFG: then: +; SIMPLIFY-CFG-NEXT: switch i8 [[V2:%.*]], label [[EXIT]] [ +; SIMPLIFY-CFG-NEXT: i8 0, label [[SWITCH_CASE_0]] +; SIMPLIFY-CFG-NEXT: i8 1, label [[SWITCH_CASE_1:%.*]] +; SIMPLIFY-CFG-NEXT: i8 2, label [[SWITCH_CASE_1]] +; SIMPLIFY-CFG-NEXT: ] +; SIMPLIFY-CFG: switch.case.0: +; SIMPLIFY-CFG-NEXT: br label [[EXIT]] +; SIMPLIFY-CFG: switch.case.1: +; SIMPLIFY-CFG-NEXT: br label [[EXIT]] +; SIMPLIFY-CFG: else: +; SIMPLIFY-CFG-NEXT: br label [[EXIT]] +; SIMPLIFY-CFG: exit: +; SIMPLIFY-CFG-NEXT: [[RET:%.*]] = phi i8 [ 0, [[ELSE]] ], [ 0, [[SWITCH_CASE_0]] ], [ 1, [[SWITCH_CASE_1]] ], [ 3, [[ENTRY:%.*]] ], [ 2, [[THEN]] ] +; SIMPLIFY-CFG-NEXT: ret i8 [[RET]] +; +entry: + store ptr blockaddress(@test_no_merge_address_taken, %switch.case.0), ptr %p + switch i8 %v1, label %exit [ + i8 0, label %then + i8 1, label %else + ] + +then: ; preds = %entry + switch i8 %v2, label %exit [ + i8 0, label %switch.case.0 + i8 1, label %switch.case.1 + i8 2, label %switch.case.2 + ] + +switch.case.0: ; preds = %then + br label %exit + +switch.case.1: ; preds = %then + br label %exit + +switch.case.2: ; preds = %then + br label %exit + +else: ; preds = %entry + br label %exit + +exit: ; preds = %else, %switch.case.2, %switch.case.1, %switch.case.0, %then, %entry + %ret = phi i8 [ 0, %else ], [ 0, %switch.case.0 ], [ 1, %switch.case.1 ], [ 1, %switch.case.2 ], [ 2, %then ], [ 3, %entry ] + ret i8 %ret +} diff --git a/llvm/test/Transforms/SimplifyCFG/switch-dup-bbs.ll b/llvm/test/Transforms/SimplifyCFG/switch-dup-bbs.ll index ae7baeb970689..e9edb33b9420c 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-dup-bbs.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-dup-bbs.ll @@ -72,16 +72,14 @@ define i32 @switch_duplicate_arms_multipred(i1 %0, i32 %1, i32 %2, i32 %3, i32 % ; SIMPLIFY-CFG-SAME: i1 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]], i32 [[TMP3:%.*]], i32 [[TMP4:%.*]]) { ; SIMPLIFY-CFG-NEXT: br i1 [[TMP0]], label %[[BB6:.*]], label %[[BB7:.*]] ; SIMPLIFY-CFG: [[BB6]]: -; SIMPLIFY-CFG-NEXT: switch i32 [[TMP2]], label %[[BB9:.*]] [ +; SIMPLIFY-CFG-NEXT: switch i32 [[TMP2]], label %[[BB8:.*]] [ ; SIMPLIFY-CFG-NEXT: i32 0, label %[[BB7]] -; SIMPLIFY-CFG-NEXT: i32 1, label %[[BB8:.*]] +; SIMPLIFY-CFG-NEXT: i32 1, label %[[BB7]] ; SIMPLIFY-CFG-NEXT: ] ; SIMPLIFY-CFG: [[BB7]]: -; SIMPLIFY-CFG-NEXT: br label %[[BB9]] +; SIMPLIFY-CFG-NEXT: br label %[[BB8]] ; SIMPLIFY-CFG: [[BB8]]: -; SIMPLIFY-CFG-NEXT: br label %[[BB9]] -; SIMPLIFY-CFG: [[BB9]]: -; SIMPLIFY-CFG-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP4]], %[[BB6]] ], [ [[TMP3]], %[[BB8]] ], [ [[TMP3]], %[[BB7]] ] +; SIMPLIFY-CFG-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP4]], %[[BB6]] ], [ [[TMP3]], %[[BB7]] ] ; SIMPLIFY-CFG-NEXT: ret i32 [[TMP10]] ; br i1 %0, label %6, label %7