-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LLVM][VPlan] Keep all VPBlend masks until VPlan transformation. #104015
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2033,12 +2033,12 @@ class VPReductionPHIRecipe : public VPHeaderPHIRecipe { | |
class VPBlendRecipe : public VPSingleDefRecipe { | ||
public: | ||
/// The blend operation is a User of the incoming values and of their | ||
/// respective masks, ordered [I0, I1, M1, I2, M2, ...]. Note that the first | ||
/// incoming value does not have a mask associated. | ||
/// respective masks, ordered [I0, M0, I1, M1, I2, M2, ...]. Note that M0 can | ||
/// be ommited (implied by passing an odd number of operands) in which case | ||
/// all other incoming values are merged into it. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. M0 should be the complement of all other M's, so could be rematerialized if dropped, but it may have a simpler way of computing, which might be lost if dropped? |
||
VPBlendRecipe(PHINode *Phi, ArrayRef<VPValue *> Operands) | ||
: VPSingleDefRecipe(VPDef::VPBlendSC, Operands, Phi, Phi->getDebugLoc()) { | ||
assert((Operands.size() + 1) % 2 == 0 && | ||
"Expected an odd number of operands"); | ||
assert(Operands.size() > 0 && "Expected at least one operand!"); | ||
} | ||
|
||
VPBlendRecipe *clone() override { | ||
|
@@ -2048,19 +2048,25 @@ class VPBlendRecipe : public VPSingleDefRecipe { | |
|
||
VP_CLASSOF_IMPL(VPDef::VPBlendSC) | ||
|
||
/// Return the number of incoming values, taking into account that the first | ||
/// incoming value has no mask. | ||
unsigned getNumIncomingValues() const { return (getNumOperands() + 1) / 2; } | ||
/// A normalized blend is one that has an odd number of operands, whereby the | ||
/// first operand does not have an associated mask. | ||
bool isNormalized() const { return getNumOperands() % 2; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Operands that may or may not be present typically appear last, thereby fixing the access to all other operands. While we're making changes here, how about reversing the order of operand pairs, so that the last operand drops its mask rather than the first? Note that an absent mask represents a full mask elsewhere in VPlan, so best clarify that this last mask is dropped because it is not needed rather than because it is known to be full. The last mask can be retained (until execution) rather than dropped but doing so might prevent dead code elimination. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've investigated a little and to me this change makes VBlend harder to work with. Nothing major, just minor things like not being able to write There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Changing the order in which operand pairs are held should keep the API of accessing and traversing them in tact, only simplifying how these are implemented. Perhaps main distinction is traversing from last to first instead of from first to last.
Sounds like another "optional" operand. Suffice to simplify how the redundant mask operand is handled? |
||
|
||
/// Return the number of incoming values, taking into account when normalized | ||
/// the first incoming value will have no mask. | ||
unsigned getNumIncomingValues() const { | ||
return (getNumOperands() + isNormalized()) / 2; | ||
} | ||
|
||
/// Return incoming value number \p Idx. | ||
VPValue *getIncomingValue(unsigned Idx) const { | ||
return Idx == 0 ? getOperand(0) : getOperand(Idx * 2 - 1); | ||
return Idx == 0 ? getOperand(0) : getOperand(Idx * 2 - isNormalized()); | ||
} | ||
|
||
/// Return mask number \p Idx. | ||
VPValue *getMask(unsigned Idx) const { | ||
assert(Idx > 0 && "First index has no mask associated."); | ||
return getOperand(Idx * 2); | ||
assert((Idx > 0 || !isNormalized()) && "First index has no mask!"); | ||
return Idx == 0 ? getOperand(1) : getOperand(Idx * 2 + !isNormalized()); | ||
} | ||
|
||
/// Generate the phi/select nodes. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -989,15 +989,47 @@ void VPlanTransforms::clearReductionWrapFlags(VPlan &Plan) { | |
/// Try to simplify recipe \p R. | ||
static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) { | ||
using namespace llvm::VPlanPatternMatch; | ||
// Try to remove redundant blend recipes. | ||
|
||
if (auto *Blend = dyn_cast<VPBlendRecipe>(&R)) { | ||
VPValue *Inc0 = Blend->getIncomingValue(0); | ||
// Try to remove redundant blend recipes. | ||
SmallPtrSet<VPValue *, 4> UniqueValues; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UniqueValues is used here only for checking if all non-masked-out operands are the same, which is more simply done by comparing them to the first non-masked-out operand, as the previous implementation was doing (assuming first operand is not masked out). |
||
if (Blend->isNormalized() || !match(Blend->getMask(0), m_False())) | ||
UniqueValues.insert(Blend->getIncomingValue(0)); | ||
for (unsigned I = 1; I != Blend->getNumIncomingValues(); ++I) | ||
if (Inc0 != Blend->getIncomingValue(I) && | ||
!match(Blend->getMask(I), m_False())) | ||
return; | ||
Blend->replaceAllUsesWith(Inc0); | ||
if (!match(Blend->getMask(I), m_False())) | ||
UniqueValues.insert(Blend->getIncomingValue(I)); | ||
|
||
if (UniqueValues.size() == 1) { | ||
Blend->replaceAllUsesWith(*UniqueValues.begin()); | ||
Blend->eraseFromParent(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth running recursivelyDeleteDeadRecipes() in this case too - on all masks? Or does the elimination of Blend here suffice to trigger dce. |
||
return; | ||
} | ||
|
||
if (Blend->isNormalized()) | ||
return; | ||
|
||
// Normalize the blend so its first incomming value is used as the initial | ||
paulwalker-arm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// value with the others blended into it. | ||
|
||
unsigned StartIndex = 0; | ||
paulwalker-arm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
SmallVector<VPValue *, 4> OperandsWithMask; | ||
OperandsWithMask.push_back(Blend->getIncomingValue(StartIndex)); | ||
|
||
for (unsigned I = 0; I != Blend->getNumIncomingValues(); ++I) { | ||
if (I == StartIndex) | ||
continue; | ||
OperandsWithMask.push_back(Blend->getIncomingValue(I)); | ||
OperandsWithMask.push_back(Blend->getMask(I)); | ||
} | ||
|
||
auto *NewBlend = new VPBlendRecipe( | ||
cast<PHINode>(Blend->getUnderlyingValue()), OperandsWithMask); | ||
NewBlend->insertBefore(&R); | ||
|
||
VPValue *DeadMask = Blend->getMask(StartIndex); | ||
Blend->replaceAllUsesWith(NewBlend); | ||
Blend->eraseFromParent(); | ||
recursivelyDeleteDeadRecipes(DeadMask); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed here? Other patterns below and here previously rely on DCE to clean them up later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #104019 shows relying on DCE to clean up the plan can hamper VPlanTransformations, which is only run once, so I figure it's better to exit a transformation with the plan in the most optimal state. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I see, that PR introduces a pattern that checks the number of uses? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's correct. It tries to pick a mask that it knows will be dead once removed from the blend. |
||
return; | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.