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

Christian Hagedorn chagedorn at openjdk.org
Thu Nov 20 10:29:21 UTC 2025


On Wed, 19 Nov 2025 12:19:02 GMT, Christian Hagedorn <chagedorn at openjdk.org> wrote:

> ### 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" heig...

Thanks for having a look!

> Do we need to clone the template predicates for correctness as well? Maybe pre/main/post loops are created next then unrolling happens?

Yes, I think so. We could probably come up with some case where we would also hit correctness issues down the road with more loop opts to be applied similar to the cases we have in `TestAssertionPredicates.java`.

> Otherwise another way to fix this may have been to replace the CountedLoop with a Loop and let the next round of loop opts create a new CountedLoop

Which would mean that this is not really an option: When we convert back to a `Loop`, we will remove the Template Assertion Predicates.

> Where do the template predicates branch to when they fail? A predicate uncommon trap?

Not sure if I understand your question. The Template Assertion Predicates themselves are never executed and just serve as templates to create Initialized Assertion Predicates from which will result in a halt if they fail at runtime.

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

PR Comment: https://git.openjdk.org/jdk/pull/28389#issuecomment-3557123650


More information about the hotspot-compiler-dev mailing list