Bug type: miscompilation
Component: LoopVectorize
Bug location: llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp: LoopVectorizationLegality::canVectorizeWithIfConvert
Issue title: loop-vectorize miscompile
Labels: miscompilation
vectorizers
Reported by: mikaelholmen

llvm commit: 6955a7d134e7
Reproduce with:
```
opt -passes="loop-vectorize" bbi-107525_lv.ll -S -o - -mtriple=aarch64-none-linux-gnu --data-layout="p:16:16"
```
The loop-vectorize output contains
```
pred.sdiv.continue:                               ; preds = %pred.sdiv.if, %vector.body
  %3 = phi i16 [ poison, %vector.body ], [ %2, %pred.sdiv.if ]
  [...]

pred.sdiv.continue2:                              ; preds = %pred.sdiv.if1, %pred.sdiv.continue
  [...]
  %7 = shl i16 %3, 14
  %8 = sub i16 %6, %7
  %9 = getelementptr [16 x i64], ptr @g_855, i16 0, i16 %8
  %10 = getelementptr i64, ptr %9, i32 0
  %wide.load = load <2 x i64>, ptr %10, align 1
```

and the problem here is that we will get to the %3 phi from %vector.body and then continue to pred.sdiv.continue2 where the %3 poison value will make %7, %8, %9 and %10 poison, and then we will do a load from %10 which is instant UB.

The input program has no UB.

Before 0d61ffd350
```
[Loads] Support SCEVAddExpr as start for pointer AddRec.
```
the result is ok. Then with 0d61ffd350 it crashes with
```
opt: ../include/llvm/Support/Casting.h:109: static bool llvm::isa_impl_cl<llvm::Instruction, const llvm::Value *>::doit(const From *) [To = llvm::Instruction, From = const llvm::Value *]: Assertion `Val && "isa<> used on a null pointer"' failed.
```
and that crash goes away with fd82b5b287
```
[LV] Support recieps without underlying instr in collectPoisonGenRec.

Support recipes without underlying instruction in
collectPoisonGeneratingRecipes by directly trying to dyn_cast_or_null
the underlying value.

Fixes https://github.com/llvm/llvm-project/issues/70590.
```
but then we get the miscompile instead.

I also tested to cherry-pick the fd82b5b287 to directly after 0d61ffd350 and we get the miscompile then too, so it doesn't seem to be some other patch between them that causes the miscompile.

[bbi-107525_lv.ll.gz](https://github.com/user-attachments/files/20610502/bbi-107525_lv.ll.gz)

Comments: author: fhahn
body: Put up a potential fix: https://github.com/llvm/llvm-project/pull/143204

Fix patch:
commit fd97dfbb78e3c9aea16873617b6d61b5b8a64474
Author: Florian Hahn <flo@fhahn.com>
Date:   Fri Jun 20 13:05:19 2025 +0100

    [LV] Don't mark ptrs as safe to speculate if fed by UB/poison op. (#143204)
    
    Add additional checks before marking pointers safe to load
    speculatively. If some computations feeding the pointer may trigger UB,
    we cannot load the pointer speculatively, because we cannot compute the
    address speculatively. The UB triggering instructions will be
    predicated, but if the predicated block does not execute the result is
    poison.
    
    Similarly, we also cannot load the pointer speculatively if it may be
    poison. The patch also checks if any of the operands defined outside the
    loop may be poison when entering the loop. We *don't* need to check if
    any operation inside the loop may produce poison due to flags, as those
    will be dropped if needed.
    
    There are some types of instructions inside the loop that can produce
    poison independent of flags. Currently loads are also checked, not sure
    if there's a convenient API to check for all such operands.
    
    Fixes https://github.com/llvm/llvm-project/issues/142957.
    
    PR: https://github.com/llvm/llvm-project/pull/143204

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
index 0c4e5bb3d472..969d225c6ef2 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
@@ -1491,10 +1491,51 @@ bool LoopVectorizationLegality::canVectorizeWithIfConvert() {
     SmallVector<const SCEVPredicate *, 4> Predicates;
     for (Instruction &I : *BB) {
       LoadInst *LI = dyn_cast<LoadInst>(&I);
+
+      // Make sure we can execute all computations feeding into Ptr in the loop
+      // w/o triggering UB and that none of the out-of-loop operands are poison.
+      // We do not need to check if operations inside the loop can produce
+      // poison due to flags (e.g. due to an inbounds GEP going out of bounds),
+      // because flags will be dropped when executing them unconditionally.
+      // TODO: Results could be improved by considering poison-propagation
+      // properties of visited ops.
+      auto CanSpeculatePointerOp = [this](Value *Ptr) {
+        SmallVector<Value *> Worklist = {Ptr};
+        SmallPtrSet<Value *, 4> Visited;
+        while (!Worklist.empty()) {
+          Value *CurrV = Worklist.pop_back_val();
+          if (!Visited.insert(CurrV).second)
+            continue;
+
+          auto *CurrI = dyn_cast<Instruction>(CurrV);
+          if (!CurrI || !TheLoop->contains(CurrI)) {
+            // If operands from outside the loop may be poison then Ptr may also
+            // be poison.
+            if (!isGuaranteedNotToBePoison(CurrV, AC,
+                                           TheLoop->getLoopPredecessor()
+                                               ->getTerminator()
+                                               ->getIterator()))
+              return false;
+            continue;
+          }
+
+          // A loaded value may be poison, independent of any flags.
+          if (isa<LoadInst>(CurrI) && !isGuaranteedNotToBePoison(CurrV, AC))
+            return false;
+
+          // For other ops, assume poison can only be introduced via flags,
+          // which can be dropped.
+          if (!isa<PHINode>(CurrI) && !isSafeToSpeculativelyExecute(CurrI))
+            return false;
+          append_range(Worklist, CurrI->operands());
+        }
+        return true;
+      };
       // Pass the Predicates pointer to isDereferenceableAndAlignedInLoop so
       // that it will consider loops that need guarding by SCEV checks. The
       // vectoriser will generate these checks if we decide to vectorise.
       if (LI && !LI->getType()->isVectorTy() && !mustSuppressSpeculation(*LI) &&
+          CanSpeculatePointerOp(LI->getPointerOperand()) &&
           isDereferenceableAndAlignedInLoop(LI, TheLoop, SE, *DT, AC,
                                             &Predicates))
         SafePointers.insert(LI->getPointerOperand());


Regression tests:
file: llvm/test/Transforms/LoopVectorize/load-deref-pred-poison-ub-ops-feeding-pointer.ll
commands: opt -p loop-vectorize -force-widen-divrem-via-safe-divisor=false -force-vector-width=2 -S %s
tests: test_name: ptr_depends_on_sdiv
test_body: target datalayout = "p:16:16"

@src = external global [16 x i64]

define void @ptr_depends_on_sdiv(ptr noalias %dst, i16 noundef %off) {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i16 [ 9, %entry ], [ %iv.next, %loop.latch ]
  %cmp1 = icmp eq i16 %iv, 10
  br i1 %cmp1, label %if.then, label %loop.latch

if.then:                                          ; preds = %loop.header
  %div = sdiv i16 24316, %off
  %add.iv = add i16 %iv, 16383
  %s = shl i16 %div, 14
  %sub6 = sub i16 %add.iv, %s
  %gep.src = getelementptr inbounds i64, ptr @src, i16 %sub6
  %l = load i64, ptr %gep.src, align 1
  %gep.dst = getelementptr inbounds i64, ptr %dst, i16 %iv
  store i64 %l, ptr %gep.dst, align 1
  br label %loop.latch

loop.latch:                                       ; preds = %if.then, %loop.header
  %iv.next = add i16 %iv, 1
  %ec = icmp eq i16 %iv.next, 11
  br i1 %ec, label %exit, label %loop.header

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

additional_args: -src-unroll=16 -tgt-unroll=16