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

Alexey Bataev via llvm-commits llvm-commits at lists.llvm.org
Fri May 1 22:03:03 PDT 2026


================
@@ -27302,20 +27240,137 @@ bool SLPVectorizerPass::vectorizeStores(
                         return A && (!B || A->getStride() < B->getStride());
                       });
 
+    auto TryVectorizeVectorLaneContext = [&](StoreChainContext &Context,
+                                             unsigned VF) {
+      ArrayRef<StoreLane> Lanes = Context.getOperands();
+      bool HasScalarLane = any_of(
+          Lanes, [](const StoreLane &Lane) { return !Lane.IsVectorLane; });
+      // Keep the initial support transactional: every lane must belong to a
+      // full output group, so no original vector store is partly replaced.
+      if (!Context.hasVectorLane() || !HasScalarLane || Lanes.size() % VF != 0)
+        return false;
+
+      // A vector store can cover lanes that land in several output groups. For
+      // example, p[3..6] may be replaced by new stores p[0..3] and p[4..7].
+      // Only erase the original stores after every affected group is
+      // profitable.
+      SmallVector<Instruction *> StoresToReplace;
+      SmallDenseSet<Value *> UserIgnoreList;
+      for (const StoreLane &Lane : Lanes) {
+        if (is_contained(StoresToReplace, Lane.Store))
+          continue;
+        StoresToReplace.push_back(Lane.Store);
+        UserIgnoreList.insert(Lane.Store);
+        if (!Lane.IsVectorLane)
+          continue;
+        SmallVector<Value *, 16> Elts;
+        SmallVector<Instruction *, 16> BuildVectorInsts;
+        if (!collectBuildVector(Lane.Store->getValueOperand(), Elts,
+                                BuildVectorInsts))
+          return false;
+        for (Instruction *I : BuildVectorInsts)
+          UserIgnoreList.insert(I);
+      }
+
+      TypeSize EltSize =
+          DL->getTypeStoreSize(Lanes.front().ScalarValue->getType());
+      if (EltSize.isScalable())
+        return false;
+      Type *I8Ty = Type::getInt8Ty(Lanes.front().Store->getContext());
+
+      auto TryBuildStoreValue = [&](ArrayRef<StoreLane> Group,
+                                    Value **VectorizedValue = nullptr) {
+        SmallVector<Value *, 4> Values;
+        for (const StoreLane &Lane : Group)
+          Values.push_back(Lane.ScalarValue);
+        R.buildTree(Values, UserIgnoreList, /*IsReduction=*/false);
+        if (R.isTreeTinyAndNotFullyVectorizable())
+          return false;
+        if (R.isProfitableToReorder()) {
+          R.reorderTopToBottom();
+          R.reorderBottomToTop();
+        }
+        R.transformNodes();
+        R.computeMinimumValueSizes();
+        InstructionCost TreeCost = R.calculateTreeCostAndTrimNonProfitable();
+        R.buildExternalUses();
+        InstructionCost Cost = R.getTreeCost(TreeCost);
+        if (Cost >= -SLPCostThreshold)
+          return false;
+        if (VectorizedValue)
+          *VectorizedValue = R.vectorizeTree();
+        return true;
+      };
+
+      for (unsigned I = 0, E = Lanes.size(); I != E; I += VF)
+        if (!TryBuildStoreValue(Lanes.slice(I, VF)))
+          return false;
+
+      for (unsigned I = 0, E = Lanes.size(); I != E; I += VF) {
+        ArrayRef<StoreLane> Group = Lanes.slice(I, VF);
+        Value *Vec = nullptr;
+        bool Profitable = TryBuildStoreValue(Group, &Vec);
+        assert(Profitable && Vec && "Expected profitable group from dry run");
+        (void)Profitable;
+
+        IRBuilder<> Builder(Group.front().Store);
+        Value *Ptr = Builder.CreateConstGEP1_64(
+            I8Ty, Group.front().Store->getPointerOperand(),
+            Group.front().Lane * EltSize.getFixedValue());
+        Align StoreAlign = Group.front().getAlignment(EltSize);
+        int64_t GroupOffset = Group.front().Offset;
+        for (const StoreLane &Lane : Group.drop_front()) {
+          Align LaneAlignAtGroupBase = commonAlignment(
+              Lane.getAlignment(EltSize),
+              (Lane.Offset - GroupOffset) * EltSize.getFixedValue());
+          StoreAlign = std::min(StoreAlign, LaneAlignAtGroupBase);
+        }
+        Builder.CreateAlignedStore(Vec, Ptr, StoreAlign);
+      }
+
+      for (Instruction *SI : StoresToReplace)
+        R.eraseInstruction(SI);
+      for (Value *V : UserIgnoreList)
+        if (auto *I = dyn_cast<Instruction>(V);
+            I && !R.isDeleted(I) && isInstructionTriviallyDead(I))
+          R.eraseInstruction(I);
+      VectorizedStores.insert_range(StoresToReplace);
+      Changed = true;
+      return true;
+    };
+
     for (unsigned LimitVF = GlobalMaxVF; LimitVF > 0;
          LimitVF = bit_ceil(LimitVF) / 2) {
       for (auto &CtxPtr : AllContexts) {
         if (!CtxPtr)
           break;
         StoreChainContext &Context = *CtxPtr;
+        if (Context.hasVectorLane()) {
+          if (std::optional<unsigned> VF = Context.getCurrentVF();
+              VF && *VF >= LimitVF) {
----------------
alexey-bataev wrote:

```suggestion
              VF.value_or(0) >= LimitVF) {
```

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


More information about the llvm-commits mailing list