Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/VPlan.cpp: VPCostContext::getScalarizationOverhead
llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp: VPReplicateRecipe::computeCost
Issue title: Assertion "Request for a fixed element count on a scalable object" failed, vectorizer crashing with -Rpass=loop-vectorize
Labels: vectorizers
crash
Reported by: namikukr-qc

Ran into a crash recently with the vectorizer while trying to build something. Note that this crash only happens if -Rpass=loop-vectorize is enabled.

Reproducer: https://godbolt.org/z/Ms47v6Eea
Note: using the x86 trunk for the assertions build since there doesn't seem to one for armv8-a.

Can't seem to reproduce it while giving the resulting IR into llc to produce assembly.

Seems to be associated with commit 1858532c48cf5d93aa82966110fe1cada6ab6ba8  (tag: @fhahn ).

Source C code used to produce the issue:

```
void foo(int *a, int *b, int *c) {
  int i;
  int temp;
  int *ptr = a;

  long qval;

  for (i = 0; i < 64; i++) {
    qval = b[i];
    temp = c[i];
    if (temp < 0) {
      temp += qval >> 1;
      temp /= qval;
    }
    ptr[i] = temp;
  }
}

int main(int argc, char **argv) {
  int a[] = {5, 3, 4, 5, 6, 7, 6};
  int b[] = {0, 1, 2, 3, 4, 5, 6};
  int c[] = {1, 2, 3, 4, 5, 6, 7};
  foo(a, b, c);
  return 0;
}
```
`clang -march=armv8-a+sve -O3 -Rpass=loop-vectorize test.c -o test.s`


Fix patch:
commit ae7b15f2e29cea642ca847a4f0ae91d2d793f6ad
Author: Florian Hahn <flo@fhahn.com>
Date:   Sat Oct 11 19:01:27 2025 +0100

    [VPlan] Return invalid for scalable VF in VPReplicateRecipe::computeCost
    
    Replication is currently not supported for scalable VFs. Make sure
    VPReplicateRecipe::computeCost returns an invalid cost early, for
    scalable VFs if the recipe is not a single-scalar.
    
    Note that this moves the existing invalid-costs.ll out of the AArch64
    subdirectory, as it does not use a target triple.
    
    Fixes https://github.com/llvm/llvm-project/issues/160792.

diff --git a/llvm/lib/Transforms/Vectorize/VPlan.cpp b/llvm/lib/Transforms/Vectorize/VPlan.cpp
index 2555ebe2ad89..1fea068bfafd 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlan.cpp
@@ -1777,6 +1777,9 @@ InstructionCost VPCostContext::getScalarizationOverhead(
   if (VF.isScalar())
     return 0;
 
+  assert(!VF.isScalable() &&
+         "Scalarization overhead not supported for scalable vectors");
+
   InstructionCost ScalarizationCost = 0;
   // Compute the cost of scalarizing the result if needed.
   if (!ResultTy->isVoidTy()) {
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 600ff8ac33fe..8e9167729278 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -3174,6 +3174,9 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
   // transform, avoid computing their cost multiple times for now.
   Ctx.SkipCostComputation.insert(UI);
 
+  if (VF.isScalable() && !isSingleScalar())
+    return InstructionCost::getInvalid();
+
   switch (UI->getOpcode()) {
   case Instruction::GetElementPtr:
     // We mark this instruction as zero-cost because the cost of GEPs in
@@ -3221,9 +3224,6 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
       return ScalarCallCost;
     }
 
-    if (VF.isScalable())
-      return InstructionCost::getInvalid();
-
     return ScalarCallCost * VF.getFixedValue() +
            Ctx.getScalarizationOverhead(ResultTy, ArgOps, VF);
   }
@@ -3274,9 +3274,6 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
   }
   case Instruction::Load:
   case Instruction::Store: {
-    if (VF.isScalable() && !isSingleScalar())
-      return InstructionCost::getInvalid();
-
     // TODO: See getMemInstScalarizationCost for how to handle replicating and
     // predicated cases.
     const VPRegionBlock *ParentRegion = getParent()->getParent();


Regression tests:
file: llvm/test/Transforms/LoopVectorize/AArch64/invalid-costs.ll
commands: opt -passes="loop-vectorize" -pass-remarks-output=/dev/null -S %s
tests: test_name: replicate_sdiv_conditional
test_body: target triple = "arm64-apple-macosx"

define void @replicate_sdiv_conditional(ptr noalias %a, ptr noalias %b, ptr noalias %c) #0 {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %gep.c = getelementptr inbounds i32, ptr %c, i64 %iv
  %val.c = load i32, ptr %gep.c, align 4
  %cmp = icmp slt i32 %val.c, 0
  br i1 %cmp, label %if.then, label %loop.latch

if.then:                                          ; preds = %loop.header
  %gep.b = getelementptr inbounds i32, ptr %b, i64 %iv
  %val.b = load i32, ptr %gep.b, align 4
  %sext = sext i32 %val.b to i64
  %shr = ashr i32 %val.b, 1
  %add = add i32 %shr, %val.c
  %conv = sext i32 %add to i64
  %div = sdiv i64 %conv, %sext
  %trunc = trunc i64 %div to i32
  br label %loop.latch

loop.latch:                                       ; preds = %if.then, %loop.header
  %result = phi i32 [ %trunc, %if.then ], [ %val.c, %loop.header ]
  %gep.a = getelementptr inbounds i32, ptr %a, i64 %iv
  store i32 %result, ptr %gep.a, align 4
  %iv.next = add nuw nsw i64 %iv, 1
  %exit = icmp eq i64 %iv.next, 64
  br i1 %exit, label %for.end, label %loop.header

for.end:                                          ; preds = %loop.latch
  ret void
}

attributes #0 = { "target-cpu"="neoverse-512tvb" }

file: llvm/test/Transforms/LoopVectorize/invalid-costs.ll
commands: opt -passes="loop-vectorize" -pass-remarks-output=/dev/null -S %s
tests: test_name: <module>
test_body: 
; REMARKS: the cost-model indicates that vectorization is not beneficial

; Test for https://github.com/llvm/llvm-project/issues/116375.
define void @test_i24_load_for(ptr noalias %src, ptr %dst) {
;
entry:
  br label %loop

loop:
  %iv = phi i16 [ 0, %entry ], [ %iv.next, %loop ]
  %for = phi i24 [ 0, %entry ], [ %for.next, %loop ]
  %iv.next = add i16 %iv, 1
  %gep.src = getelementptr inbounds i24, ptr %src, i16 %iv
  %for.next = load i24, ptr %gep.src, align 1
  %gep.dst = getelementptr inbounds i24, ptr %dst, i16 %iv
  store i24 %for, ptr %gep.dst
  %ec = icmp eq i16 %iv.next, 1000
  br i1 %ec, label %exit, label %loop

exit:
  ret void
}