Bug type: miscompilation
Component: CorrelatedValuePropagation
Bug location: llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp: processSwitch
Issue title: CorrelatedValuePropagation miscompilation
Labels: miscompilation
release:cherry-pick-failed
generated by fuzzer
Reported by: saethlin

Like https://github.com/llvm/llvm-project/issues/139866 this is from a fuzzer that generates Rust MIR: https://github.com/cbeuw/rustlantis.

The Rust MIR reproducer is: https://godbolt.org/z/sb568G8r4

With this command line, the program runs:
```
rustc 9870897538617236803.rs -Copt-level=2 -Cllvm-args=-opt-bisect-limit=478
```
Any greater value of opt-bisect-limit runs CorrelatedValuePropagation on `main` and produces a program that just executes a `ud2`.

Comments: author: nikic
body: https://llvm.godbolt.org/z/s1sYaWbc3
```llvm
; RUN: opt -S -passes=correlated-propagation < %s
define void @test() {
start:
  br label %loop

loop:
  %phi = phi i8 [ -69, %start ], [ 0, %loop3 ]
  br label %loop2

loop2:
  br label %loop3

loop3:
  switch i8 %phi, label %exit [
    i8 0, label %loop3
    i8 1, label %loop2
    i8 2, label %loop
  ]

exit:
  call void @foo()
  ret void
}

declare void @foo()
```

Fix patch:
commit 0f7cc4132b62e0ecdbd3193e954b745c5f492e90
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Mon Jun 2 17:42:02 2025 +0800

    [CVP] Keep `ReachableCaseCount` in sync with range of condition (#142302)
    
    https://github.com/llvm/llvm-project/pull/79993 assumes that a reachable
    case must be contained by `CR`. However, it doesn't hold for some edge
    cases. This patch adds additional checks to ensure `ReachableCaseCount`
    is correct.
    
    Note: Similar optimization in SCCP isn't affected by this bug because it
    uses `CR` to compute `ReachableCaseCount`.
    
    Closes https://github.com/llvm/llvm-project/issues/142286.

diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 5226aeb66f65..b95a851c99b4 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -370,15 +370,30 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
   { // Scope for SwitchInstProfUpdateWrapper. It must not live during
     // ConstantFoldTerminator() as the underlying SwitchInst can be changed.
     SwitchInstProfUpdateWrapper SI(*I);
+    ConstantRange CR =
+        LVI->getConstantRangeAtUse(I->getOperandUse(0), /*UndefAllowed=*/false);
     unsigned ReachableCaseCount = 0;
 
     for (auto CI = SI->case_begin(), CE = SI->case_end(); CI != CE;) {
       ConstantInt *Case = CI->getCaseValue();
-      auto *Res = dyn_cast_or_null<ConstantInt>(
-          LVI->getPredicateAt(CmpInst::ICMP_EQ, Cond, Case, I,
-                              /* UseBlockValue */ true));
+      std::optional<bool> Predicate = std::nullopt;
+      if (!CR.contains(Case->getValue()))
+        Predicate = false;
+      else if (CR.isSingleElement() &&
+               *CR.getSingleElement() == Case->getValue())
+        Predicate = true;
+      if (!Predicate) {
+        // Handle missing cases, e.g., the range has a hole.
+        auto *Res = dyn_cast_or_null<ConstantInt>(
+            LVI->getPredicateAt(CmpInst::ICMP_EQ, Cond, Case, I,
+                                /* UseBlockValue=*/true));
+        if (Res && Res->isZero())
+          Predicate = false;
+        else if (Res && Res->isOne())
+          Predicate = true;
+      }
 
-      if (Res && Res->isZero()) {
+      if (Predicate && !*Predicate) {
         // This case never fires - remove it.
         BasicBlock *Succ = CI->getCaseSuccessor();
         Succ->removePredecessor(BB);
@@ -395,7 +410,7 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
           DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB, Succ}});
         continue;
       }
-      if (Res && Res->isOne()) {
+      if (Predicate && *Predicate) {
         // This case always fires.  Arrange for the switch to be turned into an
         // unconditional branch by replacing the switch condition with the case
         // value.
@@ -410,27 +425,24 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
       ++ReachableCaseCount;
     }
 
-    if (ReachableCaseCount > 1 && !SI->defaultDestUnreachable()) {
+    // The default dest is unreachable if all cases are covered.
+    if (!SI->defaultDestUnreachable() &&
+        !CR.isSizeLargerThan(ReachableCaseCount)) {
       BasicBlock *DefaultDest = SI->getDefaultDest();
-      ConstantRange CR = LVI->getConstantRangeAtUse(I->getOperandUse(0),
-                                                    /*UndefAllowed*/ false);
-      // The default dest is unreachable if all cases are covered.
-      if (!CR.isSizeLargerThan(ReachableCaseCount)) {
-        BasicBlock *NewUnreachableBB =
-            BasicBlock::Create(BB->getContext(), "default.unreachable",
-                               BB->getParent(), DefaultDest);
-        new UnreachableInst(BB->getContext(), NewUnreachableBB);
+      BasicBlock *NewUnreachableBB =
+          BasicBlock::Create(BB->getContext(), "default.unreachable",
+                             BB->getParent(), DefaultDest);
+      new UnreachableInst(BB->getContext(), NewUnreachableBB);
 
-        DefaultDest->removePredecessor(BB);
-        SI->setDefaultDest(NewUnreachableBB);
+      DefaultDest->removePredecessor(BB);
+      SI->setDefaultDest(NewUnreachableBB);
 
-        if (SuccessorsCount[DefaultDest] == 1)
-          DTU.applyUpdates({{DominatorTree::Delete, BB, DefaultDest}});
-        DTU.applyUpdates({{DominatorTree::Insert, BB, NewUnreachableBB}});
+      if (SuccessorsCount[DefaultDest] == 1)
+        DTU.applyUpdates({{DominatorTree::Delete, BB, DefaultDest}});
+      DTU.applyUpdates({{DominatorTree::Insert, BB, NewUnreachableBB}});
 
-        ++NumDeadCases;
-        Changed = true;
-      }
+      ++NumDeadCases;
+      Changed = true;
     }
   }
 


Regression tests:
file: llvm/test/Transforms/CorrelatedValuePropagation/switch.ll
commands: opt < %s -passes=correlated-propagation -S
tests: test_name: pr142286
test_body: define void @pr142286() {
start:
  br label %loop

loop:                                             ; preds = %loop3, %start
  %phi = phi i8 [ -1, %start ], [ 0, %loop3 ]
  br label %loop2

loop2:                                            ; preds = %loop3, %loop
  br label %loop3

loop3:                                            ; preds = %loop3, %loop2
  switch i8 %phi, label %exit [
    i8 0, label %loop3
    i8 1, label %loop2
    i8 2, label %loop
  ]

exit:                                             ; preds = %loop3
  ret void
}