-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
8330004: Refactor cloning down code in Split If for Template Assertion Predicates #18723
Conversation
👋 Welcome back chagedorn! A progress list of the required criteria for merging this PR into |
@chhagedorn This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 165 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
@chhagedorn The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
@@ -1394,40 +1394,6 @@ void PhaseIdealLoop::copy_assertion_predicates_to_main_loop_helper(const Predica | |||
} | |||
} | |||
|
|||
// Is 'n' a node that can be found on the input chain of a Template Assertion Predicate bool (i.e. between a Template | |||
// Assertion Predicate If node and the OpaqueLoop* nodes)? | |||
static bool is_part_of_template_assertion_predicate_bool(Node* n) { |
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.
Only used by the now dead subgraph_has_opaque()
method.
Node* m = n->in(j); | ||
if (m != nullptr) { | ||
wq.push(m); | ||
if (TemplateAssertionPredicateExpressionNode::valid_opcode(n)) { |
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.
valid_opcode()
also includes the OpaqueLoop*
nodes which was not the case for is_part_of_template_assertion_predicate_bool()
. Therefore needed to adapt the code below to handle that.
@@ -323,7 +323,7 @@ Opaque4Node* TemplateAssertionPredicateExpression::clone(const TransformStrategy | |||
auto is_opaque_loop_node = [](const Node* node) { | |||
return node->is_Opaque1(); | |||
}; | |||
DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionPredicateExpression::maybe_contains, | |||
DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionPredicateExpressionNode::valid_opcode, |
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.
Moved TemplateAssertionPredicateExpression::maybe_contains()
to TemplateAssertionPredicateExpressionNode::valid_opcode()
which suited better.
src/hotspot/share/opto/split_if.cpp
Outdated
// we completely clone the entire Template Assertion Predicate Expression "down". This ensures that we have an | ||
// untouched copy that is still recognized by the Template Assertion Predicate matching code. | ||
void PhaseIdealLoop::clone_template_assertion_predicate_expression_down(Node* node) { | ||
if (!TemplateAssertionPredicateExpressionNode::is_valid(node)) { |
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.
Covers previous subgraph_has_opaque()
call to check whether this node is part of a Template Assertion Predicate Expression.
|
||
package compiler.predicates.assertion; | ||
|
||
public class TestSplitIfCloningDown { |
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 a sanity test that exercises the refactored code. There was no assert or anything alike when running the test before this refactoring.
// | ||
// The expression itself can belong to no, one, or two Template Assertion Predicates: | ||
// - None: This node is already dead (i.e. we replaced the Bool condition of the Template Assertion Predicate). | ||
// - Two: A OpaqueLoopInitNode could be part of two Template Assertion Predicates. |
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.
This is a little odd but I did not want to change that. This can only happen when we have not cloned a Template Assertion Predicate Expression, yet (when cloning, we will not common the OpaqueLoopInitNodes
). Maybe we could create a separate OpaqueLoopInitNode
for the init and the last value Template Assertion Predicate at some point - but that would just beautify this code here and does not currently offer any other benefit. On top of that (even though rather minor), we have additional nodes that are actually not required to make Template Assertion Predicates work. So, I'm not sure if we really want that.
Webrevs
|
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.
Nice refactoring! I left a few comments/suggestions.
|
||
// Check if the opcode of node could be found in a Template Assertion Predicate Expression. | ||
// This also provides a fast check whether a node is unrelated. | ||
static bool valid_opcode(const Node* node) { |
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'm not a fan of this name, it implies that nodes could be "valid" or "invalid". For one, I think this should start with is_...
. This would be very long, but at least more accurate: is_maybe_in_template_assertion_predicate_expression
.
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's always a good sign when you point out the names I'm also not quite satisfied to start with - so that's an indicator to change it :-)
I tried to be concise here but then there are really no good and precise options. Let's go with is_maybe_in_expression()
and is_in_expression()
as also suggested above.
|
||
public: | ||
// Check whether the provided node is part of a Template Assertion Predicate Expression or not. | ||
static bool is_valid(Node* node) { |
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 would rename this too. To keep it similar to is_maybe_in_template_assertion_predicate_expression
, name it is_in_template_assertion_predicate_expression
.
Hmm. You could also shorten the names, since we know from the context that we are talking about TAPE:
is_in_expression
is_maybe_in_expression
Also: why is there the find_opaque_loop_nodes
method? Is it even used anywhere else?
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 think that's a left-over from an earlier refactoring. I removed find_opaque_loop_nodes()
and directly use is_in_expression()
.
@@ -332,3 +332,41 @@ Opaque4Node* TemplateAssertionPredicateExpression::clone(const TransformStrategy | |||
Node* opaque4_clone = *orig_to_new.get(_opaque4_node); | |||
return opaque4_clone->as_Opaque4(); | |||
} | |||
|
|||
// Check if this node belongs a Template Assertion Predicate Expression (including OpaqueLoop* nodes). | |||
bool TemplateAssertionPredicateExpressionNode::find_opaque_loop_nodes(Node* node) { |
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.
find
usually tells me that you are going to return what you "find". You could name it more like what you have in the description: is_in_expression
or belongs_to_expression
. Also, looks like the single use of this is in is_valid
, and that just wraps it. Is that intended?
Node* m = n->in(j); | ||
if (m != nullptr) { | ||
wq.push(m); | ||
} |
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.
This code now looks almost identical to TemplateAssertionPredicateExpressionNode::find_opaque_loop_nodes
, except that there we just are looking for one init/stride, and here we count them. Can we refactor this?
Idea: have a callback/lambda we call on init/stride nodes. That callback can then count, or give a "terminate" return: enum Action { Continue, Terminate };
.
Or maybe you just use the count method also for checking if there are any init/stride. That is slightly more expensive, but maybe it does not matter. Or you give your count method a parameter that simply terminates early, and a return value true/false if we found any. Lots of options.
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.
Ah wait. That is what we used to do: count to check if there are any. That is what subgraph_has_opaque
did.
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.
Yes, I eventually want to get rid of count_opaque_loop_nodes()
. This is kinda an intermediate state.
// Apply the given function to all Template Assertion Predicates (if any) to which this Template Assertion Predicate | ||
// Expression Node belongs to. | ||
template <class ApplyToTemplateFunction> | ||
void for_each_template_assertion_predicate(ApplyToTemplateFunction apply_to_template_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.
void for_each_template_assertion_predicate(ApplyToTemplateFunction apply_to_template_function) { | |
void for_each_template_assertion_predicate(ApplyToTemplateFunction apply_to_template_function) const { |
Would that work?
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 think the name ApplyToTemplateFunction
/ apply_to_template_function
is more noisy than necessary. I would just do Callback
and callback
. In the for-each context it is clear what this means.
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.
Fair point, updated.
return node->is_If() && node->in(1)->is_Opaque4(); | ||
} | ||
|
||
void TemplateAssertionPredicateExpressionNode::push_outputs(Unique_Node_List& list, const Node* node) { |
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.
Why not make this a method in Node
? node->push_outs(list)
.
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 as above, better inside Node_List
?
return false; | ||
} | ||
|
||
void TemplateAssertionPredicateExpressionNode::push_non_null_inputs(Unique_Node_List& list, const Node* node) { |
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.
Why not make this a method in Node? node->push_non_null_inputs(list)
.
If that can be part of the header file, then it would even be efficiently inlined, I assume.
We could then use it all over the place!
Well, you should probably indicate that you are not traversing in(0)
... not sure what would be an adequate name.
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 point. Might it even be better suited inside Node_List
? It does more sound like a thing a list should know how to do.
How about going with list.push_non_null_non_cfg_inputs_of(node)
?
FWIW, there are quite some places where we only want to put the non-cfg nodes on a node list. I suggest to file a follow up RFE to replace those with this new method if you agree. Same for the outputs below.
} | ||
} | ||
assert(template_counter <= 2, "a node cannot be part of more than two templates"); | ||
assert(template_counter <= 1 || _node->is_OpaqueLoopInit(), "only OpaqueLoopInit nodes can be part of two templates"); |
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 this be true? Maybe there are some implicit assumptions about _node
. But if it was for example an AddI
, then this node could be used all over the place, and certainly could be used by many TAPE. Am I wrong?
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 these asserts are correct, you probably want to add some comments.
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.
When we follow all inputs of a TemplateAssertionPredicateExpressionNode
, we eventually end up at an OpaqueLoop*Node
. These nodes do not common up. Therefore, each TemplateAssertionPredicateExpressionNode
can only be part of one Template Assertion Predicate Expression. One exception is the OpaqueLoopInitNode
itself. Due to convenience, the init and last value Template Assertion Predicate share this node. I can add a comment to explain these asserts further.
// - None: This node is already dead (i.e. we replaced the Bool condition of the Template Assertion Predicate). | ||
// - Two: A OpaqueLoopInitNode could be part of two Template Assertion Predicates. | ||
// - One: In all other cases. | ||
class TemplateAssertionPredicateExpressionNode : public StackObj { |
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 have a slight irritation that this has a ...Node
suffix. Indicates that it is a subclass of Node
, which is not correct. But probably it is still a good name, so you can leave it.
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 see your point. Due to a lack of having a better naming idea, I went with ...Node
. Let me think some more about it.
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 a lot for your review!
I've addressed all your comments and pushed an update. I already incorporated the idea of moving the push
methods to Node_List
.
Node* m = n->in(j); | ||
if (m != nullptr) { | ||
wq.push(m); | ||
} |
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.
Yes, I eventually want to get rid of count_opaque_loop_nodes()
. This is kinda an intermediate state.
return false; | ||
} | ||
|
||
void TemplateAssertionPredicateExpressionNode::push_non_null_inputs(Unique_Node_List& list, const Node* node) { |
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 point. Might it even be better suited inside Node_List
? It does more sound like a thing a list should know how to do.
How about going with list.push_non_null_non_cfg_inputs_of(node)
?
FWIW, there are quite some places where we only want to put the non-cfg nodes on a node list. I suggest to file a follow up RFE to replace those with this new method if you agree. Same for the outputs below.
return node->is_If() && node->in(1)->is_Opaque4(); | ||
} | ||
|
||
void TemplateAssertionPredicateExpressionNode::push_outputs(Unique_Node_List& list, const Node* node) { |
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 as above, better inside Node_List
?
// - None: This node is already dead (i.e. we replaced the Bool condition of the Template Assertion Predicate). | ||
// - Two: A OpaqueLoopInitNode could be part of two Template Assertion Predicates. | ||
// - One: In all other cases. | ||
class TemplateAssertionPredicateExpressionNode : public StackObj { |
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 see your point. Due to a lack of having a better naming idea, I went with ...Node
. Let me think some more about it.
|
||
// Check if the opcode of node could be found in a Template Assertion Predicate Expression. | ||
// This also provides a fast check whether a node is unrelated. | ||
static bool valid_opcode(const Node* node) { |
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's always a good sign when you point out the names I'm also not quite satisfied to start with - so that's an indicator to change it :-)
I tried to be concise here but then there are really no good and precise options. Let's go with is_maybe_in_expression()
and is_in_expression()
as also suggested above.
|
||
public: | ||
// Check whether the provided node is part of a Template Assertion Predicate Expression or not. | ||
static bool is_valid(Node* node) { |
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 think that's a left-over from an earlier refactoring. I removed find_opaque_loop_nodes()
and directly use is_in_expression()
.
// Apply the given function to all Template Assertion Predicates (if any) to which this Template Assertion Predicate | ||
// Expression Node belongs to. | ||
template <class ApplyToTemplateFunction> | ||
void for_each_template_assertion_predicate(ApplyToTemplateFunction apply_to_template_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.
Fair point, updated.
} | ||
} | ||
assert(template_counter <= 2, "a node cannot be part of more than two templates"); | ||
assert(template_counter <= 1 || _node->is_OpaqueLoopInit(), "only OpaqueLoopInit nodes can be part of two templates"); |
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.
When we follow all inputs of a TemplateAssertionPredicateExpressionNode
, we eventually end up at an OpaqueLoop*Node
. These nodes do not common up. Therefore, each TemplateAssertionPredicateExpressionNode
can only be part of one Template Assertion Predicate Expression. One exception is the OpaqueLoopInitNode
itself. Due to convenience, the init and last value Template Assertion Predicate share this node. I can add a comment to explain these asserts further.
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.
Looks good to me now.
Thanks Emanuel for your review and good catch with |
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.
Looks good.
Thanks Vladimir for your review! I'll submit some more testing with the latest changes before integration. |
/integrate |
Going to push as commit 20546c1.
Your commit was automatically rebased without conflicts. |
@chhagedorn Pushed as commit 20546c1. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
This is another patch split off #16877. It refactors the "cloning down" code for Split If with Template Assertion Predicates. This mainly includes the replacement of
subgraph_has_opaque()
with a new classTemplateAssertionPredicateExpressionNode
. More details can be found as PR comments.Background
The cloning down code is required in Split If when trying to split any node up that belongs to a Template Assertion Predicate Expression (TAPE) (including the
OpaqueLoop*
nodes). We need to prevent that to avoid having any phi nodes in the TAPE which could result in failures when trying to later match and clone Template Assertion Predicates. Instead of cloning such a TAPE node up, we clone ("down") the entire TAPE.Thanks,
Christian
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/18723/head:pull/18723
$ git checkout pull/18723
Update a local copy of the PR:
$ git checkout pull/18723
$ git pull https://git.openjdk.org/jdk.git pull/18723/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 18723
View PR using the GUI difftool:
$ git pr show -t 18723
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/18723.diff
Webrev
Link to Webrev Comment