[llvm] [SimplifyCFG] Fix branch-weight overflow when folding switch case into default (PR #212520)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 28 08:25:11 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Alok Kumar Sharma (alokkrsharma)

<details>
<summary>Changes</summary>

The overflow guard added in #<!-- -->178964 for merging a switch case's weight into the default's calls fitWeights() to downscale the weights, but the returned value was never assigned back to Weights, so the downscale didn't take effect. As a result, a default+case sum that overflows uint64_t would wrap around, turning a dominant default weight into a small, incorrect one.

Assign fitWeights()'s result back to Weights before the addition so the downscale takes effect as intended.

---
Full diff: https://github.com/llvm/llvm-project/pull/212520.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Utils/Local.cpp (+4-2) 
- (added) llvm/test/Transforms/SimplifyCFG/switch-default-fold-weight-overflow.ll (+45) 


``````````diff
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index b17740c0bc192..643ad0fb24267 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -228,8 +228,10 @@ bool llvm::ConstantFoldTerminator(BasicBlock *BB, bool DeleteDeadConditions,
           unsigned Idx = It->getCaseIndex();
 
           // Check for and prevent uint64_t overflow by reducing branch weights.
-          if (Weights[0] > UINT64_MAX - Weights[Idx + 1])
-            fitWeights(Weights);
+          if (Weights[0] > UINT64_MAX - Weights[Idx + 1]) {
+            SmallVector<uint32_t> Fitted = fitWeights(Weights);
+            Weights.assign(Fitted.begin(), Fitted.end());
+          }
 
           Weights[0] += Weights[Idx + 1];
           // Remove weight for this case.
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-default-fold-weight-overflow.ll b/llvm/test/Transforms/SimplifyCFG/switch-default-fold-weight-overflow.ll
new file mode 100644
index 0000000000000..91311343e5572
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/switch-default-fold-weight-overflow.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=simplifycfg -S | FileCheck %s
+
+; A case whose destination is the same as the default gets folded into the
+; default, adding its weight to the default's. If default + case overflows
+; uint64_t, the weights must be downscaled via fitWeights() before the
+; addition; otherwise it wraps around and the default's huge weight turns
+; into a tiny, wrong one.
+;
+; default = 2^64 - 100, folded case = 200 (default + case wraps around to
+; 100 as a uint64_t), surviving case = 1000. Correctly scaled, default stays
+; by far the biggest weight. If the downscale isn't applied, it collapses to
+; 100 -- smaller than the surviving case's 1000, which is the wrong relative
+; order.
+
+define void @switch_default_fold_overflow(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @switch_default_fold_overflow(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[X]], 1
+; CHECK-NEXT:    br i1 [[COND]], label %[[OTHER:.*]], label %[[DEFAULT:.*]], !prof [[PROF0:![0-9]+]]
+; CHECK:       [[DEFAULT]]:
+; CHECK-NEXT:    ret void
+; CHECK:       [[OTHER]]:
+; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
+; CHECK-NEXT:    br label %[[DEFAULT]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %default
+  i32 1, label %other
+  ], !prof !0
+
+default:
+  ret void
+
+other:
+  store i32 0, ptr %p
+  br label %default
+}
+
+!0 = !{!"branch_weights", i64 18446744073709551516, i64 200, i64 1000}
+;.
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 -1}
+;.

``````````

</details>


https://github.com/llvm/llvm-project/pull/212520


More information about the llvm-commits mailing list