-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LV] Improve AnyOf reduction codegen. #78304
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
9846f97
bfd5d93
0732641
0057eb2
eafe294
f9fea8e
fa841cd
ba35741
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 |
---|---|---|
|
@@ -7405,7 +7405,6 @@ static void createAndCollectMergePhiForReduction( | |
auto *PhiR = cast<VPReductionPHIRecipe>(RedResult->getOperand(0)); | ||
const RecurrenceDescriptor &RdxDesc = PhiR->getRecurrenceDescriptor(); | ||
|
||
TrackingVH<Value> ReductionStartValue = RdxDesc.getRecurrenceStartValue(); | ||
Value *FinalValue = | ||
State.get(RedResult, VPIteration(State.UF - 1, VPLane::getFirstLane())); | ||
auto *ResumePhi = | ||
|
@@ -7430,7 +7429,7 @@ static void createAndCollectMergePhiForReduction( | |
BCBlockPhi->addIncoming(ResumePhi->getIncomingValueForBlock(Incoming), | ||
Incoming); | ||
else | ||
BCBlockPhi->addIncoming(ReductionStartValue, Incoming); | ||
BCBlockPhi->addIncoming(RdxDesc.getRecurrenceStartValue(), Incoming); | ||
} | ||
|
||
auto *OrigPhi = cast<PHINode>(PhiR->getUnderlyingValue()); | ||
|
@@ -8854,6 +8853,10 @@ VPlanPtr LoopVectorizationPlanner::buildVPlan(VFRange &Range) { | |
// A ComputeReductionResult recipe is added to the middle block, also for | ||
// in-loop reductions which compute their result in-loop, because generating | ||
// the subsequent bc.merge.rdx phi is driven by ComputeReductionResult recipes. | ||
// | ||
// Adjust AnyOf reductions; replace the reduction phi for the selected value | ||
// with a boolean reduction phi node to check if the condition is true in any | ||
// iteration. The final value is selected by the final ComputeReductionResult. | ||
void LoopVectorizationPlanner::adjustRecipesForReductions( | ||
VPBasicBlock *LatchVPBB, VPlanPtr &Plan, VPRecipeBuilder &RecipeBuilder, | ||
ElementCount MinVF) { | ||
|
@@ -9027,6 +9030,41 @@ void LoopVectorizationPlanner::adjustRecipesForReductions( | |
continue; | ||
|
||
const RecurrenceDescriptor &RdxDesc = PhiR->getRecurrenceDescriptor(); | ||
// Adjust AnyOf reductions; replace the reduction phi for the selected value | ||
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. adjustRecipesForReductions() is getting excessively long, should be refactored. Its documentation above should (also) include the adjustment of AnyOf reductions described here. 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. Agreed, probably best as followup? |
||
// with a boolean reduction phi node to check if the condition is true in | ||
// any iteration. The final value is selected by the final | ||
// ComputeReductionResult. | ||
if (RecurrenceDescriptor::isAnyOfRecurrenceKind( | ||
RdxDesc.getRecurrenceKind())) { | ||
auto *Select = cast<VPRecipeBase>(*find_if(PhiR->users(), [](VPUser *U) { | ||
return isa<VPWidenSelectRecipe>(U) || | ||
(isa<VPReplicateRecipe>(U) && | ||
cast<VPReplicateRecipe>(U)->getUnderlyingInstr()->getOpcode() == | ||
Instruction::Select); | ||
})); | ||
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. nit: assert(Select && "a meaningful error message"); 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.
|
||
VPValue *Cmp = Select->getOperand(0); | ||
// If the compare is checking the reduction PHI node, adjust it to check | ||
// the start value. | ||
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. Hmm, either the true or false values of the select should use PhiR, but only it? The condition of an AnyOf reduction should be any boolean-expression predicate that depends on the current iteration only, i.e., should be a recipe (if it's live-in the entire select should be LICM'd), independent of AnyOf's PhiR, neither directly nor indirectly? 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. At the moment, AnyOf reduction are also formed for code like
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. @ayalz The current recognition for AnyOf reduction involves a set of cmp- select instruction and cannot identify cases with only a single select. This requires expanding |
||
if (VPRecipeBase *CmpR = Cmp->getDefiningRecipe()) { | ||
for (unsigned I = 0; I != CmpR->getNumOperands(); ++I) | ||
if (CmpR->getOperand(I) == PhiR) | ||
CmpR->setOperand(I, PhiR->getStartValue()); | ||
} | ||
VPBuilder::InsertPointGuard Guard(Builder); | ||
Builder.setInsertPoint(Select); | ||
|
||
// If the true value of the select is the reduction phi, the new value is | ||
// selected if the negated condition is true in any iteration. | ||
if (Select->getOperand(1) == PhiR) | ||
Cmp = Builder.createNot(Cmp); | ||
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. As Mel pointed out, this case of "Red = cond ? PhiR : Other" where a single false cond suffices for the result to be Other, could be considered an Negating cond swaps the operands and translates into the "Red = !cond ? Other : PhiR" form of an 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, this could be handled like that, but I think then we would need to support both patterns here and also in codegen, so negating seems simpler (and the negation should be removable by instcombine). Left as is for now, but happy to adjust if needed. But then it should probably be modeled as AllOf directly in the reduction descriptor. |
||
VPValue *Or = Builder.createOr(PhiR, Cmp); | ||
Select->getVPSingleValue()->replaceAllUsesWith(Or); | ||
|
||
// Convert the reduction phi to operate on bools. | ||
PhiR->setOperand(0, Plan->getVPValueOrAddLiveIn(ConstantInt::getFalse( | ||
OrigLoop->getHeader()->getContext()))); | ||
} | ||
|
||
// If tail is folded by masking, introduce selects between the phi | ||
// and the live-out instruction of each reduction, at the beginning of the | ||
// dedicated latch block. | ||
|
@@ -9059,7 +9097,9 @@ void LoopVectorizationPlanner::adjustRecipesForReductions( | |
// then extend the loop exit value to enable InstCombine to evaluate the | ||
// entire expression in the smaller type. | ||
Type *PhiTy = PhiR->getStartValue()->getLiveInIRValue()->getType(); | ||
if (MinVF.isVector() && PhiTy != RdxDesc.getRecurrenceType()) { | ||
if (MinVF.isVector() && PhiTy != RdxDesc.getRecurrenceType() && | ||
!RecurrenceDescriptor::isAnyOfRecurrenceKind( | ||
RdxDesc.getRecurrenceKind())) { | ||
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. Would be it better to truncate AnyOf reductions to smaller type (boolean) here instead of above? 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. We only reach this path now because we adjust PhiR's start value to a bool. It requires more than plain truncates as below, so I think it's probably worth to keep it separate. I think it also needs handling before introducing selects for tail-folding; otherwise those selects would also need updating. |
||
assert(!PhiR->isInLoop() && "Unexpected truncated inloop reduction!"); | ||
Type *RdxTy = RdxDesc.getRecurrenceType(); | ||
auto *Trunc = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -442,6 +442,8 @@ Value *VPInstruction::generateInstruction(VPTransformState &State, | |
// Reduce all of the unrolled parts into a single vector. | ||
Value *ReducedPartRdx = RdxParts[0]; | ||
unsigned Op = RecurrenceDescriptor::getOpcode(RK); | ||
if (RecurrenceDescriptor::isAnyOfRecurrenceKind(RK)) | ||
Op = Instruction::Or; | ||
|
||
if (PhiR->isOrdered()) { | ||
ReducedPartRdx = RdxParts[State.UF - 1]; | ||
|
@@ -454,19 +456,16 @@ Value *VPInstruction::generateInstruction(VPTransformState &State, | |
if (Op != Instruction::ICmp && Op != Instruction::FCmp) | ||
ReducedPartRdx = Builder.CreateBinOp( | ||
(Instruction::BinaryOps)Op, RdxPart, ReducedPartRdx, "bin.rdx"); | ||
else if (RecurrenceDescriptor::isAnyOfRecurrenceKind(RK)) { | ||
TrackingVH<Value> ReductionStartValue = | ||
RdxDesc.getRecurrenceStartValue(); | ||
ReducedPartRdx = createAnyOfOp(Builder, ReductionStartValue, RK, | ||
ReducedPartRdx, RdxPart); | ||
} else | ||
else | ||
ReducedPartRdx = createMinMaxOp(Builder, RK, ReducedPartRdx, RdxPart); | ||
} | ||
} | ||
|
||
// Create the reduction after the loop. Note that inloop reductions create | ||
// the target reduction in the loop using a Reduction recipe. | ||
if (State.VF.isVector() && !PhiR->isInLoop()) { | ||
if ((State.VF.isVector() || | ||
RecurrenceDescriptor::isAnyOfRecurrenceKind(RK)) && | ||
!PhiR->isInLoop()) { | ||
ReducedPartRdx = | ||
createTargetReduction(Builder, RdxDesc, ReducedPartRdx, OrigPhi); | ||
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. As raised above, it may be better to have ComputeReductionResult recipe take care only of reducing AnyOf to a boolean here, followed by a Select recipe to chose between Start and Other live-in values. 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, but this will need a bit of additional refactoring, in particular how createAndCollectMergePhiForReduction looks up the reduction result value and when ComputeReductionResult VPInstructions are created. |
||
// If the reduction can be performed in a smaller type, we need to extend | ||
|
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.
Further refactoring thought: the AnyOf boolean reduction completes with the CreateOrReduce(), and freezing it. Similar to a plain "result |= value[i]" OR reduction, on i1 values, where freezing may also be needed? The subsequent select deserves a separate recipe, which post-processes the result of the AnyOf reduction and also depends on the "IfAny" and "Else" (or "Start" and "Other") Live-In VP/Values directly, rather than looking for them here and now.
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.
Freeze won't be needed in that case; if there's already a binary OR in the input, poison from the compare gets already propagated. It is only needed when converting from the
select
form (which doesn't propagate poison from the condition to its result)Yes, but this will need a bit of additional refactoring, in particular how
createAndCollectMergePhiForReduction
looks up the reduction result value and whenComputeReductionResult
VPInstructions are created.