Skip to content

[VPlan] Add VPPhiAccessors to provide interface for phi recipes (NFC) #138472

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 14 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
5 changes: 1 addition & 4 deletions llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2876,11 +2876,8 @@ void InnerLoopVectorizer::fixNonInductionPHIs(VPTransformState &State) {
PHINode *NewPhi = cast<PHINode>(State.get(VPPhi));
// Make sure the builder has a valid insert point.
Builder.SetInsertPoint(NewPhi);
for (unsigned Idx = 0; Idx < VPPhi->getNumIncoming(); ++Idx) {
VPValue *Inc = VPPhi->getIncomingValue(Idx);
const VPBasicBlock *VPBB = VPPhi->getIncomingBlock(Idx);
for (const auto &[Inc, VPBB] : VPPhi->incoming_values_and_blocks())
NewPhi->addIncoming(State.get(Inc), State.CFG.VPBB2IRBB[VPBB]);
}
}
}
}
Expand Down
92 changes: 88 additions & 4 deletions llvm/lib/Transforms/Vectorize/VPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -1189,15 +1189,54 @@ class VPPhiAccessors {
/// Returns the incoming block with index \p Idx.
const VPBasicBlock *getIncomingBlock(unsigned Idx) const;

/// Returns the number of incoming values, also number of incoming blocks.
unsigned getNumIncoming() const { return getAsRecipe()->getNumOperands(); }
unsigned getNumIncoming() const {
auto *R = getAsRecipe();
return R->getVPDefID() == VPDef::VPWidenIntOrFpInductionSC
? 1
: R->getNumOperands();
}

/// Returns an interator range over the incoming values
VPUser::const_operand_range incoming_values() const {
return getAsRecipe()->operands();
}

using const_incoming_block_iterator =
mapped_iterator<detail::index_iterator,
std::function<const VPBasicBlock *(size_t)>>;
using const_incoming_blocks_range =
iterator_range<const_incoming_block_iterator>;

const_incoming_block_iterator incoming_block_begin() const {
return const_incoming_block_iterator(
detail::index_iterator(0),
[this](size_t Idx) { return getIncomingBlock(Idx); });
}
const_incoming_block_iterator incoming_block_end() const {
return const_incoming_block_iterator(
detail::index_iterator(getNumIncoming()),
[this](size_t Idx) { return getIncomingBlock(Idx); });
}

/// Returns an iterator range over the incoming blocks.
const_incoming_blocks_range incoming_blocks() const {
return make_range(incoming_block_begin(), incoming_block_end());
}

/// Returns an iterator range over pairs of incoming values and corresponding
/// incoming blocks.
detail::zippy<llvm::detail::zip_shortest, VPUser::const_operand_range,
const_incoming_blocks_range>
incoming_values_and_blocks() const {
return zip(incoming_values(), incoming_blocks());
}
};

/// An overlay for VPIRInstructions wrapping PHI nodes enabling convenient use
/// cast/dyn_cast/isa and execute() implementation. A single VPValue operand is
/// allowed, and it is used to add a new incoming value for the single
/// predecessor VPBB.
struct VPIRPhi : public VPIRInstruction {
struct VPIRPhi : public VPIRInstruction, public VPPhiAccessors {
VPIRPhi(PHINode &PN) : VPIRInstruction(PN) {}

static inline bool classof(const VPRecipeBase *U) {
Expand All @@ -1214,6 +1253,9 @@ struct VPIRPhi : public VPIRInstruction {
void print(raw_ostream &O, const Twine &Indent,
VPSlotTracker &SlotTracker) const override;
#endif

protected:
const VPRecipeBase *getAsRecipe() const override { return this; }
};

/// Helper to manage IR metadata for recipes. It filters out metadata that
Expand Down Expand Up @@ -1739,13 +1781,15 @@ class VPVectorPointerRecipe : public VPRecipeWithIRFlags,
/// * VPWidenPointerInductionRecipe: Generate vector and scalar values for a
/// pointer induction. Produces either a vector PHI per-part or scalar values
/// per-lane based on the canonical induction.
class VPHeaderPHIRecipe : public VPSingleDefRecipe {
class VPHeaderPHIRecipe : public VPSingleDefRecipe, public VPPhiAccessors {
protected:
VPHeaderPHIRecipe(unsigned char VPDefID, Instruction *UnderlyingInstr,
VPValue *Start, DebugLoc DL = {})
: VPSingleDefRecipe(VPDefID, ArrayRef<VPValue *>({Start}), UnderlyingInstr, DL) {
}

const VPRecipeBase *getAsRecipe() const override { return this; }

public:
~VPHeaderPHIRecipe() override = default;

Expand Down Expand Up @@ -3222,6 +3266,46 @@ class VPScalarIVStepsRecipe : public VPRecipeWithIRFlags,
}
};

/// Casting from VPRecipeBase -> VPPhiAccessors is supported for all recipe
/// types implementing VPPhiAccessors.
template <> struct CastIsPossible<VPPhiAccessors, const VPRecipeBase *> {
static inline bool isPossible(const VPRecipeBase *f) {
return isa<VPIRPhi, VPHeaderPHIRecipe, VPWidenPHIRecipe>(f);
}
};
/// Support casting from VPRecipeBase -> VPPhiAccessors, by down-casting to the
/// recipe types implementing VPPhiAccessors.
template <>
struct CastInfo<VPPhiAccessors, const VPRecipeBase *>
: public CastIsPossible<VPPhiAccessors, const VPRecipeBase *> {

using Self = CastInfo<VPPhiAccessors, const VPRecipeBase *>;

using CastReturnType =
typename cast_retty<VPPhiAccessors, VPRecipeBase *>::ret_type;

static inline VPPhiAccessors *doCast(const VPRecipeBase *R) {
return const_cast<VPPhiAccessors *>([R]() -> const VPPhiAccessors * {
switch (R->getVPDefID()) {
case VPDef::VPIRInstructionSC:
return cast<VPIRPhi>(R);
case VPDef::VPWidenPHISC:
return cast<VPWidenPHIRecipe>(R);
default:
return cast<VPHeaderPHIRecipe>(R);
}
}());
}

static inline VPPhiAccessors *castFailed() { return nullptr; }

static inline VPPhiAccessors *doCastIfPossible(const VPRecipeBase *f) {
if (!Self::isPossible(f))
return castFailed();
return doCast(f);
}
};

/// VPBasicBlock serves as the leaf of the Hierarchical Control-Flow Graph. It
/// holds a sequence of zero or more VPRecipe's each representing a sequence of
/// output IR instructions. All PHI-like recipes must come before any non-PHI recipes.
Expand Down
12 changes: 6 additions & 6 deletions llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,12 +1194,12 @@ void VPIRPhi::print(raw_ostream &O, const Twine &Indent,

if (getNumOperands() != 0) {
O << " (extra operand" << (getNumOperands() > 1 ? "s" : "") << ": ";
interleaveComma(
enumerate(operands()), O, [this, &O, &SlotTracker](auto Op) {
Op.value()->printAsOperand(O, SlotTracker);
O << " from ";
getParent()->getPredecessors()[Op.index()]->printAsOperand(O);
});
interleaveComma(incoming_values_and_blocks(), O,
[&O, &SlotTracker](auto Op) {
std::get<0>(Op)->printAsOperand(O, SlotTracker);
O << " from ";
std::get<1>(Op)->printAsOperand(O);
});
O << ")";
}
}
Expand Down
36 changes: 23 additions & 13 deletions llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
if (!verifyPhiRecipes(VPBB))
return false;

// Verify that defs in VPBB dominate all their uses. The current
// implementation is still incomplete.
// Verify that defs in VPBB dominate all their uses.
DenseMap<const VPRecipeBase *, unsigned> RecipeNumbering;
unsigned Cnt = 0;
for (const VPRecipeBase &R : *VPBB)
Expand All @@ -207,25 +206,36 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {

for (const VPUser *U : V->users()) {
auto *UI = cast<VPRecipeBase>(U);
// TODO: check dominance of incoming values for phis properly.
if (!UI ||
isa<VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPredInstPHIRecipe,
VPIRPhi>(UI) ||
(isa<VPInstruction>(UI) &&
cast<VPInstruction>(UI)->getOpcode() == Instruction::PHI))
const VPBlockBase *UserVPBB = UI->getParent();
if (auto *Phi = dyn_cast<VPPhiAccessors>(UI)) {
for (const auto &[IncVPV, IncVPBB] :
Phi->incoming_values_and_blocks()) {
if (IncVPV != V)
continue;
if (IncVPBB != VPBB && !VPDT.dominates(VPBB, IncVPBB)) {
errs() << "Use before def!\n";
return false;
}
}
continue;
}
if (isa<VPPredInstPHIRecipe>(UI))
continue;

// If the user is in the same block, check it comes after R in the
// block.
if (UI->getParent() == VPBB) {
if (auto *VPI = dyn_cast<VPInstruction>(UI)) {
if (VPI->getOpcode() == Instruction::PHI)
continue;
}
// If the user is in the same block, check it comes after R in
// the block.
if (UserVPBB == VPBB) {
if (RecipeNumbering[UI] < RecipeNumbering[&R]) {
errs() << "Use before def!\n";
return false;
}
continue;
}

if (!VPDT.dominates(VPBB, UI->getParent())) {
if (!VPDT.dominates(VPBB, UserVPBB)) {
errs() << "Use before def!\n";
return false;
}
Expand Down