Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp: simplifyRecipe
Issue title: Assertion `State.VF.isVector() && "Not vectorizing?"' failed.
Labels: vectorizers
crash
Reported by: TatyanaDoubts

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
}
```

Comments: author: fhahn
body: Taking a look now!

Fix patch:
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


Regression tests:
file: llvm/test/Transforms/LoopVectorize/interleave-and-scalarize-only.ll
commands: 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
tests: test_name: test_scalarize_call
test_body: 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) }

test_name: scalarize_ptrtoint
test_body: 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
}

test_name: first_order_recurrence_using_induction
test_body: 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
}

test_name: reduction_with_casts
test_body: 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
}

test_name: pr76986_trunc_sext_interleaving_only
test_body: 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
}

test_name: test_scalarize_with_branch_cond
test_body: 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
}