Skip to content

Conversation

@artagnon
Copy link
Contributor

@artagnon artagnon commented Oct 2, 2025

The idiom of taking a recipe and creating a single-scalar clone (often a replicate), is repeated over and over: introduce a VPUtils helper to factor out the common code.

@llvmbot
Copy link
Member

llvmbot commented Oct 2, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Ramkumar Ramachandra (artagnon)

Changes

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

3 Files Affected:

  • (modified) llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp (+6-22)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanUtils.cpp (+19)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanUtils.h (+4)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index acdb37996a443..2b3f4af8fa3ad 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -189,19 +189,7 @@ static bool sinkScalarOperands(VPlan &Plan) {
     if (NeedsDuplicating) {
       if (ScalarVFOnly)
         continue;
-      VPSingleDefRecipe *Clone;
-      if (auto *SinkCandidateRepR =
-              dyn_cast<VPReplicateRecipe>(SinkCandidate)) {
-        // TODO: Handle converting to uniform recipes as separate transform,
-        // then cloning should be sufficient here.
-        Instruction *I = SinkCandidate->getUnderlyingInstr();
-        Clone = new VPReplicateRecipe(I, SinkCandidate->operands(), true,
-                                      nullptr /*Mask*/, *SinkCandidateRepR);
-        // TODO: add ".cloned" suffix to name of Clone's VPValue.
-      } else {
-        Clone = SinkCandidate->clone();
-      }
-
+      VPSingleDefRecipe *Clone = vputils::getSingleScalarClone(SinkCandidate);
       Clone->insertBefore(SinkCandidate);
       SinkCandidate->replaceUsesWithIf(Clone, [SinkTo](VPUser &U, unsigned) {
         return cast<VPRecipeBase>(&U)->getParent() != SinkTo;
@@ -666,8 +654,7 @@ static void legalizeAndOptimizeInductions(VPlan &Plan) {
       if (!vputils::isSingleScalar(Def) && !vputils::onlyFirstLaneUsed(Def))
         continue;
 
-      auto *Clone = new VPReplicateRecipe(Def->getUnderlyingInstr(),
-                                          Def->operands(), /*IsUniform*/ true);
+      VPSingleDefRecipe *Clone = vputils::getSingleScalarClone(Def);
       Clone->insertAfter(Def);
       Def->replaceAllUsesWith(Clone);
     }
@@ -1309,15 +1296,14 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
       auto *RepOrWidenR = cast<VPSingleDefRecipe>(&R);
       if (RepR && isa<StoreInst>(RepR->getUnderlyingInstr()) &&
           vputils::isSingleScalar(RepR->getOperand(1))) {
-        auto *Clone = new VPReplicateRecipe(
-            RepOrWidenR->getUnderlyingInstr(), RepOrWidenR->operands(),
-            true /*IsSingleScalar*/, nullptr /*Mask*/, *RepR /*Metadata*/);
+        auto *Clone =
+            cast<VPReplicateRecipe>(vputils::getSingleScalarClone(RepOrWidenR));
         Clone->insertBefore(RepOrWidenR);
         auto *Ext = new VPInstruction(VPInstruction::ExtractLastElement,
                                       {Clone->getOperand(0)});
         Ext->insertBefore(Clone);
         Clone->setOperand(0, Ext);
-        RepR->eraseFromParent();
+        RepOrWidenR->eraseFromParent();
         continue;
       }
 
@@ -1332,9 +1318,7 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
           }))
         continue;
 
-      auto *Clone = new VPReplicateRecipe(RepOrWidenR->getUnderlyingInstr(),
-                                          RepOrWidenR->operands(),
-                                          true /*IsSingleScalar*/);
+      VPSingleDefRecipe *Clone = vputils::getSingleScalarClone(RepOrWidenR);
       Clone->insertBefore(RepOrWidenR);
       RepOrWidenR->replaceAllUsesWith(Clone);
     }
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 059993043dcda..8a59bfc7abfe0 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -250,3 +250,22 @@ vputils::getRecipesForUncountableExit(VPlan &Plan,
 
   return UncountableCondition;
 }
+
+VPSingleDefRecipe *vputils::getSingleScalarClone(VPSingleDefRecipe *R) {
+  return TypeSwitch<VPSingleDefRecipe *, VPSingleDefRecipe *>(R)
+      .Case<VPInstruction, VPWidenRecipe, VPWidenCastRecipe,
+            VPWidenSelectRecipe, VPWidenCallRecipe, VPReplicateRecipe>(
+          [](auto *I) {
+            return new VPReplicateRecipe(I->getUnderlyingInstr(), I->operands(),
+                                         /*IsSingleScalar*/ true,
+                                         /*Mask*/ nullptr,
+                                         /*Metadata*/ *I);
+          })
+      .Case<VPWidenGEPRecipe>([](auto *I) {
+        // WidenGEP does not have metadata.
+        return new VPReplicateRecipe(I->getUnderlyingInstr(), I->operands(),
+                                     /*IsSingleScalar*/ true, /*Mask*/ nullptr);
+      })
+      .Case<VPScalarIVStepsRecipe>([](auto *I) { return I->clone(); })
+      .Default([](auto *I) { return nullptr; });
+}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 0222b0aa81063..4979ade0c2764 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -116,6 +116,10 @@ std::optional<VPValue *>
 getRecipesForUncountableExit(VPlan &Plan,
                              SmallVectorImpl<VPRecipeBase *> &Recipes,
                              SmallVectorImpl<VPRecipeBase *> &GEPs);
+
+/// Returns a single-scalar version of \p R if possible, creating a fresh
+/// single-scalar VPReplicateRecipe or just cloning the recipe.
+VPSingleDefRecipe *getSingleScalarClone(VPSingleDefRecipe *R);
 } // namespace vputils
 
 //===----------------------------------------------------------------------===//

@llvmbot
Copy link
Member

llvmbot commented Oct 2, 2025

@llvm/pr-subscribers-vectorizers

Author: Ramkumar Ramachandra (artagnon)

Changes

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

3 Files Affected:

  • (modified) llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp (+6-22)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanUtils.cpp (+19)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanUtils.h (+4)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index acdb37996a443..2b3f4af8fa3ad 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -189,19 +189,7 @@ static bool sinkScalarOperands(VPlan &Plan) {
     if (NeedsDuplicating) {
       if (ScalarVFOnly)
         continue;
-      VPSingleDefRecipe *Clone;
-      if (auto *SinkCandidateRepR =
-              dyn_cast<VPReplicateRecipe>(SinkCandidate)) {
-        // TODO: Handle converting to uniform recipes as separate transform,
-        // then cloning should be sufficient here.
-        Instruction *I = SinkCandidate->getUnderlyingInstr();
-        Clone = new VPReplicateRecipe(I, SinkCandidate->operands(), true,
-                                      nullptr /*Mask*/, *SinkCandidateRepR);
-        // TODO: add ".cloned" suffix to name of Clone's VPValue.
-      } else {
-        Clone = SinkCandidate->clone();
-      }
-
+      VPSingleDefRecipe *Clone = vputils::getSingleScalarClone(SinkCandidate);
       Clone->insertBefore(SinkCandidate);
       SinkCandidate->replaceUsesWithIf(Clone, [SinkTo](VPUser &U, unsigned) {
         return cast<VPRecipeBase>(&U)->getParent() != SinkTo;
@@ -666,8 +654,7 @@ static void legalizeAndOptimizeInductions(VPlan &Plan) {
       if (!vputils::isSingleScalar(Def) && !vputils::onlyFirstLaneUsed(Def))
         continue;
 
-      auto *Clone = new VPReplicateRecipe(Def->getUnderlyingInstr(),
-                                          Def->operands(), /*IsUniform*/ true);
+      VPSingleDefRecipe *Clone = vputils::getSingleScalarClone(Def);
       Clone->insertAfter(Def);
       Def->replaceAllUsesWith(Clone);
     }
@@ -1309,15 +1296,14 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
       auto *RepOrWidenR = cast<VPSingleDefRecipe>(&R);
       if (RepR && isa<StoreInst>(RepR->getUnderlyingInstr()) &&
           vputils::isSingleScalar(RepR->getOperand(1))) {
-        auto *Clone = new VPReplicateRecipe(
-            RepOrWidenR->getUnderlyingInstr(), RepOrWidenR->operands(),
-            true /*IsSingleScalar*/, nullptr /*Mask*/, *RepR /*Metadata*/);
+        auto *Clone =
+            cast<VPReplicateRecipe>(vputils::getSingleScalarClone(RepOrWidenR));
         Clone->insertBefore(RepOrWidenR);
         auto *Ext = new VPInstruction(VPInstruction::ExtractLastElement,
                                       {Clone->getOperand(0)});
         Ext->insertBefore(Clone);
         Clone->setOperand(0, Ext);
-        RepR->eraseFromParent();
+        RepOrWidenR->eraseFromParent();
         continue;
       }
 
@@ -1332,9 +1318,7 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
           }))
         continue;
 
-      auto *Clone = new VPReplicateRecipe(RepOrWidenR->getUnderlyingInstr(),
-                                          RepOrWidenR->operands(),
-                                          true /*IsSingleScalar*/);
+      VPSingleDefRecipe *Clone = vputils::getSingleScalarClone(RepOrWidenR);
       Clone->insertBefore(RepOrWidenR);
       RepOrWidenR->replaceAllUsesWith(Clone);
     }
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 059993043dcda..8a59bfc7abfe0 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -250,3 +250,22 @@ vputils::getRecipesForUncountableExit(VPlan &Plan,
 
   return UncountableCondition;
 }
+
+VPSingleDefRecipe *vputils::getSingleScalarClone(VPSingleDefRecipe *R) {
+  return TypeSwitch<VPSingleDefRecipe *, VPSingleDefRecipe *>(R)
+      .Case<VPInstruction, VPWidenRecipe, VPWidenCastRecipe,
+            VPWidenSelectRecipe, VPWidenCallRecipe, VPReplicateRecipe>(
+          [](auto *I) {
+            return new VPReplicateRecipe(I->getUnderlyingInstr(), I->operands(),
+                                         /*IsSingleScalar*/ true,
+                                         /*Mask*/ nullptr,
+                                         /*Metadata*/ *I);
+          })
+      .Case<VPWidenGEPRecipe>([](auto *I) {
+        // WidenGEP does not have metadata.
+        return new VPReplicateRecipe(I->getUnderlyingInstr(), I->operands(),
+                                     /*IsSingleScalar*/ true, /*Mask*/ nullptr);
+      })
+      .Case<VPScalarIVStepsRecipe>([](auto *I) { return I->clone(); })
+      .Default([](auto *I) { return nullptr; });
+}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 0222b0aa81063..4979ade0c2764 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -116,6 +116,10 @@ std::optional<VPValue *>
 getRecipesForUncountableExit(VPlan &Plan,
                              SmallVectorImpl<VPRecipeBase *> &Recipes,
                              SmallVectorImpl<VPRecipeBase *> &GEPs);
+
+/// Returns a single-scalar version of \p R if possible, creating a fresh
+/// single-scalar VPReplicateRecipe or just cloning the recipe.
+VPSingleDefRecipe *getSingleScalarClone(VPSingleDefRecipe *R);
 } // namespace vputils
 
 //===----------------------------------------------------------------------===//

@artagnon
Copy link
Contributor Author

artagnon commented Oct 7, 2025

Gentle ping.

1 similar comment
@artagnon
Copy link
Contributor Author

Gentle ping.

@artagnon artagnon requested a review from lukel97 October 16, 2025 12:28
@artagnon
Copy link
Contributor Author

Gentle ping.

Copy link
Contributor

@david-arm david-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a brief description in the commit message please?

VPSingleDefRecipe *Clone;
if (auto *SinkCandidateRepR =
dyn_cast<VPReplicateRecipe>(SinkCandidate)) {
// TODO: Handle converting to uniform recipes as separate transform,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep the TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps not? I think we wanted to address it sometime in the past, but I don't think it's the case any longer?

Instruction *I = SinkCandidate->getUnderlyingInstr();
Clone = new VPReplicateRecipe(I, SinkCandidate->operands(), true,
nullptr /*Mask*/, *SinkCandidateRepR);
// TODO: add ".cloned" suffix to name of Clone's VPValue.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth adding the TODO to getSingleScalarClone?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that the constructor doesn't accept a Name field, so I'm not sure if we're going to resolve this: kept it for now.


VPSingleDefRecipe *vputils::getSingleScalarClone(VPSingleDefRecipe *R) {
return TypeSwitch<VPSingleDefRecipe *, VPSingleDefRecipe *>(R)
.Case<VPInstruction, VPWidenRecipe, VPWidenSelectRecipe,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about all the other VPSingleDefRecipe types, i.e. VPWidenPHIRecipe, VPBlendRecipe, VPExpressionRecipe, VPWidenCanonicalIVRecipe, etc? I'm a bit nervous that we might be missing some cases here. Have you tried running the LLVM test suite to catch any missing cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fine for the moment, as all the uses in the use-sites are covered: when we add more recipes to handle in the use-sites, we should update this (follow-ups planned).

@artagnon artagnon force-pushed the vplan-get-single-scalar-clone branch from c306a16 to 7d71949 Compare October 21, 2025 16:59
@artagnon artagnon force-pushed the vplan-get-single-scalar-clone branch from 7d71949 to 5a157dc Compare October 23, 2025 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants