Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: LoopVectorizationPlanner::buildVPlan
LoopVectorizationPlanner::planInVPlanNativePath
LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes
llvm/lib/Transforms/Vectorize/VPlan.cpp: LoopVectorizationPlanner::buildVPlans
llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp: VPlanTransforms::VPInstructionsToVPRecipes
sinkScalarOperands
Issue title: [LoopVectorize] Assertion `ID != Intrinsic::not_intrinsic && "Not an intrinsic!"' failed.
Labels: vectorizers
crash-on-valid
Reported by: dtcxzyw

Reproducer: https://godbolt.org/z/n7E1TjTv5
```
; bin/opt -passes=loop-vectorize -enable-vplan-native-path reduced.ll -S
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() {
entry:
  br label %for.cond2.preheader.i815

for.cond2.preheader.i815:                         ; preds = %for.cond.cleanup8.i835.split.us, %entry
  %I.03.i816 = phi i64 [ %inc20.i839, %for.cond.cleanup8.i835.split.us ], [ 0, %entry ]
  br label %cont.i830.us

cont.i830.us:                                     ; preds = %cont.i830.us, %for.cond2.preheader.i815
  %K.01.i824.us = phi i64 [ 0, %for.cond2.preheader.i815 ], [ %inc.i833.us, %cont.i830.us ]
  call void @use(ptr null, i64 0)
  %inc.i833.us = add i64 %K.01.i824.us, 1
  %exitcond.not.i834.us = icmp eq i64 %inc.i833.us, 0
  br i1 %exitcond.not.i834.us, label %for.cond.cleanup8.i835.split.us, label %cont.i830.us

for.cond.cleanup8.i835.split.us:                  ; preds = %cont.i830.us
  %inc20.i839 = add i64 %I.03.i816, 1
  %exitcond5.not.i840 = icmp eq i64 %inc20.i839, 0
  br i1 %exitcond5.not.i840, label %"_ZZ4mainENK3$_7clEmmmPi.exit.loopexit1081", label %for.cond2.preheader.i815, !llvm.loop !0

"_ZZ4mainENK3$_7clEmmmPi.exit.loopexit1081":      ; preds = %for.cond.cleanup8.i835.split.us
  ret i32 0
}

declare void @use(ptr, i64)

!0 = distinct !{!0, !1, !2}
!1 = !{!"llvm.loop.mustprogress"}
!2 = !{!"llvm.loop.vectorize.enable", i1 true}
```
```
opt: /root/llvm-project/llvm/lib/Analysis/VectorUtils.cpp:175: bool llvm::isVectorIntrinsicWithOverloadTypeAtArg(llvm::Intrinsic::ID, int, const llvm::TargetTransformInfo*): Assertion `ID != Intrinsic::not_intrinsic && "Not an intrinsic!"' 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 -enable-vplan-native-path <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 "main"
 #0 0x0000000005475858 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5475858)
 #1 0x0000000005473214 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #2 0x00007024fda42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x00007024fda969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x00007024fda42476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x00007024fda287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x00007024fda2871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x00007024fda39e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x0000000004bea5d7 (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4bea5d7)
 #9 0x00000000045582f0 llvm::VPWidenIntrinsicRecipe::execute(llvm::VPTransformState&) (.part.0) VPlanRecipes.cpp:0:0
#10 0x0000000004513ea7 llvm::VPBasicBlock::executeRecipes(llvm::VPTransformState*, llvm::BasicBlock*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x4513ea7)
#11 0x000000000451e4cf llvm::VPBasicBlock::execute(llvm::VPTransformState*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x451e4cf)
#12 0x000000000451f5c0 llvm::VPRegionBlock::execute(llvm::VPTransformState*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x451f5c0)
#13 0x000000000451f5c0 llvm::VPRegionBlock::execute(llvm::VPTransformState*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x451f5c0)
#14 0x000000000452b451 llvm::VPlan::execute(llvm::VPTransformState*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x452b451)
#15 0x00000000043ddd2f 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*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43ddd2f)
#16 0x00000000043f0e36 llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43f0e36)
#17 0x00000000043f45e9 llvm::LoopVectorizePass::runImpl(llvm::Function&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43f45e9)
#18 0x00000000043f4c63 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x43f4c63)
#19 0x000000000319510e 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+0x319510e)
#20 0x0000000005270840 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x5270840)
#21 0x0000000000e79f8e 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+0xe79f8e)
#22 0x000000000526f15b llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x526f15b)
#23 0x0000000000e7910e 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+0xe7910e)
#24 0x000000000526eb60 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x526eb60)
#25 0x000000000094151a 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+0x94151a)
#26 0x0000000000934c6c optMain (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x934c6c)
#27 0x00007024fda29d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#28 0x00007024fda29e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#29 0x000000000092c755 _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x92c755)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```
Reduced from llvm-test-suite/SingleSource/UnitTests/Vectorizer/VPlanNativePath/outer-loop-vect.cpp with ubsan+https://github.com/llvm/llvm-project/pull/130990 enabled




Comments: author: zhangjunphy
body: It seems for vplan vectorization, `LoopVectorizationLegality::canVectorize` only checks the loop structure and then skips the check of individual instructions. So the pass later tries to rewirte that call to `@use` and failed.
author: zhangjunphy
body: From the description of `isExplicitVecOuterLoop`. This does not seem to a problem of the LoopVectorization pass.
When `-enable-vplan-native-path` is provided, LLVM only attempts to vectorize outer loops marked with a pragma.
And the programmer is expected to ensure vectorization must be legal for those loops.
https://github.com/llvm/llvm-project/blob/125553a6779ba179a9b4b5212c531da2474258fd/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp#L2183-L2192

=============
Edited for clarity.
author: dtcxzyw
body: Does ubsan instrumentation break this assumption?

author: zhangjunphy
body: Well, I'm far from familiar with the design of vectorization and this is just my conclusion from reading the code.

From what I understand, if the flag `-enable-vplan-native-path` is specified, the IR producer is required to be certain that the loop marked with "llvm.loop.vectorize.enable" is actually vectorizable. Otherwise this will cause problems as that flag will cause vectorization to bypass some legality checks and to directly attempt vectorizing the loop. 

In this case,  the instrumentation added the "llvm.loop.vectorize.enable" metadata to the loop. But there is the call to `@use` which is not vectorizable. And this check is bypassed with  `-enable-vplan-native-path`, thus caused the problem.
author: fhahn
body: It is true that the native path somewhat relies on the loops being marked safe for vectorization by the user, we still shouldn't crash. In this particular case we can easily detect that we cannot create a valid VPlan, and bail out. I will prepare a fix.

Fix patch:
commit 2e13ec561c8139951e3f0815c35c758a7a3011db
Author: Florian Hahn <flo@fhahn.com>
Date:   Wed Mar 19 21:35:15 2025 +0000

    [VPlan] Bail out on non-intrinsic calls in VPlanNativePath.
    
    Update initial VPlan-construction in VPlanNativePath in line with the
    inner loop path, in that it bails out when encountering constructs it
    cannot handle, like non-intrinsic calls.
    
    Fixes https://github.com/llvm/llvm-project/issues/131071.

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h b/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h
index ed3e45dd2c6c..7bbe8669cef6 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h
@@ -494,8 +494,10 @@ protected:
 private:
   /// Build a VPlan according to the information gathered by Legal. \return a
   /// VPlan for vectorization factors \p Range.Start and up to \p Range.End
-  /// exclusive, possibly decreasing \p Range.End.
-  VPlanPtr buildVPlan(VFRange &Range);
+  /// exclusive, possibly decreasing \p Range.End. If no VPlan can be built for
+  /// the input range, set the largest included VF to the maximum VF for which
+  /// no plan could be built.
+  VPlanPtr tryToBuildVPlan(VFRange &Range);
 
   /// Build a VPlan using VPRecipes according to the information gather by
   /// Legal. This method is only used for the legacy inner loop vectorizer.
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 89337dc38535..61e0d18445b6 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -7121,6 +7121,9 @@ LoopVectorizationPlanner::planInVPlanNativePath(ElementCount UserVF) {
                       << "VF " << VF << " to build VPlans.\n");
     buildVPlans(VF, VF);
 
+    if (VPlans.empty())
+      return VectorizationFactor::Disabled();
+
     // For VPlan build stress testing, we bail out after VPlan construction.
     if (VPlanBuildStressTest)
       return VectorizationFactor::Disabled();
@@ -9620,7 +9623,7 @@ LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(VFRange &Range) {
   return Plan;
 }
 
-VPlanPtr LoopVectorizationPlanner::buildVPlan(VFRange &Range) {
+VPlanPtr LoopVectorizationPlanner::tryToBuildVPlan(VFRange &Range) {
   // Outer loop handling: They may require CFG and instruction level
   // transformations before even evaluating whether vectorization is profitable.
   // Since we cannot modify the incoming IR, we need to build VPlan upfront in
@@ -9640,10 +9643,13 @@ VPlanPtr LoopVectorizationPlanner::buildVPlan(VFRange &Range) {
   for (ElementCount VF : Range)
     Plan->addVF(VF);
 
-  VPlanTransforms::VPInstructionsToVPRecipes(
-      Plan,
-      [this](PHINode *P) { return Legal->getIntOrFpInductionDescriptor(P); },
-      *PSE.getSE(), *TLI);
+  if (!VPlanTransforms::tryToConvertVPInstructionsToVPRecipes(
+          Plan,
+          [this](PHINode *P) {
+            return Legal->getIntOrFpInductionDescriptor(P);
+          },
+          *PSE.getSE(), *TLI))
+    return nullptr;
 
   // Tail folding is not supported for outer loops, so the induction increment
   // is guaranteed to not wrap.
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.cpp b/llvm/lib/Transforms/Vectorize/VPlan.cpp
index b63cdd04530a..ef82734840ed 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlan.cpp
@@ -1532,12 +1532,13 @@ void LoopVectorizationPlanner::buildVPlans(ElementCount MinVF,
   auto MaxVFTimes2 = MaxVF * 2;
   for (ElementCount VF = MinVF; ElementCount::isKnownLT(VF, MaxVFTimes2);) {
     VFRange SubRange = {VF, MaxVFTimes2};
-    auto Plan = buildVPlan(SubRange);
-    VPlanTransforms::optimize(*Plan);
-    // Update the name of the latch of the top-level vector loop region region
-    // after optimizations which includes block folding.
-    Plan->getVectorLoopRegion()->getExiting()->setName("vector.latch");
-    VPlans.push_back(std::move(Plan));
+    if (auto Plan = tryToBuildVPlan(SubRange)) {
+      VPlanTransforms::optimize(*Plan);
+      // Update the name of the latch of the top-level vector loop region region
+      // after optimizations which includes block folding.
+      Plan->getVectorLoopRegion()->getExiting()->setName("vector.latch");
+      VPlans.push_back(std::move(Plan));
+    }
     VF = SubRange.End;
   }
 }
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index e6f788a18805..6c8680ac9661 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -32,7 +32,7 @@
 
 using namespace llvm;
 
-void VPlanTransforms::VPInstructionsToVPRecipes(
+bool VPlanTransforms::tryToConvertVPInstructionsToVPRecipes(
     VPlanPtr &Plan,
     function_ref<const InductionDescriptor *(PHINode *)>
         GetIntOrFpInductionDescriptor,
@@ -83,6 +83,9 @@ void VPlanTransforms::VPInstructionsToVPRecipes(
         } else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
           NewRecipe = new VPWidenGEPRecipe(GEP, Ingredient.operands());
         } else if (CallInst *CI = dyn_cast<CallInst>(Inst)) {
+          Intrinsic::ID VectorID = getVectorIntrinsicIDForCall(CI, &TLI);
+          if (VectorID == Intrinsic::not_intrinsic)
+            return false;
           NewRecipe = new VPWidenIntrinsicRecipe(
               *CI, getVectorIntrinsicIDForCall(CI, &TLI),
               {Ingredient.op_begin(), Ingredient.op_end() - 1}, CI->getType(),
@@ -106,6 +109,7 @@ void VPlanTransforms::VPInstructionsToVPRecipes(
       Ingredient.eraseFromParent();
     }
   }
+  return true;
 }
 
 static bool sinkScalarOperands(VPlan &Plan) {
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.h b/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
index 2ec07eea1ecc..fe93637ab723 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
@@ -68,12 +68,13 @@ struct VPlanTransforms {
       bool RequiresScalarEpilogueCheck, bool TailFolded, Loop *TheLoop);
 
   /// Replaces the VPInstructions in \p Plan with corresponding
-  /// widen recipes.
-  static void
-  VPInstructionsToVPRecipes(VPlanPtr &Plan,
-                            function_ref<const InductionDescriptor *(PHINode *)>
-                                GetIntOrFpInductionDescriptor,
-                            ScalarEvolution &SE, const TargetLibraryInfo &TLI);
+  /// widen recipes. Returns false if any VPInstructions could not be converted
+  /// to a wide recipe if needed.
+  static bool tryToConvertVPInstructionsToVPRecipes(
+      VPlanPtr &Plan,
+      function_ref<const InductionDescriptor *(PHINode *)>
+          GetIntOrFpInductionDescriptor,
+      ScalarEvolution &SE, const TargetLibraryInfo &TLI);
 
   /// Try to have all users of fixed-order recurrences appear after the recipe
   /// defining their previous value, by either sinking users or hoisting recipes


Regression tests:
file: llvm/test/Transforms/LoopVectorize/vplan-widen-call-instruction.ll
commands: opt -passes=loop-vectorize -force-vector-width=4 -enable-vplan-native-path -S %s
tests: test_name: call_to_non_intrinsic
test_body: define void @call_to_non_intrinsic() {
entry:
  br label %outer.header

outer.header:                                     ; preds = %outer.latch, %entry
  %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
  br label %inner.header

inner.header:                                     ; preds = %inner.header, %outer.header
  %inner.iv = phi i64 [ 0, %outer.header ], [ %inner.iv.next, %inner.header ]
  call void @use()
  %inner.iv.next = add i64 %inner.iv, 1
  %inner.ec = icmp eq i64 %inner.iv.next, 100
  br i1 %inner.ec, label %outer.latch, label %inner.header

outer.latch:                                      ; preds = %inner.header
  %outer.iv.next = add i64 %outer.iv, 1
  %outer.ec = icmp eq i64 %outer.iv.next, 100
  br i1 %outer.ec, label %exit, label %outer.header, !llvm.loop !0

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

declare void @use()

!0 = distinct !{!0, !1}
!1 = !{!"llvm.loop.vectorize.enable", i1 true}