-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[VectorUtils][VPlan] Consolidate VPWidenIntrinsicRecipe::onlyFirstLaneUsed and isVectorIntrinsicWithScalarOpAtArg #137497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[VectorUtils][VPlan] Consolidate VPWidenIntrinsicRecipe::onlyFirstLaneUsed and isVectorIntrinsicWithScalarOpAtArg #137497
Conversation
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-vectorizers Author: Luke Lau (lukel97) ChangesWe can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it. This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places. Full diff: https://github.com/llvm/llvm-project/pull/137497.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index 6448c372f5d5d..b614ac50d2f82 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -149,6 +149,11 @@ bool llvm::isVectorIntrinsicWithScalarOpAtArg(Intrinsic::ID ID,
if (TTI && Intrinsic::isTargetIntrinsic(ID))
return TTI->isTargetIntrinsicWithScalarOpAtArg(ID, ScalarOpdIdx);
+ // Vector predication intrinsics only demand the the first lane the last
+ // operand (the EVL operand).
+ if (VPIntrinsic::getVectorLengthParamPos(ID) == ScalarOpdIdx)
+ return true;
+
switch (ID) {
case Intrinsic::abs:
case Intrinsic::vp_abs:
@@ -166,7 +171,7 @@ bool llvm::isVectorIntrinsicWithScalarOpAtArg(Intrinsic::ID ID,
case Intrinsic::umul_fix_sat:
return (ScalarOpdIdx == 2);
case Intrinsic::experimental_vp_splice:
- return ScalarOpdIdx == 2 || ScalarOpdIdx == 4 || ScalarOpdIdx == 5;
+ return ScalarOpdIdx == 2 || ScalarOpdIdx == 4;
default:
return false;
}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 8ec092ce9a905..787b416ee9248 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1373,10 +1373,8 @@ StringRef VPWidenIntrinsicRecipe::getIntrinsicName() const {
bool VPWidenIntrinsicRecipe::onlyFirstLaneUsed(const VPValue *Op) const {
assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
- // Vector predication intrinsics only demand the the first lane the last
- // operand (the EVL operand).
- return VPIntrinsic::isVPIntrinsic(VectorIntrinsicID) &&
- Op == getOperand(getNumOperands() - 1);
+ unsigned Idx = std::distance(op_begin(), find(operands(), Op));
+ return isVectorIntrinsicWithScalarOpAtArg(VectorIntrinsicID, Idx, nullptr);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
// operand (the EVL operand). | ||
return VPIntrinsic::isVPIntrinsic(VectorIntrinsicID) && | ||
Op == getOperand(getNumOperands() - 1); | ||
unsigned Idx = std::distance(op_begin(), find(operands(), Op)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for Op
to appear more than once in operands()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I think so. But hopefully having multiple uses of a value in both scalar and vector positions never happens.
If so I think that would be an existing problem with onlyFirstLaneUsed. In which case we should probably be passing around the specific operand index for *Op
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onlyFirstLaneUsed
should only return true if all uses only use the first lane;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that makes more sense, I've handled that in 353adf0
return VPIntrinsic::isVPIntrinsic(VectorIntrinsicID) && | ||
Op == getOperand(getNumOperands() - 1); | ||
unsigned Idx = std::distance(op_begin(), find(operands(), Op)); | ||
return isVectorIntrinsicWithScalarOpAtArg(VectorIntrinsicID, Idx, nullptr); |
There was a problem hiding this 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 test for the new behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out yes, I was able to add a case where we no longer broadcast and extract the scalar operand to powi. It should be precomitted in this PR now
// operand (the EVL operand). | ||
return VPIntrinsic::isVPIntrinsic(VectorIntrinsicID) && | ||
Op == getOperand(getNumOperands() - 1); | ||
unsigned Idx = std::distance(op_begin(), find(operands(), Op)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onlyFirstLaneUsed
should only return true if all uses only use the first lane;
…eUsed and isVectorIntrinsicWithScalarOpAtArg We can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it. This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places.
a3475ac
to
353adf0
Compare
for (auto [Idx, V] : enumerate(operands())) { | ||
if (V != Op) | ||
continue; | ||
if (!isVectorIntrinsicWithScalarOpAtArg(VectorIntrinsicID, Idx, nullptr)) | ||
return false; | ||
} | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use any_of
/all_of
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LG
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks
// operand (the EVL operand). | ||
return VPIntrinsic::isVPIntrinsic(VectorIntrinsicID) && | ||
Op == getOperand(getNumOperands() - 1); | ||
return all_of(enumerate(operands()), [this, &Op](auto &&X) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be simpler to just use, if it works
return all_of(enumerate(operands()), [this, &Op](auto &&X) { | |
return all_of(enumerate(operands()), [this, &Op](const auto &X) { |
llvm/lib/Analysis/VectorUtils.cpp
Outdated
// Vector predication intrinsics only demand the the first lane the last | ||
// operand (the EVL operand). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Vector predication intrinsics only demand the the first lane the last | |
// operand (the EVL operand). | |
// Vector predication intrinsics only demand the first lane of the EVL operand. |
There's no reference to the last argument here, as this uses VPIntrinsic::getVectorLengthParamPos
, so better to first mention that this is for the EVL operand?
; RUN: opt < %s -passes=loop-vectorize -force-vector-width=4 -S | FileCheck %s | ||
|
||
; Check that we don't unnecessarily broadcast %pow | ||
define void @powi(ptr noalias %p, i32 %pow) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
define void @powi(ptr noalias %p, i32 %pow) { | |
define void @powi_only_first_lane_used_of_second_arg(ptr noalias %p, i32 %pow) { |
no alias
could also be stripped, as only p
is accessed.
…eUsed and isVectorIntrinsicWithScalarOpAtArg (llvm#137497) We can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it. This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places.
…eUsed and isVectorIntrinsicWithScalarOpAtArg (llvm#137497) We can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it. This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places.
…eUsed and isVectorIntrinsicWithScalarOpAtArg (llvm#137497) We can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it. This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places.
…eUsed and isVectorIntrinsicWithScalarOpAtArg (llvm#137497) We can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it. This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places.
We can reuse isVectorIntrinsicWithScalarOpAtArg in VectorUtils to determine if only the first lane will be used for a VPWidenIntrinsicRecipe, provided that we also move the VP EVL operand check into it.
This was needed by a local patch I was working on that created a VPWidenIntrinsicRecipe with a VP intrinsic, and prevents the need to update the scalar arguments in two places.