Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: LoopVectorizationCostModel::setVectorizedCallDecision
collectUniformsAndScalars
Issue title: [clang] Assertion failed in Vectorize
Labels: vectorizers
crash
Reported by: cardigan1008

I compiled this code with -Os and a crash appeared in assertion failure:

```c
#include <stdint.h>
int32_t *c;
int8_t ****d;
uint64_t g;
int a();
long b(long, long h, long p3) {
  long e;
  int f = 0;
  if (h) {
    e = h;
    f = 1;
  }
  if (e > p3)
    f = 2;
  switch (f) {
  case 1:
    e++;
  case 2:
    e--;
  }
  if (e < h)
    e = h;
  return e;
}

uint8_t div_func_uint8_t_u_u(uint8_t ui1, uint8_t ui2)
{
  return (ui1 / ui2);
}

uint64_t j() {
  uint16_t k[8];
  int i;
  for (i = 0; *c + ****d + (int)g + i < 8; i++)
    k[b(0, g, 0)] = 6;
  *c = div_func_uint8_t_u_u(k[6], a());
}
```

The crash is:
```
clang: /root/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:7369: llvm::VectorizationFactor llvm::LoopVectorizationPlanner::computeBestVF(): Assertion `(BestFactor.Width == LegacyVF.Width || planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width), CostCtx, OrigLoop)) && " VPlan cost model and legacy cost model disagreed"' failed.
```

Details can be found here: https://godbolt.org/z/T81adKPY5



Comments: author: patrick-rivos
body: Unreduced LLVM IR:
```llvm ir
; ModuleID = '/app/example.c'
source_filename = "/app/example.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"

@c = dso_local local_unnamed_addr global ptr null, align 8
@d = dso_local local_unnamed_addr global ptr null, align 8
@g = dso_local local_unnamed_addr global i64 0, align 8

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local i64 @b(i64 noundef %0, i64 noundef %h, i64 noundef %p3) local_unnamed_addr #0 {
entry:
  %tobool.not = icmp ne i64 %h, 0
  %spec.select = zext i1 %tobool.not to i32
  %cmp = icmp sgt i64 %h, %p3
  %f.1 = select i1 %cmp, i32 2, i32 %spec.select
  switch i32 %f.1, label %sw.epilog [
    i32 1, label %sw.bb
    i32 2, label %sw.bb3
  ]

sw.bb:                                            ; preds = %entry
  %inc = add nsw i64 %h, 1
  br label %sw.bb3

sw.bb3:                                           ; preds = %entry, %sw.bb
  %e.1 = phi i64 [ %h, %entry ], [ %inc, %sw.bb ]
  %dec = add nsw i64 %e.1, -1
  br label %sw.epilog

sw.epilog:                                        ; preds = %sw.bb3, %entry
  %e.2 = phi i64 [ %h, %entry ], [ %dec, %sw.bb3 ]
  %spec.select14 = tail call i64 @llvm.smax.i64(i64 %e.2, i64 %h)
  ret i64 %spec.select14
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1

; Function Attrs: mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable
define dso_local noundef zeroext i8 @div_func_uint8_t_u_u(i8 noundef zeroext %ui1, i8 noundef zeroext %ui2) local_unnamed_addr #0 {
entry:
  %0 = udiv i8 %ui1, %ui2
  ret i8 %0
}

; Function Attrs: nounwind optsize uwtable
define dso_local i64 @j() local_unnamed_addr #2 {
entry:
  %k = alloca [8 x i16], align 16
  call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %k) #5
  %0 = load ptr, ptr @c, align 8, !tbaa !6
  %1 = load i32, ptr %0, align 4, !tbaa !10
  %2 = load ptr, ptr @d, align 8, !tbaa !6
  %3 = load ptr, ptr %2, align 8, !tbaa !6
  %4 = load ptr, ptr %3, align 8, !tbaa !6
  %5 = load ptr, ptr %4, align 8, !tbaa !6
  %6 = load i8, ptr %5, align 1, !tbaa !12
  %conv = sext i8 %6 to i32
  %7 = load i64, ptr @g, align 8, !tbaa !13
  %conv1 = trunc i64 %7 to i32
  %invariant.op = add i32 %conv, %conv1
  %add3.reass12 = add i32 %1, %invariant.op
  %cmp13 = icmp slt i32 %add3.reass12, 8
  br i1 %cmp13, label %for.body.lr.ph, label %for.end

for.body.lr.ph:                                   ; preds = %entry
  %tobool.not.i = icmp ne i64 %7, 0
  %spec.select.i = zext i1 %tobool.not.i to i32
  %cmp.i = icmp sgt i64 %7, 0
  %f.1.i = select i1 %cmp.i, i32 2, i32 %spec.select.i
  %inc.i = add nsw i64 %7, 1
  br label %for.body

for.body:                                         ; preds = %for.body.lr.ph, %b.exit
  %i.014 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %b.exit ]
  switch i32 %f.1.i, label %b.exit [
    i32 1, label %sw.bb.i
    i32 2, label %sw.bb3.i
  ]

sw.bb.i:                                          ; preds = %for.body
  br label %sw.bb3.i

sw.bb3.i:                                         ; preds = %sw.bb.i, %for.body
  %e.1.i = phi i64 [ %7, %for.body ], [ %inc.i, %sw.bb.i ]
  %dec.i = add nsw i64 %e.1.i, -1
  br label %b.exit

b.exit:                                           ; preds = %for.body, %sw.bb3.i
  %e.2.i = phi i64 [ %7, %for.body ], [ %dec.i, %sw.bb3.i ]
  %spec.select14.i = tail call i64 @llvm.smax.i64(i64 %e.2.i, i64 %7)
  %arrayidx = getelementptr inbounds [8 x i16], ptr %k, i64 0, i64 %spec.select14.i
  store i16 6, ptr %arrayidx, align 2, !tbaa !15
  %inc = add nuw nsw i32 %i.014, 1
  %add = add i32 %1, %inc
  %add3.reass = add i32 %add, %invariant.op
  %cmp = icmp slt i32 %add3.reass, 8
  br i1 %cmp, label %for.body, label %for.end.loopexit, !llvm.loop !17

for.end.loopexit:                                 ; preds = %b.exit
  %arrayidx5.phi.trans.insert = getelementptr inbounds i8, ptr %k, i64 12
  %.pre = load i16, ptr %arrayidx5.phi.trans.insert, align 4, !tbaa !15
  %8 = trunc i16 %.pre to i8
  br label %for.end

for.end:                                          ; preds = %for.end.loopexit, %entry
  %conv6 = phi i8 [ %8, %for.end.loopexit ], [ undef, %entry ]
  %call7 = tail call i32 (...) @a() #6
  %conv8 = trunc i32 %call7 to i8
  %9 = udiv i8 %conv6, %conv8
  %conv10 = zext i8 %9 to i32
  %10 = load ptr, ptr @c, align 8, !tbaa !6
  store i32 %conv10, ptr %10, align 4, !tbaa !10
  call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %k) #5
  ret i64 undef
}

; Function Attrs: optsize
declare i32 @a(...) local_unnamed_addr #3

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i64 @llvm.smax.i64(i64, i64) #4

attributes #0 = { mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) uwtable "min-legal-vector-width"="0" "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 #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nounwind optsize uwtable "min-legal-vector-width"="0" "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 #3 = { optsize "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 #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #5 = { nounwind }
attributes #6 = { nounwind optsize }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 7, !"Dwarf Version", i32 4}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 8, !"PIC Level", i32 2}
!3 = !{i32 7, !"PIE Level", i32 2}
!4 = !{i32 7, !"uwtable", i32 2}
!5 = !{!"clang version 20.0.0git (https://github.com/llvm/llvm-project.git b6597f521d8a040f2b9fee54b3f89c380de8e432)"}
!6 = !{!7, !7, i64 0}
!7 = !{!"any pointer", !8, i64 0}
!8 = !{!"omnipotent char", !9, i64 0}
!9 = !{!"Simple C/C++ TBAA"}
!10 = !{!11, !11, i64 0}
!11 = !{!"int", !8, i64 0}
!12 = !{!8, !8, i64 0}
!13 = !{!14, !14, i64 0}
!14 = !{!"long", !8, i64 0}
!15 = !{!16, !16, i64 0}
!16 = !{!"short", !8, i64 0}
!17 = distinct !{!17, !18}
!18 = !{!"llvm.loop.mustprogress"}
```

https://godbolt.org/z/Yo1crEjGG

cc @fhahn 

Fix patch:
commit 3bd161e98d89d31696002994771b7761f1c74859
Author: Florian Hahn <flo@fhahn.com>
Date:   Tue Sep 3 21:06:31 2024 +0100

    [LV] Honor forced scalars in setVectorizedCallDecision.
    
    Similarly to dd94537b4, setVectorizedCallDecision also did not consider
    ForcedScalars. This lead to VPlans not reflecting the decision by the
    legacy cost model (cost computation would use scalar cost, VPlan would
    have VPWidenCallRecipe).
    
    To fix this, check if the call has been forced to scalar in
    setVectorizedCallDecision.
    
    Note that this requires moving setVectorizedCallDecision after
    collectLoopUniforms (which sets ForcedScalars). collectLoopUniforms does
    not depend on call decisions and can safely be moved.
    
    Fixes https://github.com/llvm/llvm-project/issues/107051.

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 17050b2b433c..0200525a718d 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -1290,8 +1290,8 @@ public:
     if (VF.isScalar() || Uniforms.contains(VF))
       return;
     setCostBasedWideningDecision(VF);
-    setVectorizedCallDecision(VF);
     collectLoopUniforms(VF);
+    setVectorizedCallDecision(VF);
     collectLoopScalars(VF);
   }
 
@@ -6194,6 +6194,7 @@ void LoopVectorizationCostModel::setVectorizedCallDecision(ElementCount VF) {
   assert(!VF.isScalar() &&
          "Trying to set a vectorization decision for a scalar VF");
 
+  auto ForcedScalar = ForcedScalars.find(VF);
   for (BasicBlock *BB : TheLoop->blocks()) {
     // For each instruction in the old loop.
     for (Instruction &I : *BB) {
@@ -6206,14 +6207,37 @@ void LoopVectorizationCostModel::setVectorizedCallDecision(ElementCount VF) {
       InstructionCost VectorCost = InstructionCost::getInvalid();
       InstructionCost IntrinsicCost = InstructionCost::getInvalid();
       TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput;
-
       Function *ScalarFunc = CI->getCalledFunction();
       Type *ScalarRetTy = CI->getType();
       SmallVector<Type *, 4> Tys, ScalarTys;
-      bool MaskRequired = Legal->isMaskRequired(CI);
       for (auto &ArgOp : CI->args())
         ScalarTys.push_back(ArgOp->getType());
 
+      // Estimate cost of scalarized vector call. The source operands are
+      // assumed to be vectors, so we need to extract individual elements from
+      // there, execute VF scalar calls, and then gather the result into the
+      // vector return value.
+      InstructionCost ScalarCallCost =
+          TTI.getCallInstrCost(ScalarFunc, ScalarRetTy, ScalarTys, CostKind);
+
+      // Compute costs of unpacking argument values for the scalar calls and
+      // packing the return values to a vector.
+      InstructionCost ScalarizationCost =
+          getScalarizationOverhead(CI, VF, CostKind);
+
+      ScalarCost = ScalarCallCost * VF.getKnownMinValue() + ScalarizationCost;
+      // Honor ForcedScalars decision.
+      // TODO: For calls, it might still be more profitable to widen. Use
+      // VPlan-based cost model to compare different options.
+      if (VF.isVector() && ForcedScalar != ForcedScalars.end() &&
+          ForcedScalar->second.contains(CI)) {
+        setCallWideningDecision(CI, VF, CM_Scalarize, nullptr,
+                                Intrinsic::not_intrinsic, std::nullopt,
+                                ScalarCost);
+        continue;
+      }
+
+      bool MaskRequired = Legal->isMaskRequired(CI);
       // Compute corresponding vector type for return value and arguments.
       Type *RetTy = ToVectorTy(ScalarRetTy, VF);
       for (Type *ScalarTy : ScalarTys)
@@ -6229,20 +6253,6 @@ void LoopVectorizationCostModel::setVectorizedCallDecision(ElementCount VF) {
           continue;
         }
 
-      // Estimate cost of scalarized vector call. The source operands are
-      // assumed to be vectors, so we need to extract individual elements from
-      // there, execute VF scalar calls, and then gather the result into the
-      // vector return value.
-      InstructionCost ScalarCallCost =
-          TTI.getCallInstrCost(ScalarFunc, ScalarRetTy, ScalarTys, CostKind);
-
-      // Compute costs of unpacking argument values for the scalar calls and
-      // packing the return values to a vector.
-      InstructionCost ScalarizationCost =
-          getScalarizationOverhead(CI, VF, CostKind);
-
-      ScalarCost = ScalarCallCost * VF.getKnownMinValue() + ScalarizationCost;
-
       // Find the cost of vectorizing the call, if we can find a suitable
       // vector variant of the function.
       bool UsesMask = false;


Regression tests:
file: llvm/test/Transforms/LoopVectorize/AArch64/call-costs.ll
commands: opt -p loop-vectorize -S %s
tests: test_name: call_forced_scalar
test_body: target triple = "arm64-apple-macosx11.0.0"

define void @call_forced_scalar(ptr %src.1, ptr %src.2, ptr noalias %dst.1, ptr noalias %dst.2) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %0 = load i32, ptr %src.1, align 4
  %smax = tail call i32 @llvm.smax.i32(i32 %0, i32 0)
  %umin = tail call i32 @llvm.umin.i32(i32 %smax, i32 1)
  %umin.ext = zext i32 %umin to i64
  %gep.src.2 = getelementptr i8, ptr %src.2, i64 %umin.ext
  %1 = load i8, ptr %gep.src.2, align 1
  %l.ext = zext i8 %1 to i32
  %mul = mul i32 3, %l.ext
  store i32 %mul, ptr %dst.1, align 4
  %gep.dst.2 = getelementptr i32, ptr %dst.2, i64 %iv
  store i32 0, ptr %gep.dst.2, align 4
  %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
}

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

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

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

test_name: call_scalarized
test_body: target triple = "arm64-apple-macosx11.0.0"

define void @call_scalarized(ptr noalias %src, ptr noalias %dst, double %0) {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i64 [ 100, %entry ], [ %iv.next, %loop.latch ]
  %iv.next = add i64 %iv, -1
  %gep.src = getelementptr double, ptr %src, i64 %iv.next
  %l = load double, ptr %gep.src, align 8
  %cmp295 = fcmp ugt double %0, 0.000000e+00
  %cmp299 = fcmp ugt double %l, 0.000000e+00
  %or.cond = or i1 %cmp295, %cmp299
  br i1 %or.cond, label %loop.latch, label %then

then:                                             ; preds = %loop.header
  %sqrt = call double @llvm.sqrt.f64(double %l)
  %gep.dst = getelementptr double, ptr %dst, i64 %iv.next
  store double %sqrt, ptr %gep.dst, align 8
  br label %loop.latch

loop.latch:                                       ; preds = %then, %loop.header
  %tobool.not = icmp eq i64 %iv.next, 0
  br i1 %tobool.not, label %exit, label %loop.header

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

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare double @llvm.sqrt.f64(double) #0

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

test_name: fshl_operand_first_order_recurrence
test_body: target triple = "arm64-apple-macosx11.0.0"

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

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %recur = phi i64 [ 0, %entry ], [ %l, %loop ]
  %gep.src = getelementptr inbounds i64, ptr %src, i64 %iv
  %l = load i64, ptr %gep.src, align 8
  %or = tail call i64 @llvm.fshl.i64(i64 1, i64 %recur, i64 1)
  %gep.dst = getelementptr inbounds i64, ptr %dst, i64 %iv
  store i64 %or, ptr %gep.dst, align 8
  %iv.next = add i64 %iv, 1
  %exitcond.not = icmp eq i64 %iv, 100
  br i1 %exitcond.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

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

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

test_name: powi_call
test_body: target triple = "arm64-apple-macosx11.0.0"

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

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %gep = getelementptr inbounds double, ptr %P, i64 %iv
  %l = load double, ptr %gep, align 8
  %powi = tail call double @llvm.powi.f64.i32(double %l, i32 3)
  store double %powi, ptr %gep, align 8
  %iv.next = add i64 %iv, 1
  %ec = icmp eq i64 %iv, 1
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

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

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