[llvm] [VPlan] Prove inferability of Replicate::IsSingleScalar (NFCI) (PR #194869)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 07:14:56 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Ramkumar Ramachandra (artagnon)

<details>
<summary>Changes</summary>

When making widening decisions, the only leaf-cases should be live-ins or recipes that are always single-scalar. Unfortunately, the IsSingleScalar boolean in Replicate recipes is also currently a leaf case that is unnecessarily hard-coded. To show how it can be inferred, flip the boolean when only first lane is used under no side-effects, showing no test changes. This proves that IsSingleScalar in Replicates can be inferred, and follow-ups will attempt to strip it.

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


2 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/VPlan.h (+1-1) 
- (modified) llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp (+36-34) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 934cca006e91c..cfd48175c8a40 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3235,7 +3235,7 @@ class LLVM_ABI_FOR_TEST VPReplicateRecipe : public VPRecipeWithIRFlags,
                               VPCostContext &Ctx) const override;
 
   bool isSingleScalar() const { return IsSingleScalar; }
-
+  void markSingleScalar() { IsSingleScalar = true; }
   bool isPredicated() const { return IsPredicated; }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index f2c5cefa0ba57..7af3503d295b2 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -1807,34 +1807,37 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
   for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
            vp_depth_first_shallow(Plan.getVectorLoopRegion()->getEntry()))) {
     for (VPRecipeBase &R : make_early_inc_range(reverse(*VPBB))) {
-      if (!isa<VPWidenRecipe, VPWidenGEPRecipe, VPReplicateRecipe>(&R))
-        continue;
-      auto *RepR = dyn_cast<VPReplicateRecipe>(&R);
-      if (RepR && (RepR->isSingleScalar() || RepR->isPredicated()))
-        continue;
-
-      auto *RepOrWidenR = cast<VPRecipeWithIRFlags>(&R);
-      if (RepR && RepR->getOpcode() == Instruction::Store &&
-          vputils::isSingleScalar(RepR->getOperand(1))) {
-        auto *Clone = new VPReplicateRecipe(
-            RepOrWidenR->getUnderlyingInstr(), RepOrWidenR->operands(),
-            true /*IsSingleScalar*/, nullptr /*Mask*/, *RepR /*Flags*/,
-            *RepR /*Metadata*/, RepR->getDebugLoc());
-        Clone->insertBefore(RepOrWidenR);
-        VPBuilder Builder(Clone);
-        VPValue *ExtractOp = Clone->getOperand(0);
-        if (vputils::isUniformAcrossVFsAndUFs(RepR->getOperand(1)))
+      if (auto *RepR = dyn_cast<VPReplicateRecipe>(&R)) {
+        if (RepR->isPredicated())
+          continue;
+        if (RepR->getOpcode() == Instruction::Store &&
+            vputils::isSingleScalar(RepR->getOperand(1))) {
+          auto *Clone = new VPReplicateRecipe(
+              RepR->getUnderlyingInstr(), RepR->operands(),
+              true /*IsSingleScalar*/, nullptr /*Mask*/, *RepR /*Flags*/,
+              *RepR /*Metadata*/, RepR->getDebugLoc());
+          Clone->insertBefore(RepR);
+          VPBuilder Builder(Clone);
+          VPValue *ExtractOp = Clone->getOperand(0);
+          if (vputils::isUniformAcrossVFsAndUFs(RepR->getOperand(1)))
+            ExtractOp =
+                Builder.createNaryOp(VPInstruction::ExtractLastPart, ExtractOp);
           ExtractOp =
-              Builder.createNaryOp(VPInstruction::ExtractLastPart, ExtractOp);
-        ExtractOp =
-            Builder.createNaryOp(VPInstruction::ExtractLastLane, ExtractOp);
-        Clone->setOperand(0, ExtractOp);
-        RepR->eraseFromParent();
+              Builder.createNaryOp(VPInstruction::ExtractLastLane, ExtractOp);
+          Clone->setOperand(0, ExtractOp);
+          RepR->eraseFromParent();
+        } else if (!RepR->mayHaveSideEffects() &&
+                   vputils::onlyFirstLaneUsed(RepR))
+          RepR->markSingleScalar();
         continue;
       }
 
+      if (!isa<VPWidenRecipe, VPWidenGEPRecipe>(&R))
+        continue;
+      auto *WidenR = cast<VPRecipeWithIRFlags>(&R);
+
       // Skip recipes that aren't single scalars.
-      if (!vputils::isSingleScalar(RepOrWidenR))
+      if (!vputils::isSingleScalar(WidenR))
         continue;
 
       // Predicate to check if a user of Op introduces extra broadcasts.
@@ -1851,11 +1854,10 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
         };
       };
 
-      if (any_of(RepOrWidenR->users(), IntroducesBCastOf(RepOrWidenR)) &&
-          none_of(RepOrWidenR->operands(), [&](VPValue *Op) {
-            if (any_of(
-                    make_filter_range(Op->users(), not_equal_to(RepOrWidenR)),
-                    IntroducesBCastOf(Op)))
+      if (any_of(WidenR->users(), IntroducesBCastOf(WidenR)) &&
+          none_of(WidenR->operands(), [&](VPValue *Op) {
+            if (any_of(make_filter_range(Op->users(), not_equal_to(WidenR)),
+                       IntroducesBCastOf(Op)))
               return false;
             // Non-constant live-ins require broadcasts, while constants do not
             // need explicit broadcasts.
@@ -1867,12 +1869,12 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
         continue;
 
       auto *Clone = new VPReplicateRecipe(
-          RepOrWidenR->getUnderlyingInstr(), RepOrWidenR->operands(),
-          true /*IsSingleScalar*/, nullptr, *RepOrWidenR);
-      Clone->insertBefore(RepOrWidenR);
-      RepOrWidenR->replaceAllUsesWith(Clone);
-      if (isDeadRecipe(*RepOrWidenR))
-        RepOrWidenR->eraseFromParent();
+          WidenR->getUnderlyingInstr(), WidenR->operands(),
+          true /*IsSingleScalar*/, nullptr, *WidenR);
+      Clone->insertBefore(WidenR);
+      WidenR->replaceAllUsesWith(Clone);
+      if (isDeadRecipe(*WidenR))
+        WidenR->eraseFromParent();
     }
   }
 }

``````````

</details>


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


More information about the llvm-commits mailing list