commit 2ab5c47c8752b444885d6bfaf6f570a482fb4cdf
Author: Florian Hahn <flo@fhahn.com>
Date:   Thu Jan 4 20:39:44 2024 +0000

    [VPlan] Don't replace scalarizing recipe with VPWidenCastRecipe.

    Don't replace a scalarizing recipe with a VPWidenCastRecipe. This would
    introduce wide (vectorizing) recipes when interleaving only.

    Fixes https://github.com/llvm/llvm-project/issues/76986

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 33132880d5a4..5c430620a2dc 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -829,15 +829,20 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
     Type *ATy = TypeInfo.inferScalarType(A);
     if (TruncTy == ATy) {
       Trunc->replaceAllUsesWith(A);
-    } else if (ATy->getScalarSizeInBits() < TruncTy->getScalarSizeInBits()) {
-      auto *VPC =
-          new VPWidenCastRecipe(Instruction::CastOps(ExtOpcode), A, TruncTy);
-      VPC->insertBefore(&R);
-      Trunc->replaceAllUsesWith(VPC);
-    } else if (ATy->getScalarSizeInBits() > TruncTy->getScalarSizeInBits()) {
-      auto *VPC = new VPWidenCastRecipe(Instruction::Trunc, A, TruncTy);
-      VPC->insertBefore(&R);
-      Trunc->replaceAllUsesWith(VPC);
+    } else {
+      // Don't replace a scalarizing recipe with a widened cast.
+      if (isa<VPReplicateRecipe>(&R))
+        break;
+      if (ATy->getScalarSizeInBits() < TruncTy->getScalarSizeInBits()) {
+        auto *VPC =
+            new VPWidenCastRecipe(Instruction::CastOps(ExtOpcode), A, TruncTy);
+        VPC->insertBefore(&R);
+        Trunc->replaceAllUsesWith(VPC);
+      } else if (ATy->getScalarSizeInBits() > TruncTy->getScalarSizeInBits()) {
+        auto *VPC = new VPWidenCastRecipe(Instruction::Trunc, A, TruncTy);
+        VPC->insertBefore(&R);
+        Trunc->replaceAllUsesWith(VPC);
+      }
     }
 #ifndef NDEBUG
     // Verify that the cached type info is for both A and its users is still

opt -passes=loop-vectorize -force-vector-width=1 -force-vector-interleave=2 -debug -disable-output %s 2>&1

opt -passes=loop-vectorize -force-vector-width=1 -force-vector-interleave=2 -S %s

define void @test_scalarize_call(i32 %start, ptr %dst) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ]
  %min = tail call i32 @llvm.smin.i32(i32 %iv, i32 65535)
  %arrayidx = getelementptr inbounds i32, ptr %dst, i32 %iv
  store i32 %min, ptr %arrayidx, align 8
  %iv.next = add nsw i32 %iv, 1
  %tobool.not = icmp eq i32 %iv.next, 1000
  br i1 %tobool.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

define void @scalarize_ptrtoint(ptr %src, ptr %dst) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %gep = getelementptr ptr, ptr %src, i64 %iv
  %l = load ptr, ptr %gep, align 8
  %cast = ptrtoint ptr %l to i64
  %add = add i64 %cast, 10
  %cast.2 = inttoptr i64 %add to ptr
  store ptr %cast.2, ptr %dst, align 8
  %iv.next = add i64 %iv, 1
  %ec = icmp eq i64 %iv.next, 0
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

define void @first_order_recurrence_using_induction(i32 %n, ptr %dst) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %for = phi i32 [ 0, %entry ], [ %iv.trunc, %loop ]
  %iv.trunc = trunc i64 %iv to i32
  store i32 %for, ptr %dst, align 4
  %iv.next = add nuw nsw i64 %iv, 1
  %iv.next.trunc = trunc i64 %iv.next to i32
  %ec = icmp slt i32 %iv.next.trunc, %n
  br i1 %ec, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define i16 @reduction_with_casts() {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %count.0.in1 = phi i32 [ 0, %entry ], [ %add, %loop ]
  %iv = phi i16 [ 1, %entry ], [ %iv.next, %loop ]
  %conv1 = and i32 %count.0.in1, 65535
  %add = add nuw nsw i32 %conv1, 1
  %iv.next = add i16 %iv, 1
  %cmp = icmp eq i16 %iv.next, 10000
  br i1 %cmp, label %exit, label %loop

exit:                                             ; preds = %loop
  %add.lcssa = phi i32 [ %add, %loop ]
  %count.0 = trunc i32 %add.lcssa to i16
  ret i16 %count.0
}

define void @pr76986_trunc_sext_interleaving_only(i16 %arg, ptr noalias %src, ptr noalias %dst) {
bb:
  br label %loop

loop:                                             ; preds = %loop, %bb
  %iv = phi i64 [ 0, %bb ], [ %iv.next, %loop ]
  %gep.src = getelementptr inbounds i8, ptr %src, i64 %iv
  %l = load i8, ptr %gep.src, align 1
  %sext = sext i8 %l to i32
  %trunc = trunc i32 %sext to i16
  %sdiv = sdiv i16 %trunc, %arg
  %gep.dst = getelementptr inbounds i16, ptr %dst, i64 %iv
  store i16 %sdiv, ptr %gep.dst, align 2
  %iv.next = add i64 %iv, 1
  %icmp = icmp ult i64 %iv, 14933
  br i1 %icmp, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @test_scalarize_with_branch_cond(ptr %src, ptr %dst) {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %d = phi i1 [ false, %entry ], [ %d.next, %loop.latch ]
  %d.next = xor i1 %d, true
  br i1 %d, label %cond.false, label %loop.latch

cond.false:                                       ; preds = %loop.header
  %gep.src = getelementptr inbounds i32, ptr %src, i64 %iv
  %gep.dst = getelementptr inbounds i32, ptr %dst, i64 %iv
  %l = load i32, ptr %gep.src, align 4
  store i32 %l, ptr %gep.dst, align 4
  br label %loop.latch

loop.latch:                                       ; preds = %cond.false, %loop.header
  %iv.next = add nsw i64 %iv, 1
  %ec = icmp eq i64 %iv.next, 1000
  br i1 %ec, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret void
}

Assertion `State.VF.isVector() && "Not vectorizing?"' failed.

Run opt with -passes=loop-vectorize

https://godbolt.org/z/xdrTfEddx

Test.ll
```
; ModuleID = './reduced.ll'
source_filename = "./reduced.ll"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-ni:1-p2:32:8:8:32-ni:2"
target triple = "aarch64-none-linux-gnu"

define void @wombat(i16 %arg) gc "statepoint-example" {
bb:
  br label %bb1

bb1:                                              ; preds = %bb1, %bb
  %phi = phi i64 [ 0, %bb ], [ %add, %bb1 ]
  %sext = sext i8 0 to i32
  %add = add i64 %phi, 1
  %trunc = trunc i32 %sext to i16
  %sdiv = sdiv i16 0, %arg
  %sext2 = sext i16 %trunc to i32
  %sext3 = sext i32 %sext2 to i64
  %icmp = icmp ult i64 %phi, 14933
  br i1 %icmp, label %bb1, label %bb4

bb4:                                              ; preds = %bb1
  %phi5 = phi i64 [ %sext3, %bb1 ]
  ret void
}
```

Taking a look now!


commit 83d9aa27680b6a7f3556fcf13ada70b4be95bab2
Author: Pietro Ghiglio <pietro.ghiglio@codeplay.com>
Date:   Wed May 15 15:03:21 2024 +0200

    [VPlan] Add scalar inferencing support for addrspace cast (#92107)

    Fixes https://github.com/llvm/llvm-project/issues/91434

    PR: https://github.com/llvm/llvm-project/pull/92107

diff --git a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
index 5f93339083f0..efe8c21874a3 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
@@ -171,6 +171,7 @@ Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPReplicateRecipe *R) {
   case Instruction::ICmp:
   case Instruction::FCmp:
     return IntegerType::get(Ctx, 1);
+  case Instruction::AddrSpaceCast:
   case Instruction::Alloca:
   case Instruction::BitCast:
   case Instruction::Trunc:

opt -passes=loop-vectorize %s -force-vector-width=1 -force-vector-interleave=2 -S -o -

define void @foo(ptr addrspace(1) %in) {
entry:
  br label %loop

loop:
  %iter = phi i64 [ %next, %loop ], [ 0, %entry ]
  %ascast = addrspacecast ptr addrspace(1) %in to ptr
  %next = add i64 %iter, 1
  %arrayidx = getelementptr inbounds i64, ptr %ascast, i64 %next
  store i64 %next, ptr %arrayidx, align 4

; check that we find the two interleaved blocks with ascast, gep and store:

  %cmp = icmp eq i64 %next, 7
  br i1 %cmp, label %exit, label %loop

; check that we branch to the exit block

exit:
  ret void
}

[Loop Vectorizer] Unhandled opcode UNREACHABLE executed at llvm/llvm-project/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp:208!

```
opt -passes=loop-vectorize before-loop-vectorize.ll

Unhandled opcode
UNREACHABLE executed at /llvm/llvm-project/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp:208!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /llvm/llvm-project/build/bin/opt -passes=loop-vectorize before-loop-vectorize.ll
 #0 0x0000000003167161 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/llvm/llvm-project/build/bin/opt+0x3167161)
 #1 0x00000000031646c4 SignalHandler(int) Signals.cpp:0:0
 #2 0x00007fcb77b7bdb0 __restore_rt (/lib64/libc.so.6+0x59db0)
 #3 0x00007fcb77bc842c __pthread_kill_implementation (/lib64/libc.so.6+0xa642c)
 #4 0x00007fcb77b7bd06 gsignal (/lib64/libc.so.6+0x59d06)
 #5 0x00007fcb77b4e7d3 abort (/lib64/libc.so.6+0x2c7d3)
 #6 0x00000000030ac16a (/llvm/llvm-project/build/bin/opt+0x30ac16a)
 #7 0x00000000022ffc8d llvm::VPTypeAnalysis::inferScalarTypeForRecipe(llvm::VPReplicateRecipe const*) (/llvm/llvm-project/build/bin/opt+0x22ffc8d)
 #8 0x00000000022ff04c llvm::VPTypeAnalysis::inferScalarType(llvm::VPValue const*) (/llvm/llvm-project/build/bin/opt+0x22ff04c)
 #9 0x00000000021cb7f4 llvm::InnerLoopVectorizer::scalarizeInstruction(llvm::Instruction const*, llvm::VPReplicateRecipe*, llvm::VPIteration const&, llvm::VPTransformState&) (/llvm/llvm-project/build/bin/opt+0x21cb7f4)
#10 0x00000000021cecb5 llvm::VPReplicateRecipe::execute(llvm::VPTransformState&) (/llvm/llvm-project/build/bin/opt+0x21cecb5)
#11 0x00000000022f834f llvm::VPBasicBlock::execute(llvm::VPTransformState*) (/llvm/llvm-project/build/bin/opt+0x22f834f)
#12 0x00000000022f0130 llvm::VPRegionBlock::execute(llvm::VPTransformState*) (/llvm/llvm-project/build/bin/opt+0x22f0130)
#13 0x00000000022f03d8 llvm::VPRegionBlock::execute(llvm::VPTransformState*) (/llvm/llvm-project/build/bin/opt+0x22f03d8)
#14 0x00000000022fb806 llvm::VPlan::execute(llvm::VPTransformState*) (/llvm/llvm-project/build/bin/opt+0x22fb806)
#15 0x00000000021e983b llvm::LoopVectorizationPlanner::executePlan(llvm::ElementCount, unsigned int, llvm::VPlan&, llvm::InnerLoopVectorizer&, llvm::DominatorTree*, bool, llvm::DenseMap<llvm::SCEV const*, llvm::Value*, llvm::DenseMapInfo<llvm::SCEV const*, void>, llvm::detail::DenseMapPair<llvm::SCEV const*, llvm::Value*>> const*) (/llvm/llvm-project/build/bin/opt+0x21e983b)
#16 0x00000000021fbbe0 llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/llvm/llvm-project/build/bin/opt+0x21fbbe0)
#17 0x00000000021fde5e 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*) (/llvm/llvm-project/build/bin/opt+0x21fde5e)
#18 0x00000000021fef78 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/llvm/llvm-project/build/bin/opt+0x21fef78)
#19 0x0000000001125c8e llvm::detail::PassModel<llvm::Function, llvm::LoopVectorizePass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/llvm/llvm-project/build/bin/opt+0x1125c8e)
#20 0x0000000002f8671a llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/llvm/llvm-project/build/bin/opt+0x2f8671a)
#21 0x000000000082c95e llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/llvm/llvm-project/build/bin/opt+0x82c95e)
#22 0x0000000002f853cd llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/llvm/llvm-project/build/bin/opt+0x2f853cd)
#23 0x000000000082da6e llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/llvm/llvm-project/build/bin/opt+0x82da6e)
#24 0x0000000002f832cc llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/llvm/llvm-project/build/bin/opt+0x2f832cc)
#25 0x00000000007a0d5d 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) (/llvm/llvm-project/build/bin/opt+0x7a0d5d)
#26 0x0000000000793a7c optMain (/llvm/llvm-project/build/bin/opt+0x793a7c)
#27 0x00007fcb77b66e50 __libc_start_call_main (/lib64/libc.so.6+0x44e50)
#28 0x00007fcb77b66efc __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x44efc)
#29 0x0000000000789e15 _start (/llvm/llvm-project/build/bin/opt+0x789e15)
Aborted (core dumped)
```
Input IR file before-loop-vectorize.ll is:
```
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"

%"class.sycl::_V1::range" = type { %"class.sycl::_V1::detail::array" }
%"class.sycl::_V1::detail::array" = type { [1 x i64] }

$_ZTSN4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EE = comdat any

$_ZTSZZ12foo_parallelRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_EUlNS0_2idILi1EEEE_ = comdat any

$_ZTSZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_EUlvE_ = comdat any

; Function Attrs: convergent norecurse nounwind
define weak_odr dso_local spir_kernel void @_ZTSN4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EE(ptr noundef byval(%"class.sycl::_V1::range") align 8 %_arg_UserRange, ptr addrspace(1) noundef align 4 %_arg_acc, ptr noundef byval(%"class.sycl::_V1::range") align 8 %_arg_acc3) local_unnamed_addr #0 comdat !srcloc !7 !kernel_arg_buffer_location !8 !kernel_arg_runtime_aligned !9 !kernel_arg_exclusive_ptr !9 !sycl_fixed_targets !10 !sycl_kernel_omit_args !11 {
entry:
  %0 = load i64, ptr %_arg_UserRange, align 8
  %agg.tmp7.sroa.0.0.copyload = load i64, ptr %_arg_acc3, align 8
  %add.ptr.i = getelementptr inbounds float, ptr addrspace(1) %_arg_acc, i64 %agg.tmp7.sroa.0.0.copyload
  %call.i = tail call i64 @_Z21__spirv_WorkgroupId_xv() #5, !noalias !12
  %call1.i = tail call i64 @_Z23__spirv_WorkgroupSize_xv() #5, !noalias !12
  %mul.i = mul i64 %call1.i, %call.i
  %call2.i = tail call i64 @_Z27__spirv_LocalInvocationId_xv() #5, !noalias !12
  %add.i = add i64 %mul.i, %call2.i
  %call3.i = tail call i64 @_Z22__spirv_GlobalOffset_xv() #5, !noalias !12
  %add4.i = add i64 %add.i, %call3.i
  %call.i1 = tail call i64 @_Z23__spirv_NumWorkgroups_xv() #5, !noalias !12
  %call1.i2 = tail call i64 @_Z23__spirv_WorkgroupSize_xv() #5, !noalias !12
  %mul.i3 = mul i64 %call1.i2, %call.i1
  %call.i.i.i9.i.i = tail call noundef i64 @_Z22__spirv_GlobalOffset_xv() #6, !noalias !12
  %cmp6.not.i.not.i = icmp ult i64 %add4.i, %0
  br i1 %cmp6.not.i.not.i, label %for.body.i.preheader, label %_ZNK4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EclES4_.exit

for.body.i.preheader:                             ; preds = %entry
  br label %for.body.i

for.body.i:                                       ; preds = %for.body.i.preheader, %for.body.i
  %Gen.sroa.0.019.i = phi i64 [ %add.i.i, %for.body.i ], [ %add4.i, %for.body.i.preheader ]
  %conv.i.i = uitofp i64 %Gen.sroa.0.019.i to float
  %arrayidx.i.i = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %Gen.sroa.0.019.i
  %arrayidx.ascast.i.i = addrspacecast ptr addrspace(1) %arrayidx.i.i to ptr
  store float %conv.i.i, ptr %arrayidx.ascast.i.i, align 4, !tbaa !17
  %add.i.i = add i64 %Gen.sroa.0.019.i, %mul.i3
  %cmp6.i.not.i = icmp ult i64 %add.i.i, %0
  br i1 %cmp6.i.not.i, label %for.body.i, label %_ZNK4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EclES4_.exit.loopexit, !llvm.loop !21

_ZNK4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EclES4_.exit.loopexit: ; preds = %for.body.i
  br label %_ZNK4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EclES4_.exit

_ZNK4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EclES4_.exit: ; preds = %_ZNK4sycl3_V16detail18RoundedRangeKernelINS0_4itemILi1ELb1EEELi1EZZ12foo_parallelRNS0_5queueERNS0_6bufferIfLi1ENS1_17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESD_EUlNS0_2idILi1EEEE_EclES4_.exit.loopexit, %entry
  ret void
}

; Function Attrs: convergent nounwind
declare dso_local noundef i64 @_Z22__spirv_GlobalOffset_xv() local_unnamed_addr #1

; Function Attrs: convergent norecurse nounwind
define weak_odr dso_local spir_kernel void @_ZTSZZ12foo_parallelRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_EUlNS0_2idILi1EEEE_(ptr addrspace(1) noundef align 4 %_arg_acc, ptr noundef byval(%"class.sycl::_V1::range") align 8 %_arg_acc3) local_unnamed_addr #0 comdat !srcloc !23 !kernel_arg_buffer_location !24 !kernel_arg_runtime_aligned !25 !kernel_arg_exclusive_ptr !25 !sycl_fixed_targets !10 !sycl_kernel_omit_args !26 {
entry:
  %agg.tmp6.sroa.0.0.copyload = load i64, ptr %_arg_acc3, align 8
  %add.ptr.i = getelementptr inbounds float, ptr addrspace(1) %_arg_acc, i64 %agg.tmp6.sroa.0.0.copyload
  %call.i = tail call i64 @_Z21__spirv_WorkgroupId_xv() #5, !noalias !27
  %call1.i = tail call i64 @_Z23__spirv_WorkgroupSize_xv() #5, !noalias !27
  %mul.i = mul i64 %call1.i, %call.i
  %call2.i = tail call i64 @_Z27__spirv_LocalInvocationId_xv() #5, !noalias !27
  %add.i = add i64 %mul.i, %call2.i
  %call3.i = tail call i64 @_Z22__spirv_GlobalOffset_xv() #5, !noalias !27
  %add4.i = add i64 %add.i, %call3.i
  %call.i1 = tail call i64 @_Z23__spirv_NumWorkgroups_xv() #5, !noalias !27
  %call1.i2 = tail call i64 @_Z23__spirv_WorkgroupSize_xv() #5, !noalias !27
  %call.i.i.i9.i.i = tail call noundef i64 @_Z22__spirv_GlobalOffset_xv() #6, !noalias !27
  %cmp.i.i = icmp ult i64 %add4.i, 2147483648
  tail call void @llvm.assume(i1 %cmp.i.i)
  %conv.i = uitofp i64 %add4.i to float
  %arrayidx.i = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %add4.i
  %arrayidx.ascast.i = addrspacecast ptr addrspace(1) %arrayidx.i to ptr
  store float %conv.i, ptr %arrayidx.ascast.i, align 4, !tbaa !17
  ret void
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

; Function Attrs: mustprogress norecurse nounwind
define weak_odr dso_local spir_kernel void @_ZTSZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_EUlvE_(i64 noundef %_arg_n, ptr addrspace(1) noundef align 4 %_arg_acc, ptr noundef byval(%"class.sycl::_V1::range") align 8 %_arg_acc3) local_unnamed_addr #3 comdat !srcloc !32 !kernel_arg_buffer_location !8 !kernel_arg_runtime_aligned !9 !kernel_arg_exclusive_ptr !9 !sycl_fixed_targets !10 !sycl_kernel_omit_args !11 {
entry:
  %agg.tmp6.sroa.0.0.copyload = load i64, ptr %_arg_acc3, align 8
  %add.ptr.i = getelementptr inbounds float, ptr addrspace(1) %_arg_acc, i64 %agg.tmp6.sroa.0.0.copyload
  %cmp.i12.not = icmp eq i64 %_arg_n, 0
  br i1 %cmp.i12.not, label %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit, label %for.body.i.preheader

for.body.i.preheader:                             ; preds = %entry
  %xtraiter = and i64 %_arg_n, 7
  %0 = icmp ult i64 %_arg_n, 8
  br i1 %0, label %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa, label %for.body.i.preheader.new

for.body.i.preheader.new:                         ; preds = %for.body.i.preheader
  %unroll_iter = and i64 %_arg_n, -8
  br label %for.body.i

for.body.i:                                       ; preds = %for.body.i, %for.body.i.preheader.new
  %i.0.i13 = phi i64 [ 0, %for.body.i.preheader.new ], [ %inc.i.7, %for.body.i ]
  %conv.i = uitofp i64 %i.0.i13 to float
  %arrayidx.i = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %i.0.i13
  %arrayidx.ascast.i = addrspacecast ptr addrspace(1) %arrayidx.i to ptr
  store float %conv.i, ptr %arrayidx.ascast.i, align 4, !tbaa !17
  %inc.i = or disjoint i64 %i.0.i13, 1
  %conv.i.1 = uitofp i64 %inc.i to float
  %arrayidx.i.1 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i
  %arrayidx.ascast.i.1 = addrspacecast ptr addrspace(1) %arrayidx.i.1 to ptr
  store float %conv.i.1, ptr %arrayidx.ascast.i.1, align 4, !tbaa !17
  %inc.i.1 = or disjoint i64 %i.0.i13, 2
  %conv.i.2 = uitofp i64 %inc.i.1 to float
  %arrayidx.i.2 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i.1
  %arrayidx.ascast.i.2 = addrspacecast ptr addrspace(1) %arrayidx.i.2 to ptr
  store float %conv.i.2, ptr %arrayidx.ascast.i.2, align 4, !tbaa !17
  %inc.i.2 = or disjoint i64 %i.0.i13, 3
  %conv.i.3 = uitofp i64 %inc.i.2 to float
  %arrayidx.i.3 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i.2
  %arrayidx.ascast.i.3 = addrspacecast ptr addrspace(1) %arrayidx.i.3 to ptr
  store float %conv.i.3, ptr %arrayidx.ascast.i.3, align 4, !tbaa !17
  %inc.i.3 = or disjoint i64 %i.0.i13, 4
  %conv.i.4 = uitofp i64 %inc.i.3 to float
  %arrayidx.i.4 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i.3
  %arrayidx.ascast.i.4 = addrspacecast ptr addrspace(1) %arrayidx.i.4 to ptr
  store float %conv.i.4, ptr %arrayidx.ascast.i.4, align 4, !tbaa !17
  %inc.i.4 = or disjoint i64 %i.0.i13, 5
  %conv.i.5 = uitofp i64 %inc.i.4 to float
  %arrayidx.i.5 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i.4
  %arrayidx.ascast.i.5 = addrspacecast ptr addrspace(1) %arrayidx.i.5 to ptr
  store float %conv.i.5, ptr %arrayidx.ascast.i.5, align 4, !tbaa !17
  %inc.i.5 = or disjoint i64 %i.0.i13, 6
  %conv.i.6 = uitofp i64 %inc.i.5 to float
  %arrayidx.i.6 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i.5
  %arrayidx.ascast.i.6 = addrspacecast ptr addrspace(1) %arrayidx.i.6 to ptr
  store float %conv.i.6, ptr %arrayidx.ascast.i.6, align 4, !tbaa !17
  %inc.i.6 = or disjoint i64 %i.0.i13, 7
  %conv.i.7 = uitofp i64 %inc.i.6 to float
  %arrayidx.i.7 = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %inc.i.6
  %arrayidx.ascast.i.7 = addrspacecast ptr addrspace(1) %arrayidx.i.7 to ptr
  store float %conv.i.7, ptr %arrayidx.ascast.i.7, align 4, !tbaa !17
  %inc.i.7 = add nuw i64 %i.0.i13, 8
  %niter.ncmp.7 = icmp eq i64 %inc.i.7, %unroll_iter
  br i1 %niter.ncmp.7, label %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa, label %for.body.i, !llvm.loop !33

_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa: ; preds = %for.body.i, %for.body.i.preheader
  %i.0.i13.unr = phi i64 [ 0, %for.body.i.preheader ], [ %unroll_iter, %for.body.i ]
  %lcmp.mod.not = icmp eq i64 %xtraiter, 0
  br i1 %lcmp.mod.not, label %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit, label %for.body.i.epil

for.body.i.epil:                                  ; preds = %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa, %for.body.i.epil
  %i.0.i13.epil = phi i64 [ %inc.i.epil, %for.body.i.epil ], [ %i.0.i13.unr, %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa ]
  %epil.iter = phi i64 [ %epil.iter.next, %for.body.i.epil ], [ 0, %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa ]
  %conv.i.epil = uitofp i64 %i.0.i13.epil to float
  %arrayidx.i.epil = getelementptr inbounds float, ptr addrspace(1) %add.ptr.i, i64 %i.0.i13.epil
  %arrayidx.ascast.i.epil = addrspacecast ptr addrspace(1) %arrayidx.i.epil to ptr
  store float %conv.i.epil, ptr %arrayidx.ascast.i.epil, align 4, !tbaa !17
  %inc.i.epil = add nuw nsw i64 %i.0.i13.epil, 1
  %epil.iter.next = add nuw nsw i64 %epil.iter, 1
  %epil.iter.cmp.not = icmp eq i64 %epil.iter.next, %xtraiter
  br i1 %epil.iter.cmp.not, label %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit, label %for.body.i.epil, !llvm.loop !34

_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit: ; preds = %for.body.i.epil, %_ZZZ15foo_single_taskRN4sycl3_V15queueERNS0_6bufferIfLi1ENS0_6detail17aligned_allocatorIfEEvEEmENKUlRNS0_7handlerEE_clESA_ENKUlvE_clEv.exit.loopexit.unr-lcssa, %entry
  ret void
}

; Function Attrs: convergent nounwind
declare dso_local i64 @_Z21__spirv_WorkgroupId_xv() local_unnamed_addr #4

; Function Attrs: convergent nounwind
declare dso_local i64 @_Z23__spirv_WorkgroupSize_xv() local_unnamed_addr #4

; Function Attrs: convergent nounwind
declare dso_local i64 @_Z27__spirv_LocalInvocationId_xv() local_unnamed_addr #4

; Function Attrs: convergent nounwind
declare dso_local i64 @_Z23__spirv_NumWorkgroups_xv() local_unnamed_addr #4

attributes #0 = { convergent norecurse nounwind "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="native_cpu.cpp" "sycl-optlevel"="2" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "uniform-work-group-size"="true" }
attributes #1 = { convergent nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { mustprogress nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
attributes #3 = { mustprogress norecurse nounwind "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="native_cpu.cpp" "sycl-optlevel"="2" "sycl-single-task" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "uniform-work-group-size"="true" }
attributes #4 = { convergent nounwind "frame-pointer"="all" "no-builtins" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+avx,+avx2,+avx512f,+cmov,+crc32,+cx8,+evex512,+f16c,+fma,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" "tune-cpu"="generic" }
attributes #5 = { convergent nobuiltin nounwind "no-builtins" }
attributes #6 = { convergent nounwind }

!opencl.spir.version = !{!0}
!spirv.Source = !{!1}
!llvm.ident = !{!2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2}
!llvm.module.flags = !{!3, !4, !5, !6}

!0 = !{i32 1, i32 2}
!1 = !{i32 4, i32 100000}
!2 = !{!"clang version 19.0.0git (https://github.com/intel/llvm.git f56d7d7ffd798e78146510bef7ba02575d53bbb2)"}
!3 = !{i32 1, !"wchar_size", i32 4}
!4 = !{i32 1, !"is-native-cpu", i32 1}
!5 = !{i32 7, !"uwtable", i32 2}
!6 = !{i32 7, !"frame-pointer", i32 2}
!7 = !{i32 8377463}
!8 = !{i32 -1, i32 -1, i32 -1}
!9 = !{i1 false, i1 true, i1 false}
!10 = !{}
!11 = !{i1 false, i1 false, i1 true, i1 true, i1 false}
!12 = !{!13, !15}
!13 = distinct !{!13, !14, !"_ZN4sycl3_V16detail7Builder7getItemILi1ELb1EEENSt9enable_ifIXT0_EKNS0_4itemIXT_EXT0_EEEE4typeEv: %agg.result"}
!14 = distinct !{!14, !"_ZN4sycl3_V16detail7Builder7getItemILi1ELb1EEENSt9enable_ifIXT0_EKNS0_4itemIXT_EXT0_EEEE4typeEv"}
!15 = distinct !{!15, !16, !"_ZN4sycl3_V16detail7Builder10getElementILi1ELb1EEEDTcl7getItemIXT_EXT0_EEEEPNS0_4itemIXT_EXT0_EEE: %agg.result"}
!16 = distinct !{!16, !"_ZN4sycl3_V16detail7Builder10getElementILi1ELb1EEEDTcl7getItemIXT_EXT0_EEEEPNS0_4itemIXT_EXT0_EEE"}
!17 = !{!18, !18, i64 0}
!18 = !{!"float", !19, i64 0}
!19 = !{!"omnipotent char", !20, i64 0}
!20 = !{!"Simple C++ TBAA"}
!21 = distinct !{!21, !22}
!22 = !{!"llvm.loop.mustprogress"}
!23 = !{i32 738}
!24 = !{i32 -1, i32 -1}
!25 = !{i1 true, i1 false}
!26 = !{i1 false, i1 true, i1 true, i1 false}
!27 = !{!28, !30}
!28 = distinct !{!28, !29, !"_ZN4sycl3_V16detail7Builder7getItemILi1ELb1EEENSt9enable_ifIXT0_EKNS0_4itemIXT_EXT0_EEEE4typeEv: %agg.result"}
!29 = distinct !{!29, !"_ZN4sycl3_V16detail7Builder7getItemILi1ELb1EEENSt9enable_ifIXT0_EKNS0_4itemIXT_EXT0_EEEE4typeEv"}
!30 = distinct !{!30, !31, !"_ZN4sycl3_V16detail7Builder10getElementILi1ELb1EEEDTcl7getItemIXT_EXT0_EEEEPNS0_4itemIXT_EXT0_EEE: %agg.result"}
!31 = distinct !{!31, !"_ZN4sycl3_V16detail7Builder10getElementILi1ELb1EEEDTcl7getItemIXT_EXT0_EEEEPNS0_4itemIXT_EXT0_EEE"}
!32 = !{i32 1035}
!33 = distinct !{!33, !22}
!34 = distinct !{!34, !35}
!35 = !{!"llvm.loop.unroll.disable"}

```

Hi, this should be addressed by https://github.com/llvm/llvm-project/pull/92107


commit f0e34f381866b82a26241f7e9aa5964f0dd11ebd
Author: Florian Hahn <flo@fhahn.com>
Date:   Fri Aug 30 11:29:29 2024 +0100

    [VPlan] Don't skip optimizable truncs in planContainsAdditionalSimps.

    A optimizable cast can also be removed by VPlan simplifications. Remove
    the restriction from planContainsAdditionalSimplifications, as this
    causes it to miss relevant simplifications, triggering false positives
    for the cost decision verification.

    Also adds debug output for printing additional cost-precomputations.

    Fixes https://github.com/llvm/llvm-project/issues/106641.

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 6babfd1eee91..fa05b8dd2242 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -7147,7 +7147,12 @@ LoopVectorizationPlanner::precomputeCosts(VPlan &Plan, ElementCount VF,
     if (!OrigLoop->contains(CondI) ||
         !CostCtx.SkipCostComputation.insert(CondI).second)
       continue;
-    Cost += CostCtx.getLegacyCost(CondI, VF);
+    InstructionCost CondICost = CostCtx.getLegacyCost(CondI, VF);
+    LLVM_DEBUG({
+      dbgs() << "Cost of " << CondICost << " for VF " << VF
+             << ": exit condition instruction " << *CondI << "\n";
+    });
+    Cost += CondICost;
     for (Value *Op : CondI->operands()) {
       auto *OpI = dyn_cast<Instruction>(Op);
       if (!OpI || any_of(OpI->users(), [&ExitInstrs, this](User *U) {
@@ -7250,10 +7255,9 @@ InstructionCost LoopVectorizationPlanner::cost(VPlan &Plan,
 /// not have corresponding recipes in \p Plan and are not marked to be ignored
 /// in \p CostCtx. This means the VPlan contains simplification that the legacy
 /// cost-model did not account for.
-static bool
-planContainsAdditionalSimplifications(VPlan &Plan, ElementCount VF,
-                                      VPCostContext &CostCtx, Loop *TheLoop,
-                                      LoopVectorizationCostModel &CM) {
+static bool planContainsAdditionalSimplifications(VPlan &Plan,
+                                                  VPCostContext &CostCtx,
+                                                  Loop *TheLoop) {
   // First collect all instructions for the recipes in Plan.
   auto GetInstructionForCost = [](const VPRecipeBase *R) -> Instruction * {
     if (auto *S = dyn_cast<VPSingleDefRecipe>(R))
@@ -7284,16 +7288,13 @@ planContainsAdditionalSimplifications(VPlan &Plan, ElementCount VF,
   // Return true if the loop contains any instructions that are not also part of
   // the VPlan or are skipped for VPlan-based cost computations. This indicates
   // that the VPlan contains extra simplifications.
-  return any_of(
-      TheLoop->blocks(), [&SeenInstrs, VF, &CostCtx, &CM](BasicBlock *BB) {
-        return any_of(*BB, [&SeenInstrs, VF, &CostCtx, &CM](Instruction &I) {
-          if (isa<PHINode>(&I))
-            return false;
-          return !SeenInstrs.contains(&I) &&
-                 !CostCtx.skipCostComputation(&I, true) &&
-                 !CM.canTruncateToMinimalBitwidth(&I, VF);
-        });
-      });
+  return any_of(TheLoop->blocks(), [&SeenInstrs, &CostCtx](BasicBlock *BB) {
+    return any_of(*BB, [&SeenInstrs, &CostCtx](Instruction &I) {
+      if (isa<PHINode>(&I))
+        return false;
+      return !SeenInstrs.contains(&I) && !CostCtx.skipCostComputation(&I, true);
+    });
+  });
 }
 #endif

@@ -7364,8 +7365,7 @@ VectorizationFactor LoopVectorizationPlanner::computeBestVF() {
   precomputeCosts(BestPlan, BestFactor.Width, CostCtx);
   assert((BestFactor.Width == LegacyVF.Width ||
           planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width),
-                                                BestFactor.Width, CostCtx,
-                                                OrigLoop, CM)) &&
+                                                CostCtx, OrigLoop)) &&
          " VPlan cost model and legacy cost model disagreed");
   assert((BestFactor.Width.isScalar() || BestFactor.ScalarCost > 0) &&
          "when vectorizing, the scalar cost must be computed.");

target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64-unknown-linux-gnu"

define void @truncate_to_i1_used_by_branch(i8 %x, ptr %dst) #0 {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %f.039 = phi i8 [ 0, %entry ], [ %add, %loop.latch ]
  %0 = or i8 23, %x
  %extract.t = trunc i8 %0 to i1
  br i1 %extract.t, label %then, label %loop.latch

then:                                             ; preds = %loop.header
  store i8 0, ptr %dst, align 1
  br label %loop.latch

loop.latch:                                       ; preds = %then, %loop.header
  %add = add i8 %f.039, 1
  %conv = sext i8 %f.039 to i32
  %cmp = icmp slt i32 %conv, 1
  br i1 %cmp, label %loop.header, label %exit

exit:                                             ; preds = %loop.latch
  ret void
}

attributes #0 = { "target-features"="+64bit,+v,+zvl256b" }

target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64-unknown-linux-gnu"

define void @test_pr98413_sext_removed(ptr %src, ptr noalias %dst, i64 %x) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %gep.src = getelementptr inbounds i16, ptr %src, i64 %iv
  %l = load i16, ptr %gep.src, align 8
  %ext.l = sext i16 %l to i64
  %and = and i64 %x, %ext.l
  %trunc.and = trunc i64 %and to i8
  %gep.dst = getelementptr inbounds i8, ptr %dst, i64 %iv
  store i8 %trunc.and, ptr %gep.dst, align 1
  %iv.next = add i64 %iv, 1
  %exitcond.not = icmp eq i64 %iv, 96
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64-unknown-linux-gnu"

define void @test_pr98413_zext_removed(ptr %src, ptr noalias %dst, i64 %x) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %gep.src = getelementptr inbounds i16, ptr %src, i64 %iv
  %l = load i16, ptr %gep.src, align 8
  %ext.l = zext i16 %l to i64
  %and = and i64 %x, %ext.l
  %trunc.and = trunc i64 %and to i8
  %gep.dst = getelementptr inbounds i8, ptr %dst, i64 %iv
  store i8 %trunc.and, ptr %gep.dst, align 1
  %iv.next = add i64 %iv, 1
  %exitcond.not = icmp eq i64 %iv, 96
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

[VPlan] Assertion " VPlan cost model and legacy cost model disagreed"' failed.

Tested using c4906588ce47de33d59bcd95f3e82ce2c3e61c23

Testcase:
```c
_Bool a;
char b;
unsigned char c;
void d(_Bool e[][6]) {
  for (signed char f = 0; f < c; f += 3)
    for (_Bool g = ({
           __typeof__(0) h = e[1][2];
           e[f][f] > h ?: h;
         });
         g; g = b)
      for (_Bool i = 0; i < (0 >= 0); i = 1)
        a = 0;
}
```
https://godbolt.org/z/Eab8s5e1K

Reduced LLVM IR Testcase:
```llvm ir
; ModuleID = 'reduced.bc'
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64-unknown-linux-gnu"

define void @d() #0 {
entry:
  br label %for.body

for.cond.cleanup.loopexit:                        ; preds = %for.cond.cleanup14
  ret void

for.body:                                         ; preds = %for.cond.cleanup14, %entry
  %f.039 = phi i8 [ %add, %for.cond.cleanup14 ], [ 0, %entry ]
  %0 = or i8 0, 0
  %extract.t = trunc i8 %0 to i1
  br i1 %extract.t, label %for.body22.lr.ph, label %for.cond.cleanup14

for.body22.lr.ph:                                 ; preds = %for.body
  store i8 0, ptr null, align 1
  br label %for.cond.cleanup14

for.cond.cleanup14:                               ; preds = %for.body22.lr.ph, %for.body
  %add = add i8 %f.039, 1
  %conv = sext i8 %f.039 to i32
  %cmp = icmp slt i32 %conv, 1
  br i1 %cmp, label %for.body, label %for.cond.cleanup.loopexit
}

attributes #0 = { "target-features"="+64bit,+a,+c,+d,+f,+m,+relax,+v,+zicsr,+zifencei,+zmmul,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl256b,+zvl32b,+zvl64b,-b,-e,-experimental-smctr,-experimental-smmpm,-experimental-smnpm,-experimental-ssctr,-experimental-ssnpm,-experimental-sspm,-experimental-supm,-experimental-zacas,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-h,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssqosid,-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,-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,-zvl32768b,-zvl4096b,-zvl512b,-zvl65536b,-zvl8192b" }
```

Command/backtrace:
```
> /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:7365: llvm::VectorizationFactor llvm::LoopVectorizationPlanner::computeBestVF(): Assertion `(BestFactor.Width == LegacyVF.Width || planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width), BestFactor.Width, CostCtx, OrigLoop, CM)) && " 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 "d"
 #0 0x000064cd5b412560 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x301e560)
 #1 0x000064cd5b40f96f llvm::sys::RunSignalHandlers() (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x301b96f)
 #2 0x000064cd5b40fac5 SignalHandler(int) Signals.cpp:0:0
 #3 0x000072ec5a042520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x000072ec5a0969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x000072ec5a0969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x000072ec5a0969fc pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x000072ec5a042476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x000072ec5a0287f3 abort ./stdlib/abort.c:81:7
 #9 0x000072ec5a02871b _nl_load_domain ./intl/loadmsgcat.c:1177:9
#10 0x000072ec5a039e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
#11 0x000064cd5a3dd109 llvm::LoopVectorizationPlanner::computeBestVF() (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x1fe9109)
#12 0x000064cd5a3f3ccb llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x1fffccb)
#13 0x000064cd5a3f6ce1 llvm::LoopVectorizePass::runImpl(llvm::Function&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2002ce1)
#14 0x000064cd5a3f7357 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2003357)
#15 0x000064cd592541b6 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+0xe601b6)
#16 0x000064cd5b21d41f 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+0x2e2941f)
#17 0x000064cd59257346 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+0xe63346)
#18 0x000064cd5b21c19b llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x2e2819b)
#19 0x000064cd592535e6 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+0xe5f5e6)
#20 0x000064cd5b21a1dd 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+0x2e261dd)
#21 0x000064cd58a7c106 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+0x688106)
#22 0x000064cd58a6de91 optMain (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x679e91)
#23 0x000072ec5a029d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#24 0x000072ec5a029e40 call_init ./csu/../csu/libc-start.c:128:20
#25 0x000072ec5a029e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#26 0x000064cd58a64245 _start (/scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt+0x670245)
zsh: IOT instruction (core dumped)  /scratch/tc-testing/tc-compiler-fuzz-trunk/build-gcv/build-llvm-linux/bin/opt
```
https://godbolt.org/z/EzdEYKW7c

Found via fuzzer

Related/prior issue: #106417

cc @fhahn


commit e914421d7fbf22059ea2180233add0c914ec80f0
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Thu Oct 24 08:57:07 2024 -0700

    [SLP]Do correct signedness analysis for externally used scalars

    If the scalars is used externally is in the root node, it may have
    incorrect signedness info because of the conflict with the demanded bits
    analysis. Need to perform exact signedness analysis and compute it
    rather than rely on the precomputed value, which might be incorrect for
    alternate zext/sext nodes.

    Fixes #113520

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 012d85353f08..2afd02dae3a8 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -12068,8 +12068,9 @@ InstructionCost BoUpSLP::getTreeCost(ArrayRef<Value *> VectorizedVals) {
     auto It = MinBWs.find(Entry);
     if (It != MinBWs.end()) {
       auto *MinTy = IntegerType::get(F->getContext(), It->second.first);
-      unsigned Extend =
-          It->second.second ? Instruction::SExt : Instruction::ZExt;
+      unsigned Extend = isKnownNonNegative(EU.Scalar, SimplifyQuery(*DL))
+                            ? Instruction::ZExt
+                            : Instruction::SExt;
       VecTy = getWidenedType(MinTy, BundleWidth);
       ExtraCost = TTI->getExtractWithExtendCost(Extend, EU.Scalar->getType(),
                                                 VecTy, EU.Lane);
@@ -15784,8 +15785,9 @@ BoUpSLP::vectorizeTree(const ExtraValueToDebugLocsMap &ExternallyUsedValues,
           // to the larger type.
           ExV = Ex;
           if (Scalar->getType() != Ex->getType())
-            ExV = Builder.CreateIntCast(Ex, Scalar->getType(),
-                                        MinBWs.find(E)->second.second);
+            ExV = Builder.CreateIntCast(
+                Ex, Scalar->getType(),
+                !isKnownNonNegative(Scalar, SimplifyQuery(*DL)));
           auto *I = dyn_cast<Instruction>(Ex);
           ScalarToEEs[Scalar].try_emplace(I ? I->getParent()
                                             : &F->getEntryBlock(),

`slp-vectorizer` miscompilation

Alive2 report: https://alive2.llvm.org/ce/z/FmxNvP

```llvm
----------------------------------------
define <3 x i32> @add3_i96.2(<3 x i32> %#0, <3 x i32> %#1) {
#2:
  %#3 = extractelement <3 x i32> %#0, i64 0
  %#4 = sext i32 %#3 to i64
  %#5 = extractelement <3 x i32> %#1, i64 0
  %#6 = zext i32 %#5 to i64
  %#7 = add nsw nuw i64 %#6, %#4
  %#21 = trunc i64 %#7 to i32
  %#22 = insertelement <3 x i32> %#1, i32 %#21, i32 0
  ret <3 x i32> %#22
}
=>
define <3 x i32> @add3_i96.2(<3 x i32> %#0, <3 x i32> %#1) {
#2:
  %#3 = shufflevector <3 x i32> %#1, <3 x i32> %#0, 0, 3
  %#4 = extractelement <2 x i32> %#3, i32 0
  %#5 = sext i32 %#4 to i64
  %#6 = extractelement <2 x i32> %#3, i32 1
  %#7 = sext i32 %#6 to i64
  %#8 = add nsw nuw i64 %#5, %#7
  %#22 = trunc i64 %#8 to i32
  %#23 = insertelement <3 x i32> %#1, i32 %#22, i32 0
  ret <3 x i32> %#23
}
Transformation doesn't verify!

ERROR: Target is more poisonous than source

Example:
<3 x i32> %#0 = < #x00000008 (8), #x00000000 (0), #x00000000 (0) >
<3 x i32> %#1 = < #xfffffff9 (4294967289, -7), #x00000000 (0), #x00000000 (0) >

Source:
i32 %#3 = #x00000008 (8)
i64 %#4 = #x0000000000000008 (8)
i32 %#5 = #xfffffff9 (4294967289, -7)
i64 %#6 = #x00000000fffffff9 (4294967289)
i64 %#7 = #x0000000100000001 (4294967297)
i32 %#21 = #x00000001 (1)
<3 x i32> %#22 = < #x00000001 (1), #x00000000 (0), #x00000000 (0) >

Target:
<2 x i32> %#3 = < #xfffffff9 (4294967289, -7), #x00000008 (8) >
i32 %#4 = #xfffffff9 (4294967289, -7)
i64 %#5 = #xfffffffffffffff9 (18446744073709551609, -7)
i32 %#6 = #x00000008 (8)
i64 %#7 = #x0000000000000008 (8)
i64 %#8 = poison
i32 %#22 = poison
<3 x i32> %#23 = < poison, #x00000000 (0), #x00000000 (0) >
Source value: < #x00000001 (1), #x00000000 (0), #x00000000 (0) >
Target value: < poison, #x00000000 (0), #x00000000 (0) >

Summary:
  0 correct transformations
  1 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```


commit 0c70a26f46e4efd5a29eb281ff99d2cf7f04c6f6
Author: Alexey Bataev <a.bataev@outlook.com>
Date:   Mon Feb 3 04:37:49 2025 -0800

    [SLP]Clear root node reordering only if the root node is not re-used in graph

    The reordering of the root node can be safely cleared only if the root
    node is not reused, otherwise the graph might be broken

    Fixes #125357

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 4f83b9cfa18d..539c9227af7e 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -20147,7 +20147,7 @@ public:
         }
         V.reorderTopToBottom();
         // No need to reorder the root node at all.
-        V.reorderBottomToTop(/*IgnoreReorder=*/true);
+        V.reorderBottomToTop(!V.doesRootHaveInTreeUses());
         // Keep extracted other reduction values, if they are used in the
         // vectorization trees.
         BoUpSLP::ExtraValueToDebugLocsMap LocalExternallyUsedValues(

opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu < %s

; ModuleID = 'test.ll'
source_filename = "test.c"
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 i32 @main(i32 %0) {
entry:
  %tobool.not = icmp eq i32 %0, 0
  br i1 %tobool.not, label %if.end6, label %if.then

if.then:                                          ; preds = %entry
  br i1 %tobool.not, label %if.end, label %i

if.end:                                           ; preds = %if.then
  br i1 %tobool.not, label %if.end6, label %i

if.end6:                                          ; preds = %if.end, %entry
  %h.0 = phi i32 [ %0, %if.end ], [ 0, %entry ]
  br i1 %tobool.not, label %i, label %if.end13

i:                                                ; preds = %if.end13, %if.end6, %if.end, %if.then
  %h.1 = phi i32 [ %0, %if.then ], [ %h.2, %if.end13 ], [ %0, %if.end ], [ %h.0, %if.end6 ]
  %j.0 = phi i32 [ undef, %if.then ], [ %j.1, %if.end13 ], [ undef, %if.end ], [ %0, %if.end6 ]
  %k.0 = phi i32 [ undef, %if.then ], [ %k.1, %if.end13 ], [ undef, %if.end ], [ %0, %if.end6 ]
  %l.0 = phi i32 [ undef, %if.then ], [ %l.1, %if.end13 ], [ undef, %if.end ], [ %0, %if.end6 ]
  %or = or i32 %j.0, %h.1
  %or9 = or i32 %or, %k.0
  %or10 = or i32 %or9, %l.0
  %tobool11.not = icmp eq i32 %or10, 0
  br i1 %tobool11.not, label %if.end13, label %land.rhs

land.rhs:                                         ; preds = %i
  br label %if.end13

if.end13:                                         ; preds = %i, %land.rhs, %if.end6
  %h.2 = phi i32 [ %h.0, %if.end6 ], [ %h.1, %land.rhs ], [ %h.1, %i ]
  %j.1 = phi i32 [ %0, %if.end6 ], [ %j.0, %land.rhs ], [ %j.0, %i ]
  %k.1 = phi i32 [ %0, %if.end6 ], [ %k.0, %land.rhs ], [ %k.0, %i ]
  %l.1 = phi i32 [ %0, %if.end6 ], [ %l.0, %land.rhs ], [ %l.0, %i ]
  br i1 true, label %if.end15, label %i

if.end15:                                         ; preds = %if.end13
  ret i32 0

; uselistorder directives
  uselistorder i32 %k.0, { 1, 2, 0 }
  uselistorder i32 %l.0, { 1, 2, 0 }
}

clang crashes on valid code at -O{s,2,3} on x86_64-linux-gnu: Assertion `TE->isSame(VL) && "Expected same scalars."' failed

It appears to be a recent regression as it doesn't reproduce with 19.1.0 and earlier.

Compiler Explorer: https://godbolt.org/z/6ca9841xE

```
[512] % clangtk -v
clang version 21.0.0git (https://github.com/llvm/llvm-project.git 7612dcc6e8d8e7f19b364084effbb01946294720)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /local/suz-local/software/local/clang-trunk/bin
Build config: +assertions
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Candidate multilib: .;@m64
Selected multilib: .;@m64
[513] %
[513] % clangtk -O3 small.c
clang-21: /local/suz-local/software/clangbuild/llvm-project/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp:3011: llvm::slpvectorizer::BoUpSLP::TreeEntry* llvm::slpvectorizer::BoUpSLP::getVectorizedOperand(llvm::slpvectorizer::BoUpSLP::TreeEntry*, unsigned int): Assertion `TE->isSame(VL) && "Expected same scalars."' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /local/suz-local/software/local/clang-trunk/bin/clang-21 -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -dumpdir a- -disable-free -clear-ast-before-backend -main-file-name small.c -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/local/suz-local/software/emitesting/bugs/20250201-clangtk-m64-O3-build-174536/delta -fcoverage-compilation-dir=/local/suz-local/software/emitesting/bugs/20250201-clangtk-m64-O3-build-174536/delta -resource-dir /local/suz-local/software/local/clang-trunk/lib/clang/21 -I /usr/local/include -I /local/suz-local/software/local/include -internal-isystem /local/suz-local/software/local/clang-trunk/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/small-b035e7.o -x c small.c
1.	<eof> parser at end of file
2.	Optimizer
3.	Running pass "function<eager-inv>(float2int,lower-constant-intrinsics,chr,loop(loop-rotate<header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,slp-vectorizer,vector-combine,instcombine<max-iterations=1;no-verify-fixpoint>,loop-unroll<O3>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>)" on module "small.c"
4.	Running pass "slp-vectorizer" on function "main"
 #0 0x00005596296c84af llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x479d4af)
 #1 0x00005596296c5c84 SignalHandler(int) Signals.cpp:0:0
 #2 0x00007fe817057420 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x14420)
 #3 0x00007fe816a8e00b raise /build/glibc-LcI20x/glibc-2.31/signal/../sysdeps/unix/sysv/linux/raise.c:51:1
 #4 0x00007fe816a6d859 abort /build/glibc-LcI20x/glibc-2.31/stdlib/abort.c:81:7
 #5 0x00007fe816a6d729 get_sysdep_segment_value /build/glibc-LcI20x/glibc-2.31/intl/loadmsgcat.c:509:8
 #6 0x00007fe816a6d729 _nl_load_domain /build/glibc-LcI20x/glibc-2.31/intl/loadmsgcat.c:970:34
 #7 0x00007fe816a7efd6 (/lib/x86_64-linux-gnu/libc.so.6+0x33fd6)
 #8 0x000055962b1df5c7 llvm::slpvectorizer::BoUpSLP::getSpillCost() (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x62b45c7)
 #9 0x000055962b243030 llvm::slpvectorizer::BoUpSLP::getTreeCost(llvm::ArrayRef<llvm::Value*>) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x6318030)
#10 0x000055962b247f34 (anonymous namespace)::HorizontalReduction::tryToReduce(llvm::slpvectorizer::BoUpSLP&, llvm::DataLayout const&, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo const&, llvm::AssumptionCache*) SLPVectorizer.cpp:0:0
#11 0x000055962b24a205 llvm::SLPVectorizerPass::vectorizeHorReduction(llvm::PHINode*, llvm::Instruction*, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&, llvm::SmallVectorImpl<llvm::WeakTrackingVH>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x631f205)
#12 0x000055962b24ee25 bool llvm::SLPVectorizerPass::vectorizeCmpInsts<std::reverse_iterator<llvm::CmpInst* const*>>(llvm::iterator_range<std::reverse_iterator<llvm::CmpInst* const*>>, llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x6323e25)
#13 0x000055962b24fedb llvm::SLPVectorizerPass::vectorizeChainsInBlock(llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&)::'lambda3'(bool)::operator()(bool) const SLPVectorizer.cpp:0:0
#14 0x000055962b253720 llvm::SLPVectorizerPass::vectorizeChainsInBlock(llvm::BasicBlock*, llvm::slpvectorizer::BoUpSLP&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x6328720)
#15 0x000055962b259f46 llvm::SLPVectorizerPass::runImpl(llvm::Function&, llvm::ScalarEvolution*, llvm::TargetTransformInfo*, llvm::TargetLibraryInfo*, llvm::AAResults*, llvm::LoopInfo*, llvm::DominatorTree*, llvm::AssumptionCache*, llvm::DemandedBits*, llvm::OptimizationRemarkEmitter*) (.part.0) SLPVectorizer.cpp:0:0
#16 0x000055962b25aaa3 llvm::SLPVectorizerPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x632faa3)
#17 0x000055962ac7c7e6 llvm::detail::PassModel<llvm::Function, llvm::SLPVectorizerPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x5d517e6)
#18 0x000055962903ed49 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x4113d49)
#19 0x000055962693a376 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x1a0f376)
#20 0x000055962903d5b2 llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x41125b2)
#21 0x0000559626938aa6 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x1a0daa6)
#22 0x000055962903cfd1 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x4111fd1)
#23 0x0000559629991596 (anonymous namespace)::EmitAssemblyHelper::RunOptimizationPipeline(clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>&, std::unique_ptr<llvm::ToolOutputFile, std::default_delete<llvm::ToolOutputFile>>&, clang::BackendConsumer*) BackendUtil.cpp:0:0
#24 0x0000559629995253 clang::emitBackendOutput(clang::CompilerInstance&, clang::CodeGenOptions&, llvm::StringRef, llvm::Module*, clang::BackendAction, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>, clang::BackendConsumer*) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x4a6a253)
#25 0x000055962a09f2ce clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x51742ce)
#26 0x000055962ba0f29c clang::ParseAST(clang::Sema&, bool, bool) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x6ae429c)
#27 0x000055962a09f708 clang::CodeGenAction::ExecuteAction() (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x5174708)
#28 0x000055962a3719d9 clang::FrontendAction::Execute() (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x54469d9)
#29 0x000055962a2f25be clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x53c75be)
#30 0x000055962a466746 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x553b746)
#31 0x00005596264d4a87 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x15a9a87)
#32 0x00005596264cc33a ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#33 0x00005596264d0471 clang_main(int, char**, llvm::ToolContext const&) (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x15a5471)
#34 0x00005596263bb29b main (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x149029b)
#35 0x00007fe816a6f083 __libc_start_main /build/glibc-LcI20x/glibc-2.31/csu/../csu/libc-start.c:342:3
#36 0x00005596264cbdce _start (/local/suz-local/software/local/clang-trunk/bin/clang-21+0x15a0dce)
clangtk: error: unable to execute command: Aborted
clangtk: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 21.0.0git (https://github.com/llvm/llvm-project.git 7612dcc6e8d8e7f19b364084effbb01946294720)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /local/suz-local/software/local/clang-trunk/bin
Build config: +assertions
clangtk: note: diagnostic msg:
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clangtk: note: diagnostic msg: /tmp/small-401f68.c
clangtk: note: diagnostic msg: /tmp/small-401f68.sh
clangtk: note: diagnostic msg:

********************
[514] %
[514] % cat small.c
int a, b, c, d, e;
volatile int f;
int main() {
  int h = 0, j, k, l;
  if (d) {
    h = c;
    if (f)
      goto i;
    if (f)
      goto i;
  }
  l = j = k = d;
  if (!b)
  i:
    (l | (h | j) | k) && f;
  if (a != e)
    goto i;
  return 0;
}
```

llvm:SLPVectorizer
crash
generated by fuzzer

Reduced reproducer: https://godbolt.org/z/TWadzP8Gc


commit 3bf2295ee0ebd1eafe66ca15dff44bdb31e6198a
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Wed Oct 16 16:24:44 2024 +0800

    [InstCombine] Drop `samesign` flag in `foldAndOrOfICmpsWithConstEq` (#112489)

    In
    https://github.com/llvm/llvm-project/commit/5dbfca30c1a672cd0c5089df2b4fdd171436643a
    we assume that RHS is poison implies LHS is also poison. It doesn't hold
    after introducing samesign flag.

    This patch drops the `samesign` flag on RHS if the original expression
    is a logical and/or.

    Closes #112467.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 64bee4ab974e..c8407e8ba5ab 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3369,8 +3369,14 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   // We can convert this case to bitwise and, because both operands are used
   // on the LHS, and as such poison from both will propagate.
   if (Value *V = foldAndOrOfICmpsWithConstEq(RHS, LHS, IsAnd,
-                                             /*IsLogical*/ false, Builder, Q))
+                                             /*IsLogical=*/false, Builder, Q)) {
+    // If RHS is still used, we should drop samesign flag.
+    if (IsLogical && RHS->hasSameSign() && !RHS->use_empty()) {
+      RHS->setSameSign(false);
+      addToWorklist(RHS);
+    }
     return V;
+  }

   if (Value *V = foldIsPowerOf2OrZero(LHS, RHS, IsAnd, Builder, *this))
     return V;

define i1 @sge_and_max_commute(i8 %x, i8 %y) {
  %cmp = icmp sge i8 %x, %y
  %cmpeq = icmp eq i8 %x, 127
  %r = and i1 %cmpeq, %cmp
  ret i1 %r
}

define i1 @sge_and_max_logical(i8 %x, i8 %y) {
  %cmp = icmp sge i8 %x, %y
  %cmpeq = icmp eq i8 %x, 127
  %r = select i1 %cmp, i1 %cmpeq, i1 false
  ret i1 %r
}

define i1 @sge_and_max_logical_samesign(i8 %x, i8 %y) {
  %cmp = icmp sge i8 %x, %y
  %cmpeq = icmp samesign eq i8 %x, 127
  %r = select i1 %cmp, i1 %cmpeq, i1 false
  ret i1 %r
}

define i1 @sgt_and_min_logical_samesign(ptr %x, ptr %y) {
  %cmp = icmp sgt ptr %x, %y
  %cmpeq = icmp samesign eq ptr %x, null
  %r = select i1 %cmp, i1 %cmpeq, i1 false
  ret i1 %r
}

define i1 @sgt_and_min_logical(ptr %x, ptr %y) {
  %cmp = icmp sgt ptr %x, %y
  %cmpeq = icmp eq ptr %x, null
  %r = select i1 %cmp, i1 %cmpeq, i1 false
  ret i1 %r
}

define i1 @sle_or_not_min(ptr %x, ptr %y) {
  %cmp = icmp sle ptr %x, %y
  %cmpeq = icmp ne ptr %x, null
  %r = or i1 %cmp, %cmpeq
  ret i1 %r
}

[InstCombine] `samesign` flag should be dropped in `foldAndOrOfICmpsWithConstEq`

Reproducer:
```
; bin/opt -passes=instcombine test.ll -S
define i1 @src(i8 %x, i8 %y) {
  %cmp = icmp sgt i8 %x, %y
  %cmpeq = icmp samesign ne i8 %x, 0
  %r = select i1 %cmp, i1 true, i1 %cmpeq
  ret i1 %r
}
```
```
define i1 @tgt(i8 %x, i8 %y) {
  %cmpeq = icmp samesign ne i8 %x, 0
  %1 = icmp slt i8 %y, 0
  %r = or i1 %cmpeq, %1
  ret i1 %r
}
```
```
----------------------------------------
define i1 @src(i8 %x, i8 %y) {
#0:
  %cmp = icmp sgt i8 %x, %y
  %cmpeq = icmp samesign ne i8 %x, 0
  %r = select i1 %cmp, i1 1, i1 %cmpeq
  ret i1 %r
}
=>
define i1 @tgt(i8 %x, i8 %y) {
#0:
  %cmpeq = icmp samesign ne i8 %x, 0
  %#1 = icmp slt i8 %y, 0
  %r = or i1 %cmpeq, %#1
  ret i1 %r
}
Transformation doesn't verify!

ERROR: Target is more poisonous than source

Example:
i8 %x = #x81 (129, -127)
i8 %y = #x80 (128, -128)

Source:
i1 %cmp = #x1 (1)
i1 %cmpeq = poison
i1 %r = #x1 (1)

Target:
i1 %cmpeq = poison
i1 %#1 = #x1 (1)
i1 %r = poison
Source value: #x1 (1)
Target value: poison
```


commit 2dfb1c664c0a0afc512b900c45989e247406e523
Author: Florian Hahn <flo@fhahn.com>
Date:   Wed Oct 23 13:12:03 2024 -0700

    [VPlan] Try to hoist Previous (and operands), if sinking fails for FORs. (#108945)

    In some cases, Previous (and its operands) can be hoisted. This allows
    supporting additional cases where sinking of all users of to FOR fails,
    e.g. due having to sink recipes with side-effects.

    This fixes a crash where we fail to create a scalar VPlan for a
    first-order recurrence, but can create a vector VPlan, because the trunc
    instruction of an IV which generates the previous value of the
    recurrence has been optimized to a truncated induction recipe, thus
    hoisting it to the beginning.

    Fixes https://github.com/llvm/llvm-project/issues/106523.

    PR: https://github.com/llvm/llvm-project/pull/108945

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index d50f3c0c3f3e..c6e09c4f2e6e 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -772,6 +772,105 @@ sinkRecurrenceUsersAfterPrevious(VPFirstOrderRecurrencePHIRecipe *FOR,
   return true;
 }

+/// Try to hoist \p Previous and its operands before all users of \p FOR.
+static bool hoistPreviousBeforeFORUsers(VPFirstOrderRecurrencePHIRecipe *FOR,
+                                        VPRecipeBase *Previous,
+                                        VPDominatorTree &VPDT) {
+  if (Previous->mayHaveSideEffects() || Previous->mayReadFromMemory())
+    return false;
+
+  // Collect recipes that need hoisting.
+  SmallVector<VPRecipeBase *> HoistCandidates;
+  SmallPtrSet<VPRecipeBase *, 8> Visited;
+  VPRecipeBase *HoistPoint = nullptr;
+  // Find the closest hoist point by looking at all users of FOR and selecting
+  // the recipe dominating all other users.
+  for (VPUser *U : FOR->users()) {
+    auto *R = dyn_cast<VPRecipeBase>(U);
+    if (!R)
+      continue;
+    if (!HoistPoint || VPDT.properlyDominates(R, HoistPoint))
+      HoistPoint = R;
+  }
+  assert(all_of(FOR->users(),
+                [&VPDT, HoistPoint](VPUser *U) {
+                  auto *R = dyn_cast<VPRecipeBase>(U);
+                  return !R || HoistPoint == R ||
+                         VPDT.properlyDominates(HoistPoint, R);
+                }) &&
+         "HoistPoint must dominate all users of FOR");
+
+  auto NeedsHoisting = [HoistPoint, &VPDT,
+                        &Visited](VPValue *HoistCandidateV) -> VPRecipeBase * {
+    VPRecipeBase *HoistCandidate = HoistCandidateV->getDefiningRecipe();
+    if (!HoistCandidate)
+      return nullptr;
+    VPRegionBlock *EnclosingLoopRegion =
+        HoistCandidate->getParent()->getEnclosingLoopRegion();
+    assert((!HoistCandidate->getParent()->getParent() ||
+            HoistCandidate->getParent()->getParent() == EnclosingLoopRegion) &&
+           "CFG in VPlan should still be flat, without replicate regions");
+    // Hoist candidate was already visited, no need to hoist.
+    if (!Visited.insert(HoistCandidate).second)
+      return nullptr;
+
+    // Candidate is outside loop region or a header phi, dominates FOR users w/o
+    // hoisting.
+    if (!EnclosingLoopRegion || isa<VPHeaderPHIRecipe>(HoistCandidate))
+      return nullptr;
+
+    // If we reached a recipe that dominates HoistPoint, we don't need to
+    // hoist the recipe.
+    if (VPDT.properlyDominates(HoistCandidate, HoistPoint))
+      return nullptr;
+    return HoistCandidate;
+  };
+  auto CanHoist = [&](VPRecipeBase *HoistCandidate) {
+    // Avoid hoisting candidates with side-effects, as we do not yet analyze
+    // associated dependencies.
+    return !HoistCandidate->mayHaveSideEffects();
+  };
+
+  if (!NeedsHoisting(Previous->getVPSingleValue()))
+    return true;
+
+  // Recursively try to hoist Previous and its operands before all users of FOR.
+  HoistCandidates.push_back(Previous);
+
+  for (unsigned I = 0; I != HoistCandidates.size(); ++I) {
+    VPRecipeBase *Current = HoistCandidates[I];
+    assert(Current->getNumDefinedValues() == 1 &&
+           "only recipes with a single defined value expected");
+    if (!CanHoist(Current))
+      return false;
+
+    for (VPValue *Op : Current->operands()) {
+      // If we reach FOR, it means the original Previous depends on some other
+      // recurrence that in turn depends on FOR. If that is the case, we would
+      // also need to hoist recipes involving the other FOR, which may break
+      // dependencies.
+      if (Op == FOR)
+        return false;
+
+      if (auto *R = NeedsHoisting(Op))
+        HoistCandidates.push_back(R);
+    }
+  }
+
+  // Order recipes to hoist by dominance so earlier instructions are processed
+  // first.
+  sort(HoistCandidates, [&VPDT](const VPRecipeBase *A, const VPRecipeBase *B) {
+    return VPDT.properlyDominates(A, B);
+  });
+
+  for (VPRecipeBase *HoistCandidate : HoistCandidates) {
+    HoistCandidate->moveBefore(*HoistPoint->getParent(),
+                               HoistPoint->getIterator());
+  }
+
+  return true;
+}
+
 bool VPlanTransforms::adjustFixedOrderRecurrences(VPlan &Plan,
                                                   VPBuilder &LoopBuilder) {
   VPDominatorTree VPDT;
@@ -795,7 +894,8 @@ bool VPlanTransforms::adjustFixedOrderRecurrences(VPlan &Plan,
       Previous = PrevPhi->getBackedgeValue()->getDefiningRecipe();
     }

-    if (!sinkRecurrenceUsersAfterPrevious(FOR, Previous, VPDT))
+    if (!sinkRecurrenceUsersAfterPrevious(FOR, Previous, VPDT) &&
+        !hoistPreviousBeforeFORUsers(FOR, Previous, VPDT))
       return false;

     // Introduce a recipe to combine the incoming and previous values of a
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.h b/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
index 60a44bfb0dca..11e094db6294 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
@@ -36,11 +36,11 @@ struct VPlanTransforms {
                                 GetIntOrFpInductionDescriptor,
                             ScalarEvolution &SE, const TargetLibraryInfo &TLI);

-  /// Sink users of fixed-order recurrences after the recipe defining their
-  /// previous value. Then introduce FirstOrderRecurrenceSplice VPInstructions
-  /// to combine the value from the recurrence phis and previous values. The
-  /// current implementation assumes all users can be sunk after the previous
-  /// value, which is enforced by earlier legality checks.
+  /// Try to have all users of fixed-order recurrences appear after the recipe
+  /// defining their previous value, by either sinking users or hoisting recipes
+  /// defining their previous value (and its operands). Then introduce
+  /// FirstOrderRecurrenceSplice VPInstructions to combine the value from the
+  /// recurrence phis and previous values.
   /// \returns true if all users of fixed-order recurrences could be re-arranged
   /// as needed or false if it is not possible. In the latter case, \p Plan is
   /// not valid.

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc_linux"

define i64 @test_pr62954_scalar_epilogue_required(ptr %A, ptr noalias %B, ptr %C) {
entry:
  %gep = getelementptr i8, ptr %A, i64 872
  %rec.start = load i64, ptr %gep, align 8
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 1, %entry ], [ %iv.next, %loop ]
  %for = phi i64 [ %rec.start, %entry ], [ %neg.iv, %loop ]
  %gep.B = getelementptr double, ptr %B, i64 %iv
  %l.B = load double, ptr %gep.B, align 8
  %neg.iv = sub nsw i64 0, %iv
  store i64 %neg.iv, ptr %gep, align 8
  %iv.next = add nuw nsw i64 %iv, 2
  %ec = icmp ugt i64 %iv, 74
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  %.in.lcssa = phi i64 [ %for, %loop ]
  %.lcssa = phi double [ %l.B, %loop ]
  store double %.lcssa, ptr %C, align 8
  ret i64 %.in.lcssa
}

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc_linux"

define void @thirdorderrec(ptr nocapture noundef readonly %x, ptr noalias nocapture noundef writeonly %y, i32 noundef %n) {
entry:
  %cmp38 = icmp sgt i32 %n, 3
  br i1 %cmp38, label %for.body.preheader, label %for.cond.cleanup

for.body.preheader:                               ; preds = %entry
  %wide.trip.count = zext i32 %n to i64
  %.pre = load i8, ptr %x, align 1
  %arrayidx5.phi.trans.insert = getelementptr inbounds i8, ptr %x, i64 1
  %.pre44 = load i8, ptr %arrayidx5.phi.trans.insert, align 1
  %arrayidx12.phi.trans.insert = getelementptr inbounds i8, ptr %x, i64 2
  %.pre45 = load i8, ptr %arrayidx12.phi.trans.insert, align 1
  br label %for.body

for.cond.cleanup:                                 ; preds = %for.body, %entry
  ret void

for.body:                                         ; preds = %for.body, %for.body.preheader
  %0 = phi i8 [ %.pre45, %for.body.preheader ], [ %3, %for.body ]
  %1 = phi i8 [ %.pre44, %for.body.preheader ], [ %0, %for.body ]
  %2 = phi i8 [ %.pre, %for.body.preheader ], [ %1, %for.body ]
  %indvars.iv = phi i64 [ 3, %for.body.preheader ], [ %indvars.iv.next, %for.body ]
  %add8 = add i8 %1, %2
  %add15 = add i8 %add8, %0
  %arrayidx18 = getelementptr inbounds i8, ptr %x, i64 %indvars.iv
  %3 = load i8, ptr %arrayidx18, align 1
  %add21 = add i8 %add15, %3
  %arrayidx24 = getelementptr inbounds i8, ptr %y, i64 %indvars.iv
  store i8 %add21, ptr %arrayidx24, align 1
  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
  %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count
  br i1 %exitcond.not, label %for.cond.cleanup, label %for.body
}

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc_linux"

define void @for_iv_trunc_optimized(ptr %dst) {
bb:
  br label %loop

loop:                                             ; preds = %loop, %bb
  %iv = phi i64 [ 1, %bb ], [ %add, %loop ]
  %for.1 = phi i32 [ 1, %bb ], [ %trunc, %loop ]
  %for.2 = phi i32 [ 0, %bb ], [ %or, %loop ]
  %or = or i32 %for.1, 3
  %add = add i64 %iv, 1
  store i32 %for.2, ptr %dst, align 4
  %icmp = icmp ult i64 %iv, 337
  %trunc = trunc i64 %iv to i32
  br i1 %icmp, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc_linux"

define void @firstorderrec(ptr nocapture noundef readonly %x, ptr noalias nocapture noundef writeonly %y, i32 noundef %n) {
entry:
  %cmp18 = icmp sgt i32 %n, 1
  br i1 %cmp18, label %for.body.preheader, label %for.cond.cleanup

for.body.preheader:                               ; preds = %entry
  %wide.trip.count = zext i32 %n to i64
  %.pre = load i8, ptr %x, align 1
  br label %for.body

for.cond.cleanup:                                 ; preds = %for.body, %entry
  ret void

for.body:                                         ; preds = %for.body, %for.body.preheader
  %0 = phi i8 [ %.pre, %for.body.preheader ], [ %1, %for.body ]
  %indvars.iv = phi i64 [ 1, %for.body.preheader ], [ %indvars.iv.next, %for.body ]
  %arrayidx4 = getelementptr inbounds i8, ptr %x, i64 %indvars.iv
  %1 = load i8, ptr %arrayidx4, align 1
  %add7 = add i8 %1, %0
  %arrayidx10 = getelementptr inbounds i8, ptr %y, i64 %indvars.iv
  store i8 %add7, ptr %arrayidx10, align 1
  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
  %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count
  br i1 %exitcond.not, label %for.cond.cleanup, label %for.body
}

opt -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -debug-only=loop-vectorize -disable-output -S %s 2>&1

define i32 @test_chained_first_order_recurrences_4(ptr %base, i64 %x) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ %iv.next, %loop ], [ 0, %entry ]
  %for.x = phi i64 [ %for.x.next, %loop ], [ 0, %entry ]
  %for.y = phi i32 [ %for.x.prev, %loop ], [ 0, %entry ]
  %iv.next = add i64 %iv, 1
  %gep = getelementptr i64, ptr %base, i64 %iv
  %for.x.prev = trunc i64 %for.x to i32
  %for.y.i64 = sext i32 %for.y to i64
  store i64 %for.y.i64, ptr %gep, align 4
  %for.x.next = mul i64 %x, 2
  %icmp = icmp ugt i64 %iv, 4096
  br i1 %icmp, label %ret, label %loop

ret:                                              ; preds = %loop
  ret i32 0
}

define void @test_chained_first_order_recurrences_3(ptr %ptr) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %for.1 = phi i16 [ 22, %entry ], [ %for.1.next, %loop ]
  %for.2 = phi i16 [ 33, %entry ], [ %for.1, %loop ]
  %for.3 = phi i16 [ 33, %entry ], [ %for.2, %loop ]
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %iv.next = add nuw nsw i64 %iv, 1
  %gep.ptr = getelementptr inbounds i16, ptr %ptr, i64 %iv
  %for.1.next = load i16, ptr %gep.ptr, align 2
  %add.1 = add i16 %for.1, %for.2
  %add.2 = add i16 %add.1, %for.3
  store i16 %add.2, ptr %gep.ptr, align 2
  %exitcond.not = icmp eq i64 %iv.next, 1000
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

define void @test_chained_first_order_recurrences_1(ptr %ptr) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %for.1 = phi i16 [ 22, %entry ], [ %for.1.next, %loop ]
  %for.2 = phi i16 [ 33, %entry ], [ %for.1, %loop ]
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %iv.next = add nuw nsw i64 %iv, 1
  %gep.ptr = getelementptr inbounds i16, ptr %ptr, i64 %iv
  %for.1.next = load i16, ptr %gep.ptr, align 2
  %add = add i16 %for.1, %for.2
  store i16 %add, ptr %gep.ptr, align 2
  %exitcond.not = icmp eq i64 %iv.next, 1000
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

define i32 @test_chained_first_order_recurrences_5_hoist_to_load(ptr %base) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ %iv.next, %loop ], [ 0, %entry ]
  %for.x = phi i64 [ %for.x.next, %loop ], [ 0, %entry ]
  %for.y = phi i32 [ %for.x.prev, %loop ], [ 0, %entry ]
  %iv.next = add i64 %iv, 1
  %gep = getelementptr i64, ptr %base, i64 %iv
  %l = load i64, ptr %gep, align 4
  %for.x.prev = trunc i64 %for.x to i32
  %for.y.i64 = sext i32 %for.y to i64
  store i64 %for.y.i64, ptr %gep, align 4
  %for.x.next = mul i64 %l, 2
  %icmp = icmp ugt i64 %iv, 4096
  br i1 %icmp, label %ret, label %loop

ret:                                              ; preds = %loop
  ret i32 0
}

define void @test_pr54223_sink_after_insertion_order(ptr noalias %a, ptr noalias %b, ptr noalias %dst) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %for.1 = phi float [ 0.000000e+00, %entry ], [ %for.1.next, %loop ]
  %for.2 = phi float [ 0.000000e+00, %entry ], [ %for.2.next, %loop ]
  %neg = fneg float %for.2
  %muladd = call float @llvm.fmuladd.f32(float %for.1, float %neg, float 0.000000e+00)
  %dst.gep = getelementptr inbounds float, ptr %dst, i64 %iv
  %iv.next = add nuw nsw i64 %iv, 1
  %for.1.next = load float, ptr %a, align 4
  %for.2.next = load float, ptr %b, align 4
  store float %muladd, ptr %dst.gep, align 4
  %exitcond.not = icmp eq i64 %iv.next, 10000
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.fmuladd.f32(float, float, float) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

define void @test_pr54233_for_depend_on_each_other(ptr noalias %a, ptr noalias %b) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %for.1 = phi i32 [ 0, %entry ], [ %for.1.next, %loop ]
  %for.2 = phi i32 [ 0, %entry ], [ %for.2.next, %loop ]
  %or = or i32 %for.2, 10
  %shl = shl i32 %for.2, %for.1
  %xor = xor i32 %shl, 255
  %and = and i32 %xor, %or
  %for.1.next = xor i32 12, %for.2
  %for.2.next = load i32, ptr %b, align 4
  %a.gep = getelementptr inbounds i32, ptr %a, i64 %iv
  store i32 %and, ptr %a.gep, align 4
  %iv.next = add nuw i64 %iv, 1
  %exitcond = icmp eq i64 %iv, 1000
  br i1 %exitcond, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

To reproduce run the following test with -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 void @wombat() gc "statepoint-example" {
bb:
  br label %bb1

bb1:                                              ; preds = %bb1, %bb
  %phi = phi i64 [ %add, %bb1 ], [ 1, %bb ]
  %phi2 = phi i32 [ %trunc, %bb1 ], [ 1, %bb ]
  %phi3 = phi i32 [ %or, %bb1 ], [ 0, %bb ]
  %or = or i32 %phi2, 0
  %add = add i64 %phi, 1
  store i32 %phi3, ptr addrspace(1) null, align 4
  %icmp = icmp ult i64 %phi, 337
  %trunc = trunc i64 %phi to i32
  br i1 %icmp, label %bb1, label %bb4

bb4:                                              ; preds = %bb1
  ret void
}
```

Reproducer: https://godbolt.org/z/e14eWvxMb

Stack dump:
```
opt: /root/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:7304: llvm::VectorizationFactor llvm::LoopVectorizationPlanner::computeBestVF(): Assertion `hasPlanWithVF(ScalarVF) && "More than a single plan/VF w/o any plan having scalar VF"' 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 "wombat"
 #0 0x0000000004fa4138 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4fa4138)
 #1 0x0000000004fa18ac SignalHandler(int) Signals.cpp:0:0
 #2 0x000073e073a42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000073e073a969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x000073e073a42476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x000073e073a287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x000073e073a2871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x000073e073a39e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x0000000003fe5276 llvm::LoopVectorizationPlanner::computeBestVF() (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x3fe5276)
 #9 0x0000000004001214 llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4001214)
#10 0x0000000004003eb9 llvm::LoopVectorizePass::runImpl(llvm::Function&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4003eb9)
#11 0x0000000004004543 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4004543)
#12 0x0000000002ec85ae 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+0x2ec85ae)
#13 0x0000000004da08f8 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4da08f8)
#14 0x0000000000df11be 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+0xdf11be)
#15 0x0000000004d9f2fe llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4d9f2fe)
#16 0x0000000000df0aae 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+0xdf0aae)
#17 0x0000000004d9d490 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4d9d490)
#18 0x00000000008e8002 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+0x8e8002)
#19 0x00000000008daedc optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8daedc)
#20 0x000073e073a29d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#21 0x000073e073a29e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#22 0x00000000008d296e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x8d296e)
Program terminated with signal: SIGSEGV
Compiler returned: 139

Very interesting issue thanks! Proposed fix https://github.com/llvm/llvm-project/issues/106523