-
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
8307683: Loop Predication should not hoist range checks with trap on success projection by negating their condition #14156
Conversation
…ithout LoadRangeNodes
👋 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.
Looks good to me. Scary, that we didn't find this earlier.
|
||
flag = !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.
flag = !flag; | |
flag = !flag; |
// fails for i = 0 and we halt. When not splitting this loop (with LoopMaxUnroll=0), we have a wrong execution due | ||
// to never executing iFld2++ (we removed the check and the branch with the trap). | ||
for (int i = -1; i < 1000; i++) { | ||
if (Integer.compareUnsigned(i, 100) < 0) { // Loop Predication creates a Hoisted Range Check Predicate due to trap with Float.isNan(). |
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 add a comment explaining that this is equivalent to i <u 100
<=> i >= 0 && i < 100
and we add a predicate for the else branch, i.e. for i < 0 || i >= 100
, and remove the if branch.
@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 88 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 |
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 for the quick fix.
Do we want to file a bug to revisit this in a correct way?
// This allows optimization of loops where the length of the | ||
// array is a known value and doesn't need to be loaded back | ||
// from the array. | ||
if (!iff->is_RangeCheck() || tinteger == nullptr || tinteger->empty() || tinteger->lo_as_long() < 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.
Can the problem not happen with a LoadRange as second input? Couldn't the LoadRange constant fold and then the predicates could constant fold as well?
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.
You're right. It's also a problem with a LoadRange
, even if it does not constant fold.
Do we want to file a bug to investigate whether this can be done in a correct way? |
Thanks Tobias and Roland for the initial reviews. After thinking about the fix again and discussing it with Roland, I updated the fix: We disallow this pattern for an
Because this is flipped to:
to have the trap to the false projection. However, this does not match the range check pattern of a
We should therefore bailout in these cases, regardless of whether
However, if |
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.
This fails the
which seems related? |
Thanks @jerboaa for reporting that. I've seen that in my testing over the weekend as well. I'm looking into it. |
In the failing test case (see newly added I'm therefore suggesting to remove the entire negation of range checks which seems to be wrong. We should bail out in I will run some testing again. |
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 re-reviewing it again and the offline discussion! |
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. Looks good to me. As we discussed offline, let's run some sanity performance testing (we can already integrate this).
test/hotspot/jtreg/compiler/predicates/TestHoistedPredicateForNonRangeCheck.java
Outdated
Show resolved
Hide resolved
…NonRangeCheck.java Co-authored-by: Tobias Hartmann <tobias.hartmann@oracle.com>
Co-authored-by: Tobias Hartmann <tobias.hartmann@oracle.com>
Thanks Tobias for reviewing the new fix again! That's a good idea. I've run some standard benchmarks and could not find any regressions. |
/integrate |
Going to push as commit dfd3da3.
Your commit was automatically rebased without conflicts. |
@chhagedorn Pushed as commit dfd3da3. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
May I know if the fix applied to jdk11 as well? We got the same issue with jdk11.0.19. |
@hungk20 The negation of the condition for range checks was introduced in JDK-7173584 which went into JDK 9. Therefore, JDK 11u is also affected. You could either backport this fix or backout JDK-8297951 (see JDK-8308884). |
OpenJDK 11.0.20 in July will have JDK-8297951 backed out. We'll revisit the situation for OpenJDK 11.0.21 (October) where the actual fix (i.e. this bug) will likely get in. See https://bugs.openjdk.org/browse/JDK-8309119 |
Thanks @chhagedorn @jerboaa, I will keep an eye on the the new versions. |
@jerboaa && @chhagedorn : I'm not familiar with the term "backed out". Does "OpenJDK 11.0.20 in July will have JDK-8297951 backed out." mean that JDK-8297951 has been reverted? |
Hi @KarlHakanNordgren, yes, in OpenJDK 11.0.20, JDK-8297951 is backed out/reverted (see JDK-8309121). There is a REDO task (JDK-8309119) which has not (yet?) made it into OpenJDK 11.0.21 but only in OpenJDK 17.0.9. |
@KarlHakanNordgren What @chhagedorn said. Current JDK 11 release (11.0.20) have JDK-8297951 reverted. |
JDK-4809552 allowed Loop Predication to be applied to
IfNodes
that have a positive value instead of aLoadRangeNode
:jdk/src/hotspot/share/opto/loopPredicate.cpp
Lines 854 to 862 in 48d21bd
This, however, is only correct if we have an actual
RangeCheckNode
for an array. The reason for that is that if we hoist a real range check and create a Hoisted Predicate for it, we only need to check the lower and upper bound of all array accesses (i.e. the array access of the first and the last loop iteration). All array accesses in between are implicitly covered and do not need to be checked again.But if we face an
IfNode
without aLoadRangeNode
, we could be comparing anything. We do not have any guarantee that if the first and last loop iteration check succeed that the other loop iteration checks will also succeed. An example of this is shown in the test casetest()
. We wrongly create a Hoisted Range Check Predicate where the lower and upper bound are always true, but for some values of the loop induction variable, the hoisted check would actually fail. We then crash because an added Assertion Predicate exactly performs this failing check (crash with halt). Without any loop splitting (i.e. no Assertion Predicates), we have a wrong execution due to never executing the branch where we incrementiFld2
because we removed it together with the check.Thanks,
Christian
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/14156/head:pull/14156
$ git checkout pull/14156
Update a local copy of the PR:
$ git checkout pull/14156
$ git pull https://git.openjdk.org/jdk.git pull/14156/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 14156
View PR using the GUI difftool:
$ git pr show -t 14156
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/14156.diff
Webrev
Link to Webrev Comment