Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: LoopVectorizationCostModel::collectValuesToIgnore
Issue title: [VPlan] Assertion `VF.Width == Width && "VPlan cost model and legacy cost model disagreed"' failed.
Labels: vectorizers
crash
Reported by: patrick-rivos

I have a few finds by the fuzzer that affect LLVM (tested using a1d77caaabbb5279b734c061dab36b2138ec476d).
I think they're all duplicates but I'm attaching them all in a .zip so it can be confirmed.

Testcase:
```llvm ir
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64-unknown-linux-gnu"

define void @e(i64 %0) #0 {
entry:
  br label %for.body

for.cond.for.cond.cleanup_crit_edge:              ; preds = %for.body
  ret void

for.body:                                         ; preds = %for.body, %entry
  %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]
  %arrayidx = getelementptr i16, ptr null, i64 %indvars.iv
  %1 = load i16, ptr %arrayidx, align 2
  %arrayidx5 = getelementptr [0 x i8], ptr null, i64 0, i64 %indvars.iv
  store i8 0, ptr %arrayidx5, align 1
  %indvars.iv.next = add nsw i64 %indvars.iv, 4
  %cmp = icmp ult i64 %indvars.iv, %0
  br i1 %cmp, label %for.body, label %for.cond.for.cond.cleanup_crit_edge
}

attributes #0 = { "target-features"="+64bit,+a,+c,+d,+f,+m,+relax,+v,+zicsr,+zifencei,+zmmul,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl32b,+zvl64b,-b,-e,-experimental-smmpm,-experimental-smnpm,-experimental-ssnpm,-experimental-sspm,-experimental-ssqosid,-experimental-supm,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-h,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zacas,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl4096b,-zvl512b,-zvl65536b,-zvl8192b" }

```

Command/backtrace:
```bash
> /scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt -passes=loop-vectorize reduced.ll -S
opt: /scratch/tc-testing/tc-compiler-fuzz-trunk/llvm/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:10444: bool llvm::LoopVectorizePass::processLoop(llvm::Loop*): Assertion `VF.Width == Width && "VPlan cost model and legacy cost model disagreed"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt -passes=loop-vectorize reduced.ll -S
1.      Running pass "function(loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>)" on module "reduced.ll"
2.      Running pass "loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>" on function "e"
 #0 0x000055b365fe4a60 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2f0ea60)
 #1 0x000055b365fe1e7f llvm::sys::RunSignalHandlers() (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2f0be7f)
 #2 0x000055b365fe1fd5 SignalHandler(int) Signals.cpp:0:0
 #3 0x00007612a3042520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007612a30969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007612a30969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x00007612a30969fc pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x00007612a3042476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x00007612a30287f3 abort ./stdlib/abort.c:81:7
 #9 0x00007612a302871b _nl_load_domain ./intl/loadmsgcat.c:1177:9
#10 0x00007612a3039e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
#11 0x000055b365005ba8 llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x1f2fba8)
#12 0x000055b365008761 llvm::LoopVectorizePass::runImpl(llvm::Function&, llvm::ScalarEvolution&, llvm::LoopInfo&, llvm::TargetTransformInfo&, llvm::DominatorTree&, llvm::BlockFrequencyInfo*, llvm::TargetLibraryInfo*, llvm::DemandedBits&, llvm::AssumptionCache&, llvm::LoopAccessInfoManager&, llvm::OptimizationRemarkEmitter&, llvm::ProfileSummaryInfo*) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x1f32761)
#13 0x000055b365008ee0 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x1f32ee0)
#14 0x000055b363e81296 llvm::detail::PassModel<llvm::Function, llvm::LoopVectorizePass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0xdab296)
#15 0x000055b365dfd4be llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2d274be)
#16 0x000055b363e84426 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0xdae426)
#17 0x000055b365dfc24b llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2d2624b)
#18 0x000055b363e80776 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0xdaa776)
#19 0x000055b365dfa39d llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2d2439d)
#20 0x000055b3636cbdc6 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) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x5f5dc6)
#21 0x000055b3636bda11 optMain (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x5e7a11)
#22 0x00007612a3029d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#23 0x00007612a3029e40 call_init ./csu/../csu/libc-start.c:128:20
#24 0x00007612a3029e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#25 0x000055b3636b3855 _start (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x5dd855)
zsh: IOT instruction  /scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt
```

Godbolt: https://godbolt.org/z/a7KPnMh94

Zip with all finds reduced: 
[finds.zip](https://github.com/user-attachments/files/16316789/finds.zip)

Related: https://github.com/llvm/llvm-project/pull/92555 @fhahn 

Found via fuzzer.

Comments: author: JonPsson1
body: I'm seeing this as well with a multitude of hits with csmith. One reduced test case:

clang -O3 -march=z16 crash0.i -o a.out -w -mllvm -disable-licm-promotion -mllvm -force-target-instruction-cost=1 -mllvm -unroll-count=4
LoopVectorize.cpp:10071: bool llvm::LoopVectorizePass::processLoop(llvm::Loop*): Assertion `VF.Width == Width && "VPlan cost model and legacy cost model disagreed"' failed.
#9 0x0000000004749e30 llvm::LoopVectorizePass::processLoop()

[tc_lv.i.tar.gz](https://github.com/user-attachments/files/16337463/tc_lv.i.tar.gz)


author: fhahn
body: Thanks @patrick-rivos, it looks like all test cases had dead instructions, which would be considered by the legacy cost model but not the VPlan-based one (because VPlan-based DCE would clean them up before). Should be fixed now.

@JonPsson1's case was a different issue and should also be fixed.

Fix patch:
commit ba8126b6fef79bd344a247f6291aaec7b67bdff0
Author: Florian Hahn <flo@fhahn.com>
Date:   Wed Jul 24 09:31:32 2024 +0100

    [LV] Mark dead instructions in loop as free.
    
    Update collectValuesToIgnore to also ignore dead instructions in the
    loop. Such instructions will be removed by VPlan-based DCE and won't be
    considered by the VPlan-based cost model.
    
    This closes a gap between the legacy and VPlan-based cost model. In
    practice with the default pipelines, there shouldn't be any dead
    instructions in loops reaching LoopVectorize, but it is easy to generate
    such cases by hand or automatically via fuzzers.
    
    Fixes https://github.com/llvm/llvm-project/issues/99701.

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 8fa1a57177d9..e4477f96aa62 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -137,6 +137,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
 #include "llvm/Transforms/Utils/LoopUtils.h"
 #include "llvm/Transforms/Utils/LoopVersioning.h"
@@ -6681,6 +6682,7 @@ void LoopVectorizationCostModel::collectValuesToIgnore() {
   CodeMetrics::collectEphemeralValues(TheLoop, AC, ValuesToIgnore);
 
   SmallVector<Value *, 4> DeadInterleavePointerOps;
+  SmallVector<Value *, 4> DeadOps;
   for (BasicBlock *BB : TheLoop->blocks())
     for (Instruction &I : *BB) {
       // Find all stores to invariant variables. Since they are going to sink
@@ -6690,6 +6692,17 @@ void LoopVectorizationCostModel::collectValuesToIgnore() {
           Legal->isInvariantAddressOfReduction(SI->getPointerOperand()))
         ValuesToIgnore.insert(&I);
 
+      if (VecValuesToIgnore.contains(&I) || ValuesToIgnore.contains(&I))
+        continue;
+
+      // Add instructions that would be trivially dead and are only used by
+      // values already ignored to DeadOps to seed worklist.
+      if (wouldInstructionBeTriviallyDead(&I, TLI) &&
+          all_of(I.users(), [this](User *U) {
+            return VecValuesToIgnore.contains(U) || ValuesToIgnore.contains(U);
+          }))
+        DeadOps.push_back(&I);
+
       // For interleave groups, we only create a pointer for the start of the
       // interleave group. Queue up addresses of group members except the insert
       // position for further processing.
@@ -6717,6 +6730,29 @@ void LoopVectorizationCostModel::collectValuesToIgnore() {
     DeadInterleavePointerOps.append(Op->op_begin(), Op->op_end());
   }
 
+  // Mark ops that would be trivially dead and are only used by ignored
+  // instructions as free.
+  for (unsigned I = 0; I != DeadOps.size(); ++I) {
+    auto *Op = dyn_cast<Instruction>(DeadOps[I]);
+    // Skip any op that shouldn't be considered dead.
+    if (!Op || !TheLoop->contains(Op) ||
+        !wouldInstructionBeTriviallyDead(Op, TLI) ||
+        any_of(Op->users(), [this](User *U) {
+          return !VecValuesToIgnore.contains(U) && !ValuesToIgnore.contains(U);
+        }))
+      continue;
+
+    // If all of Op's users are in ValuesToIgnore, add it to ValuesToIgnore
+    // which applies for both scalar and vector versions. Otherwise it is only
+    // dead in vector versions, so only add it to VecValuesToIgnore.
+    if (all_of(Op->users(),
+               [this](User *U) { return ValuesToIgnore.contains(U); }))
+      ValuesToIgnore.insert(Op);
+
+    VecValuesToIgnore.insert(Op);
+    DeadOps.append(Op->op_begin(), Op->op_end());
+  }
+
   // Ignore type-promoting instructions we identified during reduction
   // detection.
   for (const auto &Reduction : Legal->getReductionVars()) {


Regression tests:
file: llvm/test/Transforms/LoopVectorize/RISCV/dead-ops-cost.ll
commands: opt -p loop-vectorize -mtriple riscv64-linux-gnu -mattr=+v,+f -S %s
tests: test_name: <module>
test_body: 
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"

; Test with a dead load in the loop, from
; https://github.com/llvm/llvm-project/issues/99701
define void @dead_load(ptr %p, i16 %start) {
;
entry:
  %start.ext = sext i16 %start to i64
  br label %loop

loop:
  %iv = phi i64 [ %start.ext, %entry ], [ %iv.next, %loop ]
  %gep = getelementptr i16, ptr %p, i64 %iv
  store i16 0, ptr %gep, align 2
  %l = load i16, ptr %gep, align 2
  %iv.next = add i64 %iv, 3
  %cmp = icmp slt i64 %iv, 111
  br i1 %cmp, label %loop, label %exit

exit:
  ret void
}
;.
;.