Skip to content

[VPlan] Verify dominance for incoming values of phi-like recipes. #124838

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 24 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b357af4
[VPlan] Add VPPhiAccessors to provide interface for phi recipes (NFC)
fhahn Feb 27, 2025
9113022
Merge remote-tracking branch 'origin/main' into vplan-phi-accessors
fhahn Mar 13, 2025
c61233f
!fixup address latest comments, thanks
fhahn Mar 13, 2025
a73a137
Merge remote-tracking branch 'origin/main' into vplan-phi-accessors
fhahn Apr 23, 2025
90b2d4d
Merge remote-tracking branch 'origin/main' into vplan-phi-accessors
fhahn Apr 26, 2025
f768b97
!fixup address latest comments, thanks
fhahn Apr 26, 2025
d6d6bb5
Merge remote-tracking branch 'origin/main' into vplan-phi-accessors
fhahn Apr 28, 2025
8dfd569
Merge remote-tracking branch 'origin/main' into vplan-phi-accessors
fhahn May 1, 2025
595b057
!fixup address latest comments, thanks
fhahn May 1, 2025
a2b4ebb
Merge remote-tracking branch 'origin/main' into vplan-phi-accessors
fhahn May 3, 2025
46780ab
!fixup address comments, make getAsRecipe pure virtual.
fhahn May 3, 2025
5fee077
Use virtual
fhahn May 2, 2025
450901e
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 4, 2025
31eec38
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 11, 2025
60d5ec9
!fixup update after merging f2e62cfca5e571.
fhahn May 11, 2025
5554c6d
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 11, 2025
ba48230
!fixup fix formatting
fhahn May 11, 2025
8e60892
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 13, 2025
370eaf1
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 13, 2025
56d4784
!fxup improve verifier message, add verification test.
fhahn May 13, 2025
d4317a8
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 14, 2025
d371f10
!fixup address latest comments, thanks!
fhahn May 14, 2025
e5cf64d
Merge remote-tracking branch 'origin/main' into vplan-verify-def-use-phi
fhahn May 15, 2025
5fa41af
!fixup address latest comments, thanks!
fhahn May 15, 2025
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
58 changes: 55 additions & 3 deletions llvm/lib/Transforms/Vectorize/VPlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,9 @@ class VPPhiAccessors {
const VPBasicBlock *getIncomingBlock(unsigned Idx) const;

/// Returns the number of incoming values, also number of incoming blocks.
unsigned getNumIncoming() const { return getAsRecipe()->getNumOperands(); }
virtual unsigned getNumIncoming() const {
return getAsRecipe()->getNumOperands();
}

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// Print the recipe.
Expand Down Expand Up @@ -1234,7 +1236,7 @@ class VPIRInstruction : public VPRecipeBase {
/// 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 @@ -1251,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 @@ -1785,13 +1790,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 @@ -1980,6 +1987,11 @@ class VPWidenIntOrFpInductionRecipe : public VPWidenInductionRecipe {
return isUnrolled() ? getOperand(getNumOperands() - 2) : nullptr;
}

/// Returns the number of incoming values, also number of incoming blocks.
/// Note that at the moment, VPWidenIntOrFpInductionRecipes only have a single
/// incoming value, its start value.
unsigned getNumIncoming() const override { return 1; }

/// Returns the first defined value as TruncInst, if it is one or nullptr
/// otherwise.
TruncInst *getTruncInst() { return Trunc; }
Expand Down Expand Up @@ -3283,6 +3295,46 @@ class VPScalarIVStepsRecipe : public VPRecipeWithIRFlags,
}
};

/// Casting from VPRecipeBase -> VPPhiAccessors is supported for all recipe
/// types implementing VPPhiAccessors. Used by isa<> & co.
template <> struct CastIsPossible<VPPhiAccessors, const VPRecipeBase *> {
static inline bool isPossible(const VPRecipeBase *f) {
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
static inline bool isPossible(const VPRecipeBase *f) {
static inline bool isPossible(const VPRecipeBase *f) {
// TODO: include VPPredInstPHIRecipe too.

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

// TODO: include VPPredInstPHIRecipe too, once it implements VPPhiAccessors.
return isa<VPIRPhi, VPHeaderPHIRecipe, VPWidenPHIRecipe, VPPhi>(f);
Copy link
Collaborator

Choose a reason for hiding this comment

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

VPPredInstPHIRecipe might be another?

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, but I left it out for now because more work will be needed as there the operands also don't match the # of predecessors.

}
};
/// Support casting from VPRecipeBase -> VPPhiAccessors, by down-casting to the
/// recipe types implementing VPPhiAccessors. Used by cast<>, dyn_cast<> & co.
template <>
struct CastInfo<VPPhiAccessors, const VPRecipeBase *>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where is CastInfo used?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is used by cast/dyn_cast to perform the actual cast.

: public CastIsPossible<VPPhiAccessors, const VPRecipeBase *> {

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

/// doCast is used by cast<>.
static inline VPPhiAccessors *doCast(const VPRecipeBase *R) {
return const_cast<VPPhiAccessors *>([R]() -> const VPPhiAccessors * {
switch (R->getVPDefID()) {
case VPDef::VPInstructionSC:
return cast<VPPhi>(R);
case VPDef::VPIRInstructionSC:
return cast<VPIRPhi>(R);
case VPDef::VPWidenPHISC:
return cast<VPWidenPHIRecipe>(R);
default:
return cast<VPHeaderPHIRecipe>(R);
Comment on lines +3324 to +3325
Copy link
Collaborator

Choose a reason for hiding this comment

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

An unreachable default, or one that returns null for !isPossible cases, trigger const_cast failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

VPHeaderPhiRecipe includes multiple recipes, which is why I put it in the default, to avoid missing cases if new ones get added. The cast should already verify and assert that the cast is possible.

}
}());
}

/// doCastIfPossible is used by dyn_cast<>.
static inline VPPhiAccessors *doCastIfPossible(const VPRecipeBase *f) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where is doCastIfPossible used?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is used by dyn_cast

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
static inline VPPhiAccessors *doCastIfPossible(const VPRecipeBase *f) {
/// doCastIfPossible is used by dyn_cast.
static inline VPPhiAccessors *doCastIfPossible(const VPRecipeBase *f) {

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

if (!Self::isPossible(f))
return nullptr;
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
34 changes: 26 additions & 8 deletions llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
if (!verifyPhiRecipes(VPBB))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the dominance verification of phi-like recipes take place inside verifyPhiRecipes() above rather than here?
Does VPRecipeBase::isPhi() need to also consider isVPIRInstructionPhi()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure how we could move the new code to verifyPhiRecipes, as here we verify def before use property looking at the uses of a defined VPValue and when looking at the uses we need to handle phi-like recipes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

  // Verify that defs in VPBB dominate all their uses. The current
  // implementation is still incomplete.

does the above second sentence from below still hold?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should be complete now, updated, thanks!

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 @@ -220,12 +219,31 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {

for (const VPUser *U : V->users()) {
auto *UI = cast<VPRecipeBase>(U);
Copy link
Collaborator

Choose a reason for hiding this comment

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

unrelated nit:

Suggested change
auto *UI = cast<VPRecipeBase>(U);
auto *UR = cast<VPRecipeBase>(U);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix independently.

// 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))
if (auto *Phi = dyn_cast<VPPhiAccessors>(UI)) {
for (unsigned Idx = 0; Idx != Phi->getNumIncoming(); ++Idx) {
VPValue *IncomingVPV = Phi->getIncomingValue(Idx);
if (IncomingVPV != V)
continue;

const VPBasicBlock *IncomingVPBB = Phi->getIncomingBlock(Idx);
if (VPDT.dominates(VPBB, IncomingVPBB))
continue;

errs() << "Incoming def at index " << Idx
<< " does not dominate incoming block!\n";
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
VPSlotTracker Tracker(VPBB->getPlan());
IncomingVPV->getDefiningRecipe()->print(errs(), " ", Tracker);
errs() << "\n does not dominate " << IncomingVPBB->getName()
<< " for\n";
UI->print(errs(), " ", Tracker);
#endif
return false;
}
continue;
}
// TODO: Also verify VPPredInstPHIRecipe.
if (isa<VPPredInstPHIRecipe>(UI))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Anything to check?

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, added a TODO for now, thanks

continue;

// If the user is in the same block, check it comes after R in the
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
// If the user is in the same block, check it comes after R in the
// If the user is in the same block, check that it comes after R in the

Copy link
Collaborator

Choose a reason for hiding this comment

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

independent typo.

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 can fix separately, thanks

Expand Down
37 changes: 37 additions & 0 deletions llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,43 @@ TEST_F(VPVerifierTest, VPBlendUseBeforeDefDifferentBB) {
delete Phi;
}

TEST_F(VPVerifierTest, VPPhiIncomingValueDoesntDominateIncomingBlock) {
VPlan &Plan = getPlan();
IntegerType *Int32 = IntegerType::get(C, 32);
VPValue *Zero = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 0));

VPBasicBlock *VPBB1 = Plan.getEntry();
VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");

VPInstruction *DefI = new VPInstruction(Instruction::Add, {Zero});
VPPhi *Phi = new VPPhi({DefI}, {});
VPBB2->appendRecipe(Phi);
VPBB2->appendRecipe(DefI);
auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});
VPBB3->appendRecipe(CanIV);

VPRegionBlock *R1 = Plan.createVPRegionBlock(VPBB3, VPBB3, "R1");
VPBlockUtils::connectBlocks(VPBB1, VPBB2);
VPBlockUtils::connectBlocks(VPBB2, R1);
#if GTEST_HAS_STREAM_REDIRECTION
::testing::internal::CaptureStderr();
#endif
EXPECT_FALSE(verifyVPlanIsValid(Plan));
#if GTEST_HAS_STREAM_REDIRECTION
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
EXPECT_STREQ("Incoming def at index 0 does not dominate incoming block!\n"
" EMIT vp<%2> = add ir<0>\n"
" does not dominate preheader for\n"
" EMIT vp<%1> = phi [ vp<%2>, preheader ]",
::testing::internal::GetCapturedStderr().c_str());
#else
EXPECT_STREQ("Use before def!\n",
::testing::internal::GetCapturedStderr().c_str());
#endif
#endif
}

TEST_F(VPVerifierTest, DuplicateSuccessorsOutsideRegion) {
VPlan &Plan = getPlan();
VPValue *Zero = Plan.getOrAddLiveIn(ConstantInt::get(Type::getInt32Ty(C), 0));
Expand Down
Loading