[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