Bug type: crash
Component: LoopVectorize
ScalarEvolution
Bug location: llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp: SCEVExpander::expandCodeFor
llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp: VPExpandSCEVRecipe::execute
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp: vputils::getOrCreateVPValueForSCEVExpr
Issue title: 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.
Labels: release:backport
vectorizers
llvm:SCEV
crash
Reported by: TatyanaDoubts

To reproduce run opt with the test below (-passes=loop-vectorize ):
```
; ModuleID = './reduced.ll'
source_filename = "./reduced.ll"
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 ptr addrspace(1) @wombat(i32 %arg) gc "statepoint-example" {
bb:
  %select = select i1 false, i32 0, i32 8
  br label %bb3

bb1:                                              ; preds = %bb3
  br i1 false, label %bb4, label %bb9

bb2:                                              ; No predecessors!
  %zext = zext i32 %select to i64
  br label %bb3

bb3:                                              ; preds = %bb3, %bb2, %bb
  %phi = phi i64 [ %zext, %bb2 ], [ 0, %bb3 ], [ 0, %bb ]
  br i1 false, label %bb1, label %bb3

bb4:                                              ; preds = %bb4, %bb1
  %phi5 = phi i64 [ %add7, %bb4 ], [ 0, %bb1 ]
  %phi6 = phi i32 [ %add, %bb4 ], [ %arg, %bb1 ]
  %add = add i32 %phi6, 1
  %add7 = add i64 %phi5, 1
  %shl = shl i32 %phi6, 1
  %zext8 = zext i32 %shl to i64
  %getelementptr = getelementptr i8, ptr addrspace(1) null, i64 %zext8
  store i16 0, ptr addrspace(1) %getelementptr, align 2
  %icmp = icmp ult i64 %phi5, %phi
  br i1 %icmp, label %bb4, label %bb15

bb9:                                              ; preds = %bb9, %bb1
  %phi10 = phi i64 [ %add12, %bb9 ], [ 0, %bb1 ]
  %getelementptr11 = getelementptr i8, ptr addrspace(1) null, i64 %phi10
  store i8 0, ptr addrspace(1) %getelementptr11, align 1
  %add12 = add i64 %phi10, 1
  %icmp13 = icmp ult i64 %add12, %phi
  br i1 %icmp13, label %bb9, label %bb14

bb14:                                             ; preds = %bb9
  ret ptr addrspace(1) null

bb15:                                             ; preds = %bb4
  ret ptr addrspace(1) null
}
```
Reproducer: https://godbolt.org/z/67vadzMbr
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 "wombat"
 #0 0x000000000525d5d8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x525d5d8)
 #1 0x000000000525afdc SignalHandler(int) Signals.cpp:0:0
 #2 0x00007cf6f6042520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x00007cf6f60969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x00007cf6f6042476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x00007cf6f60287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x00007cf6f602871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x00007cf6f6039e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000045a6237 llvm::SCEVExpanderCleaner::cleanup() (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x45a6237)
 #9 0x00000000041cf7df (anonymous namespace)::GeneratedRTChecks::~GeneratedRTChecks() LoopVectorize.cpp:0:0
#10 0x0000000004227f36 llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4227f36)
#11 0x0000000004229cc9 llvm::LoopVectorizePass::runImpl(llvm::Function&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4229cc9)
#12 0x000000000422a303 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x422a303)
#13 0x0000000003029e0e llvm::detail::PassModel<llvm::Function, llvm::LoopVectorizePass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x3029e0e)
#14 0x000000000505b4d0 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x505b4d0)
#15 0x0000000000e6bc0e 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+0xe6bc0e)
#16 0x0000000005059deb llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5059deb)
#17 0x0000000000e6b29e 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+0xe6b29e)
#18 0x00000000050597f0 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x50597f0)
#19 0x0000000000941fba 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) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x941fba)
#20 0x0000000000934559 optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x934559)
#21 0x00007cf6f6029d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#22 0x00007cf6f6029e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#23 0x000000000092be35 _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x92be35)
Program terminated with signal: SIGSEGV
```

Comments: author: TatyanaDoubts
body: Triage on our side found the fault commit

commit 6dc23b70097e4135ecde33f49550b1f473a5c385

Author: Nikita Popov <[npopov@redhat.com](mailto:npopov@redhat.com)>

Date:   Mon Nov 11 12:36:29 2024 +0100

 

    [SCEVExpander] Don't try to reuse SCEVUnknown values (#115141)

 

    The expansion of a SCEVUnknown is trivial (it's just the wrapped value).

    If we try to reuse an existing value it might be a more complex

    expression that simplifies to the SCEVUnknown.

 

    This is inspired by [WRONG code: GVN? Loop opts? · Issue #114879 · llvm/llvm-project](https://github.com/llvm/llvm-project/issues/114879) ,

    because SCEVExpander replacing a constant with a phi node is just silly.

    (I don't consider this a fix for that issue though.)

@nikic , could you please have a look? Thank you!
author: danilaml
body: Looks like when `FindValueInExprValueMap` returns a `nullptr` (like it does for a phi in reproducer after `SCEVUnknown` changes) the code runs `fixupLCSSAFormFor`. This function runs `llvm::formLCSSAForInstructions` that can insert a new LCSSA phi and more importantly can rewrite the uses of instructions in the worklist. So what happens is this function is run, new phi is added, some old phi uses are replaced with this new phi, then the changes are "rolled back", i.e. the cleanup is called on SCEVExpander, it tries to remove the inserted lcssa phi but it doesn't know how to rewrite the uses back so it just asserts (since those users were not inserted by the expander itself, they were already there - they just had their arguments replaced to use instruction from expander).

@nikic this looks like an old issue, although I wasn't yet able to figure out how to trigger it before `SCEVUnknown` changes (and if it even practically possible or just theoretically).
author: danilaml
body: `fixupLCSSAFormFor` was introduced by @fhahn so perhaps they have a better understanding where the issue lies.
author: nikic
body: I think there are two issues here:
1. It looks like LV is generating references to the TC that break LCSSA form.
2. SCEVExpander cleanup can't handle the case where LCSSA formation modified existing uses.
author: nikic
body: This is a possible fix for the SCEVExpander issue: https://github.com/nikic/llvm-project/commit/3217b044ce2b153621f91f8edaaa90984c0998e7

But after thinking about this, I'm not convinced that SCEVExpander really needs fixing. It is being invoked in PreserveLCSSA mode, so it should be able to rely on the original IR being in LCSSA form.

Note that the `[[N_RND_UP:%.*]] = add i64 [[PHI]], 15` in https://github.com/nikic/llvm-project/commit/3217b044ce2b153621f91f8edaaa90984c0998e7#diff-9e3a00a1cffe1764ee50ab6c79bdea53cd1847cf7b77810a4cdc6835bfc79a52R44 doesn't satisfy LCSSA form, it should be using `PHI_LCSSA`.

Maybe @fhahn could take a look at the LoopVectorize issue?

I could also be convinced to land the SCEVExpander change, as it seems harmless even if LV is fixed...
author: fhahn
body: Yep, I'll take a look in the next few days
author: fhahn
body: https://github.com/llvm/llvm-project/pull/125235 should fix the place where LCSSA is broken

Fix patch:
commit e258bca9505f35e0a22cb213a305eea9b76d11ea
Author: Florian Hahn <flo@fhahn.com>
Date:   Tue Feb 11 13:03:12 2025 +0100

    [VPlan] Only skip expansion for SCEVUnknown if it isn't an instruction. (#125235)
    
    Update getOrCreateVPValueForSCEVExpr to only skip expansion of
    SCEVUnknown if the underlying value isn't an instruction. Instructions
    may be defined in a loop and using them without expansion may break
    LCSSA form. SCEVExpander will take care of preserving LCSSA if needed.
    
    We could also try to pass LoopInfo, but there are some users of the
    function where it won't be available and main benefit from skipping
    expansion is slightly more concise VPlans.
    
    Note that SCEVExpander is now used to expand SCEVUnknown with floats.
    Adjust the check in expandCodeFor to only check the types and casts if
    the type of the value is different to the requested type. Otherwise we
    crash when trying to expand a float and requesting a float type.
    
    Fixes https://github.com/llvm/llvm-project/issues/121518.
    
    PR: https://github.com/llvm/llvm-project/pull/125235

diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
index 3a761bc4e811..d429fe96f9be 100644
--- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
+++ b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
@@ -1451,7 +1451,7 @@ Value *SCEVExpander::expandCodeFor(const SCEV *SH, Type *Ty) {
   // Expand the code for this SCEV.
   Value *V = expand(SH);
 
-  if (Ty) {
+  if (Ty && Ty != V->getType()) {
     assert(SE.getTypeSizeInBits(Ty) == SE.getTypeSizeInBits(SH->getType()) &&
            "non-trivial casts should be done with the SCEVs directly!");
     V = InsertNoopCastOfTo(V, Ty);
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 618c8aef3840..1855fb67aa54 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -3404,7 +3404,7 @@ void VPExpandSCEVRecipe::execute(VPTransformState &State) {
   }
 
   const DataLayout &DL = State.CFG.PrevBB->getDataLayout();
-  SCEVExpander Exp(SE, DL, "induction");
+  SCEVExpander Exp(SE, DL, "induction", /*PreserveLCSSA=*/true);
 
   Value *Res = Exp.expandCodeFor(Expr, Expr->getType(),
                                  &*State.Builder.GetInsertPoint());
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index e40af3e2e3d3..1a7322ec0aff 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -30,11 +30,18 @@ VPValue *vputils::getOrCreateVPValueForSCEVExpr(VPlan &Plan, const SCEV *Expr,
   VPValue *Expanded = nullptr;
   if (auto *E = dyn_cast<SCEVConstant>(Expr))
     Expanded = Plan.getOrAddLiveIn(E->getValue());
-  else if (auto *E = dyn_cast<SCEVUnknown>(Expr))
-    Expanded = Plan.getOrAddLiveIn(E->getValue());
   else {
-    Expanded = new VPExpandSCEVRecipe(Expr, SE);
-    Plan.getEntry()->appendRecipe(Expanded->getDefiningRecipe());
+    auto *U = dyn_cast<SCEVUnknown>(Expr);
+    // Skip SCEV expansion if Expr is a SCEVUnknown wrapping a non-instruction
+    // value. Otherwise the value may be defined in a loop and using it directly
+    // will break LCSSA form. The SCEV expansion takes care of preserving LCSSA
+    // form.
+    if (U && !isa<Instruction>(U->getValue())) {
+      Expanded = Plan.getOrAddLiveIn(U->getValue());
+    } else {
+      Expanded = new VPExpandSCEVRecipe(Expr, SE);
+      Plan.getEntry()->appendRecipe(Expanded->getDefiningRecipe());
+    }
   }
   Plan.addSCEVExpansion(Expr, Expanded);
   return Expanded;


Regression tests:
file: llvm/test/Transforms/LoopVectorize/X86/scev-checks-unprofitable.ll
commands: opt -p loop-vectorize -vectorizer-min-trip-count=8  -mcpu=skylake-avx512 -S %s
tests: test_name: <module>
test_body: 
target triple = "x86_64-unknown-linux-gnu"

; Test case for https://github.com/llvm/llvm-project/issues/121518. Make sure
; that we preserve LCSSA form when using %iv.1 from loop.1 in the trip count
; expression when vectorizing loop.2
define void @value_defined_in_loop1_used_for_trip_counts(i32 %start, i1 %c, ptr %dst) {
;
entry:
  %select = select i1 %c, i32 0, i32 7
  %zext = zext i32 %select to i64
  br label %loop.1

loop.1:
  %iv.1 = phi i64 [ 0, %entry ], [ %zext, %loop.1 ]
  br i1 false, label %loop.1.exit, label %loop.1

loop.1.exit:
  br i1 %c, label %loop.2, label %loop.3

loop.2:
  %iv.2 = phi i64 [ 0, %loop.1.exit ], [ %iv.2.next, %loop.2 ]
  %iv.3 = phi i32 [ %start, %loop.1.exit ], [ %iv.3.next, %loop.2 ]
  %iv.3.next = add i32 %iv.3, 1
  %iv.2.next = add i64 %iv.2, 1
  %shl = shl i32 %iv.3, 1
  %zext8 = zext i32 %shl to i64
  %gep.dst = getelementptr i8, ptr %dst, i64 %zext8
  store i16 0, ptr %gep.dst, align 2
  %ec.2 = icmp ult i64 %iv.2, %iv.1
  br i1 %ec.2, label %loop.2, label %exit.1

loop.3:
  %iv.4 = phi i64 [ 0, %loop.1.exit ], [ %iv.4.next, %loop.3 ]
  %gep.dst.2 = getelementptr i8, ptr %dst, i64 %iv.4
  store i8 0, ptr %gep.dst.2, align 1
  %iv.4.next = add i64 %iv.4, 1
  %ec.3 = icmp ult i64 %iv.4.next, %iv.1
  br i1 %ec.3, label %loop.3, label %exit.1

exit.1:
  ret void
}
;.
;.
file: llvm/test/Transforms/LoopVectorize/float-induction.ll
commands: opt < %s -passes=loop-vectorize,dce,instcombine -force-vector-interleave=1 -force-vector-width=4 -S
opt < %s -passes=loop-vectorize,dce,instcombine -force-vector-interleave=2 -force-vector-width=4 -S
opt < %s -passes=loop-vectorize,dce,instcombine -force-vector-interleave=2 -force-vector-width=1 -S
opt < %s -passes=loop-vectorize,dce,simplifycfg,instcombine,simplifycfg -force-vector-interleave=1 -force-vector-width=2 -simplifycfg-require-and-preserve-domtree=1 -keep-loops=false -S
tests: test_name: fp_iv_loop3
test_body: @fp_inc = external global float, align 4

define void @fp_iv_loop3(float %init, ptr noalias captures(none) %A, ptr noalias captures(none) %B, ptr noalias captures(none) %C, i32 %N) {
entry:
  %cmp9 = icmp sgt i32 %N, 0
  br i1 %cmp9, label %for.body.lr.ph, label %for.end

for.body.lr.ph:                                   ; preds = %entry
  %0 = load float, ptr @fp_inc, align 4
  br label %for.body

for.body:                                         ; preds = %for.body, %for.body.lr.ph
  %indvars.iv = phi i64 [ 0, %for.body.lr.ph ], [ %indvars.iv.next, %for.body ]
  %y.012 = phi float [ 0x3FB99999A0000000, %for.body.lr.ph ], [ %conv1, %for.body ]
  %x.011 = phi float [ %init, %for.body.lr.ph ], [ %add, %for.body ]
  %arrayidx = getelementptr inbounds float, ptr %A, i64 %indvars.iv
  store float %x.011, ptr %arrayidx, align 4
  %add = fadd fast float %x.011, %0
  %conv1 = fadd fast float %y.012, -5.000000e-01
  %add2 = fadd fast float %conv1, %add
  %arrayidx4 = getelementptr inbounds float, ptr %B, i64 %indvars.iv
  store float %add2, ptr %arrayidx4, align 4
  %arrayidx6 = getelementptr inbounds float, ptr %C, i64 %indvars.iv
  store float %conv1, ptr %arrayidx6, align 4
  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
  %lftr.wideiv = trunc i64 %indvars.iv.next to i32
  %exitcond = icmp eq i32 %lftr.wideiv, %N
  br i1 %exitcond, label %for.end.loopexit, label %for.body

for.end.loopexit:                                 ; preds = %for.body
  br label %for.end

for.end:                                          ; preds = %for.end.loopexit, %entry
  ret void
}

!llvm.module.flags = !{!0}

!0 = !{i32 2, !"Debug Info Version", i32 3}