[llvm-branch-commits] [llvm] [NFC][VPlan] Split `makeMemOpWideningDecisions` into subpasses (PR #182593)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Feb 20 13:20:16 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-vectorizers

Author: Andrei Elovikov (eas)

<details>
<summary>Changes</summary>

The idea is to have handling of strided memory operations (either from https://github.com/llvm/llvm-project/pull/147297 or for VPlan-based multiversioning for unit-strided accesses) done after some mandatory processing has been performed (e.g., some types **must** be scalarized) but before legacy CM's decision to widen (gather/scatter) or scalarize has been committed.

And in longer term, we can uplift all other memory widening decision to be done here directly at VPlan level. I expect this structure would also be beneficial for that.

Stacked on top of https://github.com/llvm/llvm-project/pull/182592.

---
Full diff: https://github.com/llvm/llvm-project/pull/182593.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/LoopVectorize.cpp (+83-38) 
- (modified) llvm/test/Transforms/LoopVectorize/VPlan/vplan-print-after-all.ll (+3) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index c0694ebcad464..5ea9fa7ac3288 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -10042,48 +10042,93 @@ void VPlanTransforms::makeMemOpWideningDecisions(VPlan &Plan, VFRange &Range,
 
   auto *Legal = CostCtx.CM.Legal;
 
-  auto *MiddleVPBB = Plan.getMiddleBlock();
-  VPBasicBlock::iterator MBIP = MiddleVPBB->getFirstNonPhi();
+  // Few helpers to process different kinds of memory operations.
 
-  for (VPInstruction *VPI : MemOps) {
-    Instruction *Instr = cast<Instruction>(VPI->getUnderlyingValue());
-    RecipeBuilder.getVPBuilder().setInsertPoint(VPI);
-
-    auto ReplaceWith = [&](VPRecipeBase *New) {
-      RecipeBuilder.setRecipe(Instr, New);
-      RecipeBuilder.getVPBuilder().insert(New);
-      if (VPI->getOpcode() == Instruction::Load)
-        VPI->replaceAllUsesWith(New->getVPSingleValue());
-      VPI->eraseFromParent();
-    };
+  // To be used as argument to `VPlanTransforms::runPass` which explicitly
+  // specified pass name, hence `VPlan &` parameter.
+  auto ProcessSubset = [&](VPlan &, auto ProcessVPInst) {
+    SmallVector<VPInstruction *> RemainingMemOps;
+    for (VPInstruction *VPI : MemOps) {
+      RecipeBuilder.getVPBuilder().setInsertPoint(VPI);
 
-    // The stores with invariant address inside the loop will be deleted, and
-    // in the exit block, a uniform store recipe will be created for the final
-    // invariant store of the reduction.
-    StoreInst *SI;
-    if ((SI = dyn_cast<StoreInst>(Instr)) &&
-        Legal->isInvariantAddressOfReduction(SI->getPointerOperand())) {
-      // Only create recipe for the final invariant store of the reduction.
-      if (Legal->isInvariantStoreOfReduction(SI)) {
-        auto *Recipe = new VPReplicateRecipe(
-            SI, VPI->operandsWithoutMask(), true /* IsUniform */,
-            nullptr /*Mask*/, *VPI, *VPI, VPI->getDebugLoc());
-        Recipe->insertBefore(*MiddleVPBB, MBIP);
-      }
-      VPI->eraseFromParent();
-      continue;
+      if (!ProcessVPInst(VPI))
+        RemainingMemOps.push_back(VPI);
     }
 
-    if (VPI->getOpcode() == Instruction::Store)
-      if (auto HistInfo = Legal->getHistogramInfo(cast<StoreInst>(Instr))) {
-        ReplaceWith(RecipeBuilder.tryToWidenHistogram(*HistInfo, VPI));
-        continue;
-      }
+    MemOps.clear();
+    std::swap(MemOps, RemainingMemOps);
+  };
+
+  auto ReplaceWith = [&](VPInstruction *VPI, VPRecipeBase *New) {
+    Instruction *Instr = cast<Instruction>(VPI->getUnderlyingValue());
+    RecipeBuilder.setRecipe(Instr, New);
+    RecipeBuilder.getVPBuilder().insert(New);
+    if (VPI->getOpcode() == Instruction::Load)
+      VPI->replaceAllUsesWith(New->getVPSingleValue());
+    VPI->eraseFromParent();
 
-    VPRecipeBase *Recipe = RecipeBuilder.tryToWidenMemory(VPI, Range);
-    if (!Recipe)
-      Recipe = RecipeBuilder.handleReplication(cast<VPInstruction>(VPI), Range);
+    return true;
+  };
 
-    ReplaceWith(Recipe);
-  }
+  auto Scalarize = [&](VPInstruction *VPI) {
+    return ReplaceWith(VPI, RecipeBuilder.handleReplication(VPI, Range));
+  };
+
+  VPlanTransforms::runPass(
+      "lowerMemoryIdioms", ProcessSubset, Plan,
+      // Reduction stores need to happen in the same order, so MBIP shares state
+      // between iterations, hence mutable lambda.
+      [&, MBIP = Plan.getMiddleBlock()->getFirstNonPhi()](
+          VPInstruction *VPI) mutable {
+        Instruction *Instr = cast<Instruction>(VPI->getUnderlyingValue());
+
+        // The stores with invariant address inside the loop will be deleted,
+        // and in the exit block, a uniform store recipe will be created for
+        // the final invariant store of the reduction.
+        StoreInst *SI = dyn_cast<StoreInst>(Instr);
+        if (!SI)
+          return false;
+        if (Legal->isInvariantAddressOfReduction(SI->getPointerOperand())) {
+          // Only create recipe for the final invariant store of the
+          // reduction.
+          if (Legal->isInvariantStoreOfReduction(SI)) {
+            auto *Recipe = new VPReplicateRecipe(
+                SI, VPI->operandsWithoutMask(), true /* IsUniform */,
+                nullptr /*Mask*/, *VPI, *VPI, VPI->getDebugLoc());
+            Recipe->insertBefore(*Plan.getMiddleBlock(), MBIP);
+          }
+          VPI->eraseFromParent();
+          return true;
+        }
+
+        if (auto HistInfo = Legal->getHistogramInfo(cast<StoreInst>(Instr))) {
+          return ReplaceWith(VPI,
+                             RecipeBuilder.tryToWidenHistogram(*HistInfo, VPI));
+        }
+
+        return false;
+      });
+
+  // If the instruction's allocated size doesn't equal it's type size, it
+  // requires padding and will be scalarized.
+  VPlanTransforms::runPass(
+      "scalarizeMemOpsWithIrregularTypes", ProcessSubset, Plan,
+      [&](VPInstruction *VPI) {
+        Instruction *I = VPI->getUnderlyingInstr();
+        if (hasIrregularType(getLoadStoreType(I), I->getDataLayout()))
+          return Scalarize(VPI);
+
+        return false;
+      });
+
+  VPlanTransforms::runPass("delegateMemOpWideningToLegacyCM", ProcessSubset,
+                           Plan, [&](VPInstruction *VPI) {
+                             VPRecipeBase *Recipe =
+                                 RecipeBuilder.tryToWidenMemory(VPI, Range);
+                             if (!Recipe)
+                               Recipe = RecipeBuilder.handleReplication(
+                                   cast<VPInstruction>(VPI), Range);
+
+                             return ReplaceWith(VPI, Recipe);
+                           });
 }
diff --git a/llvm/test/Transforms/LoopVectorize/VPlan/vplan-print-after-all.ll b/llvm/test/Transforms/LoopVectorize/VPlan/vplan-print-after-all.ll
index 8617788c90584..706d91260a70e 100644
--- a/llvm/test/Transforms/LoopVectorize/VPlan/vplan-print-after-all.ll
+++ b/llvm/test/Transforms/LoopVectorize/VPlan/vplan-print-after-all.ll
@@ -5,6 +5,9 @@
 
 ; CHECK: VPlan for loop in 'foo' after printAfterInitialConstruction
 ; CHECK: VPlan for loop in 'foo' after VPlanTransforms::introduceMasksAndLinearize
+; CHECK: VPlan for loop in 'foo' after lowerMemoryIdioms
+; CHECK: VPlan for loop in 'foo' after scalarizeMemOpsWithIrregularTypes
+; CHECK: VPlan for loop in 'foo' after delegateMemOpWideningToLegacyCM
 ; CHECK: VPlan for loop in 'foo' after VPlanTransforms::makeMemOpWideningDecisions
 ; CHECK: VPlan for loop in 'foo' after VPlanTransforms::clearReductionWrapFlags
 ; CHECK: VPlan for loop in 'foo' after VPlanTransforms::optimizeFindIVReductions

``````````

</details>


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


More information about the llvm-branch-commits mailing list