[llvm] [SLP] Split blocking build-vector stores in scalar chains (PR #194970)
Yaxun Liu via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 30 06:57:50 PDT 2026
================
@@ -27270,6 +27270,90 @@ void SLPVectorizerPass::collectSeedInstructions(BasicBlock *BB) {
Stores.clear();
GEPs.clear();
+ SmallVector<StoreInst *> StoresToErase;
+ // Recognize a vector value built lane-by-lane with insertelement:
+ //
+ // %v0 = insertelement <4 x float> poison, float %a, i32 0
+ // %v1 = insertelement <4 x float> %v0, float %b, i32 1
+ //
+ // Such a value can be split back into the scalar lane values without changing
+ // semantics. Do not handle shuffles or partial vectors here; those need the
+ // normal SLP cost model.
+ auto CollectBuildVector = [](Value *V, SmallVectorImpl<Value *> &Elts) {
+ 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);
+ Cur = IE->getOperand(0);
+ }
+ if (!isa<PoisonValue, UndefValue>(Cur))
+ return false;
+ return all_of(Elts, [](Value *Elt) { return Elt != nullptr; });
+ };
+
+ for (Instruction &I : make_early_inc_range(*BB)) {
+ auto *SI = dyn_cast<StoreInst>(&I);
+ if (!SI || !SI->isSimple())
+ continue;
+
+ SmallVector<Value *, 16> BuildVectorOpds;
+ if (!CollectBuildVector(SI->getValueOperand(), BuildVectorOpds))
+ continue;
+
+ Type *EltTy = BuildVectorOpds.front()->getType();
+ Value *UnderlyingObject = getUnderlyingObject(SI->getPointerOperand());
+ bool HasScalarBefore = false;
+ bool HasScalarAfter = false;
+ for (Instruction &J : *BB) {
+ auto *OtherSI = dyn_cast<StoreInst>(&J);
+ if (!OtherSI || OtherSI == SI || !OtherSI->isSimple() ||
+ OtherSI->getValueOperand()->getType() != EltTy ||
+ getUnderlyingObject(OtherSI->getPointerOperand()) != UnderlyingObject)
+ continue;
+
+ std::optional<int64_t> Diff =
+ getPointersDiff(EltTy, SI->getPointerOperand(), EltTy,
+ OtherSI->getPointerOperand(), *DL, *SE,
+ /*StrictCheck=*/true);
+ if (!Diff)
+ continue;
+ HasScalarBefore |= *Diff < 0;
+ HasScalarAfter |= *Diff >= static_cast<int64_t>(BuildVectorOpds.size());
+ }
+ if (!HasScalarBefore || !HasScalarAfter)
+ continue;
+
+ // An existing vector store can hide a better scalar store chain from SLP.
+ // For example:
+ //
+ // store float %a, ptr %p
+ // store <4 x float> %v, ptr %p+4
+ // store float %b, ptr %p+20
+ //
+ // SLP collects scalar stores as seeds, so the middle vector store blocks it
+ // from seeing one continuous store range. Split only when scalar stores to
+ // the same object appear on both sides; the later store-chain vectorizer
+ // can then decide the final profitable vector layout.
+ IRBuilder<> Builder(SI);
+ uint64_t EltSize = DL->getTypeStoreSize(EltTy).getFixedValue();
+ Type *I8Ty = Type::getInt8Ty(SI->getContext());
+ for (auto [Idx, V] : enumerate(BuildVectorOpds)) {
+ Value *Ptr = Builder.CreateConstGEP1_64(I8Ty, SI->getPointerOperand(),
+ Idx * EltSize);
+ Builder.CreateAlignedStore(
----------------
yxsamliu wrote:
Thanks, that makes sense. I reworked this to model the build-vector store inside SLP instead of creating temporary scalar store IR.
https://github.com/llvm/llvm-project/pull/194970
More information about the llvm-commits
mailing list