[llvm] [SimplifyCFG] Simplify switch default branch when branch proves operand value (PR #206597)
Sayan Sivakumaran via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 16 07:47:06 PDT 2026
https://github.com/sivakusayan updated https://github.com/llvm/llvm-project/pull/206597
>From 3e21d6fdf74f6062d1ca49d19ccafd769d896b9a Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Wed, 1 Jul 2026 10:10:32 -0600
Subject: [PATCH 1/7] Pre-commit test cases
---
.../SimplifyCFG/switch-simplify-default.ll | 120 ++++++++++++++++++
1 file changed, 120 insertions(+)
create mode 100644 llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
new file mode 100644
index 0000000000000..d32fbe4390863
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
@@ -0,0 +1,120 @@
+; 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
+
+define i32 @default_branch_proves_operand_value(i8 %x) {
+; CHECK-LABEL: @default_branch_proves_operand_value(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[ENTRY_UNREACHABLEDEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ ]
+default:
+ %cmp = icmp eq i8 %x, 1
+ call void @llvm.assume(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ ret i32 %retval
+}
+
+define i32 @default_branch_with_weight_proves_operand_value(i8 %x) {
+; CHECK-LABEL: @default_branch_with_weight_proves_operand_value(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]]
+; CHECK: default:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[ENTRY_UNREACHABLEDEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ ], !prof !0
+
+default:
+ %cmp = icmp eq i8 %x, 1
+ call void @llvm.assume(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ ret i32 %retval
+}
+
+define i32 @default_branch_cant_prove_operand_value(i8 %x) {
+; CHECK-LABEL: @default_branch_cant_prove_operand_value(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[X]], 10
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ ]
+
+default:
+ %cmp = icmp ule i8 %x, 10
+ call void @llvm.assume(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ ret i32 %retval
+}
+
+!0 = !{!"branch_weights", i32 8, i32 4, i32 2}
>From f378153a18c81601f1415ebdbef97e8d800355b2 Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Wed, 1 Jul 2026 10:31:30 -0600
Subject: [PATCH 2/7] Simplify switch default branch when branch proves operand
value
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 38 +++++++++++++++++++
.../SimplifyCFG/switch-simplify-default.ll | 11 +++++-
2 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 1e9c66c5a660c..4c80672088070 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7813,6 +7813,41 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) {
return true;
}
+static bool simplifySwitchDefaultBranch(SwitchInst *SI, DomTreeUpdater *DTU,
+ const DataLayout &DL,
+ AssumptionCache *AC) {
+ if (SI->defaultDestUnreachable())
+ return false;
+
+ // If the switch condition is guaranteed to take some concrete value
+ // in the default block, we can create a new case based on that concrete
+ // value and make the default block unreachable. For example, we can
+ // move the default branch into an explicit case here:
+ //
+ // switch_bb:
+ // switch i8 %x, label %default_bb [...]
+ //
+ // default_bb:
+ // %cmp = icmp eq i8 %x, 1
+ // call void llvm.assume(i1 %cmp) ; Implies %x must be 1
+ // ...
+ //
+ BasicBlock *Default = SI->getDefaultDest();
+ const Instruction *CxtI = Default->getTerminator();
+ const KnownBits Known = computeKnownBits(SI->getOperand(0), DL, AC, CxtI);
+ if (!Known.isConstant())
+ return false;
+
+ SwitchInstProfUpdateWrapper SIW(*SI);
+ ConstantInt *CaseVal =
+ ConstantInt::get(SI->getContext(), Known.getConstant());
+ SIW.addCase(CaseVal, Default, SIW.getSuccessorWeight(0));
+ SIW.setSuccessorWeight(0, 0);
+ createUnreachableSwitchDefault(SI, DTU,
+ /*RemoveOrigDefaultBlock*/ false);
+ return true;
+}
+
/// 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: }
@@ -8353,6 +8388,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
if (simplifySwitchWhenUMin(SI, DTU))
return requestResimplify();
+ if (simplifySwitchDefaultBranch(SI, DTU, DL, Options.AC))
+ return requestResimplify();
+
return false;
}
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
index d32fbe4390863..f1c8b4b136f3a 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
@@ -7,7 +7,10 @@ define i32 @default_branch_proves_operand_value(i8 %x) {
; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: i8 1, label [[DEFAULT:%.*]]
; CHECK-NEXT: ]
+; CHECK: entry.unreachabledefault:
+; CHECK-NEXT: unreachable
; CHECK: default:
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
@@ -15,7 +18,7 @@ define i32 @default_branch_proves_operand_value(i8 %x) {
; CHECK: case_zero:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: return:
-; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[ENTRY_UNREACHABLEDEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
; CHECK-NEXT: ret i32 [[RETVAL]]
;
entry:
@@ -45,7 +48,10 @@ define i32 @default_branch_with_weight_proves_operand_value(i8 %x) {
; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: i8 1, label [[DEFAULT:%.*]]
; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]]
+; CHECK: entry.unreachabledefault:
+; CHECK-NEXT: unreachable
; CHECK: default:
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
@@ -53,7 +59,7 @@ define i32 @default_branch_with_weight_proves_operand_value(i8 %x) {
; CHECK: case_zero:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: return:
-; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[ENTRY_UNREACHABLEDEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
; CHECK-NEXT: ret i32 [[RETVAL]]
;
entry:
@@ -117,4 +123,5 @@ return:
ret i32 %retval
}
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 4, i32 2, i32 8}
!0 = !{!"branch_weights", i32 8, i32 4, i32 2}
>From e21abc6065929e52c02cbcf8d6f39d2b48dab3bb Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Wed, 1 Jul 2026 13:31:16 -0600
Subject: [PATCH 3/7] Don't emit invalid switch IR
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 8 +++-
.../SimplifyCFG/switch-simplify-default.ll | 46 +++++++++++++++++++
2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 4c80672088070..4356c95a6b363 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7838,9 +7838,15 @@ static bool simplifySwitchDefaultBranch(SwitchInst *SI, DomTreeUpdater *DTU,
if (!Known.isConstant())
return false;
- SwitchInstProfUpdateWrapper SIW(*SI);
+ // Make sure we don't create invalid IR if the switch already
+ // has an explicit case for this constant.
ConstantInt *CaseVal =
ConstantInt::get(SI->getContext(), Known.getConstant());
+ const llvm::SwitchInst::CaseIt CaseIt = SI->findCaseValue(CaseVal);
+ if (CaseIt != SI->case_default())
+ return false;
+
+ SwitchInstProfUpdateWrapper SIW(*SI);
SIW.addCase(CaseVal, Default, SIW.getSuccessorWeight(0));
SIW.setSuccessorWeight(0, 0);
createUnreachableSwitchDefault(SI, DTU,
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
index f1c8b4b136f3a..b3ef9005c48cb 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
@@ -123,5 +123,51 @@ return:
ret i32 %retval
}
+define i32 @default_branch_proves_operand_value_but_has_existing_case(i8 %x) {
+; CHECK-LABEL: @default_branch_proves_operand_value_but_has_existing_case(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: i8 1, label [[CASE_ONE:%.*]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[X]], 10
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_one:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 1, [[CASE_ONE]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ i8 1, label %case_one
+ ]
+
+default:
+ %cmp = icmp ule i8 %x, 10
+ call void @llvm.assume(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+case_one:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ], [1, %case_one ]
+ ret i32 %retval
+}
+
; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 4, i32 2, i32 8}
!0 = !{!"branch_weights", i32 8, i32 4, i32 2}
>From b95f5c1add9c1ed7543492e4f0b1b7eb6b0f0de6 Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Wed, 1 Jul 2026 14:20:12 -0600
Subject: [PATCH 4/7] Mark default branch in crashing case as dead code
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 26 +++++-------
.../SimplifyCFG/switch-simplify-default.ll | 40 +++++++++----------
2 files changed, 29 insertions(+), 37 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 4356c95a6b363..25d3e627d3b9f 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7820,32 +7820,26 @@ static bool simplifySwitchDefaultBranch(SwitchInst *SI, DomTreeUpdater *DTU,
return false;
// If the switch condition is guaranteed to take some concrete value
- // in the default block, we can create a new case based on that concrete
- // value and make the default block unreachable. For example, we can
- // move the default branch into an explicit case here:
- //
- // switch_bb:
- // switch i8 %x, label %default_bb [...]
- //
- // default_bb:
- // %cmp = icmp eq i8 %x, 1
- // call void llvm.assume(i1 %cmp) ; Implies %x must be 1
- // ...
- //
+ // in the default block, we can make some nice simplifications to the
+ // switch.
BasicBlock *Default = SI->getDefaultDest();
const Instruction *CxtI = Default->getTerminator();
const KnownBits Known = computeKnownBits(SI->getOperand(0), DL, AC, CxtI);
if (!Known.isConstant())
return false;
- // Make sure we don't create invalid IR if the switch already
- // has an explicit case for this constant.
+ // If the known value for the switch operand already has a case,
+ // the default branch must be dead - mark it as unreachable.
ConstantInt *CaseVal =
ConstantInt::get(SI->getContext(), Known.getConstant());
const llvm::SwitchInst::CaseIt CaseIt = SI->findCaseValue(CaseVal);
- if (CaseIt != SI->case_default())
- return false;
+ if (CaseIt != SI->case_default()) {
+ createUnreachableSwitchDefault(SI, DTU,
+ /*RemoveOrigDefaultBlock*/ false);
+ return true;
+ }
+ // Otherwise, we can move the default branch into an explicit case branch.
SwitchInstProfUpdateWrapper SIW(*SI);
SIW.addCase(CaseVal, Default, SIW.getSuccessorWeight(0));
SIW.setSuccessorWeight(0, 0);
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
index b3ef9005c48cb..5f8f5df1ba6cd 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
@@ -84,31 +84,33 @@ return:
ret i32 %retval
}
-define i32 @default_branch_cant_prove_operand_value(i8 %x) {
-; CHECK-LABEL: @default_branch_cant_prove_operand_value(
+define i32 @default_branch_proves_operand_value_but_has_existing_case(i8 %x) {
+; CHECK-LABEL: @default_branch_proves_operand_value_but_has_existing_case(
; CHECK-NEXT: entry:
-; CHECK-NEXT: switch i8 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: i8 1, label [[CASE_ONE:%.*]]
; CHECK-NEXT: ]
-; CHECK: default:
-; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[X]], 10
-; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
-; CHECK-NEXT: br label [[RETURN]]
+; CHECK: entry.unreachabledefault:
+; CHECK-NEXT: unreachable
; CHECK: case_zero:
; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_one:
+; CHECK-NEXT: br label [[RETURN]]
; CHECK: return:
-; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[CASE_ONE]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
; CHECK-NEXT: ret i32 [[RETVAL]]
;
entry:
switch i8 %x, label %default [
i8 -1, label %case_neg_one
i8 0, label %case_zero
+ i8 1, label %case_one
]
default:
- %cmp = icmp ule i8 %x, 10
+ %cmp = icmp eq i8 %x, 1
call void @llvm.assume(i1 %cmp)
br label %return
@@ -118,18 +120,20 @@ case_neg_one:
case_zero:
br label %return
+case_one:
+ br label %return
+
return:
- %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ], [1, %case_one ]
ret i32 %retval
}
-define i32 @default_branch_proves_operand_value_but_has_existing_case(i8 %x) {
-; CHECK-LABEL: @default_branch_proves_operand_value_but_has_existing_case(
+define i32 @default_branch_cant_prove_operand_value(i8 %x) {
+; CHECK-LABEL: @default_branch_cant_prove_operand_value(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i8 [[X:%.*]], label [[DEFAULT:%.*]] [
; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
-; CHECK-NEXT: i8 1, label [[CASE_ONE:%.*]]
; CHECK-NEXT: ]
; CHECK: default:
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[X]], 10
@@ -137,17 +141,14 @@ define i32 @default_branch_proves_operand_value_but_has_existing_case(i8 %x) {
; CHECK-NEXT: br label [[RETURN]]
; CHECK: case_zero:
; CHECK-NEXT: br label [[RETURN]]
-; CHECK: case_one:
-; CHECK-NEXT: br label [[RETURN]]
; CHECK: return:
-; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 1, [[CASE_ONE]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
; CHECK-NEXT: ret i32 [[RETVAL]]
;
entry:
switch i8 %x, label %default [
i8 -1, label %case_neg_one
i8 0, label %case_zero
- i8 1, label %case_one
]
default:
@@ -161,11 +162,8 @@ case_neg_one:
case_zero:
br label %return
-case_one:
- br label %return
-
return:
- %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ], [1, %case_one ]
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
ret i32 %retval
}
>From de6726a84c07e280661ade44513e0e0799c61c4c Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Wed, 1 Jul 2026 14:27:18 -0600
Subject: [PATCH 5/7] Fix format issues
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 25d3e627d3b9f..a860113b89655 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7834,9 +7834,8 @@ static bool simplifySwitchDefaultBranch(SwitchInst *SI, DomTreeUpdater *DTU,
ConstantInt::get(SI->getContext(), Known.getConstant());
const llvm::SwitchInst::CaseIt CaseIt = SI->findCaseValue(CaseVal);
if (CaseIt != SI->case_default()) {
- createUnreachableSwitchDefault(SI, DTU,
- /*RemoveOrigDefaultBlock*/ false);
- return true;
+ createUnreachableSwitchDefault(SI, DTU, /*RemoveOrigDefaultBlock*/ false);
+ return true;
}
// Otherwise, we can move the default branch into an explicit case branch.
>From 43740d6b1668e0209234987980aff7305257bde7 Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Wed, 1 Jul 2026 18:44:11 -0600
Subject: [PATCH 6/7] Add some asserts
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 28 ++++++++++++-----------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index a860113b89655..bb9bbabe381d4 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7816,34 +7816,36 @@ static bool simplifySwitchWhenUMin(SwitchInst *SI, DomTreeUpdater *DTU) {
static bool simplifySwitchDefaultBranch(SwitchInst *SI, DomTreeUpdater *DTU,
const DataLayout &DL,
AssumptionCache *AC) {
+ assert(SI);
if (SI->defaultDestUnreachable())
return false;
- // If the switch condition is guaranteed to take some concrete value
+ // If it can be proved that the switch condition takes some concrete value
// in the default block, we can make some nice simplifications to the
// switch.
BasicBlock *Default = SI->getDefaultDest();
const Instruction *CxtI = Default->getTerminator();
- const KnownBits Known = computeKnownBits(SI->getOperand(0), DL, AC, CxtI);
+ const KnownBits Known = computeKnownBits(SI->getCondition(), DL, AC, CxtI);
if (!Known.isConstant())
return false;
- // If the known value for the switch operand already has a case,
- // the default branch must be dead - mark it as unreachable.
+ // At this point, we know that only one value can be mapped to the
+ // default block. So, if a case doesn't exist for it already, we
+ // can create one pointing to the default block.
ConstantInt *CaseVal =
ConstantInt::get(SI->getContext(), Known.getConstant());
const llvm::SwitchInst::CaseIt CaseIt = SI->findCaseValue(CaseVal);
- if (CaseIt != SI->case_default()) {
- createUnreachableSwitchDefault(SI, DTU, /*RemoveOrigDefaultBlock*/ false);
- return true;
+ if (CaseIt == SI->case_default()) {
+ SwitchInstProfUpdateWrapper SIW(*SI);
+ SIW.addCase(CaseVal, Default, SIW.getSuccessorWeight(0));
+ SIW.setSuccessorWeight(0, 0);
}
+ createUnreachableSwitchDefault(SI, DTU, /*RemoveOrigDefaultBlock*/ false);
- // Otherwise, we can move the default branch into an explicit case branch.
- SwitchInstProfUpdateWrapper SIW(*SI);
- SIW.addCase(CaseVal, Default, SIW.getSuccessorWeight(0));
- SIW.setSuccessorWeight(0, 0);
- createUnreachableSwitchDefault(SI, DTU,
- /*RemoveOrigDefaultBlock*/ false);
+ assert(SI->getNumCases() > 0 && "Switch should have at least one case");
+ assert(SI->findCaseValue(CaseVal) != SI->case_default() &&
+ "Proven value should have a dedicated case");
+ assert(SI->defaultDestUnreachable());
return true;
}
>From bdc4ec7dc521b0deb842ff818e0e0b4d160888f7 Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Mon, 6 Jul 2026 19:50:29 -0600
Subject: [PATCH 7/7] Add check for valid assumptions
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 7 +-
.../SimplifyCFG/switch-simplify-default.ll | 130 +++++++++++++++++-
2 files changed, 133 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index bb9bbabe381d4..5deb7efbb610c 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7824,8 +7824,11 @@ static bool simplifySwitchDefaultBranch(SwitchInst *SI, DomTreeUpdater *DTU,
// in the default block, we can make some nice simplifications to the
// switch.
BasicBlock *Default = SI->getDefaultDest();
- const Instruction *CxtI = Default->getTerminator();
- const KnownBits Known = computeKnownBits(SI->getCondition(), DL, AC, CxtI);
+ const Instruction *CxtI = &*Default->getFirstNonPHIIt();
+ DominatorTree *DT = DTU ? &DTU->getDomTree() : nullptr;
+ const KnownBits Known =
+ computeKnownBits(SI->getCondition(),
+ SimplifyQuery(DL, DT, AC, CxtI).allowEphemerals(true));
if (!Known.isConstant())
return false;
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
index 5f8f5df1ba6cd..e81911c9064e5 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
@@ -84,6 +84,91 @@ return:
ret i32 %retval
}
+define i32 @default_branch_proves_operand_value_different_blocks(i8 %x) {
+; CHECK-LABEL: @default_branch_proves_operand_value_different_blocks(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 1
+; CHECK-NEXT: switch i8 [[X]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: i8 1, label [[DEFAULT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: entry.unreachabledefault:
+; CHECK-NEXT: unreachable
+; CHECK: default:
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ %cmp = icmp eq i8 %x, 1
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ ]
+default:
+ call void @llvm.assume(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ ret i32 %retval
+}
+
+declare void @foo(i8 %bar) nounwind
+define i32 @default_branch_proves_operand_value_assume_operand_not_ephemeral(i8 %x) {
+; CHECK-LABEL: @default_branch_proves_operand_value_assume_operand_not_ephemeral(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: i8 1, label [[DEFAULT:%.*]]
+; CHECK-NEXT: ]
+; CHECK: entry.unreachabledefault:
+; CHECK-NEXT: unreachable
+; CHECK: default:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: call void @foo(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[DEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ ]
+default:
+ %cmp = icmp eq i8 %x, 1
+ call void @llvm.assume(i1 %cmp)
+ call void @foo(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ ret i32 %retval
+}
+
define i32 @default_branch_proves_operand_value_but_has_existing_case(i8 %x) {
; CHECK-LABEL: @default_branch_proves_operand_value_but_has_existing_case(
; CHECK-NEXT: entry:
@@ -128,8 +213,8 @@ return:
ret i32 %retval
}
-define i32 @default_branch_cant_prove_operand_value(i8 %x) {
-; CHECK-LABEL: @default_branch_cant_prove_operand_value(
+define i32 @default_branch_assume_doesnt_prove_all_bits(i8 %x) {
+; CHECK-LABEL: @default_branch_assume_doesnt_prove_all_bits(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i8 [[X:%.*]], label [[DEFAULT:%.*]] [
; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
@@ -167,5 +252,46 @@ return:
ret i32 %retval
}
+declare void @foo_may_unwind()
+define i32 @default_branch_may_throw_before_assume(i8 %x) {
+; CHECK-LABEL: @default_branch_may_throw_before_assume(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[X:%.*]], label [[ENTRY_UNREACHABLEDEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 0, label [[CASE_ZERO:%.*]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X]], 1
+; CHECK-NEXT: call void @foo_may_unwind()
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: case_zero:
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 1, [[ENTRY_UNREACHABLEDEFAULT]] ], [ 0, [[CASE_ZERO]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: ret i32 [[RETVAL]]
+;
+entry:
+ switch i8 %x, label %default [
+ i8 -1, label %case_neg_one
+ i8 0, label %case_zero
+ ]
+default:
+ %cmp = icmp eq i8 %x, 1
+ call void @foo_may_unwind()
+ call void @llvm.assume(i1 %cmp)
+ br label %return
+
+case_neg_one:
+ br label %return
+
+case_zero:
+ br label %return
+
+return:
+ %retval = phi i32 [ 1, %default ], [ -1, %case_neg_one ], [ 0, %case_zero ]
+ ret i32 %retval
+}
+
; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 4, i32 2, i32 8}
!0 = !{!"branch_weights", i32 8, i32 4, i32 2}
More information about the llvm-commits
mailing list