Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: getCallWideningDecision
needsExtract
Issue title: [VPlan] VPlan cost model and legacy cost model disagreed assertion hit
Labels: vectorizers
crash
Reported by: vzakhari

[reduced.ll.gz](https://github.com/user-attachments/files/22052471/reduced.ll.gz)

Reproducer: `opt --passes=loop-vectorize reduced.ll`:
```
opt: .../llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:7066: llvm::VectorizationFactor llvm::LoopVectorizationPlanner::computeBestVF(): Assertion `(BestFactor.Width == LegacyVF.Width || BestPlan.hasEarlyExit() || planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width), CostCtx, OrigLoop, BestFactor.Width) || planContainsAdditionalSimplifications( getPlanFor(LegacyVF.Width), CostCtx, OrigLoop, LegacyVF.Width)) && " VPlan cost model and legacy cost model disagreed"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
```

The issue appeared between 3addfd0186c1 and 988ab5aca06e.  It affects `CPU2017/621.wrf_s`.

The only suspect (by just looking through the commits) is #154617. @fhahn can you please take a look?


Fix patch:
commit 0aac22758a81a98d9612ed1ad4853d9e434e8451
Author: Florian Hahn <flo@fhahn.com>
Date:   Sun Aug 31 15:12:41 2025 +0100

    [LV] Correctly cost chains of replicating calls in legacy CM.
    
    Check for scalarized calls in needsExtract to fix a divergence between
    legacy and VPlan-based cost model.
    
    The legacy cost model was missing a check for scalarized calls in
    needsExtract, which meant if incorrectly assumed the result of a
    scalarized call needs extracting.
    
    Exposed by https://github.com/llvm/llvm-project/pull/154617.
    
    Fixes https://github.com/llvm/llvm-project/issues/156091.

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 56403dba92df..1f4610ad191c 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -1134,7 +1134,10 @@ public:
   CallWideningDecision getCallWideningDecision(CallInst *CI,
                                                ElementCount VF) const {
     assert(!VF.isScalar() && "Expected vector VF");
-    return CallWideningDecisions.at({CI, VF});
+    auto I = CallWideningDecisions.find({CI, VF});
+    if (I == CallWideningDecisions.end())
+      return {CM_Unknown, nullptr, Intrinsic::not_intrinsic, std::nullopt, 0};
+    return I->second;
   }
 
   /// Return True if instruction \p I is an optimizable truncate whose operand
@@ -1657,7 +1660,9 @@ private:
     Instruction *I = dyn_cast<Instruction>(V);
     if (VF.isScalar() || !I || !TheLoop->contains(I) ||
         TheLoop->isLoopInvariant(I) ||
-        getWideningDecision(I, VF) == CM_Scalarize)
+        getWideningDecision(I, VF) == CM_Scalarize ||
+        (isa<CallInst>(I) &&
+         getCallWideningDecision(cast<CallInst>(I), VF).Kind == CM_Scalarize))
       return false;
 
     // Assume we can vectorize V (and hence we need extraction) if the


Regression tests:
file: llvm/test/Transforms/LoopVectorize/X86/cost-conditional-branches.ll
commands: opt < %s -passes=loop-vectorize -mtriple=x86_64-apple-macosx10.8.0 -mcpu=corei7-avx -S
tests: test_name: <module>
test_body: 
target triple = "x86_64-apple-macosx10.8.0"

; Test case for https://github.com/llvm/llvm-project/issues/156091.
define void @test_replicate_call_chain(float %x, ptr noalias %A, ptr noalias %B, ptr align 4 noalias %C, ptr align 4 noalias %D, ptr noalias %E) #0 {
;
entry:
  br label %loop.header

loop.header:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %dec.iv = phi i64 [ 100, %entry ], [ %dec.iv.next, %loop.latch ]
  %iv.inc = add i64 %iv, 1
  %gep.A = getelementptr inbounds float, ptr %A, i64 %iv
  %l.A = load float, ptr %gep.A, align 4
  %c.A = fcmp ogt float %l.A, 0.0
  %gep.B = getelementptr inbounds float, ptr %B, i64 %iv
  %l.B = load float, ptr %gep.B, align 4
  %c.B = fcmp ogt float %l.B, 0.0
  %gep.C = getelementptr float, ptr %C, i64 %iv.inc
  %and = and i1 %c.A, %c.B
  br i1 %and, label %then, label %else

then:
  store float 0.0, ptr %gep.C, align 4
  br label %loop.latch

else:
  %iv.mul.2 = shl i64 %iv, 2
  %gep.D = getelementptr i8, ptr %D, i64 %iv.mul.2
  %l.D = load float, ptr %gep.D, align 4
  %mul = fmul float %l.D, 2.0
  %pow.1 = tail call float @llvm.pow.f32(float %mul, float %x)
  %pow.2 = tail call float @llvm.pow.f32(float %pow.1, float %x)
  store float %pow.2, ptr %gep.C, align 4
  br label %loop.latch

loop.latch:
  store float 0.000000e+00, ptr %E, align 4
  %iv.next = add i64 %iv, 1
  %dec.iv.next = add i64 %dec.iv, -1
  %ec = icmp ne i64 %dec.iv.next, 0
  br i1 %ec, label %loop.header, label %exit

exit:
  ret void
}

define i64 @avx512_cond_load_cost(ptr %src, i32 %a, i64 %b, i32 %c, i32 %d) #1 {
;
entry:
  br label %loop.header

loop.header:
  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %c.1 = icmp slt i32 %iv, 0
  br i1 %c.1, label %if.then, label %loop.latch

if.then:
  %1 = urem i32 %a, %c
  %mul = sub i32 0, %1
  %div = udiv i32 %c, %d
  %or = or i32 %div, %mul
  %ext = sext i32 %or to i64
  %gep = getelementptr { i64, i64, i64 }, ptr %src, i64 %ext, i32 2
  %l = load i64, ptr %gep, align 8
  %or.2 = or i64 %l, %b
  br label %loop.latch

loop.latch:
  %res = phi i64 [ 0, %loop.header ], [ %or.2, %if.then ]
  %iv.next = add i32 %iv, 1
  %ec = icmp ult i32 %iv, %c
  br i1 %ec, label %loop.header, label %exit

exit:
  ret i64 %res
}

define void @cost_duplicate_recipe_for_sinking(ptr %A, i64 %N) #2 {
;
entry:
  br label %loop.header

loop.header:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.shl = shl nsw i64 %iv, 2
  %gep.0 = getelementptr nusw double, ptr %A, i64 %iv.shl
  %l = load double, ptr %gep.0, align 8
  %c = fcmp oeq double %l, 0.000000e+00
  br i1 %c, label %if.then, label %loop.latch

if.then:
  %gep.1 = getelementptr double, ptr %A, i64 %iv.shl
  store double 0.000000e+00, ptr %gep.1, align 8
  br label %loop.latch

loop.latch:
  %iv.next = add nsw i64 %iv, 1
  %ec = icmp eq i64 %iv, %N
  br i1 %ec, label %exit, label %loop.header

exit:
  ret void
}

; Test for https://github.com/llvm/llvm-project/issues/129236.
define i32 @cost_ashr_with_op_known_invariant_via_scev(i8 %a) {
;
entry:
  %cmp.i = icmp eq i16 0, 0
  %conv.i = sext i16 0 to i32
  %conv5.i = sext i8 %a to i32
  br label %loop.header

loop.header:
  %iv = phi i8 [ 100, %entry ], [ %iv.next, %loop.latch ]
  br i1 %cmp.i, label %then, label %else

then:
  %p.1 = phi i32 [ %rem.i, %else ], [ 0, %loop.header ]
  %shr.i = ashr i32 %conv5.i, %p.1
  %tobool6.not.i = icmp eq i32 %shr.i, 0
  %sext.i = shl i32 %p.1, 24
  %2 = ashr exact i32 %sext.i, 24
  %3 = select i1 %tobool6.not.i, i32 %2, i32 0
  br label %loop.latch

else:
  %rem.i = urem i32 -1, %conv.i
  %cmp3.i = icmp sgt i32 %rem.i, 1
  br i1 %cmp3.i, label %loop.latch, label %then

loop.latch:
  %p.2 = phi i32 [ 0, %else ], [ %3, %then ]
  %iv.next = add i8 %iv, -1
  %ec = icmp eq i8 %iv.next, 0
  br i1 %ec, label %exit, label %loop.header

exit:
  ret i32 %p.2
}

attributes #0 = { "target-cpu"="znver4" }
attributes #1 = { "target-features"="+avx512bw,+avx512cd,+avx512dq,+avx512f,+avx512vl" }
attributes #2 = { "target-cpu"="znver3" }
file: llvm/test/Transforms/LoopVectorize/X86/cost-model.ll
commands: opt < %s -passes=loop-vectorize -mtriple=x86_64-apple-macosx10.8.0 -mcpu=corei7-avx -S
tests: test_name: cost_assume
test_body: target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i64 @cost_assume(ptr %end, i64 %N) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %0 = phi i64 [ 0, %entry ], [ %1, %loop ]
  %1 = add i64 %0, 1
  %iv.next = add nsw i64 %iv, 1
  %c = icmp ne i64 %N, 0
  tail call void @llvm.assume(i1 %c)
  %gep = getelementptr nusw [9 x i8], ptr null, i64 %iv.next
  %ec = icmp eq ptr %gep, %end
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret i64 %1
}

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

attributes #0 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }