Skip to content

[VPlan] Add VPSingleDefBundleRecipe, replacing extended reduction recipes. #144281

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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
333 changes: 119 additions & 214 deletions llvm/lib/Transforms/Vectorize/VPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,14 +525,13 @@ class VPSingleDefRecipe : public VPRecipeBase, public VPValue {

static inline bool classof(const VPRecipeBase *R) {
switch (R->getVPDefID()) {
case VPRecipeBase::VPBundleSC:
case VPRecipeBase::VPDerivedIVSC:
case VPRecipeBase::VPEVLBasedIVPHISC:
case VPRecipeBase::VPExpandSCEVSC:
case VPRecipeBase::VPInstructionSC:
case VPRecipeBase::VPReductionEVLSC:
case VPRecipeBase::VPReductionSC:
case VPRecipeBase::VPMulAccumulateReductionSC:
case VPRecipeBase::VPExtendedReductionSC:
case VPRecipeBase::VPReplicateSC:
case VPRecipeBase::VPScalarIVStepsSC:
case VPRecipeBase::VPVectorPointerSC:
Expand Down Expand Up @@ -852,9 +851,7 @@ struct VPRecipeWithIRFlags : public VPSingleDefRecipe, public VPIRFlags {
R->getVPDefID() == VPRecipeBase::VPReductionEVLSC ||
R->getVPDefID() == VPRecipeBase::VPReplicateSC ||
R->getVPDefID() == VPRecipeBase::VPVectorEndPointerSC ||
R->getVPDefID() == VPRecipeBase::VPVectorPointerSC ||
R->getVPDefID() == VPRecipeBase::VPExtendedReductionSC ||
R->getVPDefID() == VPRecipeBase::VPMulAccumulateReductionSC;
R->getVPDefID() == VPRecipeBase::VPVectorPointerSC;
}

static inline bool classof(const VPUser *U) {
Expand Down Expand Up @@ -2436,28 +2433,6 @@ class VPReductionRecipe : public VPRecipeWithIRFlags {
setUnderlyingValue(I);
}

/// For VPExtendedReductionRecipe.
/// Note that the debug location is from the extend.
VPReductionRecipe(const unsigned char SC, const RecurKind RdxKind,
ArrayRef<VPValue *> Operands, VPValue *CondOp,
bool IsOrdered, DebugLoc DL)
: VPRecipeWithIRFlags(SC, Operands, DL), RdxKind(RdxKind),
IsOrdered(IsOrdered), IsConditional(CondOp) {
if (CondOp)
addOperand(CondOp);
}

/// For VPMulAccumulateReductionRecipe.
/// Note that the NUW/NSW flags and the debug location are from the Mul.
VPReductionRecipe(const unsigned char SC, const RecurKind RdxKind,
ArrayRef<VPValue *> Operands, VPValue *CondOp,
bool IsOrdered, WrapFlagsTy WrapFlags, DebugLoc DL)
: VPRecipeWithIRFlags(SC, Operands, WrapFlags, DL), RdxKind(RdxKind),
IsOrdered(IsOrdered), IsConditional(CondOp) {
if (CondOp)
addOperand(CondOp);
}

public:
VPReductionRecipe(RecurKind RdxKind, FastMathFlags FMFs, Instruction *I,
VPValue *ChainOp, VPValue *VecOp, VPValue *CondOp,
Expand All @@ -2483,9 +2458,7 @@ class VPReductionRecipe : public VPRecipeWithIRFlags {

static inline bool classof(const VPRecipeBase *R) {
return R->getVPDefID() == VPRecipeBase::VPReductionSC ||
R->getVPDefID() == VPRecipeBase::VPReductionEVLSC ||
R->getVPDefID() == VPRecipeBase::VPExtendedReductionSC ||
R->getVPDefID() == VPRecipeBase::VPMulAccumulateReductionSC;
R->getVPDefID() == VPRecipeBase::VPReductionEVLSC;
}

static inline bool classof(const VPUser *U) {
Expand Down Expand Up @@ -2624,190 +2597,6 @@ class VPReductionEVLRecipe : public VPReductionRecipe {
}
};

/// A recipe to represent inloop extended reduction operations, performing a
/// reduction on a extended vector operand into a scalar value, and adding the
/// result to a chain. This recipe is abstract and needs to be lowered to
/// concrete recipes before codegen. The operands are {ChainOp, VecOp,
/// [Condition]}.
class VPExtendedReductionRecipe : public VPReductionRecipe {
/// Opcode of the extend for VecOp.
Instruction::CastOps ExtOp;

/// The scalar type after extending.
Type *ResultTy;

/// For cloning VPExtendedReductionRecipe.
VPExtendedReductionRecipe(VPExtendedReductionRecipe *ExtRed)
: VPReductionRecipe(
VPDef::VPExtendedReductionSC, ExtRed->getRecurrenceKind(),
{ExtRed->getChainOp(), ExtRed->getVecOp()}, ExtRed->getCondOp(),
ExtRed->isOrdered(), ExtRed->getDebugLoc()),
ExtOp(ExtRed->getExtOpcode()), ResultTy(ExtRed->getResultType()) {
transferFlags(*ExtRed);
setUnderlyingValue(ExtRed->getUnderlyingValue());
}

public:
VPExtendedReductionRecipe(VPReductionRecipe *R, VPWidenCastRecipe *Ext)
: VPReductionRecipe(VPDef::VPExtendedReductionSC, R->getRecurrenceKind(),
{R->getChainOp(), Ext->getOperand(0)}, R->getCondOp(),
R->isOrdered(), Ext->getDebugLoc()),
ExtOp(Ext->getOpcode()), ResultTy(Ext->getResultType()) {
assert((ExtOp == Instruction::CastOps::ZExt ||
ExtOp == Instruction::CastOps::SExt) &&
"VPExtendedReductionRecipe only supports zext and sext.");

transferFlags(*Ext);
setUnderlyingValue(R->getUnderlyingValue());
}

~VPExtendedReductionRecipe() override = default;

VPExtendedReductionRecipe *clone() override {
return new VPExtendedReductionRecipe(this);
}

VP_CLASSOF_IMPL(VPDef::VPExtendedReductionSC);

void execute(VPTransformState &State) override {
llvm_unreachable("VPExtendedReductionRecipe should be transform to "
"VPExtendedRecipe + VPReductionRecipe before execution.");
};

/// Return the cost of VPExtendedReductionRecipe.
InstructionCost computeCost(ElementCount VF,
VPCostContext &Ctx) const override;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// Print the recipe.
void print(raw_ostream &O, const Twine &Indent,
VPSlotTracker &SlotTracker) const override;
#endif

/// The scalar type after extending.
Type *getResultType() const { return ResultTy; }

/// Is the extend ZExt?
bool isZExt() const { return getExtOpcode() == Instruction::ZExt; }

/// Get the opcode of the extend for VecOp.
Instruction::CastOps getExtOpcode() const { return ExtOp; }
};

/// A recipe to represent inloop MulAccumulateReduction operations, multiplying
/// the vector operands (which may be extended), performing a reduction.add on
/// the result, and adding the scalar result to a chain. This recipe is abstract
/// and needs to be lowered to concrete recipes before codegen. The operands are
/// {ChainOp, VecOp1, VecOp2, [Condition]}.
class VPMulAccumulateReductionRecipe : public VPReductionRecipe {
/// Opcode of the extend for VecOp1 and VecOp2.
Instruction::CastOps ExtOp;

/// Non-neg flag of the extend recipe.
bool IsNonNeg = false;

/// The scalar type after extending.
Type *ResultTy = nullptr;

/// For cloning VPMulAccumulateReductionRecipe.
VPMulAccumulateReductionRecipe(VPMulAccumulateReductionRecipe *MulAcc)
: VPReductionRecipe(
VPDef::VPMulAccumulateReductionSC, MulAcc->getRecurrenceKind(),
{MulAcc->getChainOp(), MulAcc->getVecOp0(), MulAcc->getVecOp1()},
MulAcc->getCondOp(), MulAcc->isOrdered(),
WrapFlagsTy(MulAcc->hasNoUnsignedWrap(), MulAcc->hasNoSignedWrap()),
MulAcc->getDebugLoc()),
ExtOp(MulAcc->getExtOpcode()), IsNonNeg(MulAcc->isNonNeg()),
ResultTy(MulAcc->getResultType()) {
transferFlags(*MulAcc);
setUnderlyingValue(MulAcc->getUnderlyingValue());
}

public:
VPMulAccumulateReductionRecipe(VPReductionRecipe *R, VPWidenRecipe *Mul,
VPWidenCastRecipe *Ext0,
VPWidenCastRecipe *Ext1, Type *ResultTy)
: VPReductionRecipe(
VPDef::VPMulAccumulateReductionSC, R->getRecurrenceKind(),
{R->getChainOp(), Ext0->getOperand(0), Ext1->getOperand(0)},
R->getCondOp(), R->isOrdered(),
WrapFlagsTy(Mul->hasNoUnsignedWrap(), Mul->hasNoSignedWrap()),
R->getDebugLoc()),
ExtOp(Ext0->getOpcode()), ResultTy(ResultTy) {
assert(RecurrenceDescriptor::getOpcode(getRecurrenceKind()) ==
Instruction::Add &&
"The reduction instruction in MulAccumulateteReductionRecipe must "
"be Add");
assert((ExtOp == Instruction::CastOps::ZExt ||
ExtOp == Instruction::CastOps::SExt) &&
"VPMulAccumulateReductionRecipe only supports zext and sext.");
setUnderlyingValue(R->getUnderlyingValue());
// Only set the non-negative flag if the original recipe contains.
if (Ext0->hasNonNegFlag())
IsNonNeg = Ext0->isNonNeg();
}

VPMulAccumulateReductionRecipe(VPReductionRecipe *R, VPWidenRecipe *Mul,
Type *ResultTy)
: VPReductionRecipe(
VPDef::VPMulAccumulateReductionSC, R->getRecurrenceKind(),
{R->getChainOp(), Mul->getOperand(0), Mul->getOperand(1)},
R->getCondOp(), R->isOrdered(),
WrapFlagsTy(Mul->hasNoUnsignedWrap(), Mul->hasNoSignedWrap()),
R->getDebugLoc()),
ExtOp(Instruction::CastOps::CastOpsEnd), ResultTy(ResultTy) {
assert(RecurrenceDescriptor::getOpcode(getRecurrenceKind()) ==
Instruction::Add &&
"The reduction instruction in MulAccumulateReductionRecipe must be "
"Add");
setUnderlyingValue(R->getUnderlyingValue());
}

~VPMulAccumulateReductionRecipe() override = default;

VPMulAccumulateReductionRecipe *clone() override {
return new VPMulAccumulateReductionRecipe(this);
}

VP_CLASSOF_IMPL(VPDef::VPMulAccumulateReductionSC);

void execute(VPTransformState &State) override {
llvm_unreachable("VPMulAccumulateReductionRecipe should transform to "
"VPWidenCastRecipe + "
"VPWidenRecipe + VPReductionRecipe before execution");
}

/// Return the cost of VPMulAccumulateReductionRecipe.
InstructionCost computeCost(ElementCount VF,
VPCostContext &Ctx) const override;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// Print the recipe.
void print(raw_ostream &O, const Twine &Indent,
VPSlotTracker &SlotTracker) const override;
#endif

Type *getResultType() const { return ResultTy; }

/// The first vector value to be extended and reduced.
VPValue *getVecOp0() const { return getOperand(1); }

/// The second vector value to be extended and reduced.
VPValue *getVecOp1() const { return getOperand(2); }

/// Return true if this recipe contains extended operands.
bool isExtended() const { return ExtOp != Instruction::CastOps::CastOpsEnd; }

/// Return the opcode of the extends for the operands.
Instruction::CastOps getExtOpcode() const { return ExtOp; }

/// Return if the operands are zero-extended.
bool isZExt() const { return ExtOp == Instruction::CastOps::ZExt; }

/// Return true if the operand extends have the non-negative flag.
bool isNonNeg() const { return IsNonNeg; }
};

/// VPReplicateRecipe replicates a given instruction producing multiple scalar
/// copies of the original scalar type, one per lane, instead of producing a
/// single copy of widened type for all lanes. If the instruction is known to be
Expand Down Expand Up @@ -2926,6 +2715,122 @@ class VPBranchOnMaskRecipe : public VPRecipeBase {
}
};

/// A recipe to combine multiple recipes into a 'bundle' recipe, which should be
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// A recipe to combine multiple recipes into a 'bundle' recipe, which should be
/// A recipe to combine multiple 'bundled' recipes into one 'bundle' recipe, which should be

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recipes aren't bundled until they're in the recipe so I don't think saying "multiple 'bundled' recipes" is correct. The original comment is correct in that the recipes are separate until they're bundled into this class.

/// considered a single entity for cost-modeling and transforms. The recipe
/// needs to be 'unbundled', i.e. replaced by its bundled recipes before
/// execute. The bundled recipes are completely disconnected from the def-use
/// graph of other, non-bundled recipes. Def-use edges between pairs of bundled
/// recipes remain intact, whereas every edge between a bundled and a
/// non-bundled recipe is elevated to connect the non-bundled recipe with the
/// VPSingleDefBundleRecipe itself.
class VPSingleDefBundleRecipe : public VPSingleDefRecipe {
/// Recipes bundled together in this VPSingleDefBundleRecipe.
SmallVector<VPSingleDefRecipe *> BundledRecipes;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could bundle multi-def recipes, perhaps excluding the last one if the bundle recipe is to have a single def.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this could be extended in the future. I left it as VPSingleDefRecipe for now initially, as it simplifies some of the bundle/unbundle logic


/// Temporary VPValues used for external operands of the bundle, i.e. operands
/// not defined by recipes in the bundle.
Comment on lines +2730 to +2731
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are placeholder VPValues defined temporarily by the bundle recipe itself to feed its bundled recipes. Note that the VPBundleRecipe, being a single def VPValue, feeds out-of bundle recipes itself.

SmallVector<VPValue *> BundleLiveInPlaceholders;

enum class BundleTypes {
/// Represents an inloop extended reduction operation, performing a
/// reduction on a extended vector operand into a scalar value, and adding
/// the result to a chain.
ExtendedReduction,
/// Represent an inloop multiply-accumulate reduction, multiplying the
/// extended vector operands, performing a reduction.add on the result, and
/// adding the scalar result to a chain.
ExtMulAccumulateReduction,
/// Represent an inloop multiply-accumulate reduction, multiplying the
/// vector operands, performing a reduction.add on the result, and adding
/// the scalar result to a chain.
MulAccumulateReduction,
};

/// Type of the bundle.
BundleTypes BundleType;
Comment on lines +2749 to +2750
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better placed next to the enum definition above, documenting each?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the definition down here, thanks


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth documenting, especially \p Operands - perhaps better called CloneOperands, perhaps defined to be optional, passed only to save time? Does it really save anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operands is gone now, moved logic from bundle() directly to the constructor, thanks

/// Construct a new VPSingleDefBundleRecipe by internalizing recipes in \p
/// BundledRecipes. External operands (i.e. not defined by another recipe in
/// the bundle) are replaced by temporary VPValues and the original operands
/// are transferred to the VPSingleDefBundleRecipe itself. Clone recipes as
/// needed (excluding last) to ensure they are only used by other recipes in
/// the bundle.
VPSingleDefBundleRecipe(BundleTypes BundleType,
ArrayRef<VPSingleDefRecipe *> ToBundle);

public:
VPSingleDefBundleRecipe(VPWidenCastRecipe *Ext, VPReductionRecipe *Red)
: VPSingleDefBundleRecipe(BundleTypes::ExtendedReduction, {Ext, Red}) {}
VPSingleDefBundleRecipe(VPWidenRecipe *Mul, VPReductionRecipe *Red)
: VPSingleDefBundleRecipe(BundleTypes::MulAccumulateReduction,
{Mul, Red}) {}
VPSingleDefBundleRecipe(VPWidenCastRecipe *Ext0, VPWidenCastRecipe *Ext1,
VPWidenRecipe *Mul, VPReductionRecipe *Red)
: VPSingleDefBundleRecipe(BundleTypes::ExtMulAccumulateReduction,
{Ext0, Ext1, Mul, Red}) {}

~VPSingleDefBundleRecipe() override {
SmallPtrSet<VPRecipeBase *, 4> Seen;
for (auto *R : reverse(BundledRecipes))
if (Seen.insert(R).second)
Comment on lines +2773 to +2775
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same recipe may be bundled twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, one example would be a MulAccumulateReduction with both extends are the same

delete R;
for (VPValue *T : BundleLiveInPlaceholders)
delete T;
}

VP_CLASSOF_IMPL(VPDef::VPBundleSC)

VPSingleDefBundleRecipe *clone() override {
assert(!BundledRecipes.empty() && "empty bundles should be removed");
SmallVector<VPSingleDefRecipe *> NewBundledRecipes;
for (auto *R : BundledRecipes)
NewBundledRecipes.push_back(R->clone());
for (auto *New : NewBundledRecipes) {
for (const auto &[Idx, Old] : enumerate(BundledRecipes))
New->replaceUsesOfWith(Old, NewBundledRecipes[Idx]);
// Update placeholder operands in the cloned recipe to use the external
// operands, to be internalized when the cloned bundle is constructed.
for (const auto &[Placeholder, OutsideOp] :
zip(BundleLiveInPlaceholders, operands()))
New->replaceUsesOfWith(Placeholder, OutsideOp);
}
return new VPSingleDefBundleRecipe(BundleType, NewBundledRecipes);
}

/// Return the VPValue to use to infer the result type of the recipe.
VPValue *getTypeVPValue() const {
unsigned OpIdx =
cast<VPReductionRecipe>(BundledRecipes.back())->isConditional() ? 2 : 1;
return getOperand(getNumOperands() - OpIdx);
}

/// Insert the bundled recipes back into the VPlan, directly before the
/// current recipe. Leaves the bundle recipe empty, which must be removed
/// before codegen.
void unbundle();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added thanks.


/// Method for generating code, must not be called as this recipe is abstract.
void execute(VPTransformState &State) override {
llvm_unreachable("recipe must be removed before execute");
}

InstructionCost computeCost(ElementCount VF,
VPCostContext &Ctx) const override;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// Print the recipe.
void print(raw_ostream &O, const Twine &Indent,
VPSlotTracker &SlotTracker) const override;
#endif

/// Returns true if this bundle contains recipes that may read from or write
/// to memory.
bool mayReadOrWriteMemory() const;

/// Returns true if this bundle contains recipes that may have side effects.
bool mayHaveSideEffects() const;
};

/// VPPredInstPHIRecipe is a recipe for generating the phi nodes needed when
/// control converges back from a Branch-on-Mask. The phi nodes are needed in
/// order to merge values that are set under such a branch and feed their uses.
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,13 +298,14 @@ Type *VPTypeAnalysis::inferScalarType(const VPValue *V) {
// TODO: Use info from interleave group.
return V->getUnderlyingValue()->getType();
})
.Case<VPExtendedReductionRecipe, VPMulAccumulateReductionRecipe>(
[](const auto *R) { return R->getResultType(); })
.Case<VPExpandSCEVRecipe>([](const VPExpandSCEVRecipe *R) {
return R->getSCEV()->getType();
})
.Case<VPReductionRecipe>([this](const auto *R) {
return inferScalarType(R->getChainOp());
})
.Case<VPSingleDefBundleRecipe>([this](const auto *R) {
return inferScalarType(R->getTypeVPValue());
});

assert(ResultTy && "could not infer type for the given VPValue");
Expand Down
Loading
Loading