-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
JDK-8272574: C2: assert(false) failed: Bad graph detected in build_loop_late #5142
Conversation
👋 Welcome back casparcwang! A progress list of the required criteria for merging this PR into |
@casparcwang 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. |
Webrevs
|
/contributor add hshi |
@casparcwang Could not parse
|
/contributor add hshi |
@casparcwang |
@casparcwang @hshi, does the test assert in a debug build? I'm just wondering if this is related to other bugs. |
Thank you for your review. The test asserts in fastdebug & slowdebug build, and give the assert message of |
The test 'TestBufferVectorization.java' in the pre-submit tests failed, but it does not failed on my machine. The jit code of 'compiler.vectorization.TestBufferVectorization$TestBuffer::run' is different after applying the patch: one loop predication is not allowed but another different loop predication is performed. So I agree with you that the patch is not complete, there must exist other latent bugs. |
We have two other bugs failing with the same assert: JDK-8272562 and JDK-8271954. Could they be related? |
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.
Could you please explain in more detail how we ended up with the 400 LoadI
having a dependency on 207 IfTrue
which is below the predicate insertion point? Is it part of another predicate? I think it would help if you could add comments to the test, explaining what is going on (i.e. where we insert predicates for which range checks).
Why did you add the test to loopstripmining/LoadSplitThruPhi.java
? It's unrelated to loop strip mining, right?
@@ -54,6 +67,23 @@ public static void main(String[] args) { | |||
for (int i = 0; i < 1000000; i++) { | |||
getPermutations(inputArray, outputArray); | |||
} | |||
|
|||
for (int i = 0; i < 100; ++i) { | |||
Thread t = new Thread(new Runnable() { |
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 do you need a new thread?
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 (int a = 0; a < (int)(a + 16); a++) {
only will terminate when a
overflows, so I add a thread to execute the test, and use main thread to control the test time. I will refactor the test if the final fix is done.
@@ -662,6 +662,10 @@ bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invari | |||
if (offset && !invar.is_invariant(offset)) { // offset must be invariant | |||
return false; | |||
} | |||
if (offset && phase->is_dominator(predicate_proj, phase->get_ctrl(offset))) { | |||
// offset must be able to float before predicte projection 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.
predicte
-> predicate
public static void getPermutations1(byte[] inputArray, byte[][] outputArray) { | ||
int[] indexes = new int[]{0, 2}; | ||
|
||
for (int a = 0; a < (int)(a + 16); a++) { |
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 int cast seems useless.
They are both regression from JDK-8252372 (split if optimization) and unrelated to this bug which is related to loop predication. |
Thank you for your review very much! I removed one line in the test to simplify the graph, the idx of nodes changes, but the logic is the same.
The node corresponding relation is: Loop predication optimization wants to float the range check of
So the range check of The predicate insertion point is calculated from node 207, and skips all the predicates, which leads to node 183. So, the solution is very straight forward: stop the predication to be performed if it has a control which is dominated by the predication point. But the implementation seems to have some latent problem. |
I'm not sure if this is too restrictive to not apply loop predication at all. It seems to me that this I'm currently looking at: jdk/src/hotspot/share/opto/memnode.cpp Lines 1627 to 1629 in 30b0f82
wondering if we could set the control input of the cloned load to the same control input as the memory input of the phi (if the control input of the old load was the region of the phi through which we split)? Something like this:
|
Thank you for your review and explanation.
I agree with you and your suggested fix looks more elegant in preventing such pinned load. I will analyze and test it to see whether it has unexpected side effects. What do you think about if the following guard is added to
|
Thanks for having a closer look at my suggestion.
Given that we could fix this bug without a bail out of loop predication, either by my suggested fix or something else, I'd say that this check should never be taken. And if it is taken it indicates a missed range check elimination opportunity. But you're right, we cannot really prove that there are currently no other such cases. I would therefore suggest to either turn your bailout code into an assertion check or keep your bailout but add an |
/contributor add chhagedorn |
@casparcwang Could not parse
|
Thank you very much! I have make the bailing out code into an assertion, and include your suggested fix in this patch. The first thing I worry about is: change the control of load will lead to wrong code sequence of different nodes. But during the tests, I found no problems. For two different loads, if a I have run tests with your suggested fix, and find no problem. |
/contributor add chagedorn |
Thanks for your help and testing. I have also run tests during last weekend, and find no problem currently. The comment has been modified according to your suggestion. |
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.
Otherwise, looks good! Thanks for making these changes.
I tested your code through tier1-7 over the weekend. Test results looked good!Thanks for your help and testing. I have also run tests during last weekend, and find no problem currently.
Great! I'm curious what others think about it.
// then it will lead to cyclic dependency. | ||
// Previously promoted loop predication is in the same loop of predication | ||
// point. | ||
// This situation can occur when pinning nodes too conservatively. - can we do better? |
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.
Dot can be removed before the -
.
May I have more review of this pr? |
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.
Place the test into compiler/loopopts
.
@@ -662,6 +662,20 @@ bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invari | |||
if (offset && !invar.is_invariant(offset)) { // offset must be invariant | |||
return false; | |||
} | |||
#ifndef PRODUCT |
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.
Should be #ifdef ASSERT
for debug build.
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.
On other hand, consider do this check in PRODUCT too to bailout (return false
).
// Previously promoted loop predication is in the same loop of predication | ||
// point. | ||
// This situation can occur when pinning nodes too conservatively - can we do better? | ||
assert(false, "cyclic dependency prevents range check elimination"); |
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.
Print useful data before assert to help debugging issue without rerunning.
Dump offset
node, for example.
@@ -621,7 +621,7 @@ class Invariance : public StackObj { | |||
// Returns true if the predicate of iff is in "scale*iv + offset u< load_range(ptr)" format | |||
// Note: this function is particularly designed for loop predication. We require load_range | |||
// and offset to be loop invariant computed on the fly by "invar" | |||
bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar) const { | |||
bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar, ProjNode *predicate_proj) const { |
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.
predicate_proj
is used only for assert. Use DEBUG_ONLY(, ProjNode *predicate_proj)
.
Unless you want to do the check in PRODUCT.
@@ -1141,7 +1155,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree *loop, ProjNode* | |||
loop->dump_head(); | |||
} | |||
#endif | |||
} else if (cl != NULL && loop->is_range_check_if(iff, this, invar)) { | |||
} else if (cl != NULL && loop->is_range_check_if(iff, this, invar, predicate_proj)) { |
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.
Use DEBUG_ONLY(, predicate_proj).
src/hotspot/share/opto/loopnode.hpp
Outdated
@@ -737,7 +737,7 @@ class IdealLoopTree : public ResourceObj { | |||
bool policy_range_check( PhaseIdealLoop *phase ) const; | |||
|
|||
// Return TRUE if "iff" is a range check. | |||
bool is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar) const; | |||
bool is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar, ProjNode *predicate_proj) const; |
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.
Use DEBUG_ONLY(, ProjNode *predicate_proj).
src/hotspot/share/opto/memnode.cpp
Outdated
@@ -1625,7 +1625,13 @@ Node *LoadNode::split_through_phi(PhaseGVN *phase) { | |||
the_clone = x; // Remember for possible deletion. | |||
// Alter data node to use pre-phi inputs | |||
if (this->in(0) == region) { | |||
x->set_req(0, in); | |||
if (mem->is_Phi() && (mem->in(0) == region) && mem->in(i)->in(0) != NULL) { |
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 concern about this because you can have load's address dependency on a control node below memory's control.
I saw cases when Load's address and memory control were Region through which it splits. And all controls were set to Region's inputs.
Also consider that we did not split memory node yet. We may end up with load's clone placed above its memory.
Thank you for your review. The test has moved to
The assertion has been changed to debug only, and more debug information are printed.
I have the same concern too, so I just add another guard
when the control of the load is changed to the control of its memory, memory dependency of the load is also set in the following:
|
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.
Okay. Let me test latest version.
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.
Testing passed clean.
Thank you very much for the testing and review. |
ping, this PR needs 3 reviewers. May I have more review of this PR? |
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 reasonable to me.
|
@casparcwang 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 296 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. As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@TobiHartmann, @chhagedorn, @vnkozlov) but any other Committer may sponsor as well. ➡️ To flag this PR as ready for integration with the above commit message, type |
Thank you for the review. |
/integrate |
/sponsor |
@casparcwang |
Going to push as commit 16c3ad1.
Your commit was automatically rebased without conflicts. |
@DamonFool @casparcwang Pushed as commit 16c3ad1. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
Current loop predication will promote nodes(with a dependency on a later control node) to the insertion point which dominates the later control node.
In the following example, loopPrediction will promote node 434 to the outer loop(predicted insert point is right after node 424), and it depends on control node 207. But node 424 dominates node 207, which means after the promotion, the cloned nodes have a control dependency on a later control node, which leads to a bad graph.
Progress
Issue
Reviewers
Contributors
<hshi@openjdk.org>
<chagedorn@openjdk.org>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/5142/head:pull/5142
$ git checkout pull/5142
Update a local copy of the PR:
$ git checkout pull/5142
$ git pull https://git.openjdk.java.net/jdk pull/5142/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 5142
View PR using the GUI difftool:
$ git pr show -t 5142
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/5142.diff