Bug type: crash
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp: VPRecipeBuilder::getScaledReductions
Issue title: [LV] Assertion `Val && "isa<> used on a null pointer"' failed
Labels: vectorizers
crash
Reported by: sjoerdmeijer

Compile this:

```
int a;
extern bool b[];
char d, c;
void f() {
  for (int e(d); e; e += 4LL) {
    b[e] = 0;
    a += c;
  }
}
```

with:

    clang++  -Ofast  -mcpu=grace  -c func.cpp

to trigger this assert:

```
llvm/include/llvm/Support/Casting.h:109: static bool llvm::isa_impl_cl<llvm::VPWidenCastRecipe, const llvm::VPRecipeBase *>::doit(const From *) [To = llvm::VPWidenCastRecipe, From = const llvm::VPRecipeBase *]: Assertion `Val && "isa<> used on a null pointer"' failed.
```

See also: https://godbolt.org/z/G9nM71bbr

Comments: author: david-arm
body: OK I'll take a look.
author: david-arm
body: I think I have a fix. We're attempting to match a partial reduction where the extended value lives outside the loop.
author: david-arm
body: @sjoerdmeijer FYI https://github.com/llvm/llvm-project/pull/148264
author: sjoerdmeijer
body: Thanks for the quick fix @david-arm !

Fix patch:
commit c363a3f9c858b38afa34f3a8ebcc3fa7a4062315
Author: David Sherwood <david.sherwood@arm.com>
Date:   Tue Jul 15 09:54:58 2025 +0100

    [LV] Ensure getScaledReductions only matches extends inside the loop (#148264)
    
    In getScaledReductions for the case where we try to match a partial
    reduction of the form:
    
    %phi = phi i32 ...
    ...
    %add = add i32 %phi, %zext
    
    where
    
    %zext = i8 %some_val to i32
    
    we should ensure that %zext is actually inside the loop.
    
    Fixes https://github.com/llvm/llvm-project/issues/148260

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 5380a0fc6498..06db89a89bc3 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -8112,7 +8112,7 @@ bool VPRecipeBuilder::getScaledReductions(
   std::optional<unsigned> BinOpc;
   Type *ExtOpTypes[2] = {nullptr};
 
-  auto CollectExtInfo = [&Exts,
+  auto CollectExtInfo = [this, &Exts,
                          &ExtOpTypes](SmallVectorImpl<Value *> &Ops) -> bool {
     unsigned I = 0;
     for (Value *OpI : Ops) {
@@ -8120,6 +8120,11 @@ bool VPRecipeBuilder::getScaledReductions(
       if (!match(OpI, m_ZExtOrSExt(m_Value(ExtOp))))
         return false;
       Exts[I] = cast<Instruction>(OpI);
+
+      // TODO: We should be able to support live-ins.
+      if (!CM.TheLoop->contains(Exts[I]))
+        return false;
+
       ExtOpTypes[I] = ExtOp->getType();
       I++;
     }


Regression tests:
file: llvm/test/Transforms/LoopVectorize/AArch64/partial-reduce.ll
commands: opt -passes=loop-vectorize -force-vector-interleave=1 -enable-epilogue-vectorization=false -S < %s
opt -passes=loop-vectorize -enable-epilogue-vectorization=false -S < %s
opt -passes=loop-vectorize -force-vector-interleave=1 -vectorizer-maximize-bandwidth -enable-epilogue-vectorization=false -S < %s
tests: test_name: add_of_zext_outside_loop
test_body: target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-none-unknown-elf"

; Function Attrs: vscale_range(1,16)
define i32 @add_of_zext_outside_loop(i32 %a, ptr noalias %b, i8 %c, i32 %d) #0 {
entry:
  %conv1 = zext i8 %c to i32
  br label %for.body

for.body:                                         ; preds = %for.body, %entry
  %iv = phi i32 [ %d, %entry ], [ %iv.next, %for.body ]
  %rdx = phi i32 [ %a, %entry ], [ %rdx.next, %for.body ]
  %arrayidx = getelementptr inbounds i8, ptr %b, i32 %iv
  store i8 0, ptr %arrayidx, align 1
  %rdx.next = add nsw i32 %rdx, %conv1
  %iv.next = add nuw nsw i32 %iv, 1
  %cmp = icmp eq i32 %iv.next, 1024
  br i1 %cmp, label %exit, label %for.body

exit:                                             ; preds = %for.body
  %add.lcssa = phi i32 [ %rdx.next, %for.body ]
  ret i32 %add.lcssa
}

attributes #0 = { vscale_range(1,16) "target-features"="+sve" }

test_name: add_of_loop_invariant_zext
test_body: target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-none-unknown-elf"

; Function Attrs: vscale_range(1,16)
define i32 @add_of_loop_invariant_zext(i32 %a, ptr %b, i8 %c, i32 %d) #0 {
entry:
  br label %for.body

for.body:                                         ; preds = %for.body, %entry
  %iv = phi i32 [ %d, %entry ], [ %iv.next, %for.body ]
  %rdx = phi i32 [ %a, %entry ], [ %rdx.next, %for.body ]
  %arrayidx = getelementptr inbounds i8, ptr %b, i32 %iv
  store i8 0, ptr %arrayidx, align 1
  %conv1 = zext i8 %c to i32
  %rdx.next = add nsw i32 %rdx, %conv1
  %iv.next = add nuw nsw i32 %iv, 1
  %cmp = icmp eq i32 %iv.next, 1024
  br i1 %cmp, label %exit, label %for.body

exit:                                             ; preds = %for.body
  %add.lcssa = phi i32 [ %rdx.next, %for.body ]
  ret i32 %add.lcssa
}

attributes #0 = { vscale_range(1,16) "target-features"="+sve" }