[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