-
Notifications
You must be signed in to change notification settings - Fork 144
8269795: C2: Out of bounds array load floats above its range check in loop peeling resulting in SEGV #235
Conversation
… loop peeling resulting in SEGV
👋 Welcome back chagedorn! A progress list of the required criteria for merging this PR into |
@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. |
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 analysis, the fix looks good to me! Just some minor comments below.
[...] which is only the initial Bool node of the dominating test before the first invocation [...]
Should be "of the dominated test", right?
|
||
/* | ||
* @test | ||
* @key stress randomness |
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 is no randomness in the test, right? Or is that referring to the use of StressGCM
? If so, these keys are missing from other other tests that use this flag.
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, it's referring to StressGCM
. @robcasloz once updated all tests with StressGCM
to use these keys in JDK-8253765. But some newer tests miss these keys. Should we update them in an RFE to be consistent?
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 think that would be good.
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, I filed JDK-8270156 to clean it up later.
} | ||
} | ||
|
||
public void mainTest() { |
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.
Is that method needed?
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 is needed, otherwise, it does not reproduce.
!test->in(1)->is_Con() && // And not already obvious? | ||
// Condition is not a member of this loop? | ||
!loop->is_member(get_loop(get_ctrl(test->in(1))))){ | ||
// Walk loop body looking for instances of this test | ||
Node* test_cond = test->in(1); |
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.
Maybe move this up to line 517 and replace the test->in(1)->is_Con()
and get_ctrl(test->in(1))
usages in line 520/522 for better readability.
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.
Unfortunately, that's not possible as test
could be an IfProj
with only a control input.
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.
Right, too bad.
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.
Add comment why you cache the value - dominated_by() call may modify it
.
@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 38 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 |
Thanks for your review Tobias!
Yes, you're right as we are updating |
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.
Thanks Roland for your review! |
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. I have few comments.
!test->in(1)->is_Con() && // And not already obvious? | ||
// Condition is not a member of this loop? | ||
!loop->is_member(get_loop(get_ctrl(test->in(1))))){ | ||
// Walk loop body looking for instances of this test | ||
Node* test_cond = test->in(1); |
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.
Add comment why you cache the value - dominated_by() call may modify it
.
Node *test = prev->in(0); | ||
progress = false; // Reset for next iteration | ||
Node* prev = loop->_head->in(LoopNode::LoopBackControl); // loop->tail(); | ||
Node* test = prev->in(0); |
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.
May be add assert(test != NULL)
. idom()
call at the end of loop has such asserts.
In other places we have check test != NULL
but we not doing that here (at line 519).
int p_op = prev->Opcode(); | ||
if ((p_op == Op_IfFalse || p_op == Op_IfTrue) && | ||
test->is_If() && // Test? | ||
test->is_If() && // Test? |
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.
We can do what Tobias is asking by pre-check:
int p_op = prev->Opcode();
Node* test_cond = NULL;
if ((p_op == Op_IfFalse || p_op == Op_IfTrue) && test->is_If()) {
test_cond = test->in(1);
}
if (test_cond != 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.
Good.
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, Tobias and Roland for your reviews! |
/integrate |
Going to push as commit 040c02b.
Your commit was automatically rebased without conflicts. |
@chhagedorn Pushed as commit 040c02b. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
In the testcase, an out of bounds array load (
LoadI
) floats above its range check resulting in a segfault. The problem can be traced back to incorrectly treating anIf
node, on which theLoadI
is control dependent, as being dominated inPhaseIdealLoop::peeled_dom_test_elim
. As a result,PhaseIdealLoop::dominated_by()
moves theLoadI
out and before the loop while the range check stays inside the loop.peeled_dom_test_elim()
walks through the idom chain of the loop body and tries to find loop-invariant tests. Once it finds one, it not only moves this test out of the loop but also looks for other tests with the same condition node which can be removed. The condition of the dominated tests in the loop are replaced with aConI
indominated_by()
. The data dependencies on the removed dominated tests are rewired to a peeled node before the loop.However, the current code directly checks
test->in(1)
at L527 which is only the initialBool
node of the dominated test before the first invocation ofdominated_by()
in the loop on L525. Afterwards,test->in(1)
is aConI
node. We now continue to look for tests with an identicalConI
condition to move their data nodes out of this loop. This is wrong and exactly happens in the testcase: The loop body still contains a not yet cleaned up (by IGVN)If
node (with aConI
condition) of an eliminated skeleton predicate on which an out of boundsLoadI
node is control dependent. TheLoadI
is then wrongly rewired out of the loop and before its range check.The fix is to only check against the initial
test->in(1)
Bool
node which satisfies the conditions checked on the lines before (L518-520).Thanks,
Christian
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk17 pull/235/head:pull/235
$ git checkout pull/235
Update a local copy of the PR:
$ git checkout pull/235
$ git pull https://git.openjdk.java.net/jdk17 pull/235/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 235
View PR using the GUI difftool:
$ git pr show -t 235
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk17/pull/235.diff