Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp: simplifyRecipe
Issue title: [LoopVectorize] Assertion failure: Instruction does not dominate all uses!
Labels: vectorizers
crash-on-valid
Reported by: dtcxzyw

Reproducer: https://godbolt.org/z/WqTM1vavE
```
; bin/opt -passes=loop-vectorize test.ll -S
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"
target triple = "x86_64-unknown-linux-gnu"

define i8 @test(ptr %p) {
entry:
  %.promoted835.i = load i8, ptr %p, align 1
  br label %for.inc346.i

for.inc346.i:                                     ; preds = %for.inc346.i, %entry
  %storemerge827.i = phi i32 [ 1, %entry ], [ %inc347.i, %for.inc346.i ]
  %conv267822825.i = phi i8 [ %.promoted835.i, %entry ], [ %conv267.i, %for.inc346.i ]
  %conv267.i = and i8 %.promoted835.i, -1
  %inc347.i = add i32 %storemerge827.i, 1
  %exitcond.not.i217 = icmp eq i32 %inc347.i, 0
  br i1 %exitcond.not.i217, label %cleanup636.thread812.i, label %for.inc346.i

cleanup636.thread812.i:                           ; preds = %for.inc346.i
  ret i8 %conv267.i
}
```
```
Instruction does not dominate all uses!
  %broadcast.splat2 = shufflevector <16 x i8> %broadcast.splatinsert1, <16 x i8> poison, <16 x i32> zeroinitializer
  %vector.recur = phi <16 x i8> [ %vector.recur.init, %vector.ph ], [ %broadcast.splat2, %vector.body ]
opt: /root/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:10329: bool llvm::LoopVectorizePass::processLoop(llvm::Loop*): Assertion `!verifyFunction(*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: /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 "test"
 #0 0x000000000563b448 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x563b448)
 #1 0x0000000005638dd4 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #2 0x000072139da42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000072139da969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000072139da42476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000072139da287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000072139da2871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000072139da39e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x000000000348514a llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x348514a)
 #9 0x0000000003486e90 llvm::LoopVectorizePass::runImpl(llvm::Function&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x3486e90)
#10 0x00000000034874e3 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x34874e3)
#11 0x0000000002da157e 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+0x2da157e)
#12 0x000000000542c281 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x542c281)
#13 0x0000000000ea33fe 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+0xea33fe)
#14 0x000000000542c763 llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x542c763)
#15 0x0000000000ea224e 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+0xea224e)
#16 0x000000000542a331 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x542a331)
#17 0x000000000095988a 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+0x95988a)
#18 0x000000000094d05c optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x94d05c)
#19 0x000072139da29d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#20 0x000072139da29e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#21 0x0000000000944c95 _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x944c95)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```
cc @fhahn 

Comments: author: fhahn
body: Taking a look. I assume this is coming from a larger reproducer? Looks like there are missed scalar folding opportunities.
author: lukel97
body: Bisected back to 5f39be591714eba1be413e2b3ed6fc152a15bb8e

Fix patch:
commit 30b16ec3415e7ddb597d096f818d011b1b4e6a63
Author: Florian Hahn <flo@fhahn.com>
Date:   Mon Jun 16 22:54:25 2025 +0100

    [VPlan] Simplify trivial VPFirstOrderRecurrencePHI recipes.
    
    VPFirstOrderRecurrencePHIRecipes where the incoming values are the same
    can be simplified and removed.
    
    Fixes https://github.com/llvm/llvm-project/issues/144212.
    
    The new test is added together with other related tests from
    first-order-recurrence.ll

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 44a72755b9cf..05a0e15f9a19 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -1141,6 +1141,12 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
     return;
   }
 
+  if (auto *Phi = dyn_cast<VPFirstOrderRecurrencePHIRecipe>(Def)) {
+    if (Phi->getOperand(0) == Phi->getOperand(1))
+      Def->replaceAllUsesWith(Phi->getOperand(0));
+    return;
+  }
+
   // Some simplifications can only be applied after unrolling. Perform them
   // below.
   if (!Plan->isUnrolled())


Regression tests:
file: llvm/test/Transforms/LoopVectorize/first-order-recurrence-dead-instructions.ll
commands: opt -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=2 -S %s
tests: test_name: <module>
test_body: 
; Test case for https://github.com/llvm/llvm-project/issues/144212.
define i8 @recurrence_phi_with_same_incoming_values_after_simplifications(i8 %for.start, ptr %dst) {
;
entry:
  br label %loop

loop:
  %iv = phi i32 [ 1, %entry ], [ %iv.next, %loop ]
  %for = phi i8 [ %for.start, %entry ], [ %for.next, %loop ]
  %for.next = and i8 %for.start, -1
  %iv.next = add i32 %iv, 1
  %gep.dst = getelementptr inbounds i8, ptr %dst, i32 %iv
  store i8 %for, ptr %gep.dst
  %ec = icmp eq i32 %iv.next, 0
  br i1 %ec, label %exit, label %loop

exit:
  ret i8 %for.next
}

; %vec.dead will be marked as dead instruction in the vector loop and no recipe
; will be created for it. Make sure a valid sink target is used.
define i32 @sink_after_dead_inst(ptr %A.ptr) {
;
entry:
  br label %loop

loop:
  %iv = phi i16 [ 0, %entry ], [ %iv.next, %loop ]
  %for = phi i32 [ 0, %entry ], [ %for.prev, %loop ]
  %cmp = icmp eq i32 %for, 15
  %C = icmp eq i1 %cmp, true
  %vec.dead = and i1 %C, 1
  %iv.next = add i16 %iv, 1
  %B1 = or i16 %iv.next, %iv.next
  %B3 = and i1 %cmp, %C
  %for.prev = zext i16 %B1 to i32

  %ext = zext i1 %B3 to i32
  %A.gep = getelementptr i32, ptr %A.ptr, i16 %iv
  store i32 0, ptr %A.gep
  br i1 %vec.dead, label %for.end, label %loop

for.end:
  ret i32 %for
}

; Dead instructions, like the exit condition are not part of the actual VPlan
; and do not need to be sunk. PR44634.
define void @sink_dead_inst(ptr %a) {
;
entry:
  br label %for.cond

for.cond:
  %iv = phi i16 [ -27, %entry ], [ %iv.next, %for.cond ]
  %rec.1 = phi i16 [ 0, %entry ], [ %rec.1.prev, %for.cond ]
  %rec.2 = phi i32 [ -27, %entry ], [ %rec.2.prev, %for.cond ]
  %use.rec.1 = sub i16 %rec.1, 10
  %cmp = icmp eq i32 %rec.2, 15
  %iv.next = add i16 %iv, 1
  %rec.2.prev = zext i16 %iv.next to i32
  %rec.1.prev = add i16 %iv.next, 5
  %gep = getelementptr i16, ptr %a, i16 %iv
  store i16 %use.rec.1, ptr %gep
  br i1 %cmp, label %for.end, label %for.cond

for.end:
  ret void
}

; %rec.1 only has %use.rec.1 as use, which can be removed. This enables %rec.1
; to be removed also.
define void @unused_recurrence(ptr %a) {
;
entry:
  br label %for.cond

for.cond:
  %iv = phi i16 [ -27, %entry ], [ %iv.next, %for.cond ]
  %rec.1 = phi i16 [ 0, %entry ], [ %rec.1.prev, %for.cond ]
  %use.rec.1 = sub i16 %rec.1, 10
  %iv.next= add i16 %iv, 1
  %rec.1.prev = add i16 %iv.next, 5
  %cmp = icmp eq i16 %iv, 1000
  br i1 %cmp, label %for.end, label %for.cond

for.end:
  ret void
}
file: llvm/test/Transforms/LoopVectorize/first-order-recurrence.ll
commands: opt < %s -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=2 -force-widen-divrem-via-safe-divisor=0 -S
opt < %s -passes=loop-vectorize -force-vector-width=1 -force-vector-interleave=2 -force-widen-divrem-via-safe-divisor=0 -S
opt < %s -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -force-widen-divrem-via-safe-divisor=0 -S
tests: test_name: recurence_uniform_load
test_body: target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"

define i32 @recurence_uniform_load(ptr %src, ptr noalias %dst) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %phi = phi i64 [ 0, %entry ], [ %add, %loop ]
  %recur = phi i32 [ 0, %entry ], [ %load, %loop ]
  %add = add i64 %phi, 1
  %load = load i32, ptr %src, align 4
  %icmp = icmp ult i64 %phi, 1
  br i1 %icmp, label %loop, label %exit

exit:                                             ; preds = %loop
  ret i32 0
}

test_name: sink_into_replication_region
test_body: target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"

define i32 @sink_into_replication_region(i32 %y, ptr %dst) {
bb:
  br label %bb2

bb1:                                              ; preds = %bb2
  %var = phi i32 [ %var6, %bb2 ]
  ret i32 %var

bb2:                                              ; preds = %bb2, %bb
  %var3 = phi i32 [ %var8, %bb2 ], [ %y, %bb ]
  %var4 = phi i32 [ %var7, %bb2 ], [ 0, %bb ]
  %var5 = phi i32 [ %var6, %bb2 ], [ 0, %bb ]
  %var6 = add i32 %var5, %var4
  %var7 = udiv i32 219220132, %var3
  %var8 = add nsw i32 %var3, -1
  store i32 %var4, ptr %dst, align 4
  %var9 = icmp slt i32 %var3, 2
  br i1 %var9, label %bb1, label %bb2, !prof !0
}

!0 = !{!"branch_weights", i32 1, i32 1}