Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: GeneratedRTChecks
Issue title: LoopVectorizer crashes in SCEVExpanderCleanup
Labels: vectorizers
crash
Reported by: Theodor

Assert in vectorizer during SCEVExpanderCleanup: https://godbolt.org/z/Ezcf68T85

Assert message:

```
opt: .../llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp:2466: void llvm::SCEVExpanderCleaner::cleanup(): Assertion `all_of(I->users(), [&InsertedSet](Value *U) { return InsertedSet.contains(cast<Instruction>(U)); }) && "removed instruction should only be used by instructions inserted " "during expansion"' failed.
```

IR reproducer:

```llvm
target triple = "x86_64-unknown-linux-gnu"

define void @widget(i32 %arg, i64 %arg1) #0 {
bb:
  br label %bb2
bb2:                                              ; preds = %bb4, %bb
  br label %bb5
bb3:                                              ; preds = %bb5
  %load = load ptr addrspace(1), ptr null, align 8%getelementptr = getelementptr i32, ptr addrspace(1) null, i64 %arg1
  br i1 false, label %bb7, label %bb4
bb4:                                              ; preds = %bb3
  br label %bb2
bb5:                                              ; preds = %bb5, %bb2
  br i1 false, label %bb3, label %bb5
bb6:                                              ; preds = %bb7
  ret void
bb7:                                              ; preds = %bb7, %bb3
  %phi = phi i32 [ %add, %bb7 ], [ %arg, %bb3 ]
  %phi8 = phi i32 [ %or, %bb7 ], [ 0, %bb3 ]
  %load9 = load i32, ptr addrspace(1) %getelementptr, align 4
  %or = or i32 %phi8, 0
  store i32 %or, ptr addrspace(1) %load, align 4
  %add = add i32 %phi, 1
  %icmp = icmp eq i32 %phi, 0
  br i1 %icmp, label %bb6, label %bb7, !prof !0
}
attributes #0 = { "target-features"="+avx2" }
!0 = !{!"branch_weights", i32 89478484, i32 1879048192}
```
Run as: `opt -passes=loop-vectorize`


Comments: author: hiraditya
body: Stack trace
```
opt: /root/llvm-project/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp:2466: void llvm::SCEVExpanderCleaner::cleanup(): Assertion `all_of(I->users(), [&InsertedSet](Value *U) { return InsertedSet.contains(cast<Instruction>(U)); }) && "removed instruction should only be used by instructions inserted " "during expansion"' 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=loop-vectorize <source>
1.	Running pass "function(loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>)" on module "<source>"
2.	Running pass "loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>" on function "widget"
 #0 0x00000000058aae78 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x58aae78)
 #1 0x00000000058a7d24 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #2 0x000073e015642520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000073e0156969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000073e015642476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000073e0156287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000073e01562871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000073e015639e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x0000000004b3a12f llvm::SCEVExpanderCleaner::cleanup() (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4b3a12f)
 #9 0x00000000035ed532 (anonymous namespace)::GeneratedRTChecks::~GeneratedRTChecks() LoopVectorize.cpp:0:0
#10 0x00000000036409aa llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x36409aa)
```

Fix patch:
commit 45ce88758d24df7c2c322b152cf4894f3e8ee45a
Author: Florian Hahn <flo@fhahn.com>
Date:   Tue Sep 30 11:03:55 2025 +0100

    [LV] Don't preserve LCSSA in SCEVExpander for runtime checks. (#159556)
    
    LV does not preserve LCSSA, it constructs it just before processing a
    loop to vectorize. Runtime check expressions are invariant to that loop,
    so expanding them should not break LCSSA form for the loop we are about
    to vectorize.
    
    This fixes a crash when discarding instructions generated when expanding
    runtime checks, if the expansion introduces LCSSA phis for values from
    other loops which are not in LCSSA form: we would introduce new LCSSA
    phis and update all outside users, some of which are not created by the
    expander and cannot be cleaned up.
    
    Fixes https://github.com/llvm/llvm-project/issues/158259.
    
    PR: https://github.com/llvm/llvm-project/pull/159556

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index ab5c9c99b944..a0043bed2e0c 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -1762,9 +1762,10 @@ public:
   GeneratedRTChecks(PredicatedScalarEvolution &PSE, DominatorTree *DT,
                     LoopInfo *LI, TargetTransformInfo *TTI,
                     const DataLayout &DL, TTI::TargetCostKind CostKind)
-      : DT(DT), LI(LI), TTI(TTI), SCEVExp(*PSE.getSE(), DL, "scev.check"),
-        MemCheckExp(*PSE.getSE(), DL, "scev.check"), PSE(PSE),
-        CostKind(CostKind) {}
+      : DT(DT), LI(LI), TTI(TTI),
+        SCEVExp(*PSE.getSE(), DL, "scev.check", /*PreserveLCSSA=*/false),
+        MemCheckExp(*PSE.getSE(), DL, "scev.check", /*PreserveLCSSA=*/false),
+        PSE(PSE), CostKind(CostKind) {}
 
   /// Generate runtime checks in SCEVCheckBlock and MemCheckBlock, so we can
   /// accurately estimate the cost of the runtime checks. The blocks are


Regression tests:
file: llvm/test/Transforms/LoopVectorize/X86/cleanup-runtime-checks.ll
commands: opt -p loop-vectorize -S %s
tests: test_name: <module>
test_body: 
target triple = "x86_64-unknown-linux-gnu"

declare ptr @get()
declare i1 @cond()

; Make sure we can clean up the created runtime checks, if vectorization isn't
; profitable.
define void @widget(i32 %arg, i64 %arg1, ptr %src) #0 {
;
entry:
  br label %loop.1.header

loop.1.header:
  br label %inner.1

inner.1:
  %c.1 = call i1 @cond()
  br i1 %c.1, label %inner.2, label %inner.1

inner.2:
  %load = call ptr @get()
  %c.2 = call i1 @cond()
  br i1 %c.2, label %loop.2, label %loop.1.latch

loop.1.latch:
  br label %loop.1.header

loop.2:
  %iv = phi i32 [ %arg, %inner.2 ], [ %iv.next, %loop.2 ]
  %phi8 = phi i32 [ 99, %inner.2 ], [ %or, %loop.2 ]
  %gep.src = getelementptr i32, ptr  %src, i32 %iv
  %l = load i32, ptr %gep.src, align 4
  %or = or i32 %phi8, %l
  store i32 %or, ptr %load, align 4
  %iv.next = add i32 %iv, 1
  %ec = icmp eq i32 %iv, 100
  br i1 %ec, label %exit, label %loop.2, !prof !0

exit:
  ret void
}

attributes #0 = { "target-features"="+avx2" }
!0 = !{!"branch_weights", i32 89478484, i32 1879048192}
;.
;.
file: llvm/test/Transforms/LoopVectorize/pr45259.ll
commands: opt < %s -S -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1
tests: test_name: widget
test_body: define i8 @widget(ptr %arr, i8 %t9) {
bb:
  br label %bb6

bb6:                                              ; preds = %bb6, %bb
  %t1.0 = phi ptr [ %arr, %bb ], [ null, %bb6 ]
  %c = call i1 @cond()
  br i1 %c, label %for.preheader, label %bb6

for.preheader:                                    ; preds = %bb6
  br label %for.body

for.body:                                         ; preds = %for.body, %for.preheader
  %iv = phi i8 [ %iv.next, %for.body ], [ 0, %for.preheader ]
  %iv.next = add i8 %iv, 1
  %ptr = getelementptr inbounds i8, ptr %arr, i8 %iv.next
  %t3.i = icmp slt i8 %iv.next, %t9
  %t3.i8 = zext i1 %t3.i to i8
  store i8 %t3.i8, ptr %ptr, align 1
  %ec = icmp eq ptr %t1.0, %ptr
  br i1 %ec, label %for.exit, label %for.body

for.exit:                                         ; preds = %for.body
  %iv.next.lcssa = phi i8 [ %iv.next, %for.body ]
  ret i8 %iv.next.lcssa
}

declare i1 @cond()

file: llvm/test/Transforms/LoopVectorize/pr47343-expander-lcssa-after-cfg-update.ll
commands: opt -passes=loop-vectorize -force-vector-width=2 %s -S
tests: test_name: f
test_body: @f.e = external global i32, align 1
@d = external global ptr, align 1

declare i1 @cond()

define void @f() {
entry:
  br label %outer.header

outer.header:                                     ; preds = %outer.latch, %entry
  %0 = load ptr, ptr @d, align 1
  %c.0 = call i1 @cond()
  br i1 %c.0, label %outer.exit.0, label %inner.1.header

inner.1.header:                                   ; preds = %inner.1.latch, %outer.header
  %c.1 = call i1 @cond()
  br i1 %c.1, label %inner.1.latch, label %outer.latch

inner.1.latch:                                    ; preds = %inner.1.header
  %c.2 = call i1 @cond()
  br i1 %c.2, label %outer.exit.1, label %inner.1.header

outer.latch:                                      ; preds = %inner.1.header
  br label %outer.header

outer.exit.0:                                     ; preds = %outer.header
  br label %loop

outer.exit.1:                                     ; preds = %inner.1.latch
  br label %loop

loop:                                             ; preds = %loop, %outer.exit.1, %outer.exit.0
  %iv = phi i32 [ %iv.next, %loop ], [ 0, %outer.exit.0 ], [ 0, %outer.exit.1 ]
  %conv6.us.us.us = zext i1 false to i32
  store i32 %conv6.us.us.us, ptr @f.e, align 1
  store i8 10, ptr %0, align 1
  %iv.next = add nsw i32 %iv, 1
  %ec = icmp eq i32 %iv.next, 500
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

file: llvm/test/Transforms/LoopVectorize/reuse-lcssa-phi-scev-expansion.ll
commands: opt -p loop-vectorize -force-vector-width=2 -S %s
tests: test_name: runtime_checks_ptr_inductions
test_body: declare i32 @val()

define void @runtime_checks_ptr_inductions(ptr %dst.1, ptr %dst.2, i1 %c) {
entry:
  br label %loop.1

loop.1:                                           ; preds = %loop.1, %entry
  %ptr.iv.1 = phi ptr [ %dst.1, %entry ], [ %ptr.iv.1.next, %loop.1 ]
  %call = call i32 @val()
  %sel.dst = select i1 %c, ptr %dst.1, ptr %dst.2
  %ptr.iv.1.next = getelementptr i8, ptr %ptr.iv.1, i64 1
  %ec.1 = icmp eq i32 %call, 0
  br i1 %ec.1, label %loop.2.header, label %loop.1

loop.2.header:                                    ; preds = %loop.2.latch, %loop.1
  %iv = phi i32 [ 1, %loop.1 ], [ %dec7, %loop.2.latch ]
  %ptr.iv.2 = phi ptr [ %ptr.iv.1, %loop.1 ], [ %ptr.iv.2.next, %loop.2.latch ]
  %ptr.iv.3 = phi ptr [ %sel.dst, %loop.1 ], [ %ptr.iv.3.next, %loop.2.latch ]
  %ec.2 = icmp eq i32 %iv, 1024
  br i1 %ec.2, label %exit, label %loop.2.latch

loop.2.latch:                                     ; preds = %loop.2.header
  %dec7 = add i32 %iv, 1
  %ptr.iv.3.next = getelementptr i8, ptr %ptr.iv.3, i64 1
  %l = load i8, ptr %ptr.iv.3, align 1
  %ptr.iv.2.next = getelementptr i8, ptr %ptr.iv.2, i64 1
  store i8 %l, ptr %ptr.iv.2, align 1
  br label %loop.2.header

exit:                                             ; preds = %loop.2.header
  ret void
}

file: llvm/test/Transforms/LoopVectorize/skeleton-lcssa-crash.ll
commands: opt -passes=loop-vectorize -force-vector-width=2 -force-vector-interleave=1 -S %s
tests: test_name: test
test_body: define i16 @test(ptr %arg, i64 %N) {
entry:
  br label %outer

outer:                                            ; preds = %outer.latch, %outer, %entry
  %l.1 = load ptr, ptr %arg, align 8
  %l.2 = load ptr, ptr %arg, align 8
  %c.1 = call i1 @cond()
  br i1 %c.1, label %outer, label %inner

inner:                                            ; preds = %inner.latch, %outer
  %c.2 = call i1 @cond()
  br i1 %c.2, label %outer.latch, label %inner.bb

inner.bb:                                         ; preds = %inner
  %c.3 = call i1 @cond()
  br i1 %c.3, label %loop.3, label %inner.latch

inner.latch:                                      ; preds = %inner.bb
  %c.4 = call i1 @cond()
  br i1 %c.4, label %exit, label %inner

outer.latch:                                      ; preds = %inner
  br label %outer

loop.3:                                           ; preds = %loop.3, %inner.bb
  %iv = phi i64 [ %iv.next, %loop.3 ], [ 0, %inner.bb ]
  %iv.next = add nuw nsw i64 %iv, 1
  %c.5 = icmp ult i64 %iv, %N
  %gep.1 = getelementptr inbounds i16, ptr %l.1, i64 %iv.next
  %loop.l.1 = load i16, ptr %gep.1, align 2
  %gep.2 = getelementptr inbounds i16, ptr %l.2, i64 0
  store i16 %loop.l.1, ptr %gep.2, align 2
  br i1 %c.5, label %loop.3, label %exit

exit:                                             ; preds = %loop.3, %inner.latch
  %l.3 = load i16, ptr %l.1, align 2
  ret i16 %l.3
}

declare i1 @cond()