Skip to content

[VPlan] Separate out logic to manage IR flags to VPIRFlags (NFC). #140621

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

Merged
merged 5 commits into from
May 25, 2025
Merged
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
32 changes: 14 additions & 18 deletions llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,25 +164,19 @@ class VPBuilder {
DebugLoc DL, const Twine &Name = "") {
return createInstruction(Opcode, Operands, DL, Name);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, also above between lines 162,163

Suggested change
}
}

VPInstruction *createNaryOp(unsigned Opcode,
std::initializer_list<VPValue *> Operands,
std::optional<FastMathFlags> FMFs = {},
DebugLoc DL = {}, const Twine &Name = "") {
if (FMFs)
return tryInsertInstruction(
new VPInstruction(Opcode, Operands, *FMFs, DL, Name));
return createInstruction(Opcode, Operands, DL, Name);
VPInstruction *createNaryOp(unsigned Opcode, ArrayRef<VPValue *> Operands,
const VPIRFlags &Flags, DebugLoc DL = {},
const Twine &Name = "") {
return tryInsertInstruction(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consistency nit: worth overloading createInstruction() with a Flags-supporting version, to be used here and below?

new VPInstruction(Opcode, Operands, Flags, DL, Name));
}

VPInstruction *createNaryOp(unsigned Opcode,
std::initializer_list<VPValue *> Operands,
Type *ResultTy,
std::optional<FastMathFlags> FMFs = {},
Type *ResultTy, const VPIRFlags &Flags = {},
DebugLoc DL = {}, const Twine &Name = "") {
if (FMFs)
return tryInsertInstruction(new VPInstructionWithType(
Opcode, Operands, ResultTy, *FMFs, DL, Name));
return tryInsertInstruction(
new VPInstructionWithType(Opcode, Operands, ResultTy, DL, Name));
new VPInstructionWithType(Opcode, Operands, ResultTy, Flags, DL, Name));
}

VPInstruction *createOverflowingOp(unsigned Opcode,
Expand Down Expand Up @@ -236,18 +230,20 @@ class VPBuilder {
assert(Pred >= CmpInst::FIRST_ICMP_PREDICATE &&
Pred <= CmpInst::LAST_ICMP_PREDICATE && "invalid predicate");
return tryInsertInstruction(
new VPInstruction(Instruction::ICmp, Pred, A, B, DL, Name));
new VPInstruction(Instruction::ICmp, {A, B}, Pred, DL, Name));
}

VPInstruction *createPtrAdd(VPValue *Ptr, VPValue *Offset, DebugLoc DL = {},
const Twine &Name = "") {
return tryInsertInstruction(
new VPInstruction(Ptr, Offset, GEPNoWrapFlags::none(), DL, Name));
new VPInstruction(VPInstruction::PtrAdd, {Ptr, Offset},
GEPNoWrapFlags::none(), DL, Name));
}
VPValue *createInBoundsPtrAdd(VPValue *Ptr, VPValue *Offset, DebugLoc DL = {},
const Twine &Name = "") {
return tryInsertInstruction(
new VPInstruction(Ptr, Offset, GEPNoWrapFlags::inBounds(), DL, Name));
new VPInstruction(VPInstruction::PtrAdd, {Ptr, Offset},
GEPNoWrapFlags::inBounds(), DL, Name));
}

VPInstruction *createScalarPhi(ArrayRef<VPValue *> IncomingValues,
Expand All @@ -269,7 +265,7 @@ class VPBuilder {
VPInstruction *createScalarCast(Instruction::CastOps Opcode, VPValue *Op,
Type *ResultTy, DebugLoc DL) {
return tryInsertInstruction(
new VPInstructionWithType(Opcode, Op, ResultTy, DL));
new VPInstructionWithType(Opcode, Op, ResultTy, {}, DL));
}

VPWidenCastRecipe *createWidenCast(Instruction::CastOps Opcode, VPValue *Op,
Expand Down
205 changes: 83 additions & 122 deletions llvm/lib/Transforms/Vectorize/VPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,8 @@ class VPSingleDefRecipe : public VPRecipeBase, public VPValue {
#endif
};

/// Class to record LLVM IR flag for a recipe along with it.
class VPRecipeWithIRFlags : public VPSingleDefRecipe {
/// Class to record and manage LLVM IR flags.
class VPIRFlags {
enum class OperationType : unsigned char {
Cmp,
OverflowingBinOp,
Expand Down Expand Up @@ -637,23 +637,10 @@ class VPRecipeWithIRFlags : public VPSingleDefRecipe {
unsigned AllFlags;
};

protected:
void transferFlags(VPRecipeWithIRFlags &Other) {
OpType = Other.OpType;
AllFlags = Other.AllFlags;
}

public:
VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL) {
OpType = OperationType::Other;
AllFlags = 0;
}
VPIRFlags() : OpType(OperationType::Other), AllFlags(0) {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be good to document the interface here and below.


VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
Instruction &I)
: VPSingleDefRecipe(SC, Operands, &I, I.getDebugLoc()) {
VPIRFlags(Instruction &I) {
if (auto *Op = dyn_cast<CmpInst>(&I)) {
OpType = OperationType::Cmp;
CmpPredicate = Op->getPredicate();
Expand Down Expand Up @@ -681,63 +668,27 @@ class VPRecipeWithIRFlags : public VPSingleDefRecipe {
}
}

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
CmpInst::Predicate Pred, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), OpType(OperationType::Cmp),
CmpPredicate(Pred) {}
VPIRFlags(CmpInst::Predicate Pred)
: OpType(OperationType::Cmp), CmpPredicate(Pred) {}

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
WrapFlagsTy WrapFlags, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL),
OpType(OperationType::OverflowingBinOp), WrapFlags(WrapFlags) {}
VPIRFlags(WrapFlagsTy WrapFlags)
: OpType(OperationType::OverflowingBinOp), WrapFlags(WrapFlags) {}

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
FastMathFlags FMFs, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), OpType(OperationType::FPMathOp),
FMFs(FMFs) {}
VPIRFlags(FastMathFlags FMFs) : OpType(OperationType::FPMathOp), FMFs(FMFs) {}

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
DisjointFlagsTy DisjointFlags, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), OpType(OperationType::DisjointOp),
DisjointFlags(DisjointFlags) {}
VPIRFlags(DisjointFlagsTy DisjointFlags)
: OpType(OperationType::DisjointOp), DisjointFlags(DisjointFlags) {}

template <typename IterT>
VPRecipeWithIRFlags(const unsigned char SC, IterT Operands,
NonNegFlagsTy NonNegFlags, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), OpType(OperationType::NonNegOp),
NonNegFlags(NonNegFlags) {}
VPIRFlags(NonNegFlagsTy NonNegFlags)
: OpType(OperationType::NonNegOp), NonNegFlags(NonNegFlags) {}

protected:
VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
GEPNoWrapFlags GEPFlags, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), OpType(OperationType::GEPOp),
GEPFlags(GEPFlags) {}
VPIRFlags(GEPNoWrapFlags GEPFlags)
: OpType(OperationType::GEPOp), GEPFlags(GEPFlags) {}

public:
static inline bool classof(const VPRecipeBase *R) {
return R->getVPDefID() == VPRecipeBase::VPInstructionSC ||
R->getVPDefID() == VPRecipeBase::VPWidenSC ||
R->getVPDefID() == VPRecipeBase::VPWidenGEPSC ||
R->getVPDefID() == VPRecipeBase::VPWidenCallSC ||
R->getVPDefID() == VPRecipeBase::VPWidenCastSC ||
R->getVPDefID() == VPRecipeBase::VPWidenIntrinsicSC ||
R->getVPDefID() == VPRecipeBase::VPReductionSC ||
R->getVPDefID() == VPRecipeBase::VPReductionEVLSC ||
R->getVPDefID() == VPRecipeBase::VPReplicateSC ||
R->getVPDefID() == VPRecipeBase::VPVectorEndPointerSC ||
R->getVPDefID() == VPRecipeBase::VPVectorPointerSC ||
R->getVPDefID() == VPRecipeBase::VPExtendedReductionSC ||
R->getVPDefID() == VPRecipeBase::VPMulAccumulateReductionSC;
}

static inline bool classof(const VPUser *U) {
auto *R = dyn_cast<VPRecipeBase>(U);
return R && classof(R);
}

static inline bool classof(const VPValue *V) {
auto *R = dyn_cast_or_null<VPRecipeBase>(V->getDefiningRecipe());
return R && classof(R);
void transferFlags(VPIRFlags &Other) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Now transferFlags() needs to be public rather than protected?

Used only by recipe clone()'s - can it be replaced by copy constructor (or calls to flags-supporting constructors), as done in VPIRMetadata.

OpType = Other.OpType;
AllFlags = Other.AllFlags;
}

/// Drop all poison-generating flags.
Expand Down Expand Up @@ -851,11 +802,60 @@ class VPRecipeWithIRFlags : public VPSingleDefRecipe {
return DisjointFlags.IsDisjoint;
}

#if !defined(NDEBUG)
/// Returns true if the set flags are valid for \p Opcode.
bool flagsValidForOpcode(unsigned Opcode) const;
#endif

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void printFlags(raw_ostream &O) const;
#endif
};

/// A pure-virtual common base class for recipes defining a single VPValue and
/// using IR flags.
struct VPRecipeWithIRFlags : public VPSingleDefRecipe, public VPIRFlags {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This retains existing name, but perhaps VPSingleDefWithIRFlags would be more accurate, following its documentation above. Or VPSingleDefWithFlags, considering IR Flags are wrapped in VPIRFlags (along with condition predicates and potentially other constants).

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), VPIRFlags() {}

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
Instruction &I)
: VPSingleDefRecipe(SC, Operands, &I, I.getDebugLoc()), VPIRFlags(I) {}

VPRecipeWithIRFlags(const unsigned char SC, ArrayRef<VPValue *> Operands,
const VPIRFlags &Flags, DebugLoc DL = {})
: VPSingleDefRecipe(SC, Operands, DL), VPIRFlags(Flags) {}

static inline bool classof(const VPRecipeBase *R) {
return R->getVPDefID() == VPRecipeBase::VPInstructionSC ||
R->getVPDefID() == VPRecipeBase::VPWidenSC ||
R->getVPDefID() == VPRecipeBase::VPWidenGEPSC ||
R->getVPDefID() == VPRecipeBase::VPWidenCallSC ||
R->getVPDefID() == VPRecipeBase::VPWidenCastSC ||
R->getVPDefID() == VPRecipeBase::VPWidenIntrinsicSC ||
R->getVPDefID() == VPRecipeBase::VPReductionSC ||
R->getVPDefID() == VPRecipeBase::VPReductionEVLSC ||
R->getVPDefID() == VPRecipeBase::VPReplicateSC ||
R->getVPDefID() == VPRecipeBase::VPVectorEndPointerSC ||
R->getVPDefID() == VPRecipeBase::VPVectorPointerSC ||
R->getVPDefID() == VPRecipeBase::VPExtendedReductionSC ||
R->getVPDefID() == VPRecipeBase::VPMulAccumulateReductionSC;
}

static inline bool classof(const VPUser *U) {
auto *R = dyn_cast<VPRecipeBase>(U);
return R && classof(R);
}

static inline bool classof(const VPValue *V) {
auto *R = dyn_cast_or_null<VPRecipeBase>(V->getDefiningRecipe());
return R && classof(R);
}

void execute(VPTransformState &State) override = 0;
};

/// Helper to access the operand that contains the unroll part for this recipe
/// after unrolling.
template <unsigned PartOpIdx> class VPUnrollPartAccessor {
Expand Down Expand Up @@ -958,54 +958,21 @@ class VPInstruction : public VPRecipeWithIRFlags,
/// value for lane \p Lane.
Value *generatePerLane(VPTransformState &State, const VPLane &Lane);

#if !defined(NDEBUG)
/// Return true if the VPInstruction is a floating point math operation, i.e.
/// has fast-math flags.
bool isFPMathOp() const;
#endif

public:
VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands, DebugLoc DL,
VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands, DebugLoc DL = {},
const Twine &Name = "")
: VPRecipeWithIRFlags(VPDef::VPInstructionSC, Operands, DL),
Opcode(Opcode), Name(Name.str()) {}

VPInstruction(unsigned Opcode, std::initializer_list<VPValue *> Operands,
DebugLoc DL = {}, const Twine &Name = "")
: VPInstruction(Opcode, ArrayRef<VPValue *>(Operands), DL, Name) {}

VPInstruction(unsigned Opcode, CmpInst::Predicate Pred, VPValue *A,
VPValue *B, DebugLoc DL = {}, const Twine &Name = "");

VPInstruction(unsigned Opcode, std::initializer_list<VPValue *> Operands,
WrapFlagsTy WrapFlags, DebugLoc DL = {}, const Twine &Name = "")
: VPRecipeWithIRFlags(VPDef::VPInstructionSC, Operands, WrapFlags, DL),
Opcode(Opcode), Name(Name.str()) {}

VPInstruction(unsigned Opcode, std::initializer_list<VPValue *> Operands,
DisjointFlagsTy DisjointFlag, DebugLoc DL = {},
const Twine &Name = "")
: VPRecipeWithIRFlags(VPDef::VPInstructionSC, Operands, DisjointFlag, DL),
Opcode(Opcode), Name(Name.str()) {
assert(Opcode == Instruction::Or && "only OR opcodes can be disjoint");
}

VPInstruction(VPValue *Ptr, VPValue *Offset, GEPNoWrapFlags Flags,
DebugLoc DL = {}, const Twine &Name = "")
: VPRecipeWithIRFlags(VPDef::VPInstructionSC,
ArrayRef<VPValue *>({Ptr, Offset}), Flags, DL),
Opcode(VPInstruction::PtrAdd), Name(Name.str()) {}

VPInstruction(unsigned Opcode, std::initializer_list<VPValue *> Operands,
FastMathFlags FMFs, DebugLoc DL = {}, const Twine &Name = "");
VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands,
const VPIRFlags &Flags, DebugLoc DL = {},
const Twine &Name = "");

VP_CLASSOF_IMPL(VPDef::VPInstructionSC)

VPInstruction *clone() override {
SmallVector<VPValue *, 2> Operands(operands());
auto *New = new VPInstruction(Opcode, Operands, getDebugLoc(), Name);
New->transferFlags(*this);
return New;
return new VPInstruction(Opcode, Operands, *this, getDebugLoc(), Name);
Copy link
Collaborator

Choose a reason for hiding this comment

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

would this

Suggested change
return new VPInstruction(Opcode, Operands, *this, getDebugLoc(), Name);
return new VPInstruction(Opcode, operands(), *this, getDebugLoc(), Name);

also work? Additional instances below.

}

unsigned getOpcode() const { return Opcode; }
Expand Down Expand Up @@ -1082,13 +1049,9 @@ class VPInstructionWithType : public VPInstruction {

public:
VPInstructionWithType(unsigned Opcode, ArrayRef<VPValue *> Operands,
Type *ResultTy, DebugLoc DL, const Twine &Name = "")
: VPInstruction(Opcode, Operands, DL, Name), ResultTy(ResultTy) {}
VPInstructionWithType(unsigned Opcode,
std::initializer_list<VPValue *> Operands,
Type *ResultTy, FastMathFlags FMFs, DebugLoc DL = {},
Type *ResultTy, const VPIRFlags &Flags, DebugLoc DL,
const Twine &Name = "")
: VPInstruction(Opcode, Operands, FMFs, DL, Name), ResultTy(ResultTy) {}
: VPInstruction(Opcode, Operands, Flags, DL, Name), ResultTy(ResultTy) {}

static inline bool classof(const VPRecipeBase *R) {
// VPInstructionWithType are VPInstructions with specific opcodes requiring
Expand All @@ -1113,8 +1076,9 @@ class VPInstructionWithType : public VPInstruction {

VPInstruction *clone() override {
SmallVector<VPValue *, 2> Operands(operands());
auto *New = new VPInstructionWithType(
getOpcode(), Operands, getResultType(), getDebugLoc(), getName());
auto *New =
new VPInstructionWithType(getOpcode(), Operands, getResultType(), *this,
getDebugLoc(), getName());
New->setUnderlyingValue(getUnderlyingValue());
return New;
}
Expand Down Expand Up @@ -1373,15 +1337,12 @@ class VPWidenCastRecipe : public VPRecipeWithIRFlags, public VPIRMetadata {
}

VPWidenCastRecipe(Instruction::CastOps Opcode, VPValue *Op, Type *ResultTy,
DebugLoc DL = {})
: VPRecipeWithIRFlags(VPDef::VPWidenCastSC, Op, DL), VPIRMetadata(),
Opcode(Opcode), ResultTy(ResultTy) {}

VPWidenCastRecipe(Instruction::CastOps Opcode, VPValue *Op, Type *ResultTy,
bool IsNonNeg, DebugLoc DL = {})
: VPRecipeWithIRFlags(VPDef::VPWidenCastSC, Op, NonNegFlagsTy(IsNonNeg),
DL),
Opcode(Opcode), ResultTy(ResultTy) {}
const VPIRFlags &Flags = {}, DebugLoc DL = {})
Copy link
Contributor

Choose a reason for hiding this comment

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

I presume changing the bool to VPIRFlags is to bring it inline with VPInstructionWithType, so we can replace it in #129712?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The immediate case is #140623 (using VPInstructionWithType for uniform recipes), but it will also be useful for #129712, once I can get around to updating it

: VPRecipeWithIRFlags(VPDef::VPWidenCastSC, Op, Flags, DL),
VPIRMetadata(), Opcode(Opcode), ResultTy(ResultTy) {
assert(flagsValidForOpcode(Opcode) &&
"Set flags not supported for the provided opcode");
}

~VPWidenCastRecipe() override = default;

Expand Down
Loading
Loading