[llvm] [SLP] Split blocking build-vector stores in scalar chains (PR #194970)

Alexey Bataev via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 30 11:52:04 PDT 2026


================
@@ -27265,6 +27267,183 @@ bool SLPVectorizerPass::vectorizeStores(
   return Changed;
 }
 
+static bool collectBuildVector(Value *V, SmallVectorImpl<Value *> &Elts,
+                               SmallVectorImpl<Instruction *> *Insts = nullptr) {
+  auto *VecTy = dyn_cast<FixedVectorType>(V->getType());
+  if (!VecTy)
+    return false;
+  Elts.assign(VecTy->getNumElements(), nullptr);
+  Value *Cur = V;
+  while (auto *IE = dyn_cast<InsertElementInst>(Cur)) {
+    auto *Idx = dyn_cast<ConstantInt>(IE->getOperand(2));
+    if (!Idx || Idx->getValue().uge(Elts.size()))
+      return false;
+    Elts[Idx->getZExtValue()] = IE->getOperand(1);
+    if (Insts)
+      Insts->push_back(IE);
+    Cur = IE->getOperand(0);
+  }
+  if (!isa<PoisonValue, UndefValue>(Cur))
+    return false;
+  return all_of(Elts, [](Value *Elt) { return Elt != nullptr; });
+}
+
+/// Retile a scalar-store / build-vector-store / scalar-store sequence without
+/// first materializing scalar stores in the IR. The build-vector store is
+/// treated as covering its lane range, and BoUpSLP decides whether each final
+/// full-width store group is profitable.
+bool SLPVectorizerPass::vectorizeBuildVectorStoreChains(BasicBlock *BB,
+                                                        BoUpSLP &R) {
+  bool Changed = false;
+  for (Instruction &I : make_early_inc_range(*BB)) {
+    auto *VecStore = dyn_cast<StoreInst>(&I);
+    if (!VecStore || !VecStore->isSimple() || R.isDeleted(VecStore))
+      continue;
+
+    SmallVector<Value *, 16> VecElts;
+    SmallVector<Instruction *, 16> BuildVectorInsts;
+    if (!collectBuildVector(VecStore->getValueOperand(), VecElts,
+                            &BuildVectorInsts))
+      continue;
+
+    Type *EltTy = VecElts.front()->getType();
+    TypeSize EltSize = DL->getTypeStoreSize(EltTy);
+    if (EltSize.isScalable())
+      continue;
+
+    unsigned VF = VecElts.size();
+    Value *UnderlyingObject = getUnderlyingObject(VecStore->getPointerOperand());
+    MapVector<int64_t, std::pair<Value *, StoreInst *>> Lanes;
+    for (auto [Idx, V] : enumerate(VecElts))
+      Lanes[Idx] = {V, VecStore};
+
+    for (Instruction &J : *BB) {
+      auto *SI = dyn_cast<StoreInst>(&J);
+      if (!SI || SI == VecStore || !SI->isSimple() || R.isDeleted(SI) ||
+          SI->getValueOperand()->getType() != EltTy ||
+          getUnderlyingObject(SI->getPointerOperand()) != UnderlyingObject)
+        continue;
+
+      std::optional<int64_t> Diff =
+          getPointersDiff(EltTy, VecStore->getPointerOperand(), EltTy,
+                          SI->getPointerOperand(), *DL, *SE,
+                          /*StrictCheck=*/true);
+      if (!Diff || (*Diff >= 0 && *Diff < static_cast<int64_t>(VF)))
+        continue;
+      Lanes[*Diff] = {SI->getValueOperand(), SI};
+    }
+
+    int64_t MinOffset = Lanes.front().first;
+    int64_t MaxOffset = Lanes.front().first;
+    for (auto &Lane : Lanes) {
+      MinOffset = std::min(MinOffset, Lane.first);
+      MaxOffset = std::max(MaxOffset, Lane.first);
+    }
+    if (Lanes.size() <= VF || MinOffset >= 0 ||
+        MaxOffset < static_cast<int64_t>(VF))
+      continue;
+    if (static_cast<unsigned>(MaxOffset - MinOffset + 1) != Lanes.size() ||
+        Lanes.size() % VF != 0)
+      continue;
+
+    SmallVector<Instruction *> StoresToReplace;
+    for (auto &Lane : Lanes)
+      StoresToReplace.push_back(Lane.second.second);
+    sort(StoresToReplace);
+    StoresToReplace.erase(llvm::unique(StoresToReplace), StoresToReplace.end());
+
+    Instruction *FirstStore = StoresToReplace.front();
+    Instruction *LastStore = StoresToReplace.front();
+    for (Instruction *SI : StoresToReplace) {
+      if (SI->comesBefore(FirstStore))
+        FirstStore = SI;
+      if (LastStore->comesBefore(SI))
+        LastStore = SI;
+    }
+
+    bool SawFirst = false;
+    bool SafeToMove = true;
+    for (Instruction &J : *BB) {
+      if (&J == FirstStore)
+        SawFirst = true;
+      if (!SawFirst)
+        continue;
+      if (J.mayReadOrWriteMemory() && !is_contained(StoresToReplace, &J)) {
+        SafeToMove = false;
+        break;
+      }
+      if (&J == LastStore)
+        break;
+    }
+    if (!SafeToMove)
+      continue;
----------------
alexey-bataev wrote:

It is against SLP design. It does not perform any transformations before the modelling/estimation.

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


More information about the llvm-commits mailing list