Bug type: crash
Component: SimplifyCFG
Bug location: llvm/lib/Transforms/Utils/SimplifyCFG.cpp: SimplifyCFGOpt::simplifyBranchOnICmpChain
Issue title: [SimplifyCFG] Assertion `IterCnt++ < 1000 && "Iterative simplification didn't converge!"' failed.
Labels: crash-on-valid
llvm:transforms
generated by fuzzer
Reported by: dtcxzyw

Reproducer: https://godbolt.org/z/6q17cxYWh
```
; bin/opt -passes="simplifycfg<switch-range-to-icmp>" test.ll -S
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define fastcc i32 @func_209(i8 %conv74) {
entry:
  %cmp.i.i = icmp eq i8 %conv74, 0
  %cond = icmp ne i8 %conv74, 0
  %cmp.i44.i.old = icmp ugt i8 %conv74, 0
  %or.cond = and i1 %cond, %cmp.i44.i.old
  br i1 %or.cond, label %safe_mod_func_int16_t_s_s.exit.i, label %cond.false.i45.i

safe_div_func_uint16_t_u_u.exit.i:                ; preds = %safe_mod_func_int16_t_s_s.exit.i
  %cmp.i44.i.old.old = icmp ugt i8 %conv74, 1
  br i1 %cmp.i44.i.old.old, label %safe_mod_func_int16_t_s_s.exit.i, label %cond.false.i45.i

cond.false.i45.i:                                 ; preds = %safe_div_func_uint16_t_u_u.exit.i, %entry
  br label %safe_mod_func_int16_t_s_s.exit.i

safe_mod_func_int16_t_s_s.exit.i:                 ; preds = %cond.false.i45.i, %safe_div_func_uint16_t_u_u.exit.i, %entry
  br i1 %cmp.i.i, label %func_238.exit, label %safe_div_func_uint16_t_u_u.exit.i

func_238.exit:                                    ; preds = %safe_mod_func_int16_t_s_s.exit.i
  ret i32 0
}
```
```
opt: /root/llvm-project/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp:247: bool iterativelySimplifyCFG(llvm::Function&, const llvm::TargetTransformInfo&, llvm::DomTreeUpdater*, const llvm::SimplifyCFGOptions&): Assertion `IterCnt++ < 1000 && "Iterative simplification didn't converge!"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -passes=simplifycfg<switch-range-to-icmp> <source>
1.	Running pass "function(simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-arithmetic;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>)" on module "<source>"
2.	Running pass "simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-arithmetic;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>" on function "func_209"
 #0 0x0000000005965e58 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5965e58)
 #1 0x0000000005962d04 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #2 0x000075fcf1c42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000075fcf1c969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000075fcf1c42476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000075fcf1c287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000075fcf1c2871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000075fcf1c39e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000048445bc iterativelySimplifyCFG(llvm::Function&, llvm::TargetTransformInfo const&, llvm::DomTreeUpdater*, llvm::SimplifyCFGOptions const&) SimplifyCFGPass.cpp:0:0
 #9 0x0000000004845304 simplifyFunctionCFGImpl(llvm::Function&, llvm::TargetTransformInfo const&, llvm::DominatorTree*, llvm::SimplifyCFGOptions const&) SimplifyCFGPass.cpp:0:0
#10 0x0000000004846485 simplifyFunctionCFG(llvm::Function&, llvm::TargetTransformInfo const&, llvm::DominatorTree*, llvm::SimplifyCFGOptions const&) SimplifyCFGPass.cpp:0:0
#11 0x00000000048465ca llvm::SimplifyCFGPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x48465ca)
#12 0x00000000016da59e llvm::detail::PassModel<llvm::Function, llvm::SimplifyCFGPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x16da59e)
#13 0x0000000005740571 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5740571)
#14 0x0000000000efabfe llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xefabfe)
#15 0x000000000573eada llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x573eada)
#16 0x000000000097af4e llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x97af4e)
#17 0x000000000573e491 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x573e491)
#18 0x00000000009850ea llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x9850ea)
#19 0x0000000000979361 optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x979361)
#20 0x000075fcf1c29d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#21 0x000075fcf1c29e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#22 0x0000000000970775 _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x970775)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```



Fix patch:
commit e8a1162b7be6cf11041260886097843ce9f5e4a3
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Wed Oct 29 10:52:28 2025 +0800

    [SimplifyCFG] Use range check in simplifyBranchOnICmpChain if possible (#165105)
    
    In `simplifyBranchOnICmpChain`, if we can merge the comparisons into a
    range check, use a conditional branch instead. This change also breaks
    the cycle found in https://github.com/llvm/llvm-project/issues/165088.
    
    Closes https://github.com/llvm/llvm-project/issues/165088.
    
    Detailed description of the cycle:
    
    ```
    define void @pr165088_cycle_1(i8 %x) {
    entry:
      %switch = icmp uge i8 %x, 2
      %cond1 = icmp ugt i8 %x, 1
      %or.cond = and i1 %switch, %cond1
      br i1 %or.cond, label %block3, label %block2
    
    block1:
      %cond2 = icmp ugt i8 %x, 1
      br i1 %cond2, label %block3, label %block2
    
    block2:
      br label %block3
    
    block3:
      %cond3 = icmp eq i8 %x, 0
      br i1 %cond3, label %exit, label %block1
    
    exit:
      ret void
    }
    ```
    
    `simplifyBranchOnICmpChain` folds the branch in `entry` to a switch.
    Then we get:
    ```
    entry:
      switch i8 %x, label %block3 [
      i8 1, label %block2
      i8 0, label %block2
      ]
    
    ...
    ```
    
    `performValueComparisonIntoPredecessorFolding` redirects the default
    target `block3` into `block1` because `%x` is never zero.
    ```
    entry:
      switch i8 %x, label %block1 [
      i8 1, label %block2
      i8 0, label %block2
      ]
    ...
    ```
    
    Then `turnSwitchRangeIntoICmp` will convert the switch back into a
    branch on icmp.
    ```
    entry:
      %switch = icmp ult i8 %x, 2
      br i1 %switch, label %block2, label %block1
    ...
    ```
    
    Since `block1` and `block2` share the same successor `block3`,
    `performBranchToCommonDestFolding` merges the conditions of `entry` and
    `block1`, resulting in the original pattern again.

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index c537be5cba37..4fac5d36ddb3 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5228,32 +5228,52 @@ bool SimplifyCFGOpt::simplifyBranchOnICmpChain(BranchInst *BI,
         CompVal, DL.getIntPtrType(CompVal->getType()), "magicptr");
   }
 
-  // Create the new switch instruction now.
-  SwitchInst *New = Builder.CreateSwitch(CompVal, DefaultBB, Values.size());
-  if (HasProfile) {
-    // We know the weight of the default case. We don't know the weight of the
-    // other cases, but rather than completely lose profiling info, we split
-    // the remaining probability equally over them.
-    SmallVector<uint32_t> NewWeights(Values.size() + 1);
-    NewWeights[0] = BranchWeights[1]; // this is the default, and we swapped if
-                                      // TrueWhenEqual.
-    for (auto &V : drop_begin(NewWeights))
-      V = BranchWeights[0] / Values.size();
-    setBranchWeights(*New, NewWeights, /*IsExpected=*/false);
-  }
-
-  // Add all of the 'cases' to the switch instruction.
-  for (ConstantInt *Val : Values)
-    New->addCase(Val, EdgeBB);
-
-  // We added edges from PI to the EdgeBB.  As such, if there were any
-  // PHI nodes in EdgeBB, they need entries to be added corresponding to
-  // the number of edges added.
-  for (BasicBlock::iterator BBI = EdgeBB->begin(); isa<PHINode>(BBI); ++BBI) {
-    PHINode *PN = cast<PHINode>(BBI);
-    Value *InVal = PN->getIncomingValueForBlock(BB);
-    for (unsigned i = 0, e = Values.size() - 1; i != e; ++i)
-      PN->addIncoming(InVal, BB);
+  // Check if we can represent the values as a contiguous range. If so, we use a
+  // range check + conditional branch instead of a switch.
+  if (Values.front()->getValue() - Values.back()->getValue() ==
+      Values.size() - 1) {
+    ConstantRange RangeToCheck = ConstantRange::getNonEmpty(
+        Values.back()->getValue(), Values.front()->getValue() + 1);
+    APInt Offset, RHS;
+    ICmpInst::Predicate Pred;
+    RangeToCheck.getEquivalentICmp(Pred, RHS, Offset);
+    Value *X = CompVal;
+    if (!Offset.isZero())
+      X = Builder.CreateAdd(X, ConstantInt::get(CompVal->getType(), Offset));
+    Value *Cond =
+        Builder.CreateICmp(Pred, X, ConstantInt::get(CompVal->getType(), RHS));
+    BranchInst *NewBI = Builder.CreateCondBr(Cond, EdgeBB, DefaultBB);
+    if (HasProfile)
+      setBranchWeights(*NewBI, BranchWeights, /*IsExpected=*/false);
+    // We don't need to update PHI nodes since we don't add any new edges.
+  } else {
+    // Create the new switch instruction now.
+    SwitchInst *New = Builder.CreateSwitch(CompVal, DefaultBB, Values.size());
+    if (HasProfile) {
+      // We know the weight of the default case. We don't know the weight of the
+      // other cases, but rather than completely lose profiling info, we split
+      // the remaining probability equally over them.
+      SmallVector<uint32_t> NewWeights(Values.size() + 1);
+      NewWeights[0] = BranchWeights[1]; // this is the default, and we swapped
+                                        // if TrueWhenEqual.
+      for (auto &V : drop_begin(NewWeights))
+        V = BranchWeights[0] / Values.size();
+      setBranchWeights(*New, NewWeights, /*IsExpected=*/false);
+    }
+
+    // Add all of the 'cases' to the switch instruction.
+    for (ConstantInt *Val : Values)
+      New->addCase(Val, EdgeBB);
+
+    // We added edges from PI to the EdgeBB.  As such, if there were any
+    // PHI nodes in EdgeBB, they need entries to be added corresponding to
+    // the number of edges added.
+    for (BasicBlock::iterator BBI = EdgeBB->begin(); isa<PHINode>(BBI); ++BBI) {
+      PHINode *PN = cast<PHINode>(BBI);
+      Value *InVal = PN->getIncomingValueForBlock(BB);
+      for (unsigned i = 0, e = Values.size() - 1; i != e; ++i)
+        PN->addIncoming(InVal, BB);
+    }
   }
 
   // Erase the old branch instruction.


Regression tests:
file: llvm/test/Transforms/SimplifyCFG/pr165088.ll
commands: opt -S -passes="simplifycfg<switch-range-to-icmp>" < %s
tests: test_name: <module>
test_body: 
; Avoid getting stuck in the cycle pr165088_cycle_[1-4].

define void @pr165088_cycle_1(i8 %x) {
;
entry:
  %switch = icmp uge i8 %x, 2
  %cond1 = icmp ugt i8 %x, 1
  %or.cond = and i1 %switch, %cond1
  br i1 %or.cond, label %block3, label %block2

block1:
  %cond2 = icmp ugt i8 %x, 1
  br i1 %cond2, label %block3, label %block2

block2:
  br label %block3

block3:
  %cond3 = icmp eq i8 %x, 0
  br i1 %cond3, label %exit, label %block1

exit:
  ret void
}

define void @pr165088_cycle_2(i8 %x) {
;
entry:
  switch i8 %x, label %block3 [
  i8 1, label %block2
  i8 0, label %block2
  ]

block1:                                              ; preds = %block3
  %cond2 = icmp ugt i8 %x, 1
  br i1 %cond2, label %block3, label %block2

block2:                                              ; preds = %entry, %entry, %block1
  br label %block3

block3:                                              ; preds = %entry, %block2, %block1
  %cond3 = icmp eq i8 %x, 0
  br i1 %cond3, label %exit, label %block1

exit:                                             ; preds = %block3
  ret void
}

define void @pr165088_cycle_3(i8 %x) {
;
entry:
  switch i8 %x, label %block1 [
  i8 1, label %block2
  i8 0, label %block2
  ]

block1:                                              ; preds = %entry, %block3
  %cond2 = icmp ugt i8 %x, 1
  br i1 %cond2, label %block3, label %block2

block2:                                              ; preds = %entry, %entry, %block1
  br label %block3

block3:                                              ; preds = %block2, %block1
  %cond3 = icmp eq i8 %x, 0
  br i1 %cond3, label %exit, label %block1

exit:                                             ; preds = %block3
  ret void
}

define void @pr165088_cycle_4(i8 %x) {
;
entry:
  %switch = icmp ult i8 %x, 2
  br i1 %switch, label %block2, label %block1

block1:                                              ; preds = %entry, %block3
  %cond2 = icmp ugt i8 %x, 1
  br i1 %cond2, label %block3, label %block2

block2:                                              ; preds = %entry, %block1
  br label %block3

block3:                                              ; preds = %block2, %block1
  %cond3 = icmp eq i8 %x, 0
  br i1 %cond3, label %exit, label %block1

exit:                                             ; preds = %block3
  ret void
}

define void @pr165088_original(i8 %x) {
;
entry:
  %cond = icmp ne i8 %x, 0
  %cond3 = icmp ne i8 %x, 0
  %or.cond = and i1 %cond, %cond3
  br i1 %or.cond, label %block3, label %block2

block1:                                              ; preds = %block3
  %cond3.old = icmp ugt i8 %x, 1
  br i1 %cond3.old, label %block3, label %block2

block2:                                              ; preds = %block1, %entry
  br label %block3

block3:                                              ; preds = %block2, %block1, %entry
  %cond4 = icmp eq i8 %x, 0
  br i1 %cond4, label %exit, label %block1

exit:                                             ; preds = %block3
  ret void
}