[llvm] [SimplifyCFG] Simplify switch default branch if operand is a known constant (PR #206597)
Sayan Sivakumaran via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 18:53:01 PDT 2026
https://github.com/sivakusayan updated https://github.com/llvm/llvm-project/pull/206597
>From 9911da9ee6d1527704b63938673025dcb08eb59c Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Mon, 29 Jun 2026 16:02:20 -0600
Subject: [PATCH] Simplify switch default branch if switch operand is a known
constant
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 38 ++++++
.../SimplifyCFG/switch-simplify-default.ll | 129 ++++++++++++++++++
2 files changed, 167 insertions(+)
create mode 100644 llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 1e9c66c5a660c..3eb4bdf88be92 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();
+ Instruction *CxtI = Default->getTerminator();
+ 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
new file mode 100644
index 0000000000000..71101b27920cb
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-default.ll
@@ -0,0 +1,129 @@
+; 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: 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: 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)
+ 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: 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]])
+; 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
+ ], !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}
+;.
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 4, i32 2, i32 8}
+;.
More information about the llvm-commits
mailing list