RFR: 8360510: C2: Template Assertion Predicates are not cloned to the inner counted loop with -XX:+StressDuplicateBackedge

Christian Hagedorn chagedorn at openjdk.org
Wed Nov 19 12:26:32 UTC 2025


### Strong Connection between Template Assertion Predicate and Counted Loop 
In [JDK-8350579](https://bugs.openjdk.org/browse/JDK-8350579), we fixed the issue that a Template Assertion Predicate for a folded loop A could end up at another loop B. We then created an Initialized Assertion Predicate at loop B from the template of loop A and used the values from the already folded, completely unrelated loop A . As a result, we crashed with a halt because loop B violated the predicate with the wrong values. As a fix, we established a strong connection between Template Assertion Predicates and their associated loop node by adding a direct link from `OpaqueTemplateAssertionPredicate` -> `CountedLoop`.

#### Maintaining this Property
In `PhaseIdealLoop::eliminate_useless_predicates()`, we walk through all counted loops and only keep those `OpaqueTemplateAssertionPredicate` nodes that can be found from the loop heads and are actually meant for this loop (using the strong connection):
https://github.com/openjdk/jdk/blob/d2926dfd9a242928877d0b1e40eac498073975bd/src/hotspot/share/opto/predicates.cpp#L1245-L1249

All other opaque nodes are removed.

### Additional Verification for Useless `OpaqueTemplateAssertionPredicate` Nodes
As an additional verification for `OpaqueTemplateAssertionPredicate` nodes that are found to be useless in `eliminate_useless_predicates()`, we check that in this case the `CountedLoop` is really dead (otherwise, we should have found the `OpaqueTemplateAssertionPredicate` in our walks through all loop):
https://github.com/openjdk/jdk/blob/d2926dfd9a242928877d0b1e40eac498073975bd/src/hotspot/share/opto/predicates.cpp#L1294-L1301

### Violating the Additional Verification with `-XX:+StressLoopBackedge`
In `PhaseIdealLoop::duplicate_loop_backedge()`, we convert a loop with a merge point into two loops which should enable us to transform the new inner loop into a counted loop. This only makes sense for a `Loop` that is not a counted loop, yet. However, to stress the transformation, we can also run with `-XX:+StressDuplicateBackedge` that also transforms a counted loop into an inner and an outer loop. This is a problem when we have Template Assertion Predicates above a counted loop to be stressed:

<img width="581" height="556" alt="image" src="https://github.com/user-attachments/assets/e7948804-e74d-4dd0-a0f8-64eedaa259ba" />

After duplicate backedge, the Template Assertion Predicates are now at the outer non-counted `Loop`:

<img width="567" height="606" alt="image" src="https://github.com/user-attachments/assets/f8640550-663e-4ce5-9bdd-9dbcc53af47b" />

In `eliminate_useless_predicates()`, we then no longer find these Template Assertion Predicates when walking up from `275 CountedLoop`. But since the counted loop is still in the graph, the additional verification above fails when checking that a useless Template Assertion Predicate is associated with a dead counted loop - which is not the case.

### Solution
The solution I propose is to clone the Template Assertion Predicates to the inner counted loop. This can be guarded with an `ifdef ASSERT` because it can only happen with `StressLoopBackedge` which is a develop flag. This is straight forward and solves this "opaque <-> counted loop" mismatching problem. 

#### Additional Changes
- When working on this change, I noticed that the regression test for checking that data control dependencies are correctly updated with Template Assertion Predicates in `TestAssertionPredicates.java` was no longer working (i.e. disabling `TemplateAssertionPredicate::rewire_loop_data_dependencies()` did not crash). It only triggers when running with ZGC (i.e. produce a crash when disabling `rewire_loop_data_dependencies()`). I added an additional jtreg block with that flag setup.
- I added an `ifdef ASSERT` block for some code that is only executed for `StressDuplicateBackedge`.
- I ran this patch through t1-4 + hs-precheckin-comp + hs-comp-stress, once without `-XX:-StressDuplicateBackedge` and once with. In the latter run, I found that `TestVerifyLoopOptimizationsHitsMemLimit.java` hit the memory limit. This seems expected since we create more loop nodes which results in more verification work. The test already uses quite some memory when run with `VerifyLoopOptimizations`. We now add some more on top which will reach the limit of `100M` set for the test. I propose to just disable this test with `StressDuplicateBackedge`. Note that this also fails before this patch.
 
Thanks,
Christian

-------------

Commit messages:
 - Exclude StressDuplicateBackedge for TestVerifyLoopOptimizationsHitsMemLimit.java
 - 8360510: C2: Template Assertion Predicates are not cloned to the inner counted loop with -XX:+StressDuplicateBackedge

Changes: https://git.openjdk.org/jdk/pull/28389/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=28389&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8360510
  Stats: 143 lines in 4 files changed: 139 ins; 0 del; 4 mod
  Patch: https://git.openjdk.org/jdk/pull/28389.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/28389/head:pull/28389

PR: https://git.openjdk.org/jdk/pull/28389


More information about the hotspot-compiler-dev mailing list