-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LV] Add support for partial reductions without a binary op #133922
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
base: main
Are you sure you want to change the base?
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 |
---|---|---|
|
@@ -24,22 +24,23 @@ struct HistogramInfo; | |
struct VFRange; | ||
|
||
/// A chain of instructions that form a partial reduction. | ||
/// Designed to match: reduction_bin_op (bin_op (extend (A), (extend (B))), | ||
/// accumulator). | ||
/// Designed to match either: | ||
/// reduction_bin_op (extend (A), accumulator), or | ||
/// reduction_bin_op (bin_op (extend (A), (extend (B))), accumulator). | ||
struct PartialReductionChain { | ||
PartialReductionChain(Instruction *Reduction, Instruction *ExtendA, | ||
Instruction *ExtendB, Instruction *BinOp) | ||
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. Comment above needs updating 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. Done |
||
: Reduction(Reduction), ExtendA(ExtendA), ExtendB(ExtendB), BinOp(BinOp) { | ||
} | ||
Instruction *ExtendB, Instruction *ExtendUser) | ||
: Reduction(Reduction), ExtendA(ExtendA), ExtendB(ExtendB), | ||
ExtendUser(ExtendUser) {} | ||
/// The top-level binary operation that forms the reduction to a scalar | ||
/// after the loop body. | ||
Instruction *Reduction; | ||
/// The extension of each of the inner binary operation's operands. | ||
Instruction *ExtendA; | ||
Instruction *ExtendB; | ||
|
||
/// The binary operation using the extends that is then reduced. | ||
Instruction *BinOp; | ||
/// The user of the extend that is then reduced. | ||
Instruction *ExtendUser; | ||
}; | ||
|
||
/// Helper class to create VPRecipies from IR instructions. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -296,49 +296,70 @@ bool VPRecipeBase::isScalarCast() const { | |
InstructionCost | ||
VPPartialReductionRecipe::computeCost(ElementCount VF, | ||
VPCostContext &Ctx) const { | ||
std::optional<unsigned> Opcode = std::nullopt; | ||
VPValue *BinOp = getOperand(1); | ||
std::optional<unsigned> Opcode; | ||
VPValue *Op = getOperand(0); | ||
VPRecipeBase *OpR = Op->getDefiningRecipe(); | ||
|
||
// If the partial reduction is predicated, a select will be operand 0 rather | ||
// than the binary op | ||
// If the partial reduction is predicated, a select will be operand 0 | ||
using namespace llvm::VPlanPatternMatch; | ||
if (match(getOperand(1), m_Select(m_VPValue(), m_VPValue(), m_VPValue()))) | ||
BinOp = BinOp->getDefiningRecipe()->getOperand(1); | ||
|
||
// If BinOp is a negation, use the side effect of match to assign the actual | ||
// binary operation to BinOp | ||
match(BinOp, m_Binary<Instruction::Sub>(m_SpecificInt(0), m_VPValue(BinOp))); | ||
VPRecipeBase *BinOpR = BinOp->getDefiningRecipe(); | ||
|
||
if (auto *WidenR = dyn_cast<VPWidenRecipe>(BinOpR)) | ||
Opcode = std::make_optional(WidenR->getOpcode()); | ||
|
||
VPRecipeBase *ExtAR = BinOpR->getOperand(0)->getDefiningRecipe(); | ||
VPRecipeBase *ExtBR = BinOpR->getOperand(1)->getDefiningRecipe(); | ||
if (match(getOperand(1), m_Select(m_VPValue(), m_VPValue(Op), m_VPValue()))) { | ||
OpR = Op->getDefiningRecipe(); | ||
} | ||
|
||
auto *PhiType = Ctx.Types.inferScalarType(getOperand(1)); | ||
auto *InputTypeA = Ctx.Types.inferScalarType(ExtAR ? ExtAR->getOperand(0) | ||
: BinOpR->getOperand(0)); | ||
auto *InputTypeB = Ctx.Types.inferScalarType(ExtBR ? ExtBR->getOperand(0) | ||
: BinOpR->getOperand(1)); | ||
Type *InputTypeA = nullptr, *InputTypeB = nullptr; | ||
TTI::PartialReductionExtendKind ExtAType = TTI::PR_None, | ||
ExtBType = TTI::PR_None; | ||
|
||
auto GetExtendKind = [](VPRecipeBase *R) { | ||
// The extend could come from outside the plan. | ||
if (!R) | ||
return TargetTransformInfo::PR_None; | ||
return TTI::PR_None; | ||
auto *WidenCastR = dyn_cast<VPWidenCastRecipe>(R); | ||
if (!WidenCastR) | ||
return TargetTransformInfo::PR_None; | ||
return TTI::PR_None; | ||
if (WidenCastR->getOpcode() == Instruction::CastOps::ZExt) | ||
return TargetTransformInfo::PR_ZeroExtend; | ||
return TTI::PR_ZeroExtend; | ||
if (WidenCastR->getOpcode() == Instruction::CastOps::SExt) | ||
return TargetTransformInfo::PR_SignExtend; | ||
return TargetTransformInfo::PR_None; | ||
return TTI::PR_SignExtend; | ||
return TTI::PR_None; | ||
}; | ||
|
||
return Ctx.TTI.getPartialReductionCost( | ||
getOpcode(), InputTypeA, InputTypeB, PhiType, VF, GetExtendKind(ExtAR), | ||
GetExtendKind(ExtBR), Opcode, Ctx.CostKind); | ||
// Pick out opcode, type/ext information and use sub side effects from a widen | ||
// recipe. | ||
auto HandleWiden = [&](VPWidenRecipe *Widen) { | ||
if (match(Widen, | ||
m_Binary<Instruction::Sub>(m_SpecificInt(0), m_VPValue(Op)))) { | ||
Widen = dyn_cast<VPWidenRecipe>(Op->getDefiningRecipe()); | ||
} | ||
Opcode = Widen->getOpcode(); | ||
VPRecipeBase *ExtAR = Widen->getOperand(0)->getDefiningRecipe(); | ||
VPRecipeBase *ExtBR = Widen->getOperand(1)->getDefiningRecipe(); | ||
InputTypeA = Ctx.Types.inferScalarType(ExtAR ? ExtAR->getOperand(0) | ||
: Widen->getOperand(0)); | ||
InputTypeB = Ctx.Types.inferScalarType(ExtBR ? ExtBR->getOperand(0) | ||
: Widen->getOperand(1)); | ||
ExtAType = GetExtendKind(ExtAR); | ||
ExtBType = GetExtendKind(ExtBR); | ||
}; | ||
|
||
if (isa<VPWidenCastRecipe>(OpR)) { | ||
InputTypeA = Ctx.Types.inferScalarType(OpR->getOperand(0)); | ||
ExtAType = GetExtendKind(OpR); | ||
} else if (isa<VPReductionPHIRecipe>(OpR)) { | ||
auto RedPhiOp1R = getOperand(1)->getDefiningRecipe(); | ||
if (isa<VPWidenCastRecipe>(RedPhiOp1R)) { | ||
InputTypeA = Ctx.Types.inferScalarType(RedPhiOp1R->getOperand(0)); | ||
ExtAType = GetExtendKind(RedPhiOp1R); | ||
} else if (auto Widen = dyn_cast<VPWidenRecipe>(RedPhiOp1R)) | ||
HandleWiden(Widen); | ||
} else if (auto Widen = dyn_cast<VPWidenRecipe>(OpR)) { | ||
HandleWiden(Widen); | ||
} else if (auto Reduction = dyn_cast<VPPartialReductionRecipe>(OpR)) { | ||
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. Feels odd to return the cost of another operation (since it may be different?), but since we're hoping to use the bundle recipe for costing we can fix things there. |
||
return Reduction->computeCost(VF, Ctx); | ||
} | ||
auto *PhiType = Ctx.Types.inferScalarType(getOperand(1)); | ||
return Ctx.TTI.getPartialReductionCost(getOpcode(), InputTypeA, InputTypeB, | ||
PhiType, VF, ExtAType, ExtBType, | ||
Opcode, Ctx.CostKind); | ||
} | ||
|
||
void VPPartialReductionRecipe::execute(VPTransformState &State) { | ||
|
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.
The documentation for the interface should probably also be updated, documenting that
Opcode
and the second the second type are optional?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.
Yep, absolutely right! I've tried to amend the documentation for the interface. Please take a look and see if it makes sense.