-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-vectorizers Author: Florian Hahn (fhahn) ChangesUpdate the verifier to verify dominance for incoming values for phi-like recipes. The defining recipe must dominate the incoming block for the incoming value. There are 4 different cases to consider when retrieving the incoming block:
Full diff: https://github.com/llvm/llvm-project/pull/124838.diff 1 Files Affected:
diff --git a/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp b/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
index 0f151c897d938e..0948452e8d19cf 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
@@ -175,6 +175,11 @@ bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const {
});
}
+/// Return true if \p R is a VPIRInstruction wrapping a phi.
+static bool isVPIRInstructionPhi(const VPRecipeBase &R) {
+ auto *VPIRI = dyn_cast<VPIRInstruction>(&R);
+ return VPIRI && isa<PHINode>(VPIRI->getInstruction());
+}
bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
if (!verifyPhiRecipes(VPBB))
return false;
@@ -207,14 +212,57 @@ 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>(UI))
+ const VPBlockBase *UserVPBB = UI->getParent();
+
+ // Verify incoming values of VPIRInstructions wrapping phis. V most
+ // dominate the end of the incoming block. The operand index of the
+ // incoming value matches the predecessor block index of the
+ // corresponding incoming block.
+ if (isVPIRInstructionPhi(*UI)) {
+ for (const auto &[Idx, Op] : enumerate(UI->operands())) {
+ if (V != Op)
+ continue;
+ const VPBlockBase *Incoming = UserVPBB->getPredecessors()[Idx];
+ if (Incoming != VPBB && !VPDT.dominates(VPBB, Incoming)) {
+ errs() << "Use before def!\n";
+ return false;
+ }
+ }
continue;
+ }
+ // Verify incoming value of various phi-like recipes.
+ if (isa<VPWidenPHIRecipe, VPHeaderPHIRecipe, VPPredInstPHIRecipe>(UI)) {
+ const VPBlockBase *Incoming = nullptr;
+ // Get the incoming block based on the number of predecessors.
+ if (UserVPBB->getNumPredecessors() == 0) {
+ assert((isa<VPWidenPHIRecipe>(UI) || isa<VPHeaderPHIRecipe>(UI)) &&
+ "Unexpected recipe with 0 predecessors");
+ const VPRegionBlock *UserRegion = UserVPBB->getParent();
+ Incoming = V == UI->getOperand(0)
+ ? UserRegion->getSinglePredecessor()
+ : UserRegion->getExiting();
+ } else if (UserVPBB->getNumPredecessors() == 1) {
+ assert(isa<VPWidenPHIRecipe>(UI) &&
+ "Unexpected recipe with 1 predecessors");
+ Incoming = UserVPBB->getSinglePredecessor();
+ } else {
+ assert(UserVPBB->getNumPredecessors() == 2 &&
+ isa<VPPredInstPHIRecipe>(UI) &&
+ "Unexpected recipe with 2 or more predecessors");
+ Incoming = UserVPBB->getPredecessors()[1];
+ }
+ if (auto *R = dyn_cast<VPRegionBlock>(Incoming))
+ Incoming = R->getExiting();
+ if (Incoming != VPBB && !VPDT.dominates(VPBB, Incoming)) {
+ errs() << "Use before def!\n";
+ return false;
+ }
+ continue;
+ }
// If the user is in the same block, check it comes after R in the
// block.
- if (UI->getParent() == VPBB) {
+ if (UserVPBB == VPBB) {
if (RecipeNumbering[UI] < RecipeNumbering[&R]) {
errs() << "Use before def!\n";
return false;
@@ -222,7 +270,7 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
continue;
}
- if (!VPDT.dominates(VPBB, UI->getParent())) {
+ if (!VPDT.dominates(VPBB, UserVPBB)) {
errs() << "Use before def!\n";
return false;
}
|
@llvm/pr-subscribers-llvm-transforms Author: Florian Hahn (fhahn) ChangesUpdate the verifier to verify dominance for incoming values for phi-like recipes. The defining recipe must dominate the incoming block for the incoming value. There are 4 different cases to consider when retrieving the incoming block:
Full diff: https://github.com/llvm/llvm-project/pull/124838.diff 1 Files Affected:
diff --git a/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp b/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
index 0f151c897d938e..0948452e8d19cf 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
@@ -175,6 +175,11 @@ bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const {
});
}
+/// Return true if \p R is a VPIRInstruction wrapping a phi.
+static bool isVPIRInstructionPhi(const VPRecipeBase &R) {
+ auto *VPIRI = dyn_cast<VPIRInstruction>(&R);
+ return VPIRI && isa<PHINode>(VPIRI->getInstruction());
+}
bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
if (!verifyPhiRecipes(VPBB))
return false;
@@ -207,14 +212,57 @@ 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>(UI))
+ const VPBlockBase *UserVPBB = UI->getParent();
+
+ // Verify incoming values of VPIRInstructions wrapping phis. V most
+ // dominate the end of the incoming block. The operand index of the
+ // incoming value matches the predecessor block index of the
+ // corresponding incoming block.
+ if (isVPIRInstructionPhi(*UI)) {
+ for (const auto &[Idx, Op] : enumerate(UI->operands())) {
+ if (V != Op)
+ continue;
+ const VPBlockBase *Incoming = UserVPBB->getPredecessors()[Idx];
+ if (Incoming != VPBB && !VPDT.dominates(VPBB, Incoming)) {
+ errs() << "Use before def!\n";
+ return false;
+ }
+ }
continue;
+ }
+ // Verify incoming value of various phi-like recipes.
+ if (isa<VPWidenPHIRecipe, VPHeaderPHIRecipe, VPPredInstPHIRecipe>(UI)) {
+ const VPBlockBase *Incoming = nullptr;
+ // Get the incoming block based on the number of predecessors.
+ if (UserVPBB->getNumPredecessors() == 0) {
+ assert((isa<VPWidenPHIRecipe>(UI) || isa<VPHeaderPHIRecipe>(UI)) &&
+ "Unexpected recipe with 0 predecessors");
+ const VPRegionBlock *UserRegion = UserVPBB->getParent();
+ Incoming = V == UI->getOperand(0)
+ ? UserRegion->getSinglePredecessor()
+ : UserRegion->getExiting();
+ } else if (UserVPBB->getNumPredecessors() == 1) {
+ assert(isa<VPWidenPHIRecipe>(UI) &&
+ "Unexpected recipe with 1 predecessors");
+ Incoming = UserVPBB->getSinglePredecessor();
+ } else {
+ assert(UserVPBB->getNumPredecessors() == 2 &&
+ isa<VPPredInstPHIRecipe>(UI) &&
+ "Unexpected recipe with 2 or more predecessors");
+ Incoming = UserVPBB->getPredecessors()[1];
+ }
+ if (auto *R = dyn_cast<VPRegionBlock>(Incoming))
+ Incoming = R->getExiting();
+ if (Incoming != VPBB && !VPDT.dominates(VPBB, Incoming)) {
+ errs() << "Use before def!\n";
+ return false;
+ }
+ continue;
+ }
// If the user is in the same block, check it comes after R in the
// block.
- if (UI->getParent() == VPBB) {
+ if (UserVPBB == VPBB) {
if (RecipeNumbering[UI] < RecipeNumbering[&R]) {
errs() << "Use before def!\n";
return false;
@@ -222,7 +270,7 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) {
continue;
}
- if (!VPDT.dominates(VPBB, UI->getParent())) {
+ if (!VPDT.dominates(VPBB, UserVPBB)) {
errs() << "Use before def!\n";
return false;
}
|
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.
Thanks for this - looks sensible to me. I just had a couple of comments.
static bool isVPIRInstructionPhi(const VPRecipeBase &R) { | ||
auto *VPIRI = dyn_cast<VPIRInstruction>(&R); | ||
return VPIRI && isa<PHINode>(VPIRI->getInstruction()); | ||
} |
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.
nit: Add new line after function?
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.
Adde a newline, thanks
"Unexpected recipe with 2 or more predecessors"); | ||
Incoming = UserVPBB->getPredecessors()[1]; | ||
} | ||
if (auto *R = dyn_cast<VPRegionBlock>(Incoming)) |
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.
Just out of curiosity, what's the scenario where the incoming block for a VPWidenPHIRecipe, VPHeaderPHIRecipe or VPPredInstPHIRecipe recipe would be the region? Is it the recipes we create in the middle block? If so, could this check potentially fail with uncountable early exits, i.e. the incoming block is effectively the middle.split block? I assume it's fine, but just wondering if there are missing tests for all the scenarios.
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 only case at the moment is in VPWidenPHIRecipes added in the VPlan-native path. I added an assert; it cannot happen in the inner-loop vectorizer path.
29aa9b0
to
695ef3c
Compare
695ef3c
to
129e8ad
Compare
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.
ping :)
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.
Good to see this TODO being addressed, adding various comments.
@@ -207,24 +213,69 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) { | |||
|
|||
for (const VPUser *U : V->users()) { | |||
auto *UI = cast<VPRecipeBase>(U); |
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.
unrelated nit:
auto *UI = cast<VPRecipeBase>(U); | |
auto *UR = cast<VPRecipeBase>(U); |
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.
Will fix independently.
isa<PHINode>(cast<VPIRInstruction>(UI)->getInstruction()))) | ||
const VPBlockBase *UserVPBB = UI->getParent(); | ||
|
||
// Verify incoming values of VPIRInstructions wrapping phis. V most |
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.
// Verify incoming values of VPIRInstructions wrapping phis. V most | |
// Verify incoming values of VPIRInstructions wrapping phis. V must |
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.
fixed, thanks
|
||
// Verify incoming values of VPIRInstructions wrapping phis. V most | ||
// dominate the end of the incoming block. The operand index of the | ||
// incoming value matches the predecessor block index of the |
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.
// incoming value matches the predecessor block index of the | |
// incoming value must match the predecessor block index of the |
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.
Done thanks!
// incoming value matches the predecessor block index of the | ||
// corresponding incoming block. | ||
if (isVPIRInstructionPhi(*UI)) { | ||
for (const auto &[Idx, Op] : enumerate(UI->operands())) { |
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.
for (const auto &[Idx, Op] : enumerate(UI->operands())) { | |
for (const auto &[Index, OpAtIndex] : enumerate(UI->operands())) { |
for (const auto &[Idx, Op] : enumerate(UI->operands())) { | ||
if (V != Op) | ||
continue; | ||
const VPBlockBase *Incoming = UserVPBB->getPredecessors()[Idx]; |
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.
const VPBlockBase *Incoming = UserVPBB->getPredecessors()[Idx]; | |
const VPBlockBase *PredAtIndex = UserVPBB->getPredecessors()[Idx]; |
if (isa<VPWidenPHIRecipe, VPHeaderPHIRecipe, VPPredInstPHIRecipe>(UI)) { | ||
const VPBlockBase *Incoming = nullptr; | ||
// Get the incoming block based on the number of predecessors. |
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.
nit: better switch over the types of recipes involved, asserting the number of predecessors, than vice-versa? Nearly every recipe seems to be doing its own thing anyhow.
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.
Code has been replaced by type-switch and using incoming values/blocks iterator.
"Unexpected recipe with 2 or more predecessors"); | ||
Incoming = UserVPBB->getPredecessors()[1]; | ||
} | ||
if (auto *R = dyn_cast<VPRegionBlock>(Incoming)) { |
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.
if (auto *R = dyn_cast<VPRegionBlock>(Incoming)) { | |
if (auto *IncomingRegion = dyn_cast<VPRegionBlock>(Incoming)) { |
to avoid confusion with R
which is also being used for the current Recipe.
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.
Code is gone in latest version
"VPWidenPHIRecipe"); | ||
Incoming = R->getExiting(); | ||
} | ||
if (Incoming != VPBB && !VPDT.dominates(VPBB, Incoming)) { |
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.
Same comment as above.
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.
Code is gone in latest version
// dominate the end of the incoming block. The operand index of the | ||
// incoming value matches the predecessor block index of the | ||
// corresponding incoming block. | ||
if (isVPIRInstructionPhi(*UI)) { |
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.
Can the case of VPIRInstruction be handled along with all other phi recipes below?
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.
There are cases where the block containing a VPIR Phi has multiple predecessors (some cases with early exits), for those we need the proper phi-handling
return false; | ||
} | ||
continue; | ||
} | ||
|
||
// If the user is in the same block, check it comes after R in the |
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.
// 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 |
Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks, with corresponding iterators. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838.
Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks, with corresponding iterators. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838.
129e8ad
to
bbd3712
Compare
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.
I reworked the PR quite a bit and it is now building on a few additional patches:
- Add interface to retrieve incoming blocks & values + iterators, usable for all phi-like recipes: [VPlan] Add new VPIRPhi overlay for VPIRInsts wrapping phi nodes (NFC). #129387
- Add VPIRPhi [VPlan] Add new VPIRPhi overlay for VPIRInsts wrapping phi nodes (NFC). #129387
auto *VPIRI = dyn_cast<VPIRInstruction>(&R); | ||
return VPIRI && isa<PHINode>(VPIRI->getInstruction()); | ||
} | ||
|
||
bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) { | ||
if (!verifyPhiRecipes(VPBB)) |
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.
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.
@@ -175,6 +175,12 @@ bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const { | |||
}); | |||
} | |||
|
|||
/// Return true if \p R is a VPIRInstruction wrapping a phi. | |||
static bool isVPIRInstructionPhi(const VPRecipeBase &R) { | |||
auto *VPIRI = dyn_cast<VPIRInstruction>(&R); |
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.
Will check separately, thanks!
auto *VPIRI = dyn_cast<VPIRInstruction>(&R); | ||
return VPIRI && isa<PHINode>(VPIRI->getInstruction()); | ||
} | ||
|
||
bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) { | ||
if (!verifyPhiRecipes(VPBB)) |
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.
It should be complete now, updated, thanks!
@@ -207,24 +213,69 @@ bool VPlanVerifier::verifyVPBasicBlock(const VPBasicBlock *VPBB) { | |||
|
|||
for (const VPUser *U : V->users()) { | |||
auto *UI = cast<VPRecipeBase>(U); |
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.
Will fix independently.
isa<PHINode>(cast<VPIRInstruction>(UI)->getInstruction()))) | ||
const VPBlockBase *UserVPBB = UI->getParent(); | ||
|
||
// Verify incoming values of VPIRInstructions wrapping phis. V most |
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.
fixed, thanks
|
||
// Verify incoming values of VPIRInstructions wrapping phis. V most | ||
// dominate the end of the incoming block. The operand index of the | ||
// incoming value matches the predecessor block index of the |
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.
Done thanks!
// dominate the end of the incoming block. The operand index of the | ||
// incoming value matches the predecessor block index of the | ||
// corresponding incoming block. | ||
if (isVPIRInstructionPhi(*UI)) { |
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.
There are cases where the block containing a VPIR Phi has multiple predecessors (some cases with early exits), for those we need the proper phi-handling
✅ With the latest revision this PR passed the C/C++ code formatter. |
Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks, with corresponding iterators. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838.
[VPlan] Use VPPhiAccessors for VPIRPhi. astOf
bbd3712
to
5fee077
Compare
…llvm#129388) Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838. PR: llvm#129388
…llvm#129388) Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838. PR: llvm#129388
…llvm#129388) Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838. PR: llvm#129388
…cipes (NFC) (#129388) Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm/llvm-project#124838. PR: llvm/llvm-project#129388
…llvm#129388) Add a VPPhiAccessors class to provide interfaces to access incoming values and blocks. The first user is VPWidenPhiRecipe, with the other phi-like recipes following soon. This will also be used to verify def-use chains where users are phi-like recipes, simplifying llvm#124838. PR: llvm#129388
Update the verifier to verify dominance for incoming values for phi-like recipes. The defining recipe must dominate the incoming block for the incoming value.
There are 4 different cases to consider when retrieving the incoming block: