Skip to content
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

8316105: C2: Back to back Parse Predicates from different loops but with same deopt reason are wrongly grouped together #15764

Closed
wants to merge 3 commits into from

Conversation

chhagedorn
Copy link
Member

@chhagedorn chhagedorn commented Sep 15, 2023

JDK-8305636 wrongly refactored the code that groups predicates into Predicate Blocks which leads to a wrong execution when hitting a trap.

Background

In general, when a loop X is folded and its Parse Predicates end up above loop Y, we can reuse the Parse Predicates from loop X to create Runtime Predicates with for loop Y. If such a Runtime Predicate of loop Y is false during runtime, we trap and jump back to the start of the folded loop X above. This is fine because we have not executed any side effects of loop X or between loop X and Y:

  • (i) There are no other CFG nodes with possible side effects between loop X and Y. Otherwise, the Parse Predicates of loop X are not directly above loop Y and are removed by eliminate_useless_parse_predicates().
  • (ii) There can be no stores pinned at Parse Predicates (they are not hoisted out of a loop with range checks and invariant checks) except at the last one. This happens, for example, if loop X is fully unrolled and folded away in IGVN. A store is then no longer pinned at the folded CountedLoopNode but at the last Parse Predicate of loop X. If there are no other CFG nodes between loop X and Y, then the Parser Predicates from loop X end up just above loop Y. All Runtime Predicates created for loop Y with Parse Predicates from loop X will be executed before the pinned store at the last Parse Predicate (i.e. the loop entry).

Problem

In the test case, we have loop A that is fully unrolled and folded. Its Parse Predicates end up above loop B after IGVN. The only difference to the situation with loop X and Y in (ii) is that loop B still has a Loop Limit Check Parse Predicate:
image

  • 40 Parse Predicate and 51 Parse Predicate are from loop A with the pinned 80 StoreL from within the loop body of A (i.e. lFld).
  • 107 Parse Predicate is from loop B.

When eliminating useless Parse Predicates, we should remove the Parse Predicates of loop A, because (i) is violated: We have a CFG node (e.g. 107 Parse Predicate). between the loop B and the Parse Predicates of loop A. This, however, does not happen due to a bug: We wrongly group the back to back Loop Limit Check Parse Predicates 107 Parse Predicate and 51 Parse Predicate together as single Predicate Block. As a result, we keep 51 Parse Predicate and 40 Parse Predicate.

Manifestation of the Bug

Keeping the Parse Predicates of loop A for now is not a problem, yet, in the test case. Even if we created a Runtime Predicate with 40 Parse Predicate that traps at runtime, 80 StoreL would not have been executed, yet.

The problem happens when unswitching loop B. We clone 40 Parse Predicate to the slow (i.e. 403 Parse Predicate) and fast loop (i.e. 400 Parse Predicate) and kill the old Parse Predicates above the If that selects either the slow or fast loop (we don't clone Loop Limit Check Parse Predicates for counted loops because they will not be used anymore at this point).80 StoreL then ends up at 5 Parm. This would still not result in a problem at runtime, yet. But now we additionally create a Hoisted Check Predicate 411 If for an invariant check above the fast loop. We get the following graph:
image

At runtime, we execute 80 StoreL, then hit the trap of 411 If, jump back to the beginning of loop A and re-execute the store to lFld. We therefore observe a wrong value for lFld because we executed the store twice.

Previous Fix for Known Loop Unswitching Problem Does not Apply here

There is logic introduced by JDK-8235984 which prevents loop unswitching completely if there are pinned nodes at the last Parse Predicate because we could end up executing such a pinned store before trapping with a Runtime Predicate:

bool PhaseIdealLoop::has_control_dependencies_from_predicates(LoopNode* head) const {
Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
Predicates predicates(entry);
if (predicates.has_any()) {
assert(entry->is_IfProj(), "sanity - must be ifProj since there is at least one predicate");
if (entry->outcnt() > 1) {
// Bailout if there are predicates from which there are additional control dependencies (i.e. from loop
// entry 'entry') to previously partially peeled statements since this case is not handled and can lead
// to a wrong execution. Remove this bailout, once this is fixed.
return true;

But since we can normally only have pinned stores at the last Parse Predicate, we do not bail out of loop unswitching (the pin is at the second last Parse Predicate) and we end up with the described wrong execution.

Fix

This all comes down to wrongly grouping the back to back Loop Limit Check Parse Predicates together which prevents the elimination of the Parse Predicates of loop A. The actual problem introduced with JDK-8305636 is that a Parse Predicate is wrongly detected as a Runtime Predicate. The fix is straight forward to exclude that case.

Thanks,
Christian


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8316105: C2: Back to back Parse Predicates from different loops but with same deopt reason are wrongly grouped together (Bug - P3)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/15764/head:pull/15764
$ git checkout pull/15764

Update a local copy of the PR:
$ git checkout pull/15764
$ git pull https://git.openjdk.org/jdk.git pull/15764/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 15764

View PR using the GUI difftool:
$ git pr show -t 15764

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/15764.diff

Webrev

Link to Webrev Comment

…ith same deopt reason are wrongly grouped together
@bridgekeeper
Copy link

bridgekeeper bot commented Sep 15, 2023

👋 Welcome back chagedorn! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk openjdk bot added the rfr Pull request is ready for review label Sep 15, 2023
@openjdk
Copy link

openjdk bot commented Sep 15, 2023

@chhagedorn The following label will be automatically applied to this pull request:

  • hotspot-compiler

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.

@openjdk openjdk bot added the hotspot-compiler hotspot-compiler-dev@openjdk.org label Sep 15, 2023
@mlbridge
Copy link

mlbridge bot commented Sep 15, 2023

Webrevs

@rwestrel
Copy link
Contributor

Would there be some way to catch this kind of problems at compile time? I ran into this issue with a test that failed when the method was executed and the result was incorrect. These failures are much harder to investigate.
Maybe verify that all predicates in a block have same jvms?

@chhagedorn
Copy link
Member Author

I think that could be possible and would prevent such failures in the future. However, the complete fix with JDK-8288981 switches to HaltNodes instead of reusing the trap. So, it might not be worth to implement such a verification on traps at this point. But I could have a look and try to add a verification to JDK-8288981 with HaltNodes.

@chhagedorn
Copy link
Member Author

I've thought about it again and pushed a basic verification that would have caught this issue.

Copy link
Contributor

@rwestrel rwestrel left a 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.

@rwestrel
Copy link
Contributor

I've thought about it again and pushed a basic verification that would have caught this issue.

Thanks for making that change.

@openjdk
Copy link

openjdk bot commented Sep 20, 2023

@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:

8316105: C2: Back to back Parse Predicates from different loops but with same deopt reason are wrongly grouped together

Reviewed-by: roland, thartmann, kvn

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 141 new commits pushed to the master branch:

  • 54028e7: 8316562: serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java times out after JDK-8314829
  • 7c991cc: 8296246: Update Unicode Data Files to Version 15.1.0
  • a021dbc: 8316149: Open source several Swing JTree JViewport KeyboardManager tests
  • 455cfae: 8315880: change LockingMode default from LM_LEGACY to LM_LIGHTWEIGHT
  • 9e00949: 8316427: Duplicated code for {obj,type}ArrayKlass::array_klass
  • c43ebd3: 8315981: Opensource five more random Swing tests
  • e30e356: 8316461: Fix: make test outputs TEST SUCCESS after unsuccessful exit
  • 242eeae: 8286757: adlc tries to build with /pathmap but without /experimental:deterministic
  • b275bdd: 8308995: Update Network IO JFR events to be static mirror events
  • e1870d3: 8316411: compiler/compilercontrol/TestConflictInlineCommands.java fails intermittent with force inline by CompileCommand missing
  • ... and 131 more: https://git.openjdk.org/jdk/compare/94800781eae192d3e82f5635d4aad165f11eabc1...master

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 master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Sep 20, 2023
@chhagedorn
Copy link
Member Author

Thanks Roland for your review!

Copy link
Member

@TobiHartmann TobiHartmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed explanation. The fix looks good to me.

src/hotspot/share/opto/predicates.cpp Outdated Show resolved Hide resolved
Co-authored-by: Tobias Hartmann <tobias.hartmann@oracle.com>
@chhagedorn
Copy link
Member Author

Thanks Tobias for your review!

Copy link
Contributor

@vnkozlov vnkozlov left a 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.

@chhagedorn
Copy link
Member Author

Thanks Vladimir for your review!

I've run some testing again with the new verification code which looked good.

/integrate

@openjdk
Copy link

openjdk bot commented Sep 21, 2023

Going to push as commit ca47f5f.
Since your change was applied there have been 152 commits pushed to the master branch:

  • 1749ba2: 8311084: Add typeSymbol() API for applicable constant pool entries
  • 9f5d2b9: 8316285: Opensource JButton manual tests
  • a35e96a: 8313612: Use JUnit in lib-test/jdk tests
  • bee7524: 8315786: [AIX] Build Disk Local Detection Issue with GNU-utils df on AIX
  • ceff47b: 8315082: [REDO] Generational ZGC: Tests crash with assert(index == 0 || is_power_of_2(index))
  • df4a25b: 8308762: Metaspace leak with Instrumentation.retransform
  • 8412479: 8316229: Enhance class initialization logging
  • c04c9ea: 8316627: JViewport Test headless failure
  • 5cacf21: 8316156: ByteArrayInputStream.transferTo causes MaxDirectMemorySize overflow
  • 3461c7b: 8316532: Native library copying in BuildMicrobenchmark.gmk cause dups on macOS
  • ... and 142 more: https://git.openjdk.org/jdk/compare/94800781eae192d3e82f5635d4aad165f11eabc1...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Sep 21, 2023
@openjdk openjdk bot closed this Sep 21, 2023
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Sep 21, 2023
@openjdk
Copy link

openjdk bot commented Sep 21, 2023

@chhagedorn Pushed as commit ca47f5f.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotspot-compiler hotspot-compiler-dev@openjdk.org integrated Pull request has been integrated
4 participants