Bug type: crash
Component: LoopSimplifyCFG
Bug location: llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp: handleDeadExits
Issue title: [SCEV] Assertion `isAvailableAtLoopEntry(RHS, L) && "RHS is not available at Loop Entry"' failed
Labels: crash
llvm:transforms
Reported by: aleks-tmb

During our local testing, we encountered the assertion failure `isAvailableAtLoopEntry(RHS, L) && "RHS is not available at Loop Entry".

Reduced reproducer:

```llvm
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-ni:1-p2:32:8:8:32-ni:2"
target triple = "x86_64-unknown-linux-gnu"

define i64 @"main"(ptr addrspace(1) %p, i1 %check) {
entry:
  br label %loop0.pre

loop0.pre:
  br i1 %check, label %exit, label %loop0

loop0:
  %length = load atomic i32, ptr addrspace(1) %p unordered, align 4
  %28 = icmp ugt i32 %length, 1
  br i1 %28, label %loop0.out, label %loop1.preheader

loop0.out:
  %t = add i32 0, 1
  br i1 false, label %loop1.preheader, label %mid

loop1.preheader:
  %length.lcssa = phi i32 [ %length, %loop0.out ], [ %length, %loop0 ]
  %local = phi i32 [ 0, %loop0 ], [ %t, %loop0.out ]
  br label %loop1

loop1:
  %iv1 = phi i32 [ 4, %loop1.preheader ], [ %iv1.next, %loop1.guarded ]
  %82 = icmp ult i32 %iv1, %length.lcssa
  %wc = call i1 @llvm.experimental.widenable.condition()
  %guard.chk = and i1 %82, %wc
  br i1 %guard.chk, label %loop1.guarded, label %deopt-exit

loop1.guarded:
  %iv1.next = add nuw nsw i32 %iv1, 1
  %chk = icmp ugt i32 %iv1, 310
  br i1 %chk, label %loop1.exit, label %loop1

deopt-exit:
  %100 = call i64 (...) @llvm.experimental.deoptimize.i64(i32 13) [ "deopt"() ]
  ret i64 %100

loop1.exit:
  ret i64 0

mid:
  br label %loop0.pre

exit:
  ret i64 0
}

declare i64 @foo()

declare i64 @llvm.experimental.deoptimize.i64(...)

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite)
declare noundef i1 @llvm.experimental.widenable.condition()
```


Steps to reproduce:
`$opt -passes='print<scalar-evolution>,loop-mssa(licm,loop-simplifycfg,loop-predication)'`

Proof: https://godbolt.org/z/8hsbahTjq

Looks like `LoopSimplifyCFGPass` is guilty - it doesn’t invalidate SCEV when removing a dead exit:

```c++
  void handleDeadExits() {
...
      for (Instruction *I : DeadInstructions) {
        SE.forgetBlockAndLoopDispositions(I);
        I->replaceAllUsesWith(PoisonValue::get(I->getType()));
        I->eraseFromParent();
      }
```



Comments: author: aleks-tmb
body: The `LoopSimplifyCFG` pass removes `%length.lcssa.ph`, but later, in the `LoopPredication` pass, we encounter an assertion failure for the `%length.lcssa` SCEV, which remains equal to `%length`

<img width="935" alt="Image" src="https://github.com/user-attachments/assets/4d630238-569e-4d2b-b760-b09d54b4450f" />

![Image](https://github.com/user-attachments/assets/49481d3d-102c-42e1-8836-5c970461993d)
author: aleks-tmb
body: It seems the reason why forgetBlockAndLoopDispositions doesn't cover this case is that `%length.lcssa.ph` doesn't have an existing SCEV, since the instruction was just added by the previous LICM pass.
author: nikic
body: There is some pretty tricky interaction between LCSSA formation and SCEV invalidation, see:
https://github.com/llvm/llvm-project/blob/7f69a399df384c86428d0c97e3afbc8146324226/llvm/lib/Transforms/Utils/LCSSA.cpp#L206-L211

Basically, when forming LCSSA form, we take care not to break the invalidation chain, so that invalidation of the LCSSA phi node is sufficient.

I think the LICM predecessor splitting effectively does manual LCSSA formation and doesn't maintain this special logic.

I think the extra forgetValue() call works around this issue and variants of this might appear elsewhere. But TBH I'm not totally sure.

Fix patch:
commit 41437a6067b2f9dc8e7c458ff6417397796be31c
Author: Aleksandr Popov <42888396+aleks-tmb@users.noreply.github.com>
Date:   Fri Feb 21 12:26:39 2025 +0100

    [LoopSimplifyCFG] Fix SCEV invalidation after removing dead exit (#127536)
    
    Fixes #127534

diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
index 765b76e54068..4524446e5947 100644
--- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
@@ -369,7 +369,7 @@ private:
         DeadInstructions.emplace_back(LandingPad);
 
       for (Instruction *I : DeadInstructions) {
-        SE.forgetBlockAndLoopDispositions(I);
+        SE.forgetValue(I);
         I->replaceAllUsesWith(PoisonValue::get(I->getType()));
         I->eraseFromParent();
       }


Regression tests:
file: llvm/test/Transforms/LoopSimplifyCFG/pr117537.ll
commands: opt -S -passes='print<scalar-evolution>,loop-mssa(licm,loop-simplifycfg,loop-predication)' -verify-scev < %s
tests: test_name: <module>
test_body: 
; Make sure we don't assert due to insufficient SCEV invalidation.

define i64 @"main"(ptr addrspace(1) %p, i1 %check) {
;
entry:
  br label %loop0.pre

loop0.pre:
  br i1 %check, label %exit, label %loop0

loop0:
  %length = load atomic i32, ptr addrspace(1) %p unordered, align 4
  %28 = icmp ugt i32 %length, 1
  br i1 %28, label %loop0.out, label %loop1.preheader

loop0.out:
  %t = add i32 0, 1
  br i1 false, label %loop1.preheader, label %mid

loop1.preheader:
  %length.lcssa = phi i32 [ %length, %loop0.out ], [ %length, %loop0 ]
  %local = phi i32 [ 0, %loop0 ], [ %t, %loop0.out ]
  br label %loop1

loop1:
  %iv1 = phi i32 [ 4, %loop1.preheader ], [ %iv1.next, %loop1.guarded ]
  %82 = icmp ult i32 %iv1, %length.lcssa
  %wc = call i1 @llvm.experimental.widenable.condition()
  %guard.chk = and i1 %82, %wc
  br i1 %guard.chk, label %loop1.guarded, label %deopt-exit

loop1.guarded:
  %iv1.next = add nuw nsw i32 %iv1, 1
  %chk = icmp ugt i32 %iv1, 310
  br i1 %chk, label %loop1.exit, label %loop1

deopt-exit:
  %100 = call i64 (...) @llvm.experimental.deoptimize.i64(i32 13) [ "deopt"() ]
  ret i64 %100

loop1.exit:
  ret i64 0

mid:
  br label %loop0.pre

exit:
  ret i64 0
}

declare i64 @foo()

declare i64 @llvm.experimental.deoptimize.i64(...)

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite)
declare noundef i1 @llvm.experimental.widenable.condition()