[llvm] [SLP] Bail out on store-to-load forwarding hazards (PR #199606)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 4 03:52:02 PDT 2026


================
@@ -27384,6 +27395,115 @@ bool SLPVectorizerPass::runImpl(Function &F, ScalarEvolution *SE_,
   return Changed;
 }
 
+bool SLPVectorizerPass::hasStoreLoadForwardingConflict(ArrayRef<Value *> Chain,
+                                                       unsigned VF) {
+  if (Chain.empty() || !LAIs)
+    return false;
+
+  auto *FirstStore = cast<StoreInst>(Chain[0]);
+  Loop *L = LI->getLoopFor(FirstStore->getParent());
+  if (!L) {
+    // STLF stalls only matter inside loops where the store buffer stays hot
+    // across iterations.
+    LLVM_DEBUG(dbgs() << "SLP: STLF check: stores not in a loop, skipping\n");
+    return false;
+  }
+
+  Type *ValueTy = FirstStore->getValueOperand()->getType();
+  TypeSize StoreSize = DL->getTypeStoreSize(ValueTy);
+  // SLP only handles fixed-width vectors; bail out on scalable types.
+  if (StoreSize.isScalable())
+    return false;
+  uint64_t ElementSize = StoreSize.getFixedValue();
+  if (ElementSize == 0)
+    return false;
+  uint64_t VectorStoreBytes = uint64_t(VF) * ElementSize;
+  LLVM_DEBUG(dbgs() << "SLP: STLF check: VF=" << VF
+                    << " ElementSize=" << ElementSize
+                    << " VectorStoreBytes=" << VectorStoreBytes << "\n");
+
+  // Build a quick lookup of the chain's stores so we can pick out only the
+  // dependences that actually involve this chain. LAA computes a single
+  // per-loop store-load forwarding cap that is the minimum across *every*
+  // dep in the loop, which over-restricts when the loop contains multiple
+  // independent SLP chains. Iterating per-dep keeps us chain-granular.
+  SmallPtrSet<const Instruction *, 8> ChainStores;
+  for (Value *V : Chain)
+    if (auto *I = dyn_cast<Instruction>(V))
+      ChainStores.insert(I);
+  if (ChainStores.empty())
+    return false;
+
+  // LoopAccessInfo is computed lazily and cached per loop, so subsequent
+  // queries for chains in the same loop are O(1) (same machinery the loop
+  // vectorizer consumes).
+  const LoopAccessInfo &LAI = LAIs->getInfo(*L);
+  const MemoryDepChecker &Dep = LAI.getDepChecker();
+  const auto *Deps = Dep.getDependences();
+  if (!Deps) {
+    // LAA bailed (volatile load, indirect access, exceeded MaxDependences,
+    // ...). Be conservative and don't reject vectorization on the SLP side.
+    LLVM_DEBUG(dbgs() << "SLP: STLF: no LAA dependence list available\n");
+    return false;
+  }
+
+  // Avoid repeating the predicate work for loads that LAA reports against
+  // multiple chain stores in turn.
+  SmallPtrSet<const LoadInst *, 8> SeenLoads;
+  for (const MemoryDepChecker::Dependence &D : *Deps) {
+    Instruction *Src = D.getSource(Dep);
+    Instruction *Dst = D.getDestination(Dep);
+
+    // Only inspect deps where one end is a store from this chain and the
+    // other end is a (simple) load.
+    bool SrcInChain = ChainStores.contains(Src);
+    bool DstInChain = ChainStores.contains(Dst);
+    if (!SrcInChain && !DstInChain)
+      continue;
+    auto *LoadI = dyn_cast<LoadInst>(SrcInChain ? Dst : Src);
+    if (!LoadI || !LoadI->isSimple() || !SeenLoads.insert(LoadI).second)
+      continue;
+
+    // Compute distance from the chain's leading edge (Chain[0]) rather than
+    // the specific store LAA paired with the load: LAA may pick any chain
+    // store as the dependence representative, but the predicate cares about
+    // the chain window's start.
+    std::optional<int64_t> Diff =
+        getPointersDiff(ValueTy, FirstStore->getPointerOperand(),
+                        LoadI->getType(), LoadI->getPointerOperand(), *DL, *SE,
+                        /*StrictCheck=*/false, /*CheckType=*/false);
----------------
mbhade-amd wrote:

Thanks @alexey-bataev. Working on the fix.

https://github.com/llvm/llvm-project/pull/199606


More information about the llvm-commits mailing list