From 00b4bc884ca38751654ac8b88b9822ba6814df23 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 19 Dec 2025 23:27:13 +0800 Subject: [PATCH 01/23] before-commit test --- llvm/test/Transforms/SimplifyCFG/dup-preds.ll | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 llvm/test/Transforms/SimplifyCFG/dup-preds.ll diff --git a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll new file mode 100644 index 0000000000000..859ef2a64e394 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll @@ -0,0 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s -check-prefix=SIMPLIFY-CFG + +define i8 @foo(i8 %v1, i8 %v2) { +; SIMPLIFY-CFG-LABEL: @foo( +; 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 [[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]] ], [ 2, [[THEN]] ], [ 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 +} From 2c171668ea91503ad155879e6d28dc99067e6b4c Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 19 Dec 2025 23:20:33 +0800 Subject: [PATCH 02/23] feat: impl generic dup preds merging --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 221 +++++++++++++++++++++- 1 file changed, 219 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 2a957737697c3..ca7e394e99b3c 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, @@ -8666,6 +8667,219 @@ bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) { return false; } +/// 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, +/// especially since the same BasicBlock may be passed as an argument multiple +/// 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 EqualBBWrapper { + BasicBlock *BB; + + // One Phi usually has < 8 incoming values. + using BB2ValueMap = SmallDenseMap; + using Phi2IVsMap = DenseMap; + Phi2IVsMap *PhiPredIVs; +}; + +template <> struct llvm::DenseMapInfo { + static const EqualBBWrapper *getEmptyKey() { + return static_cast(DenseMapInfo::getEmptyKey()); + } + static const EqualBBWrapper *getTombstoneKey() { + return static_cast( + DenseMapInfo::getTombstoneKey()); + } + static unsigned getHashValue(const EqualBBWrapper *SSW) { + BasicBlock *BB = SSW->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(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 *Succ = BI->getSuccessor(0); + auto PhiValsForBB = map_range( + BB->phis(), [BB, &PhiPredIVs = *SSW->PhiPredIVs](PHINode &Phi) { + return 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->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 + // 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 + // for equality. + + BranchInst *ABI = cast(A->getTerminator()); + BranchInst *BBI = cast(B->getTerminator()); + assert(ABI->isUnconditional() && BBI->isUnconditional() && + "Only supporting unconditional branches for now"); + if (ABI->getSuccessor(0) != BBI->getSuccessor(0)) + return false; + + // Need to check that PHIs in successor have matching values + BasicBlock *Succ = ABI->getSuccessor(0); + auto IfPhiIVMatch = [A, B, &PhiPredIVs = *LHS->PhiPredIVs](PHINode &Phi) { + // Replace O(|Pred|) Phi.getIncomingValueForBlock with this O(1) hashmap + // query + auto &PredIVs = PhiPredIVs[&Phi]; + return PredIVs[A] == PredIVs[B]; + }; + return all_of(Succ->phis(), IfPhiIVMatch); + } +}; + +bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, + DomTreeUpdater *DTU) { + // Need at least 2 predecessors to do anything. + if (!BB || pred_empty(BB)) + return false; + // Precompute PHI incoming values in BB for all candidate preds. + // PhiPredIVs[Phi][Pred] = incoming value + EqualBBWrapper::Phi2IVsMap PhiPredIVs; + + // Collect candidate non-entry predecessors P with: + // - terminator unconditional br to Succ, + // - does not have address taken / weird control. + auto Filter = [BB](BasicBlock *Pred) { + // Entry block cannot be eliminated or have predecessors. + if (Pred->isEntryBlock()) + return false; + + // Single successor and must be Succ. + auto *BI = dyn_cast(Pred->getTerminator()); + if (!BI || !BI->isUnconditional()) + return false; + + // Avoid blocks that are "address-taken" (blockaddress) or have unusual + // uses. + if (Pred->hasAddressTaken()) + return false; + if (Pred->isLandingPad()) + return false; + + // TODO: should we support Pred with >1 instructions? + if (Pred->size() != 1) + return false; + + // Avoid self-loop predecessor merging for now. + if (Pred == BB) + return false; + + return true; + }; + + auto FilteredPreds = make_filter_range(predecessors(BB), Filter); + + SmallVector Preds( + map_range(FilteredPreds, [&PhiPredIVs](BasicBlock *Pred) { + return EqualBBWrapper{Pred, &PhiPredIVs}; + })); + + if (Preds.size() < 2) + return false; + + SmallVector Phis(make_pointer_range(BB->phis())); + + 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()}); + } + + // Group duplicates using DenseSet with custom equality/hashing. + DenseSet Keep; + Keep.reserve(Preds.size()); + + SmallVector Updates; + Updates.reserve(Preds.size() * 2); + + bool MadeChange = false; + + // Helper: redirect all edges X -> DeadPred to X -> LivePred. + auto RedirectIncomingEdges = [&](BasicBlock *DeadPred, BasicBlock *LivePred) { + // Replace successors in all predecessors of DeadPred. + SmallSetVector DeadPredPreds(llvm::from_range, + predecessors(DeadPred)); + if (DTU) { + // All predecessors of DeadPred (except the common predecessor) will be + // moved to LivePred. + Updates.reserve(Updates.size() + DeadPredPreds.size() * 2); + SmallPtrSet LivePredPreds(llvm::from_range, + predecessors(LivePred)); + for (BasicBlock *PP : DeadPredPreds) { + // Do not modify those common predecessors of DeadPred and LivePred + if (!LivePredPreds.contains(PP)) + Updates.push_back({DominatorTree::Insert, PP, LivePred}); + Updates.push_back({DominatorTree::Delete, PP, DeadPred}); + } + } + LLVM_DEBUG(dbgs() << "Replacing duplicate pred BB "; + DeadPred->printAsOperand(dbgs()); dbgs() << " with pred "; + LivePred->printAsOperand(dbgs()); dbgs() << " for "; + BB->printAsOperand(dbgs()); dbgs() << "\n"); + for (BasicBlock *PP : DeadPredPreds) { + Instruction *T = PP->getTerminator(); + T->replaceSuccessorWith(DeadPred, LivePred); + } + }; + + // Try to canonicalize duplicates. + for (const auto &Pred : Preds) { + // Pred is a candidate for simplification. If we find a duplicate BB, + // replace it. + const auto [It, Inserted] = Keep.insert(&Pred); + if (Inserted) + continue; + + // Found duplicate: merge P into canonical predecessor It->Pred. + BasicBlock *KeepPred = (*It)->BB; + BasicBlock *DeadPred = Pred.BB; + + // Avoid merging if either is the other's predecessor in weird ways. + if (KeepPred == DeadPred) + continue; + + // Redirect all edges into DeadPred to KeepPred. + RedirectIncomingEdges(DeadPred, KeepPred); + + // Now DeadPred 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.) + MadeChange = true; + } + + if (DTU && !Updates.empty()) + DTU->applyUpdates(Updates); + + return MadeChange; +} /// Check if passing a value to an instruction will cause undefined behavior. static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified) { @@ -8912,8 +9126,6 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { return true; } - IRBuilder<> Builder(BB); - if (Options.SpeculateBlocks && !BB->getParent()->hasFnAttribute(Attribute::OptForFuzzing)) { // If there is a trivial two-entry PHI node in this basic block, and we can @@ -8925,6 +9137,11 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { return true; } + // Merge identical predecessors of this block + if (simplifyDuplicatePredecessors(BB, DTU)) + return true; + + IRBuilder<> Builder(BB); Instruction *Terminator = BB->getTerminator(); Builder.SetInsertPoint(Terminator); switch (Terminator->getOpcode()) { From de6a50d227659cb3b3fb209f18afa3aaa382091b Mon Sep 17 00:00:00 2001 From: Camsyn Date: Sat, 20 Dec 2025 17:44:31 +0800 Subject: [PATCH 03/23] after-commit test --- llvm/test/Transforms/SimplifyCFG/dup-preds.ll | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll index 859ef2a64e394..cd6592a45be40 100644 --- a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll +++ b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll @@ -10,18 +10,16 @@ define i8 @foo(i8 %v1, i8 %v2) { ; 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 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.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]] ], [ 2, [[THEN]] ], [ 3, [[ENTRY:%.*]] ] +; SIMPLIFY-CFG-NEXT: [[RET:%.*]] = phi i8 [ 0, [[ELSE]] ], [ 2, [[THEN]] ], [ 1, [[SWITCH_CASE_1]] ], [ 3, [[ENTRY:%.*]] ] ; SIMPLIFY-CFG-NEXT: ret i8 [[RET]] ; entry: From 0c22056d9885292373c32552529c71bca6866fb3 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Sat, 20 Dec 2025 18:13:48 +0800 Subject: [PATCH 04/23] Regenerate some existing tests --- .../Transforms/LoopDeletion/simplify-then-delete.ll | 5 +++++ .../SimplifyCFG/X86/switch-to-lookup-globals.ll | 13 +++---------- llvm/test/Transforms/SimplifyCFG/switch-dup-bbs.ll | 10 ++++------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll b/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll index 529ee8919bdb3..869fea650f49f 100644 --- a/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll +++ b/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll @@ -11,6 +11,11 @@ define i32 @pmat(i32 %m, i32 %n, ptr %y, i1 %arg) nounwind { ; CHECK-LABEL: @pmat( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP4:%.*]] = icmp sgt i32 [[M:%.*]], 0 +; CHECK-NEXT: br i1 [[CMP4]], label [[BB_N10:%.*]], label [[W_E12:%.*]] +; CHECK: bb.n10: +; CHECK-NEXT: [[CMP51:%.*]] = icmp sgt i32 [[N:%.*]], 0 +; CHECK-NEXT: br label [[W_E12]] +; CHECK: w.e12: ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll index bfcc8344264ea..4745cd0f7ea3a 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll @@ -10,16 +10,9 @@ target triple = "x86_64-unknown-linux-gnu" define i1 @zot(i32 %arg) { ; CHECK-LABEL: @zot( ; CHECK-NEXT: bb: -; CHECK-NEXT: %0 = icmp ult i32 %arg, 3 -; CHECK-NEXT: br i1 %0, label %switch.lookup, label %bb6 -; CHECK: switch.lookup: -; CHECK-NEXT: %1 = zext nneg i32 %arg to i64 -; CHECK-NEXT: %switch.gep = getelementptr inbounds [3 x ptr], ptr @switch.table.zot, i64 0, i64 %1 -; CHECK-NEXT: %switch.load = load ptr, ptr %switch.gep, align 8 -; CHECK-NEXT: br label %bb6 -; CHECK: bb6: -; CHECK-NEXT: %tmp7 = phi ptr [ null, %bb ], [ %switch.load, %switch.lookup ] -; CHECK-NEXT: %tmp8 = icmp eq ptr %tmp7, getelementptr inbounds ([75 x { i32, i32, i32, i8, i8 }], ptr @global, i64 1, i64 0, i32 0) +; CHECK-NEXT: %cond = icmp eq i32 %arg, 1 +; CHECK-NEXT: %spec.select = select i1 %cond, ptr getelementptr inbounds ([75 x { i32, i32, i32, i8, i8 }], ptr @global, i64 0, i64 6, i32 0), ptr null +; CHECK-NEXT: %tmp8 = icmp eq ptr %spec.select, getelementptr inbounds ([75 x { i32, i32, i32, i8, i8 }], ptr @global, i64 1, i64 0, i32 0) ; CHECK-NEXT: ret i1 %tmp8 ; bb: 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 From 449be6a2f3329f803331252443722d897f01175e Mon Sep 17 00:00:00 2001 From: Camsyn Date: Sat, 20 Dec 2025 21:33:48 +0800 Subject: [PATCH 05/23] Update amd gpu testcase --- llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll b/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll index d6cc833c8c73f..9850e285ac66a 100644 --- a/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll +++ b/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll @@ -682,12 +682,15 @@ divergent.ret: } ; IR-LABEL: @multi_divergent_unreachable_exit( +; IR: Flow5: +; IR-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 +; IR-NEXT: br label %UnifiedReturnBlock + ; IR: UnifiedUnreachableBlock: ; IR-NEXT: call void @llvm.amdgcn.unreachable() -; IR-NEXT: br label %UnifiedReturnBlock +; IR-NEXT: br label %Flow5 ; IR: UnifiedReturnBlock: -; IR-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 ; IR-NEXT: ret void define amdgpu_kernel void @multi_divergent_unreachable_exit(i32 %switch) #0 { bb: From 1eb22ec2704496410e4c87013f21aa0a3bfa4f14 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Mon, 22 Dec 2025 16:58:42 +0800 Subject: [PATCH 06/23] Generalize simplifyDuplicateSwitchArms --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 489 ++++++++-------------- 1 file changed, 175 insertions(+), 314 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index ca7e394e99b3c..57c20c5dddc22 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7984,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, @@ -7992,28 +7992,59 @@ 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, + // - 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. + 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()) + return false; + if (BB->isLandingPad()) + return false; + + // TODO: should we support Pred with >1 instructions? + if (BB->size() != 1) + 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 @@ -8023,25 +8054,24 @@ template <> struct llvm::DenseMapInfo { // 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)); + BasicBlock *Succ = BI->getSuccessor(0); + auto PhiValsForBB = map_range( + BB->phis(), [BB, &PhiPredIVs = *EBW->PhiPredIVs](PHINode &Phi) { + return PhiPredIVs[&Phi][BB]; + }); + return hash_combine(Succ, hash_combine_range(PhiValsForBB)); } - static bool isEqual(const SwitchSuccWrapper *LHS, - const SwitchSuccWrapper *RHS) { - auto EKey = DenseMapInfo::getEmptyKey(); - auto TKey = DenseMapInfo::getTombstoneKey(); + 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 @@ -8056,111 +8086,153 @@ template <> struct llvm::DenseMapInfo { // Need to check that PHIs in successor have matching values BasicBlock *Succ = ABI->getSuccessor(0); - for (PHINode &Phi : Succ->phis()) { - auto &PredIVs = (*LHS->PhiPredIVs)[&Phi]; - if (PredIVs[A] != PredIVs[B]) - return false; - } - - return true; + auto IfPhiIVMatch = [A, B, &PhiPredIVs = *LHS->PhiPredIVs](PHINode &Phi) { + // Replace O(|Pred|) Phi.getIncomingValueForBlock with this O(1) hashmap + // query + auto &PredIVs = PhiPredIVs[&Phi]; + return PredIVs[A] == PredIVs[B]; + }; + return all_of(Succ->phis(), IfPhiIVMatch); } }; -bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, - DomTreeUpdater *DTU) { +static bool mergeIdenticalUncondBBs(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 &Pred : BBs2Merge) { + // Pred is a candidate for simplification. If we find a duplicate BB, + // replace it. + const auto [It, Inserted] = Keep.insert(&Pred); + if (Inserted) + continue; + + // Found duplicate: merge P into canonical predecessor It->Pred. + BasicBlock *KeepPred = (*It)->BB; + BasicBlock *DeadPred = Pred.BB; + + // Avoid merging if either is the other's predecessor in weird ways. + if (KeepPred == DeadPred) + continue; + + // Redirect all edges into DeadPred to KeepPred. + RedirectIncomingEdges(DeadPred, KeepPred); + + // Now DeadPred 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.) + 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 with: + // - terminator unconditional br to Succ, + // - does not have address taken / weird control. + SmallSetVector FilteredPreds( + llvm::from_range, + make_filter_range(successors(SI), EqualBBWrapper::canBeMerged)); + return mergeIdenticalUncondBBs(FilteredPreds.getArrayRef(), DTU); +} + +bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, + DomTreeUpdater *DTU) { + // Need at least 2 predecessors to do anything. + if (!BB || pred_empty(BB)) + return false; + + // Collect candidate predecessors bottom-up with: + // - terminator unconditional br to Succ, + // - does not have address taken / weird control. + SmallSetVector FilteredPreds( + llvm::from_range, + make_filter_range(predecessors(BB), EqualBBWrapper::canBeMerged)); + return mergeIdenticalUncondBBs(FilteredPreds.getArrayRef(), DTU); +} + bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { BasicBlock *BB = SI->getParent(); @@ -8220,6 +8292,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(); @@ -8667,219 +8741,6 @@ bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) { return false; } -/// 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, -/// especially since the same BasicBlock may be passed as an argument multiple -/// 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 EqualBBWrapper { - BasicBlock *BB; - - // One Phi usually has < 8 incoming values. - using BB2ValueMap = SmallDenseMap; - using Phi2IVsMap = DenseMap; - Phi2IVsMap *PhiPredIVs; -}; - -template <> struct llvm::DenseMapInfo { - static const EqualBBWrapper *getEmptyKey() { - return static_cast(DenseMapInfo::getEmptyKey()); - } - static const EqualBBWrapper *getTombstoneKey() { - return static_cast( - DenseMapInfo::getTombstoneKey()); - } - static unsigned getHashValue(const EqualBBWrapper *SSW) { - BasicBlock *BB = SSW->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(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 *Succ = BI->getSuccessor(0); - auto PhiValsForBB = map_range( - BB->phis(), [BB, &PhiPredIVs = *SSW->PhiPredIVs](PHINode &Phi) { - return 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->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 - // 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 - // for equality. - - BranchInst *ABI = cast(A->getTerminator()); - BranchInst *BBI = cast(B->getTerminator()); - assert(ABI->isUnconditional() && BBI->isUnconditional() && - "Only supporting unconditional branches for now"); - if (ABI->getSuccessor(0) != BBI->getSuccessor(0)) - return false; - - // Need to check that PHIs in successor have matching values - BasicBlock *Succ = ABI->getSuccessor(0); - auto IfPhiIVMatch = [A, B, &PhiPredIVs = *LHS->PhiPredIVs](PHINode &Phi) { - // Replace O(|Pred|) Phi.getIncomingValueForBlock with this O(1) hashmap - // query - auto &PredIVs = PhiPredIVs[&Phi]; - return PredIVs[A] == PredIVs[B]; - }; - return all_of(Succ->phis(), IfPhiIVMatch); - } -}; - -bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, - DomTreeUpdater *DTU) { - // Need at least 2 predecessors to do anything. - if (!BB || pred_empty(BB)) - return false; - // Precompute PHI incoming values in BB for all candidate preds. - // PhiPredIVs[Phi][Pred] = incoming value - EqualBBWrapper::Phi2IVsMap PhiPredIVs; - - // Collect candidate non-entry predecessors P with: - // - terminator unconditional br to Succ, - // - does not have address taken / weird control. - auto Filter = [BB](BasicBlock *Pred) { - // Entry block cannot be eliminated or have predecessors. - if (Pred->isEntryBlock()) - return false; - - // Single successor and must be Succ. - auto *BI = dyn_cast(Pred->getTerminator()); - if (!BI || !BI->isUnconditional()) - return false; - - // Avoid blocks that are "address-taken" (blockaddress) or have unusual - // uses. - if (Pred->hasAddressTaken()) - return false; - if (Pred->isLandingPad()) - return false; - - // TODO: should we support Pred with >1 instructions? - if (Pred->size() != 1) - return false; - - // Avoid self-loop predecessor merging for now. - if (Pred == BB) - return false; - - return true; - }; - - auto FilteredPreds = make_filter_range(predecessors(BB), Filter); - - SmallVector Preds( - map_range(FilteredPreds, [&PhiPredIVs](BasicBlock *Pred) { - return EqualBBWrapper{Pred, &PhiPredIVs}; - })); - - if (Preds.size() < 2) - return false; - - SmallVector Phis(make_pointer_range(BB->phis())); - - 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()}); - } - - // Group duplicates using DenseSet with custom equality/hashing. - DenseSet Keep; - Keep.reserve(Preds.size()); - - SmallVector Updates; - Updates.reserve(Preds.size() * 2); - - bool MadeChange = false; - - // Helper: redirect all edges X -> DeadPred to X -> LivePred. - auto RedirectIncomingEdges = [&](BasicBlock *DeadPred, BasicBlock *LivePred) { - // Replace successors in all predecessors of DeadPred. - SmallSetVector DeadPredPreds(llvm::from_range, - predecessors(DeadPred)); - if (DTU) { - // All predecessors of DeadPred (except the common predecessor) will be - // moved to LivePred. - Updates.reserve(Updates.size() + DeadPredPreds.size() * 2); - SmallPtrSet LivePredPreds(llvm::from_range, - predecessors(LivePred)); - for (BasicBlock *PP : DeadPredPreds) { - // Do not modify those common predecessors of DeadPred and LivePred - if (!LivePredPreds.contains(PP)) - Updates.push_back({DominatorTree::Insert, PP, LivePred}); - Updates.push_back({DominatorTree::Delete, PP, DeadPred}); - } - } - LLVM_DEBUG(dbgs() << "Replacing duplicate pred BB "; - DeadPred->printAsOperand(dbgs()); dbgs() << " with pred "; - LivePred->printAsOperand(dbgs()); dbgs() << " for "; - BB->printAsOperand(dbgs()); dbgs() << "\n"); - for (BasicBlock *PP : DeadPredPreds) { - Instruction *T = PP->getTerminator(); - T->replaceSuccessorWith(DeadPred, LivePred); - } - }; - - // Try to canonicalize duplicates. - for (const auto &Pred : Preds) { - // Pred is a candidate for simplification. If we find a duplicate BB, - // replace it. - const auto [It, Inserted] = Keep.insert(&Pred); - if (Inserted) - continue; - - // Found duplicate: merge P into canonical predecessor It->Pred. - BasicBlock *KeepPred = (*It)->BB; - BasicBlock *DeadPred = Pred.BB; - - // Avoid merging if either is the other's predecessor in weird ways. - if (KeepPred == DeadPred) - continue; - - // Redirect all edges into DeadPred to KeepPred. - RedirectIncomingEdges(DeadPred, KeepPred); - - // Now DeadPred 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.) - MadeChange = true; - } - - if (DTU && !Updates.empty()) - DTU->applyUpdates(Updates); - - return MadeChange; -} /// Check if passing a value to an instruction will cause undefined behavior. static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified) { From 2b5e5504f3660d333f51a5f3a9fceaeb328d02ab Mon Sep 17 00:00:00 2001 From: Camsyn Date: Mon, 22 Dec 2025 17:01:23 +0800 Subject: [PATCH 07/23] CompTime: retain canonical loop --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 57c20c5dddc22..54c8fab70c198 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8224,6 +8224,11 @@ bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, if (!BB || pred_empty(BB)) 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 with: // - terminator unconditional br to Succ, // - does not have address taken / weird control. From 8d7d0893eb7a4c28a33dc44770edcc0da25abc78 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Mon, 22 Dec 2025 17:01:54 +0800 Subject: [PATCH 08/23] Update tests after generalize simplifyDuplicateSwitchArms --- .../Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll | 4 ++-- llvm/test/Transforms/SimplifyCFG/HoistCode.ll | 4 ++-- llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll b/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll index ea81e0e4fa48c..0adedd297a58c 100644 --- a/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll +++ b/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll @@ -141,12 +141,12 @@ define i32 @PR34471(i32 %x) { ; NO_FWD-NEXT: i32 19, label [[IF19:%.*]] ; NO_FWD-NEXT: i32 42, label [[IF19]] ; NO_FWD-NEXT: ] -; NO_FWD: if19: +; NO_FWD: if42: ; NO_FWD-NEXT: br label [[RETURN]] ; NO_FWD: else3: ; NO_FWD-NEXT: br label [[RETURN]] ; NO_FWD: return: -; NO_FWD-NEXT: [[R:%.*]] = phi i32 [ [[X]], [[IF19]] ], [ 17, [[ENTRY:%.*]] ], [ 0, [[ELSE3]] ] +; NO_FWD-NEXT: [[R:%.*]] = phi i32 [ 17, [[ENTRY:%.*]] ], [ [[X]], [[IF19]] ], [ 0, [[ELSE3]] ] ; NO_FWD-NEXT: ret i32 [[R]] ; ; FWD-LABEL: @PR34471( diff --git a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll index f17652cc5e471..ca1792e281a92 100644 --- a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll +++ b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll @@ -67,10 +67,10 @@ define float @PR39535min_switch(i64 %i, float %x) { ; CHECK-NEXT: i64 1, label [[BB1:%.*]] ; CHECK-NEXT: i64 2, label [[BB1]] ; CHECK-NEXT: ] -; CHECK: bb1: +; CHECK: bb2: ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; CHECK-NEXT: [[COND:%.*]] = phi fast float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[X:%.*]], [[BB1]] ] +; CHECK-NEXT: [[COND:%.*]] = phi fast float [ [[X:%.*]], [[BB1]] ], [ 0.000000e+00, [[ENTRY:%.*]] ] ; CHECK-NEXT: ret float [[COND]] ; entry: diff --git a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll index e642cd264416e..1e37b42334e8d 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll @@ -274,12 +274,12 @@ define i8 @switch_to_select_two_case_results_no_default(i32 %i) !prof !0 { ; CHECK-NEXT: i32 4, label [[CASE3:%.*]] ; CHECK-NEXT: i32 6, label [[CASE3]] ; CHECK-NEXT: ], !prof [[PROF5]] -; CHECK: case3: +; CHECK: case4: ; CHECK-NEXT: br label [[END]] ; CHECK: default: ; CHECK-NEXT: unreachable ; CHECK: end: -; CHECK-NEXT: [[T0:%.*]] = phi i8 [ 42, [[ENTRY:%.*]] ], [ 42, [[ENTRY]] ], [ 44, [[CASE3]] ] +; CHECK-NEXT: [[T0:%.*]] = phi i8 [ 44, [[CASE3]] ], [ 42, [[ENTRY:%.*]] ], [ 42, [[ENTRY]] ] ; CHECK-NEXT: ret i8 [[T0]] ; entry: From b0978344f6c00856dbbbb3698d681c6a1a046619 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Mon, 22 Dec 2025 19:18:05 +0800 Subject: [PATCH 09/23] [NFC] Minor equivalent changes on tests --- llvm/test/Transforms/PhaseOrdering/switch-sext.ll | 2 +- .../SimplifyCFG/X86/switch_to_lookup_table.ll | 10 +++++----- .../Transforms/SimplifyCFG/switch-range-to-icmp.ll | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll index 2488b0428a537..7ff442077a23f 100644 --- a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll +++ b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll @@ -6,8 +6,8 @@ define i8 @test_switch_with_sext_phi(i8 %code) { ; CHECK-SAME: i8 [[CODE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i8 [[CODE]], label [[SW_EPILOG:%.*]] [ -; CHECK-NEXT: i8 108, label [[SW_BB2:%.*]] ; CHECK-NEXT: i8 76, label [[SW_BB3:%.*]] +; CHECK-NEXT: i8 108, label [[SW_BB2:%.*]] ; CHECK-NEXT: ] ; CHECK: sw.bb2: ; CHECK-NEXT: br label [[SW_EPILOG]] diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll index f91adcc9707d8..8d4e0d5261770 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll @@ -329,14 +329,14 @@ define i32 @overflow(i32 %type) { ; CHECK-NEXT: i32 1, label [[IF_END:%.*]] ; CHECK-NEXT: i32 2, label [[SW_BB2:%.*]] ; CHECK-NEXT: ] +; CHECK: sw.bb1: +; CHECK-NEXT: br label [[SW_DEFAULT]] ; CHECK: sw.bb2: -; CHECK-NEXT: br label [[IF_END]] +; CHECK-NEXT: br label [[SW_DEFAULT]] ; CHECK: sw.bb3: -; CHECK-NEXT: br label [[IF_END]] -; CHECK: sw.default: -; CHECK-NEXT: br label [[IF_END]] +; CHECK-NEXT: br label [[SW_DEFAULT]] ; CHECK: if.end: -; CHECK-NEXT: [[DIRENT_TYPE_0:%.*]] = phi i32 [ 3, [[SW_DEFAULT]] ], [ 6, [[SW_BB3]] ], [ 5, [[SW_BB2]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[DIRENT_TYPE_0:%.*]] = phi i32 [ 3, [[ENTRY:%.*]] ], [ 6, [[SW_BB3]] ], [ 5, [[SW_BB2]] ], [ 0, [[IF_END]] ] ; CHECK-NEXT: ret i32 [[DIRENT_TYPE_0]] ; entry: diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll index a43e7625e6736..960431d43917b 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll @@ -149,7 +149,7 @@ unreach2: define void @pr53208_single_reachable_dest(i8 %sw, ptr %p0) { ; CHECK-LABEL: @pr53208_single_reachable_dest( -; CHECK-NEXT: exit: +; CHECK-NEXT: group2: ; CHECK-NEXT: call void @bar(ptr [[P0:%.*]]) ; CHECK-NEXT: ret void ; From 507b32e94ac8cb83bda78ac4027c6a33fe2c40d8 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Tue, 23 Dec 2025 22:30:53 +0800 Subject: [PATCH 10/23] Treat common pred merging as sink-common-code --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 54c8fab70c198..eb0375601f3f3 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8981,7 +8981,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, @@ -8991,6 +8991,10 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { // after which we'd need a whole EarlyCSE pass run to cleanup them. return true; } + // Merge identical predecessors of this block + if (simplifyDuplicatePredecessors(BB, DTU)) + return true; + } if (Options.SpeculateBlocks && !BB->getParent()->hasFnAttribute(Attribute::OptForFuzzing)) { @@ -9003,10 +9007,6 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { return true; } - // Merge identical predecessors of this block - if (simplifyDuplicatePredecessors(BB, DTU)) - return true; - IRBuilder<> Builder(BB); Instruction *Terminator = BB->getTerminator(); Builder.SetInsertPoint(Terminator); From 42e53fdf2b6a01c1e9afd9428214343e674b7d13 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Wed, 24 Dec 2025 00:02:42 +0800 Subject: [PATCH 11/23] Regen tests after take the opt as sink-common-insts --- .../CodeGen/AMDGPU/multi-divergent-exit-region.ll | 7 ++----- .../Transforms/LoopDeletion/simplify-then-delete.ll | 5 ----- llvm/test/Transforms/PhaseOrdering/switch-sext.ll | 2 +- .../SimplifyCFG/ForwardSwitchConditionToPHI.ll | 4 ++-- llvm/test/Transforms/SimplifyCFG/HoistCode.ll | 4 ++-- .../SimplifyCFG/X86/switch-to-lookup-globals.ll | 13 ++++++++++--- .../SimplifyCFG/X86/switch_to_lookup_table.ll | 10 +++++----- llvm/test/Transforms/SimplifyCFG/dup-preds.ll | 4 ++-- .../Transforms/SimplifyCFG/switch-range-to-icmp.ll | 2 +- .../SimplifyCFG/switch-to-select-two-case.ll | 4 ++-- 10 files changed, 27 insertions(+), 28 deletions(-) diff --git a/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll b/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll index 9850e285ac66a..d6cc833c8c73f 100644 --- a/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll +++ b/llvm/test/CodeGen/AMDGPU/multi-divergent-exit-region.ll @@ -682,15 +682,12 @@ divergent.ret: } ; IR-LABEL: @multi_divergent_unreachable_exit( -; IR: Flow5: -; IR-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 -; IR-NEXT: br label %UnifiedReturnBlock - ; IR: UnifiedUnreachableBlock: ; IR-NEXT: call void @llvm.amdgcn.unreachable() -; IR-NEXT: br label %Flow5 +; IR-NEXT: br label %UnifiedReturnBlock ; IR: UnifiedReturnBlock: +; IR-NEXT: call void @llvm.amdgcn.end.cf.i64(i64 ; IR-NEXT: ret void define amdgpu_kernel void @multi_divergent_unreachable_exit(i32 %switch) #0 { bb: diff --git a/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll b/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll index 869fea650f49f..529ee8919bdb3 100644 --- a/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll +++ b/llvm/test/Transforms/LoopDeletion/simplify-then-delete.ll @@ -11,11 +11,6 @@ define i32 @pmat(i32 %m, i32 %n, ptr %y, i1 %arg) nounwind { ; CHECK-LABEL: @pmat( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP4:%.*]] = icmp sgt i32 [[M:%.*]], 0 -; CHECK-NEXT: br i1 [[CMP4]], label [[BB_N10:%.*]], label [[W_E12:%.*]] -; CHECK: bb.n10: -; CHECK-NEXT: [[CMP51:%.*]] = icmp sgt i32 [[N:%.*]], 0 -; CHECK-NEXT: br label [[W_E12]] -; CHECK: w.e12: ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll index 7ff442077a23f..2488b0428a537 100644 --- a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll +++ b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll @@ -6,8 +6,8 @@ define i8 @test_switch_with_sext_phi(i8 %code) { ; CHECK-SAME: i8 [[CODE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i8 [[CODE]], label [[SW_EPILOG:%.*]] [ -; CHECK-NEXT: i8 76, label [[SW_BB3:%.*]] ; CHECK-NEXT: i8 108, label [[SW_BB2:%.*]] +; CHECK-NEXT: i8 76, label [[SW_BB3:%.*]] ; CHECK-NEXT: ] ; CHECK: sw.bb2: ; CHECK-NEXT: br label [[SW_EPILOG]] diff --git a/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll b/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll index 0adedd297a58c..ea81e0e4fa48c 100644 --- a/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll +++ b/llvm/test/Transforms/SimplifyCFG/ForwardSwitchConditionToPHI.ll @@ -141,12 +141,12 @@ define i32 @PR34471(i32 %x) { ; NO_FWD-NEXT: i32 19, label [[IF19:%.*]] ; NO_FWD-NEXT: i32 42, label [[IF19]] ; NO_FWD-NEXT: ] -; NO_FWD: if42: +; NO_FWD: if19: ; NO_FWD-NEXT: br label [[RETURN]] ; NO_FWD: else3: ; NO_FWD-NEXT: br label [[RETURN]] ; NO_FWD: return: -; NO_FWD-NEXT: [[R:%.*]] = phi i32 [ 17, [[ENTRY:%.*]] ], [ [[X]], [[IF19]] ], [ 0, [[ELSE3]] ] +; NO_FWD-NEXT: [[R:%.*]] = phi i32 [ [[X]], [[IF19]] ], [ 17, [[ENTRY:%.*]] ], [ 0, [[ELSE3]] ] ; NO_FWD-NEXT: ret i32 [[R]] ; ; FWD-LABEL: @PR34471( diff --git a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll index ca1792e281a92..f17652cc5e471 100644 --- a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll +++ b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll @@ -67,10 +67,10 @@ define float @PR39535min_switch(i64 %i, float %x) { ; CHECK-NEXT: i64 1, label [[BB1:%.*]] ; CHECK-NEXT: i64 2, label [[BB1]] ; CHECK-NEXT: ] -; CHECK: bb2: +; CHECK: bb1: ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; CHECK-NEXT: [[COND:%.*]] = phi fast float [ [[X:%.*]], [[BB1]] ], [ 0.000000e+00, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[COND:%.*]] = phi fast float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[X:%.*]], [[BB1]] ] ; CHECK-NEXT: ret float [[COND]] ; entry: diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll index 4745cd0f7ea3a..bfcc8344264ea 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll @@ -10,9 +10,16 @@ target triple = "x86_64-unknown-linux-gnu" define i1 @zot(i32 %arg) { ; CHECK-LABEL: @zot( ; CHECK-NEXT: bb: -; CHECK-NEXT: %cond = icmp eq i32 %arg, 1 -; CHECK-NEXT: %spec.select = select i1 %cond, ptr getelementptr inbounds ([75 x { i32, i32, i32, i8, i8 }], ptr @global, i64 0, i64 6, i32 0), ptr null -; CHECK-NEXT: %tmp8 = icmp eq ptr %spec.select, getelementptr inbounds ([75 x { i32, i32, i32, i8, i8 }], ptr @global, i64 1, i64 0, i32 0) +; CHECK-NEXT: %0 = icmp ult i32 %arg, 3 +; CHECK-NEXT: br i1 %0, label %switch.lookup, label %bb6 +; CHECK: switch.lookup: +; CHECK-NEXT: %1 = zext nneg i32 %arg to i64 +; CHECK-NEXT: %switch.gep = getelementptr inbounds [3 x ptr], ptr @switch.table.zot, i64 0, i64 %1 +; CHECK-NEXT: %switch.load = load ptr, ptr %switch.gep, align 8 +; CHECK-NEXT: br label %bb6 +; CHECK: bb6: +; CHECK-NEXT: %tmp7 = phi ptr [ null, %bb ], [ %switch.load, %switch.lookup ] +; CHECK-NEXT: %tmp8 = icmp eq ptr %tmp7, getelementptr inbounds ([75 x { i32, i32, i32, i8, i8 }], ptr @global, i64 1, i64 0, i32 0) ; CHECK-NEXT: ret i1 %tmp8 ; bb: diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll index 8d4e0d5261770..f91adcc9707d8 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll @@ -329,14 +329,14 @@ define i32 @overflow(i32 %type) { ; CHECK-NEXT: i32 1, label [[IF_END:%.*]] ; CHECK-NEXT: i32 2, label [[SW_BB2:%.*]] ; CHECK-NEXT: ] -; CHECK: sw.bb1: -; CHECK-NEXT: br label [[SW_DEFAULT]] ; CHECK: sw.bb2: -; CHECK-NEXT: br label [[SW_DEFAULT]] +; CHECK-NEXT: br label [[IF_END]] ; CHECK: sw.bb3: -; CHECK-NEXT: br label [[SW_DEFAULT]] +; CHECK-NEXT: br label [[IF_END]] +; CHECK: sw.default: +; CHECK-NEXT: br label [[IF_END]] ; CHECK: if.end: -; CHECK-NEXT: [[DIRENT_TYPE_0:%.*]] = phi i32 [ 3, [[ENTRY:%.*]] ], [ 6, [[SW_BB3]] ], [ 5, [[SW_BB2]] ], [ 0, [[IF_END]] ] +; CHECK-NEXT: [[DIRENT_TYPE_0:%.*]] = phi i32 [ 3, [[SW_DEFAULT]] ], [ 6, [[SW_BB3]] ], [ 5, [[SW_BB2]] ], [ 0, [[ENTRY:%.*]] ] ; CHECK-NEXT: ret i32 [[DIRENT_TYPE_0]] ; entry: diff --git a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll index cd6592a45be40..e542ee03641f4 100644 --- a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll +++ b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s -check-prefix=SIMPLIFY-CFG +; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S | FileCheck %s -check-prefix=SIMPLIFY-CFG define i8 @foo(i8 %v1, i8 %v2) { ; SIMPLIFY-CFG-LABEL: @foo( @@ -19,7 +19,7 @@ define i8 @foo(i8 %v1, i8 %v2) { ; 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:%.*]] = phi i8 [ 0, [[ELSE]] ], [ 1, [[SWITCH_CASE_1]] ], [ 2, [[THEN]] ], [ 3, [[ENTRY:%.*]] ] ; SIMPLIFY-CFG-NEXT: ret i8 [[RET]] ; entry: diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll index 960431d43917b..a43e7625e6736 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll @@ -149,7 +149,7 @@ unreach2: define void @pr53208_single_reachable_dest(i8 %sw, ptr %p0) { ; CHECK-LABEL: @pr53208_single_reachable_dest( -; CHECK-NEXT: group2: +; CHECK-NEXT: exit: ; CHECK-NEXT: call void @bar(ptr [[P0:%.*]]) ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll index 1e37b42334e8d..e642cd264416e 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll @@ -274,12 +274,12 @@ define i8 @switch_to_select_two_case_results_no_default(i32 %i) !prof !0 { ; CHECK-NEXT: i32 4, label [[CASE3:%.*]] ; CHECK-NEXT: i32 6, label [[CASE3]] ; CHECK-NEXT: ], !prof [[PROF5]] -; CHECK: case4: +; CHECK: case3: ; CHECK-NEXT: br label [[END]] ; CHECK: default: ; CHECK-NEXT: unreachable ; CHECK: end: -; CHECK-NEXT: [[T0:%.*]] = phi i8 [ 44, [[CASE3]] ], [ 42, [[ENTRY:%.*]] ], [ 42, [[ENTRY]] ] +; CHECK-NEXT: [[T0:%.*]] = phi i8 [ 42, [[ENTRY:%.*]] ], [ 42, [[ENTRY]] ], [ 44, [[CASE3]] ] ; CHECK-NEXT: ret i8 [[T0]] ; entry: From 3954276cc7a8fcb7cce666e73528a271d1f32850 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 2 Jan 2026 16:42:37 +0800 Subject: [PATCH 12/23] Fix crash by ignoring BB without pred --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index eb0375601f3f3..92fea0180938b 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8025,6 +8025,10 @@ struct EqualBBWrapper { if (BB->size() != 1) return false; + // The BB must have at least one predecessor. + if (!BB->hasNPredecessorsOrMore(1)) + return false; + return true; } }; From a0fc5fd11ca9f860ec66c4bad652c669bfd7090e Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 2 Jan 2026 16:43:07 +0800 Subject: [PATCH 13/23] Adjust some comments/var name --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 92fea0180938b..a760501dad47d 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8001,7 +8001,7 @@ struct EqualBBWrapper { Phi2IVsMap *PhiPredIVs; // We only merge the identical non-entry BBs with - // - terminator unconditional br to Succ, + // - 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"); @@ -8010,6 +8010,9 @@ struct EqualBBWrapper { 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; @@ -8021,7 +8024,7 @@ struct EqualBBWrapper { if (BB->isLandingPad()) return false; - // TODO: should we support Pred with >1 instructions? + // TODO: relax this condition to merge equal blocks with >1 instructions? if (BB->size() != 1) return false; @@ -8100,8 +8103,9 @@ template <> struct llvm::DenseMapInfo { } }; -static bool mergeIdenticalUncondBBs(ArrayRef Candidates, - DomTreeUpdater *DTU) { +// Merge identical BBs into one of them. +static bool mergeIdenticalBBs(ArrayRef Candidates, + DomTreeUpdater *DTU) { if (Candidates.size() < 2) return false; @@ -8181,25 +8185,25 @@ static bool mergeIdenticalUncondBBs(ArrayRef Candidates, }; // Try to eliminate duplicate predecessors. - for (const auto &Pred : BBs2Merge) { + for (const auto &EBW : BBs2Merge) { // Pred is a candidate for simplification. If we find a duplicate BB, // replace it. - const auto [It, Inserted] = Keep.insert(&Pred); + const auto [It, Inserted] = Keep.insert(&EBW); if (Inserted) continue; // Found duplicate: merge P into canonical predecessor It->Pred. - BasicBlock *KeepPred = (*It)->BB; - BasicBlock *DeadPred = Pred.BB; + BasicBlock *KeepBB = (*It)->BB; + BasicBlock *DeadBB = EBW.BB; // Avoid merging if either is the other's predecessor in weird ways. - if (KeepPred == DeadPred) + if (KeepBB == DeadBB) continue; // Redirect all edges into DeadPred to KeepPred. - RedirectIncomingEdges(DeadPred, KeepPred); + RedirectIncomingEdges(DeadBB, KeepBB); - // Now DeadPred should become unreachable; leave DCE to later, + // 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.) MadeChange = true; @@ -8213,13 +8217,11 @@ static bool mergeIdenticalUncondBBs(ArrayRef Candidates, bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, DomTreeUpdater *DTU) { - // Collect candidate switch-arms top-down with: - // - terminator unconditional br to Succ, - // - does not have address taken / weird control. - SmallSetVector FilteredPreds( + // Collect candidate switch-arms top-down + SmallSetVector FilteredArms( llvm::from_range, make_filter_range(successors(SI), EqualBBWrapper::canBeMerged)); - return mergeIdenticalUncondBBs(FilteredPreds.getArrayRef(), DTU); + return mergeIdenticalBBs(FilteredArms.getArrayRef(), DTU); } bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, @@ -8233,13 +8235,11 @@ bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, if (Options.NeedCanonicalLoop && is_contained(LoopHeaders, BB)) return false; - // Collect candidate predecessors bottom-up with: - // - terminator unconditional br to Succ, - // - does not have address taken / weird control. + // Collect candidate predecessors bottom-up SmallSetVector FilteredPreds( llvm::from_range, make_filter_range(predecessors(BB), EqualBBWrapper::canBeMerged)); - return mergeIdenticalUncondBBs(FilteredPreds.getArrayRef(), DTU); + return mergeIdenticalBBs(FilteredPreds.getArrayRef(), DTU); } bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { From e91d6fd74d601793c9b29d2c986d50097b2a62c3 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 16:17:35 +0800 Subject: [PATCH 14/23] Follow reviewer's suggestions --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index a760501dad47d..5b5c3d9baee18 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8025,11 +8025,11 @@ struct EqualBBWrapper { return false; // TODO: relax this condition to merge equal blocks with >1 instructions? - if (BB->size() != 1) + if (/* I.e., O(n) calc: size() != 1 */ &BB->front() != &BB->back()) return false; // The BB must have at least one predecessor. - if (!BB->hasNPredecessorsOrMore(1)) + if (pred_empty(BB)) return false; return true; From cc52dfb77cf7c4303b2c0e59c3ab56e804b65959 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 16:52:24 +0800 Subject: [PATCH 15/23] Fix hash | More comp-time fine-tuning --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 5b5c3d9baee18..b733990d5fa63 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8063,7 +8063,7 @@ template <> struct llvm::DenseMapInfo { // time. BasicBlock *Succ = BI->getSuccessor(0); auto PhiValsForBB = map_range( - BB->phis(), [BB, &PhiPredIVs = *EBW->PhiPredIVs](PHINode &Phi) { + Succ->phis(), [BB, &PhiPredIVs = *EBW->PhiPredIVs](PHINode &Phi) { return PhiPredIVs[&Phi][BB]; }); return hash_combine(Succ, hash_combine_range(PhiValsForBB)); @@ -8218,7 +8218,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, DomTreeUpdater *DTU) { // Collect candidate switch-arms top-down - SmallSetVector FilteredArms( + SmallSetVector FilteredArms( llvm::from_range, make_filter_range(successors(SI), EqualBBWrapper::canBeMerged)); return mergeIdenticalBBs(FilteredArms.getArrayRef(), DTU); @@ -8227,7 +8227,7 @@ bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, DomTreeUpdater *DTU) { // Need at least 2 predecessors to do anything. - if (!BB || pred_empty(BB)) + if (!BB || !BB->hasNPredecessorsOrMore(2)) return false; // Compilation time consideration: retain the canonical loop, otherwise, we From 8c1a39e3f1dcfe2d9bf143117c3fa906ab37a371 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 16:56:30 +0800 Subject: [PATCH 16/23] Follow the reviewer's comments --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index b733990d5fa63..3b4df77849bc3 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8019,9 +8019,7 @@ struct EqualBBWrapper { // Avoid blocks that are "address-taken" (blockaddress) or have unusual // uses. - if (BB->hasAddressTaken()) - return false; - if (BB->isLandingPad()) + if (BB->hasAddressTaken() || BB->isLandingPad()) return false; // TODO: relax this condition to merge equal blocks with >1 instructions? @@ -8058,9 +8056,8 @@ template <> struct llvm::DenseMapInfo { // 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. + // 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(), [BB, &PhiPredIVs = *EBW->PhiPredIVs](PHINode &Phi) { From 8a6e45c84c9bd81901dfdc4bbec4ca2ea76ab830 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 17:14:15 +0800 Subject: [PATCH 17/23] Adhere to the review comments --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 3b4df77849bc3..277475c9efb92 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8088,11 +8088,11 @@ 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); auto IfPhiIVMatch = [A, B, &PhiPredIVs = *LHS->PhiPredIVs](PHINode &Phi) { // Replace O(|Pred|) Phi.getIncomingValueForBlock with this O(1) hashmap - // query + // query. auto &PredIVs = PhiPredIVs[&Phi]; return PredIVs[A] == PredIVs[B]; }; @@ -8163,7 +8163,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, SmallPtrSet LivePreds(llvm::from_range, predecessors(Live)); for (BasicBlock *PredOfDead : DeadPreds) { - // Do not modify those common predecessors of DeadPred and LivePred + // 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}); @@ -8185,7 +8185,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, for (const auto &EBW : BBs2Merge) { // Pred is a candidate for simplification. If we find a duplicate BB, // replace it. - const auto [It, Inserted] = Keep.insert(&EBW); + const auto &[It, Inserted] = Keep.insert(&EBW); if (Inserted) continue; @@ -8203,6 +8203,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, // 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(DeadBB->hasNPredecessors(0) && "DeadBB shoud be unreachable."); MadeChange = true; } @@ -8214,7 +8215,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI, DomTreeUpdater *DTU) { - // Collect candidate switch-arms top-down + // Collect candidate switch-arms top-down. SmallSetVector FilteredArms( llvm::from_range, make_filter_range(successors(SI), EqualBBWrapper::canBeMerged)); @@ -8232,7 +8233,7 @@ bool SimplifyCFGOpt::simplifyDuplicatePredecessors(BasicBlock *BB, if (Options.NeedCanonicalLoop && is_contained(LoopHeaders, BB)) return false; - // Collect candidate predecessors bottom-up + // Collect candidate predecessors bottom-up. SmallSetVector FilteredPreds( llvm::from_range, make_filter_range(predecessors(BB), EqualBBWrapper::canBeMerged)); @@ -8299,7 +8300,7 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { return requestResimplify(); // We can merge identical switch arms early to enhance more aggressive - // optimization on switch + // optimization on switch. if (simplifyDuplicateSwitchArms(SI, DTU)) return requestResimplify(); @@ -8992,7 +8993,7 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) { // after which we'd need a whole EarlyCSE pass run to cleanup them. return true; } - // Merge identical predecessors of this block + // Merge identical predecessors of this block. if (simplifyDuplicatePredecessors(BB, DTU)) return true; } From 7a63868bce4ce865d1a421f5a80258463e59d92a Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 17:20:04 +0800 Subject: [PATCH 18/23] hasNPredecessors(0) -> pred_empty --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 277475c9efb92..57fe1604f5a3a 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8203,7 +8203,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, // 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(DeadBB->hasNPredecessors(0) && "DeadBB shoud be unreachable."); + assert(pred_empty(DeadBB) && "DeadBB shoud be unreachable."); MadeChange = true; } From e520c4b292b9ebf0c1747cbd2bccc723bcf2a612 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 17:31:17 +0800 Subject: [PATCH 19/23] Adhere to the review comments --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +- llvm/test/Transforms/SimplifyCFG/dup-preds.ll | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 57fe1604f5a3a..e1efa565d519e 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8203,7 +8203,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, // 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 shoud be unreachable."); + assert(pred_empty(DeadBB) && "DeadBB should be unreachable."); MadeChange = true; } diff --git a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll index e542ee03641f4..4acdf9188198b 100644 --- a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll +++ b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll @@ -1,8 +1,8 @@ ; 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 @foo(i8 %v1, i8 %v2) { -; SIMPLIFY-CFG-LABEL: @foo( +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:%.*]] @@ -19,7 +19,7 @@ define i8 @foo(i8 %v1, i8 %v2) { ; SIMPLIFY-CFG: else: ; SIMPLIFY-CFG-NEXT: br label [[EXIT]] ; SIMPLIFY-CFG: exit: -; SIMPLIFY-CFG-NEXT: [[RET:%.*]] = phi i8 [ 0, [[ELSE]] ], [ 1, [[SWITCH_CASE_1]] ], [ 2, [[THEN]] ], [ 3, [[ENTRY:%.*]] ] +; SIMPLIFY-CFG-NEXT: [[RET:%.*]] = phi i8 [ 0, [[ELSE]] ], [ 2, [[THEN]] ], [ 1, [[SWITCH_CASE_1]] ], [ 3, [[ENTRY:%.*]] ] ; SIMPLIFY-CFG-NEXT: ret i8 [[RET]] ; entry: From a57a6af8f27e50a206ad551b56823f2c08ac9139 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 5 Mar 2026 23:55:33 +0800 Subject: [PATCH 20/23] Adhere to the review comments --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index e1efa565d519e..7616547812f4c 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8019,11 +8019,12 @@ struct EqualBBWrapper { // Avoid blocks that are "address-taken" (blockaddress) or have unusual // uses. - if (BB->hasAddressTaken() || BB->isLandingPad()) + if (BB->hasAddressTaken() || BB->isEHPad()) return false; // TODO: relax this condition to merge equal blocks with >1 instructions? - if (/* I.e., O(n) calc: size() != 1 */ &BB->front() != &BB->back()) + // Here, we use a O(1) form of the O(n) comparison size() != 1 + if (&BB->front() != &BB->back()) return false; // The BB must have at least one predecessor. @@ -8059,10 +8060,9 @@ template <> struct llvm::DenseMapInfo { // 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(), [BB, &PhiPredIVs = *EBW->PhiPredIVs](PHINode &Phi) { - return PhiPredIVs[&Phi][BB]; - }); + 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) { @@ -8183,7 +8183,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, // Try to eliminate duplicate predecessors. for (const auto &EBW : BBs2Merge) { - // Pred is a candidate for simplification. If we find a duplicate BB, + // EBW is a candidate for simplification. If we find a duplicate BB, // replace it. const auto &[It, Inserted] = Keep.insert(&EBW); if (Inserted) @@ -8193,7 +8193,7 @@ static bool mergeIdenticalBBs(ArrayRef Candidates, BasicBlock *KeepBB = (*It)->BB; BasicBlock *DeadBB = EBW.BB; - // Avoid merging if either is the other's predecessor in weird ways. + // Avoid merging a BB with itself. if (KeepBB == DeadBB) continue; From ad303915712058baaac8106a4026b799f954ce4b Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 6 Mar 2026 00:20:28 +0800 Subject: [PATCH 21/23] Add a test to cover `BB->hasAddressTaken()` --- llvm/test/Transforms/SimplifyCFG/dup-preds.ll | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll index 4acdf9188198b..36a7c9f05d637 100644 --- a/llvm/test/Transforms/SimplifyCFG/dup-preds.ll +++ b/llvm/test/Transforms/SimplifyCFG/dup-preds.ll @@ -51,3 +51,58 @@ exit: ; preds = %else, %switch.case. %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 +} From 6473cd5017b6bddd85ba224bc1fdccd00bd05bff Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 6 Mar 2026 00:31:32 +0800 Subject: [PATCH 22/23] Minor change for comments --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 7616547812f4c..16918f7db27b7 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8023,7 +8023,7 @@ struct EqualBBWrapper { 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 size() != 1 + // Here, we use a O(1) form of the O(n) comparison of `size() != 1`. if (&BB->front() != &BB->back()) return false; From 168a835412fe33d20793b683f2a8e62acd680692 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Fri, 6 Mar 2026 16:32:25 +0800 Subject: [PATCH 23/23] Simplify lambda --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 16918f7db27b7..fd27b08f2714a 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -8090,10 +8090,10 @@ template <> struct llvm::DenseMapInfo { // Need to check that PHIs in successor have matching values. BasicBlock *Succ = ABI->getSuccessor(0); - auto IfPhiIVMatch = [A, B, &PhiPredIVs = *LHS->PhiPredIVs](PHINode &Phi) { + auto IfPhiIVMatch = [&](PHINode &Phi) { // Replace O(|Pred|) Phi.getIncomingValueForBlock with this O(1) hashmap // query. - auto &PredIVs = PhiPredIVs[&Phi]; + auto &PredIVs = (*LHS->PhiPredIVs)[&Phi]; return PredIVs[A] == PredIVs[B]; }; return all_of(Succ->phis(), IfPhiIVMatch);