[llvm] [VPlan] Add doesGenerateSingleScalalar and use where applicable. (NFCI) (PR #222447)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 9 14:01:33 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-vectorizers
Author: Florian Hahn (fhahn)
<details>
<summary>Changes</summary>
Similar to #<!-- -->199047, factor out the non-recursive logic from isSingleScalar to doesGenerateSingleScalar to determine single-scalar-ness encoded by the recipe, without recursion.
Use the cheaper, non-recursive variant at sites where the recursive result cannot differ or is not needed:
- VPCSEDenseMapInfo hash/equality: operands are compared separately, so the recursive classification is fully determined including operands in the hash
- materializePacksAndUnpacks, VPWidenGEPRecipe::usesFirstLaneOnly: narrowToSingleScalar already scalarizes all relevant cases explicitly.
Most other users cannot be converted yet without functional changes
---
Full diff: https://github.com/llvm/llvm-project/pull/222447.diff
5 Files Affected:
- (modified) llvm/lib/Transforms/Vectorize/VPlanLowering.cpp (+1-1)
- (modified) llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp (+1-1)
- (modified) llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp (+7-5)
- (modified) llvm/lib/Transforms/Vectorize/VPlanUtils.cpp (+23-12)
- (modified) llvm/lib/Transforms/Vectorize/VPlanUtils.h (+4)
``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/VPlanLowering.cpp b/llvm/lib/Transforms/Vectorize/VPlanLowering.cpp
index ddde0cd83a704..ce8f06c561b1b 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanLowering.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanLowering.cpp
@@ -851,7 +851,7 @@ void VPlanTransforms::materializePacksAndUnpacks(VPlan &Plan) {
// TODO: The Defs skipped here may or may not be vector values.
// Introduce Unpacks, and remove them later, if they are guaranteed to
// produce scalar values.
- if (vputils::isSingleScalar(Def))
+ if (vputils::doesGenerateSingleScalar(Def))
continue;
// Only introduce an Unpack if some, but not all, users use the first
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index c0beb2e825e52..7de75811944c4 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -3250,7 +3250,7 @@ void VPScalarIVStepsRecipe::printRecipe(raw_ostream &O, const Twine &Indent,
bool VPWidenGEPRecipe::usesFirstLaneOnly(const VPValue *Op) const {
assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
- return vputils::isSingleScalar(Op);
+ return vputils::doesGenerateSingleScalar(Op);
}
void VPWidenGEPRecipe::execute(VPTransformState &State) {
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index d12aec83f2856..8fe06568e2ac8 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -2264,10 +2264,11 @@ struct VPCSEDenseMapInfo : public DenseMapInfo<VPSingleDefRecipe *> {
/// Hash the underlying data of \p Def.
static unsigned getHashValue(const VPSingleDefRecipe *Def) {
- hash_code Result = hash_combine(
- Def->getVPRecipeID(), vputils::getOpcodeOrIntrinsicID(Def),
- getGEPSourceElementType(Def), Def->getScalarType(),
- vputils::isSingleScalar(Def), hash_combine_range(Def->operands()));
+ hash_code Result =
+ hash_combine(Def->getVPRecipeID(), vputils::getOpcodeOrIntrinsicID(Def),
+ getGEPSourceElementType(Def), Def->getScalarType(),
+ vputils::doesGenerateSingleScalar(Def),
+ hash_combine_range(Def->operands()));
if (auto *RFlags = dyn_cast<VPRecipeWithIRFlags>(Def))
if (RFlags->hasPredicate())
return hash_combine(Result, RFlags->getPredicate());
@@ -2286,7 +2287,8 @@ struct VPCSEDenseMapInfo : public DenseMapInfo<VPSingleDefRecipe *> {
vputils::getOpcodeOrIntrinsicID(L) !=
vputils::getOpcodeOrIntrinsicID(R) ||
getGEPSourceElementType(L) != getGEPSourceElementType(R) ||
- vputils::isSingleScalar(L) != vputils::isSingleScalar(R) ||
+ vputils::doesGenerateSingleScalar(L) !=
+ vputils::doesGenerateSingleScalar(R) ||
!equal(L->operands(), R->operands()))
return false;
assert(vputils::getOpcodeOrIntrinsicID(L) &&
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 8327b30c7583e..43c1eb41778ac 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -431,7 +431,7 @@ bool vputils::isElementwise(const VPValue *V) {
return Instruction::isUnaryOp(Opcode) || Instruction::isBinaryOp(Opcode);
}
-bool vputils::isSingleScalar(const VPValue *VPV) {
+bool vputils::doesGenerateSingleScalar(const VPValue *VPV) {
// Live-in, symbolic and canonical-IV region values are single-scalar.
if (auto *RV = dyn_cast<VPRegionValue>(VPV))
return RV == RV->getDefiningRegion()->getCanonicalIV();
@@ -445,19 +445,10 @@ bool vputils::isSingleScalar(const VPValue *VPV) {
// lanes.
if (RegionOfR && RegionOfR->isReplicator())
return false;
- return Rep->isSingleScalar() || (preservesUniformity(Rep->getOpcode()) &&
- all_of(Rep->operands(), isSingleScalar));
- }
- if (isa<VPWidenGEPRecipe, VPBlendRecipe>(VPV))
- return all_of(VPV->getDefiningRecipe()->operands(), isSingleScalar);
- if (auto *WidenR = dyn_cast<VPWidenRecipe>(VPV)) {
- return preservesUniformity(WidenR->getOpcode()) &&
- all_of(WidenR->operands(), isSingleScalar);
+ return Rep->isSingleScalar();
}
if (auto *VPI = dyn_cast<VPInstruction>(VPV))
- return VPI->isSingleScalar() || VPI->isVectorToScalar() ||
- (preservesUniformity(VPI->getOpcode()) &&
- all_of(VPI->operands(), isSingleScalar));
+ return VPI->isSingleScalar() || VPI->isVectorToScalar();
if (auto *RR = dyn_cast<VPReductionRecipe>(VPV))
return !RR->isPartialReduction();
if (isa<VPVectorPointerRecipe, VPVectorEndPointerRecipe, VPDerivedIVRecipe>(
@@ -470,6 +461,26 @@ bool vputils::isSingleScalar(const VPValue *VPV) {
return isa<VPExpandSCEVRecipe>(VPV);
}
+bool vputils::isSingleScalar(const VPValue *VPV) {
+ if (doesGenerateSingleScalar(VPV))
+ return true;
+
+ // A uniformity-preserving operation is single-scalar if all its operands are.
+ if (auto *Rep = dyn_cast<VPReplicateRecipe>(VPV)) {
+ const VPRegionBlock *RegionOfR = Rep->getRegion();
+ if (RegionOfR && RegionOfR->isReplicator())
+ return false;
+ return preservesUniformity(Rep->getOpcode()) &&
+ all_of(Rep->operands(), isSingleScalar);
+ }
+ if (isa<VPWidenGEPRecipe, VPBlendRecipe>(VPV))
+ return all_of(VPV->getDefiningRecipe()->operands(), isSingleScalar);
+
+ return isa<VPWidenRecipe, VPInstruction>(VPV) &&
+ preservesUniformity(vputils::getOpcode(VPV)) &&
+ all_of(VPV->getDefiningRecipe()->operands(), isSingleScalar);
+}
+
bool vputils::isUniformAcrossVFsAndUFs(const VPValue *V) {
// Live-ins, symbolic and canonical-IV region values are uniform.
if (auto *RV = dyn_cast<VPRegionValue>(V))
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 738a5bc8b8066..8f1cbb9006209 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -70,6 +70,10 @@ bool isElementwise(const VPValue *V);
/// Returns true if \p R produces scalar values for all VF lanes.
bool doesGeneratePerAllLanes(const VPRecipeBase *R);
+/// Returns true if \p VPV is defined by a recipe producing a single-scalar
+/// value or a live-in/symbolic value/single-scalar region value.
+bool doesGenerateSingleScalar(const VPValue *VPV);
+
/// Returns the header block of the first, top-level loop, or null if none
/// exist.
VPBasicBlock *getFirstLoopHeader(VPlan &Plan, VPDominatorTree &VPDT);
``````````
</details>
https://github.com/llvm/llvm-project/pull/222447
More information about the llvm-commits
mailing list