Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: collectUsersInExitBlock
Issue title: [LoopVectorize] Assertion fails when IV start value is used by an exit block PHI
Labels: vectorizers
crash
Reported by: hazzlim

With the following IR:
```
define i64 @foo(i64 %start, i64 %end) {
entry:
  %p1 = alloca [1024 x i32]
  %p2 = alloca [1024 x i32]
  call void @init_mem(ptr %p1, i64 1024)
  call void @init_mem(ptr %p2, i64 1024)
  %start2 = and i64 %start, 12345
  br label %for.body

for.body:
  %ind = phi i64 [ %ind.next, %for.body ], [ %start2, %entry ]
  %arrayidx1 = getelementptr inbounds i32, ptr %p1, i64 %ind
  %0 = load i32, ptr %arrayidx1, align 4
  %arrayidx2 = getelementptr inbounds i32, ptr %p2, i64 %ind
  %1 = load i32, ptr %arrayidx2, align 4
  %ind.next = add i64 %ind, 1
  %cmp = icmp ne i64 %ind.next, %end
  br i1 %cmp, label %for.body, label %exit

exit:
  %use =  phi i64 [ %start2, %for.body ]
  ret i64 %use
}

declare void @init_mem(ptr, i64)
```

LV crashes due to hitting the following assertion:
```
LV: Vectorizing: innermost loop.
PHINode should have one entry for each predecessor of its parent basic block!
  %bob = phi i64 [ %start2, %for.body ]
opt: /work/llvm-project-wt/nuw-clang-arr/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:10244: bool llvm::LoopVectorizePass::processLoop(Loop *): Assertion `!verif
yFunction(*L->getHeader()->getParent(), &dbgs())' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: ./build/bin/opt -passes=loop-vectorize -force-vector-interleave=2 -force-vector-width=4 -debug test.ll -o - -S
1.      Running pass "function(loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>)" on module "test.ll"
2.      Running pass "loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>" on function "foo"
 #0 0x0000aaaaaec431c0 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (./build/bin/opt+0x41a31c0)
 #1 0x0000aaaaaec4102c llvm::sys::RunSignalHandlers() (./build/bin/opt+0x41a102c)
 #2 0x0000aaaaaec438dc SignalHandler(int) Signals.cpp:0:0
 #3 0x0000fffff7ffb9d0 (linux-vdso.so.1+0x9d0)
 #4 0x0000fffff7b5f200 __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x0000fffff7b1a67c gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x0000fffff7b07130 abort ./stdlib/abort.c:81:7
 #7 0x0000fffff7b13fd0 __assert_fail_base ./assert/assert.c:89:7
 #8 0x0000fffff7b14040 __assert_perror_fail ./assert/assert-perr.c:31:1
 #9 0x0000aaaaadee375c llvm::LoopVectorizePass::processLoop(llvm::Loop*) (./build/bin/opt+0x344375c)
#10 0x0000aaaaadee72d0 llvm::LoopVectorizePass::runImpl(llvm::Function&) (./build/bin/opt+0x34472d0)
#11 0x0000aaaaadee7ac0 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (./build/bin/opt+0x3447ac0)
#12 0x0000aaaaaea9676c llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (./bui
ld/bin/opt+0x3ff676c)
#13 0x0000aaaaaea9a1fc llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (./build/bin/opt+0x3ffa1fc)
#14 0x0000aaaaaea958cc llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (./build/bin/o
pt+0x3ff58cc)
#15 0x0000aaaaab5b3d88 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOu
tputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::Outpu
tKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (./build/bin/opt+0xb13d88)
#16 0x0000aaaaab5a7fc0 optMain (./build/bin/opt+0xb07fc0)
#17 0x0000fffff7b073fc __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#18 0x0000fffff7b074cc call_init ./csu/../csu/libc-start.c:128:20
#19 0x0000fffff7b074cc __libc_start_main ./csu/../csu/libc-start.c:379:5
#20 0x0000aaaaab5a1ef0 _start (./build/bin/opt+0xb01ef0)
```

Seems like we fail to properly handle fixing up the incoming value for this PHI from the middle block. 

Comments: author: hazzlim
body: Note: I am actively looking into this and plan to put up a fix 

Fix patch:
commit 7858e14547c509c95503b74ff8ffc7bf2fc5b110
Author: Hari Limaye <hari.limaye@arm.com>
Date:   Wed Sep 11 16:43:34 2024 +0100

    [LV] Amend check for IV increments in collectUsersInEntryBlock (#108020)
    
    The check for IV increments in collectUsersInEntryBlock currently
    triggers for exit-block PHIs which use the IV start value, resulting in
    us failing to add the input value for the middle block to these PHIs.
    
    Fix this by amending the check for IV increments to only include
    incoming values that are instructions inside the loop.
    
    Fixes #108004

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index b821da03c16e..640a7bf3d672 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -8663,6 +8663,7 @@ static MapVector<PHINode *, VPValue *> collectUsersInExitBlock(
          !cast<VPWidenIntOrFpInductionRecipe>(V)->getTruncInst()) ||
         isa<VPWidenPointerInductionRecipe>(V) ||
         (isa<Instruction>(IncomingValue) &&
+         OrigLoop->contains(cast<Instruction>(IncomingValue)) &&
          any_of(IncomingValue->users(), [&Inductions](User *U) {
            auto *P = dyn_cast<PHINode>(U);
            return P && Inductions.contains(P);


Regression tests:
file: llvm/test/Transforms/LoopVectorize/use-iv-start-value.ll
commands: opt < %s -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -S
tests: test_name: foo
test_body: define i64 @foo(ptr %p1, ptr %p2, i64 %start, i64 %end) {
entry:
  %start2 = and i64 %start, 12345
  br label %for.body

for.body:                                         ; preds = %for.body, %entry
  %ind = phi i64 [ %start2, %entry ], [ %ind.next, %for.body ]
  %arrayidx1 = getelementptr inbounds i32, ptr %p1, i64 %ind
  %0 = load i32, ptr %arrayidx1, align 4
  %arrayidx2 = getelementptr inbounds i32, ptr %p2, i64 %ind
  %1 = load i32, ptr %arrayidx2, align 4
  %ind.next = add i64 %ind, 1
  %cmp = icmp ne i64 %ind.next, %end
  br i1 %cmp, label %for.body, label %exit

exit:                                             ; preds = %for.body
  %use = phi i64 [ %start2, %for.body ]
  ret i64 %use
}