From fba6d47789707e2bfb92addcd605210ce698299c Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Sat, 18 Oct 2025 12:26:41 +0000 Subject: [PATCH 01/11] [SimplifyCFG]: Remove unreachable when switch with umin --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 59 +++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index d831c2737e5f8..8d3c91e69ad48 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7540,6 +7540,62 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder, return true; } +/// Tries to transform the switch when the condition is umin and a constant. +/// In that case, the default branch can be replaced by the constant's branch. +/// For example: +/// switch(umin(a, 3)) { +/// case 0: +/// case 1: +/// case 2: +/// case 3: +/// // ... +/// default: +/// unreachable +/// } +/// +/// Transforms into: +/// +/// switch(umin(a, 3)) { +/// case 0: +/// case 1: +/// case 2: +/// default: +/// // This is case 3 +/// } +static bool simplifySwitchWhenUMin(SwitchInst *SI, IRBuilder<> &Builder) { + auto *Call = dyn_cast(SI->getCondition()); + + if (!Call) + return false; + + if (Call->getIntrinsicID() != Intrinsic::umin) + return false; + + if (!SI->defaultDestUnreachable()) + return false; + + // Extract the constant operand from the intrinsic. + auto *Constant = dyn_cast(Call->getArgOperand(1)); + + if (!Constant) { + return false; + } + + for (auto Case = SI->case_begin(), e = SI->case_end(); Case != e; Case++) { + uint64_t CaseValue = Case->getCaseValue()->getValue().getZExtValue(); + + // We found the case which is equal to the case's umin argument. + // We can make the case the default case. + if (Constant->equalsInt(CaseValue)) { + SI->setDefaultDest(Case->getCaseSuccessor()); + SI->removeCase(Case); + return true; + } + } + + return false; +} + /// Tries to transform switch of powers of two to reduce switch range. /// For example, switch like: /// switch (C) { case 1: case 2: case 64: case 128: } @@ -7966,6 +8022,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { if (simplifyDuplicateSwitchArms(SI, DTU)) return requestResimplify(); + if (simplifySwitchWhenUMin(SI, Builder)) + return requestResimplify(); + return false; } From c131865a96a26987aab9cc0a6c5cd116b53a58c0 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Sat, 18 Oct 2025 15:35:29 +0000 Subject: [PATCH 02/11] [SimplifyCFG]: Added test --- .../Transforms/SimplifyCFG/switch-umin.ll | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 llvm/test/Transforms/SimplifyCFG/switch-umin.ll diff --git a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll new file mode 100644 index 0000000000000..69f78bf377dae --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll @@ -0,0 +1,105 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -passes=simplifycfg < %s | FileCheck %s + +declare void @a() +declare void @b() +declare void @c() + +define void @switch_replace_default(i32 %x) { +; CHECK-LABEL: define void @switch_replace_default( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) +; CHECK-NEXT: switch i32 [[MIN]], label %[[COMMON_RET:.*]] [ +; CHECK-NEXT: i32 0, label %[[CASE0:.*]] +; CHECK-NEXT: i32 1, label %[[CASE1:.*]] +; CHECK-NEXT: i32 2, label %[[CASE2:.*]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[CASE0]]: +; CHECK-NEXT: call void @a() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE1]]: +; CHECK-NEXT: call void @b() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE2]]: +; CHECK-NEXT: call void @c() +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %min = call i32 @llvm.umin.i32(i32 %x, i32 3) + switch i32 %min, label %unreachable [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + i32 3, label %case3 + ] + +case0: + call void @a() + ret void + +case1: + call void @b() + ret void + +case2: + call void @c() + ret void + +case3: + ret void + +unreachable: + unreachable +} + +define void @do_not_switch_replace_default(i32 %x, i32 %y) { +; CHECK-LABEL: define void @do_not_switch_replace_default( +; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 [[Y]]) +; CHECK-NEXT: switch i32 [[MIN]], label %[[UNREACHABLE:.*]] [ +; CHECK-NEXT: i32 0, label %[[CASE0:.*]] +; CHECK-NEXT: i32 1, label %[[CASE1:.*]] +; CHECK-NEXT: i32 2, label %[[CASE2:.*]] +; CHECK-NEXT: i32 3, label %[[COMMON_RET:.*]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[CASE0]]: +; CHECK-NEXT: call void @a() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE1]]: +; CHECK-NEXT: call void @b() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE2]]: +; CHECK-NEXT: call void @c() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[UNREACHABLE]]: +; CHECK-NEXT: unreachable +; + %min = call i32 @llvm.umin.i32(i32 %x, i32 %y) + switch i32 %min, label %unreachable [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + i32 3, label %case3 + ] + +case0: + call void @a() + ret void + +case1: + call void @b() + ret void + +case2: + call void @c() + ret void + +case3: + ret void + +unreachable: + unreachable +} From 4866e8f6666df8d88633f8363fafd30722420160 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Sat, 18 Oct 2025 17:45:38 +0000 Subject: [PATCH 03/11] [SimplifyCFG]: Addressed feedback --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 42 +++++++++---------- .../Transforms/SimplifyCFG/switch-umin.ll | 2 +- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 8d3c91e69ad48..ae2d216ad71ac 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7555,45 +7555,41 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder, /// /// Transforms into: /// -/// switch(umin(a, 3)) { +/// switch(a) { /// case 0: /// case 1: /// case 2: /// default: /// // This is case 3 /// } -static bool simplifySwitchWhenUMin(SwitchInst *SI, IRBuilder<> &Builder) { - auto *Call = dyn_cast(SI->getCondition()); - - if (!Call) - return false; +static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { + Value *A; + ConstantInt *Constant; - if (Call->getIntrinsicID() != Intrinsic::umin) + if (!match(SI->getCondition(), m_UMin(m_Value(A), m_ConstantInt(Constant)))) return false; if (!SI->defaultDestUnreachable()) return false; - // Extract the constant operand from the intrinsic. - auto *Constant = dyn_cast(Call->getArgOperand(1)); + auto Case = SI->findCaseValue(Constant); - if (!Constant) { + // When the case value cannot be found then `findCaseValue` returns the + // default cause. This means that there is no `case 3:` and this + // simplification fails. + if (Case == SI->case_default()) return false; - } - for (auto Case = SI->case_begin(), e = SI->case_end(); Case != e; Case++) { - uint64_t CaseValue = Case->getCaseValue()->getValue().getZExtValue(); + BasicBlock *Unreachable = SI->getDefaultDest(); + SI->setDefaultDest(Case->getCaseSuccessor()); + SI->removeCase(Case); + SI->setCondition(A); - // We found the case which is equal to the case's umin argument. - // We can make the case the default case. - if (Constant->equalsInt(CaseValue)) { - SI->setDefaultDest(Case->getCaseSuccessor()); - SI->removeCase(Case); - return true; - } - } + BasicBlock *BB = SI->getParent(); + if (DTU) + DTU->applyUpdates({{DominatorTree::Delete, BB, Unreachable}}); - return false; + return true; } /// Tries to transform switch of powers of two to reduce switch range. @@ -8022,7 +8018,7 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { if (simplifyDuplicateSwitchArms(SI, DTU)) return requestResimplify(); - if (simplifySwitchWhenUMin(SI, Builder)) + if (simplifySwitchWhenUMin(SI, DTU)) return requestResimplify(); return false; diff --git a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll index 69f78bf377dae..fbc4d475b0264 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll @@ -9,7 +9,7 @@ define void @switch_replace_default(i32 %x) { ; CHECK-LABEL: define void @switch_replace_default( ; CHECK-SAME: i32 [[X:%.*]]) { ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) -; CHECK-NEXT: switch i32 [[MIN]], label %[[COMMON_RET:.*]] [ +; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ ; CHECK-NEXT: i32 0, label %[[CASE0:.*]] ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] From 27cfa8b86fbb1263fe4e7355370d2fbe465bba87 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Sat, 18 Oct 2025 19:56:43 +0000 Subject: [PATCH 04/11] [SimplifyCFG]: Updated weights --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 10 ++++++---- llvm/test/Transforms/SimplifyCFG/switch-umin.ll | 10 ++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index ae2d216ad71ac..ca91603400743 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7580,12 +7580,14 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { if (Case == SI->case_default()) return false; + SwitchInstProfUpdateWrapper SIW(*SI); + BasicBlock *Unreachable = SI->getDefaultDest(); - SI->setDefaultDest(Case->getCaseSuccessor()); - SI->removeCase(Case); - SI->setCondition(A); + SIW->setDefaultDest(Case->getCaseSuccessor()); + SIW.removeCase(Case); + SIW->setCondition(A); - BasicBlock *BB = SI->getParent(); + BasicBlock *BB = SIW->getParent(); if (DTU) DTU->applyUpdates({{DominatorTree::Delete, BB, Unreachable}}); diff --git a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll index fbc4d475b0264..9c41c20f40ce5 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll @@ -13,7 +13,7 @@ define void @switch_replace_default(i32 %x) { ; CHECK-NEXT: i32 0, label %[[CASE0:.*]] ; CHECK-NEXT: i32 1, label %[[CASE1:.*]] ; CHECK-NEXT: i32 2, label %[[CASE2:.*]] -; CHECK-NEXT: ] +; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]] ; CHECK: [[COMMON_RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CASE0]]: @@ -32,7 +32,7 @@ define void @switch_replace_default(i32 %x) { i32 1, label %case1 i32 2, label %case2 i32 3, label %case3 - ] + ], !prof !0 case0: call void @a() @@ -53,6 +53,7 @@ unreachable: unreachable } + define void @do_not_switch_replace_default(i32 %x, i32 %y) { ; CHECK-LABEL: define void @do_not_switch_replace_default( ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { @@ -103,3 +104,8 @@ case3: unreachable: unreachable } + +!0 = !{!"branch_weights", i32 1, i32 2, i32 3, i32 99, i32 5} +;. +; CHECK: [[PROF0]] = !{!"branch_weights", i32 1, i32 2, i32 3, i32 99} +;. From 8a5bd8c133e99d2ee295635a3dbbf15b18ee78c9 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Sun, 19 Oct 2025 17:31:57 +0000 Subject: [PATCH 05/11] [SimplifyCFG]: Remove dead cases --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index ca91603400743..3f6eb896339dc 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -42,6 +42,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" @@ -7588,8 +7589,26 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { SIW->setCondition(A); BasicBlock *BB = SIW->getParent(); + SmallVector Updates; + Updates.push_back({DominatorTree::Delete, BB, Unreachable}); + + // A case is dead when its value is higher than the Constant. + SmallVector DeadCases; + for (auto Case = SI->case_begin(), e = SI->case_end(); Case != e; Case++) { + if (Case->getCaseValue()->getValue().ugt(Constant->getValue())) { + DeadCases.push_back(Case->getCaseValue()); + } + } + + for (ConstantInt *DeadCase : DeadCases) { + SwitchInst::CaseIt CaseI = SIW->findCaseValue(DeadCase); + CaseI->getCaseSuccessor()->removePredecessor(SIW->getParent()); + SIW.removeCase(Case); + Updates.push_back({DominatorTree::Delete, BB, Case->getCaseSuccessor()}); + } + if (DTU) - DTU->applyUpdates({{DominatorTree::Delete, BB, Unreachable}}); + DTU->applyUpdates(Updates); return true; } From 64443b77d0cf304fd3bb6b4eb0bd03ab78ef85ac Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Sun, 19 Oct 2025 17:33:37 +0000 Subject: [PATCH 06/11] [SimplifyCFG]: Remove header --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 3f6eb896339dc..12532cb4dc15e 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -42,7 +42,6 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" From cf5a0c2b06aa314801e502ad9be09c6c67eb4ef6 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Mon, 20 Oct 2025 09:57:20 +0000 Subject: [PATCH 07/11] [SimplifyCFG]: Addressed feedback --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 17 ++-- .../Transforms/SimplifyCFG/switch-umin.ll | 82 +++++++++++++++++++ 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 12532cb4dc15e..45bac347719ba 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7593,17 +7593,18 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { // A case is dead when its value is higher than the Constant. SmallVector DeadCases; - for (auto Case = SI->case_begin(), e = SI->case_end(); Case != e; Case++) { - if (Case->getCaseValue()->getValue().ugt(Constant->getValue())) { - DeadCases.push_back(Case->getCaseValue()); + for (auto Case : SI->cases()) { + if (Case.getCaseValue()->getValue().ugt(Constant->getValue())) { + DeadCases.push_back(Case.getCaseValue()); } } - for (ConstantInt *DeadCase : DeadCases) { - SwitchInst::CaseIt CaseI = SIW->findCaseValue(DeadCase); - CaseI->getCaseSuccessor()->removePredecessor(SIW->getParent()); - SIW.removeCase(Case); - Updates.push_back({DominatorTree::Delete, BB, Case->getCaseSuccessor()}); + for (ConstantInt *DeadCaseVal : DeadCases) { + SwitchInst::CaseIt DeadCase = SIW->findCaseValue(DeadCaseVal); + BasicBlock *DeadCaseBB = DeadCase->getCaseSuccessor(); + DeadCaseBB->removePredecessor(SIW->getParent()); + SIW.removeCase(DeadCase); + Updates.push_back({DominatorTree::Delete, BB, DeadCaseBB}); } if (DTU) diff --git a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll index 9c41c20f40ce5..4cc96c0804935 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll @@ -53,6 +53,88 @@ unreachable: unreachable } +define void @switch_replace_default_and_remove_dead_cases(i32 %x) { +; CHECK-LABEL: define void @switch_replace_default_and_remove_dead_cases( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) +; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ +; CHECK-NEXT: i32 2, label %[[CASE2:.*]] +; CHECK-NEXT: i32 1, label %[[CASE1:.*]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[CASE1]]: +; CHECK-NEXT: call void @b() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE2]]: +; CHECK-NEXT: call void @c() +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %min = call i32 @llvm.umin.i32(i32 %x, i32 3) + switch i32 %min, label %unreachable [ + i32 4, label %case4 + i32 1, label %case1 + i32 2, label %case2 + i32 3, label %case3 + ] + +case4: + call void @a() + ret void + +case1: + call void @b() + ret void + +case2: + call void @c() + ret void + +case3: + ret void + +unreachable: + unreachable +} + +define void @switch_replace_default_when_holes(i32 %x) { +; CHECK-LABEL: define void @switch_replace_default_when_holes( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) +; CHECK-NEXT: switch i32 [[X]], label %[[COMMON_RET:.*]] [ +; CHECK-NEXT: i32 1, label %[[CASE1:.*]] +; CHECK-NEXT: i32 2, label %[[CASE2:.*]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[CASE1]]: +; CHECK-NEXT: call void @b() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE2]]: +; CHECK-NEXT: call void @c() +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %min = call i32 @llvm.umin.i32(i32 %x, i32 3) + switch i32 %min, label %unreachable [ + i32 1, label %case1 + i32 2, label %case2 + i32 3, label %case3 + ] + +case1: + call void @b() + ret void + +case2: + call void @c() + ret void + +case3: + ret void + +unreachable: + unreachable +} define void @do_not_switch_replace_default(i32 %x, i32 %y) { ; CHECK-LABEL: define void @do_not_switch_replace_default( From 7443e6e9c763a07feea0fa058c3102029e44fabc Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Mon, 20 Oct 2025 12:39:03 +0000 Subject: [PATCH 08/11] [SimplifyCFG]: Update default case weight --- llvm/include/llvm/IR/Instructions.h | 4 ++++ llvm/lib/IR/Instructions.cpp | 12 ++++++++++++ llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +- llvm/test/Transforms/SimplifyCFG/switch-umin.ll | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 27930bbc651bd..655b29a9ceb9f 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -3556,6 +3556,10 @@ class SwitchInstProfUpdateWrapper { /// correspondent branch weight. LLVM_ABI SwitchInst::CaseIt removeCase(SwitchInst::CaseIt I); + /// Delegate the call to the underlying SwitchInst::setDefaultCase and + /// remove correspondent branch weight. + LLVM_ABI void setDefaultDest(SwitchInst::CaseIt I); + /// Delegate the call to the underlying SwitchInst::addCase() and set the /// specified branch weight for the added case. LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest, CaseWeightOpt W); diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 9060a89bb15ff..561e7895b2c2c 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -4171,6 +4171,18 @@ SwitchInstProfUpdateWrapper::removeCase(SwitchInst::CaseIt I) { return SI.removeCase(I); } +void SwitchInstProfUpdateWrapper::setDefaultDest(SwitchInst::CaseIt I) { + auto DestBlock = I->getCaseSuccessor(); + if (Weights) { + auto Weight = getSuccessorWeight(I->getCaseIndex() + 1); + if (Weight.has_value()) { + (*Weights)[0] = Weight.value(); + } + } + + SI.setDefaultDest(DestBlock); +} + void SwitchInstProfUpdateWrapper::addCase( ConstantInt *OnVal, BasicBlock *Dest, SwitchInstProfUpdateWrapper::CaseWeightOpt W) { diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 45bac347719ba..2748e55f8f99d 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7583,7 +7583,7 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { SwitchInstProfUpdateWrapper SIW(*SI); BasicBlock *Unreachable = SI->getDefaultDest(); - SIW->setDefaultDest(Case->getCaseSuccessor()); + SIW.setDefaultDest(Case); SIW.removeCase(Case); SIW->setCondition(A); diff --git a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll index 4cc96c0804935..1fbcb9c915b8a 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll @@ -189,5 +189,5 @@ unreachable: !0 = !{!"branch_weights", i32 1, i32 2, i32 3, i32 99, i32 5} ;. -; CHECK: [[PROF0]] = !{!"branch_weights", i32 1, i32 2, i32 3, i32 99} +; CHECK: [[PROF0]] = !{!"branch_weights", i32 5, i32 2, i32 3, i32 99} ;. From 8865c3fb3f095864928c5dde5295ea4619af27f1 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Tue, 21 Oct 2025 11:05:49 +0000 Subject: [PATCH 09/11] [SimplifyCFG]: Addressed feedback --- llvm/include/llvm/IR/Instructions.h | 7 ++++--- llvm/lib/IR/Instructions.cpp | 8 +++----- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 8 +++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 655b29a9ceb9f..8bd060ae8f485 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -3556,9 +3556,10 @@ class SwitchInstProfUpdateWrapper { /// correspondent branch weight. LLVM_ABI SwitchInst::CaseIt removeCase(SwitchInst::CaseIt I); - /// Delegate the call to the underlying SwitchInst::setDefaultCase and - /// remove correspondent branch weight. - LLVM_ABI void setDefaultDest(SwitchInst::CaseIt I); + /// Replace the default destination by given case. Delegate the call to + /// the underlying SwitchInst::setDefaultDest and remove correspondent branch + /// weight. + LLVM_ABI void replaceDefaultDest(SwitchInst::CaseIt I); /// Delegate the call to the underlying SwitchInst::addCase() and set the /// specified branch weight for the added case. diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 561e7895b2c2c..cb87691922a10 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -4171,13 +4171,11 @@ SwitchInstProfUpdateWrapper::removeCase(SwitchInst::CaseIt I) { return SI.removeCase(I); } -void SwitchInstProfUpdateWrapper::setDefaultDest(SwitchInst::CaseIt I) { - auto DestBlock = I->getCaseSuccessor(); +void SwitchInstProfUpdateWrapper::replaceDefaultDest(SwitchInst::CaseIt I) { + auto *DestBlock = I->getCaseSuccessor(); if (Weights) { auto Weight = getSuccessorWeight(I->getCaseIndex() + 1); - if (Weight.has_value()) { - (*Weights)[0] = Weight.value(); - } + (*Weights)[0] = Weight.value(); } SI.setDefaultDest(DestBlock); diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 2748e55f8f99d..b5e6875633dbe 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7583,7 +7583,7 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { SwitchInstProfUpdateWrapper SIW(*SI); BasicBlock *Unreachable = SI->getDefaultDest(); - SIW.setDefaultDest(Case); + SIW.replaceDefaultDest(Case); SIW.removeCase(Case); SIW->setCondition(A); @@ -7593,11 +7593,9 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { // A case is dead when its value is higher than the Constant. SmallVector DeadCases; - for (auto Case : SI->cases()) { - if (Case.getCaseValue()->getValue().ugt(Constant->getValue())) { + for (auto Case : SI->cases()) + if (Case.getCaseValue()->getValue().ugt(Constant->getValue())) DeadCases.push_back(Case.getCaseValue()); - } - } for (ConstantInt *DeadCaseVal : DeadCases) { SwitchInst::CaseIt DeadCase = SIW->findCaseValue(DeadCaseVal); From bf76fa45352d90ff20cde0244999d8952fec6040 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Tue, 21 Oct 2025 11:39:57 +0000 Subject: [PATCH 10/11] [SimplifyCFG]: Remove dead cases when umin pattern matches --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 45 +++++++++------- .../Transforms/SimplifyCFG/switch-umin.ll | 53 +++++++++++++++++++ 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index b5e6875633dbe..89c93dacf6348 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7540,14 +7540,18 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder, return true; } -/// Tries to transform the switch when the condition is umin and a constant. +/// Tries to transform the switch when the condition is umin with a constant. /// In that case, the default branch can be replaced by the constant's branch. +/// This method also removes dead cases when the simplification cannot replace +/// the default branch. +/// /// For example: /// switch(umin(a, 3)) { /// case 0: /// case 1: /// case 2: /// case 3: +/// case 4: /// // ... /// default: /// unreachable @@ -7569,28 +7573,11 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { if (!match(SI->getCondition(), m_UMin(m_Value(A), m_ConstantInt(Constant)))) return false; - if (!SI->defaultDestUnreachable()) - return false; - - auto Case = SI->findCaseValue(Constant); - - // When the case value cannot be found then `findCaseValue` returns the - // default cause. This means that there is no `case 3:` and this - // simplification fails. - if (Case == SI->case_default()) - return false; - + SmallVector Updates; SwitchInstProfUpdateWrapper SIW(*SI); - - BasicBlock *Unreachable = SI->getDefaultDest(); - SIW.replaceDefaultDest(Case); - SIW.removeCase(Case); - SIW->setCondition(A); - BasicBlock *BB = SIW->getParent(); - SmallVector Updates; - Updates.push_back({DominatorTree::Delete, BB, Unreachable}); + // Dead cases are removed even when the simplification fails. // A case is dead when its value is higher than the Constant. SmallVector DeadCases; for (auto Case : SI->cases()) @@ -7605,6 +7592,24 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { Updates.push_back({DominatorTree::Delete, BB, DeadCaseBB}); } + auto Case = SI->findCaseValue(Constant); + // If the case value is not found, `findCaseValue` returns the default case. + // In this scenario, since there is no explicit `case 3:`, the simplification + // fails. The simplification also fails when the switch’s default destination + // is reachable. + if (!SI->defaultDestUnreachable() || Case == SI->case_default()) { + if (DTU) + DTU->applyUpdates(Updates); + return false; + } + + BasicBlock *Unreachable = SI->getDefaultDest(); + SIW.replaceDefaultDest(Case); + SIW.removeCase(Case); + SIW->setCondition(A); + + Updates.push_back({DominatorTree::Delete, BB, Unreachable}); + if (DTU) DTU->applyUpdates(Updates); diff --git a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll index 1fbcb9c915b8a..44665365dc222 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-umin.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-umin.ll @@ -4,6 +4,7 @@ declare void @a() declare void @b() declare void @c() +declare void @d() define void @switch_replace_default(i32 %x) { ; CHECK-LABEL: define void @switch_replace_default( @@ -187,6 +188,58 @@ unreachable: unreachable } +define void @do_not_replace_switch_default_but_remove_dead_cases(i32 %x) { +; CHECK-LABEL: define void @do_not_replace_switch_default_but_remove_dead_cases( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 3) +; CHECK-NEXT: switch i32 [[MIN]], label %[[CASE0:.*]] [ +; CHECK-NEXT: i32 3, label %[[COMMON_RET:.*]] +; CHECK-NEXT: i32 1, label %[[CASE1:.*]] +; CHECK-NEXT: i32 2, label %[[CASE2:.*]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[CASE0]]: +; CHECK-NEXT: call void @a() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE1]]: +; CHECK-NEXT: call void @b() +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[CASE2]]: +; CHECK-NEXT: call void @c() +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %min = call i32 @llvm.umin.i32(i32 %x, i32 3) + switch i32 %min, label %case0 [ ; default is reachable, therefore simplification not triggered + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + i32 3, label %case3 + i32 4, label %case4 + ] + +case0: + call void @a() + ret void + +case1: + call void @b() + ret void + +case2: + call void @c() + ret void + +case3: + ret void + +case4: + call void @d() + ret void + +} + + !0 = !{!"branch_weights", i32 1, i32 2, i32 3, i32 99, i32 5} ;. ; CHECK: [[PROF0]] = !{!"branch_weights", i32 5, i32 2, i32 3, i32 99} From fb77844364cf234d7ca39253d67954a4009ff6b7 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Wed, 22 Oct 2025 13:51:17 +0000 Subject: [PATCH 11/11] [SimplifyCFG]: Fixed return when dead cases are eliminated --- 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 89c93dacf6348..1948948d4f129 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7600,7 +7600,7 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) { if (!SI->defaultDestUnreachable() || Case == SI->case_default()) { if (DTU) DTU->applyUpdates(Updates); - return false; + return !Updates.empty(); } BasicBlock *Unreachable = SI->getDefaultDest();