[llvm] [SimplifyCFG]: Switch on umin replaces default (PR #164097)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 31 05:36:10 PDT 2025
https://github.com/kper updated https://github.com/llvm/llvm-project/pull/164097
>From fba6d47789707e2bfb92addcd605210ce698299c Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Sat, 18 Oct 2025 12:26:41 +0000
Subject: [PATCH 01/12] [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<IntrinsicInst>(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<ConstantInt>(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 <kevin.per at protonmail.com>
Date: Sat, 18 Oct 2025 15:35:29 +0000
Subject: [PATCH 02/12] [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 <kevin.per at protonmail.com>
Date: Sat, 18 Oct 2025 17:45:38 +0000
Subject: [PATCH 03/12] [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<IntrinsicInst>(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<ConstantInt>(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 <kevin.per at protonmail.com>
Date: Sat, 18 Oct 2025 19:56:43 +0000
Subject: [PATCH 04/12] [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 <kevin.per at protonmail.com>
Date: Sun, 19 Oct 2025 17:31:57 +0000
Subject: [PATCH 05/12] [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<DominatorTree::UpdateType> Updates;
+ Updates.push_back({DominatorTree::Delete, BB, Unreachable});
+
+ // A case is dead when its value is higher than the Constant.
+ SmallVector<ConstantInt *, 4> 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 <kevin.per at protonmail.com>
Date: Sun, 19 Oct 2025 17:33:37 +0000
Subject: [PATCH 06/12] [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 <kevin.per at protonmail.com>
Date: Mon, 20 Oct 2025 09:57:20 +0000
Subject: [PATCH 07/12] [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<ConstantInt *, 4> 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 <kevin.per at protonmail.com>
Date: Mon, 20 Oct 2025 12:39:03 +0000
Subject: [PATCH 08/12] [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 <kevin.per at protonmail.com>
Date: Tue, 21 Oct 2025 11:05:49 +0000
Subject: [PATCH 09/12] [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<ConstantInt *, 4> 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 <kevin.per at protonmail.com>
Date: Tue, 21 Oct 2025 11:39:57 +0000
Subject: [PATCH 10/12] [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<DominatorTree::UpdateType> Updates;
SwitchInstProfUpdateWrapper SIW(*SI);
-
- BasicBlock *Unreachable = SI->getDefaultDest();
- SIW.replaceDefaultDest(Case);
- SIW.removeCase(Case);
- SIW->setCondition(A);
-
BasicBlock *BB = SIW->getParent();
- SmallVector<DominatorTree::UpdateType> 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<ConstantInt *, 4> 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 <kevin.per at protonmail.com>
Date: Wed, 22 Oct 2025 13:51:17 +0000
Subject: [PATCH 11/12] [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();
>From 40dcde6abc4dae8ccb8a317cfcfc953f83cc8d2f Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Fri, 31 Oct 2025 13:31:45 +0100
Subject: [PATCH 12/12] [SimplifyCFG]: Merge loops together
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 1948948d4f129..5e9d5861c0c31 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7579,17 +7579,16 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) {
// Dead cases are removed even when the simplification fails.
// A case is dead when its value is higher than the Constant.
- SmallVector<ConstantInt *, 4> DeadCases;
- 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);
- BasicBlock *DeadCaseBB = DeadCase->getCaseSuccessor();
- DeadCaseBB->removePredecessor(SIW->getParent());
- SIW.removeCase(DeadCase);
+ for (auto I = SI->case_begin(), E = SI->case_end(); I != E;) {
+ if (!I->getCaseValue()->getValue().ugt(Constant->getValue())) {
+ ++I;
+ continue;
+ }
+ BasicBlock *DeadCaseBB = I->getCaseSuccessor();
+ DeadCaseBB->removePredecessor(BB);
Updates.push_back({DominatorTree::Delete, BB, DeadCaseBB});
+ I = SI->removeCase(I);
+ E = SI->case_end();
}
auto Case = SI->findCaseValue(Constant);
More information about the llvm-commits
mailing list