RFR: 8350577: Fix missing Assertion Predicates when splitting loops

Christian Hagedorn chagedorn at openjdk.org
Wed Mar 26 09:27:39 UTC 2025


_Note: The actual fix is only ~80 changed lines - everything else is about tests._

After integrating many preparatory sub-tasks, I'm finally fixing the last outstanding Assertion Predicate issues with this patch.

For more background about Assertion Predicates, have a look at the following [blog post](https://chhagedorn.github.io/jdk/2023/05/05/assertion-predicates.html).

### Maintain Assertion Predicates when Splitting a Loop
When performing Loop Predication on a counted loop, we create two Template Assertion Predicates for each hoisted range check. Whenever we split this loop as part of loop opts, we need to establish the Template Assertion Predicates at the new sub loops with the new loop init and stride and create Initialized Assertion Predicates from them. This ensures that a sub loop is folded when it becomes dead due to an impossible condition (e.g. always having a negative index for the hoisted range check in all loop iterations).

#### Current State
Today, we are already covering most of the cases where Assertion Predicates are required - but we are still missing some. The following table describes the current state for the different loop splitting optimizations:

| Loop Optimization | Template Assertion Predicate | Initialized Assertion Predicate |
| ------------------------ | --------------------------------------- | --------------------------------------- |
| Create Main Loop | ✅                |  ✅  |
| Create Post Loop  | ✅                |  ✅  |
| Loop Unswitching  | ✅                | _not required, same init, stride and, limit_  |
| Loop Unrolling       | ✅                |  ✅ | 
| Range Check Elimination | ✅     | ✅ |
| Loop Peeling         | ❌                                            |  ✅ |
| Splitting Main Loop | ❌                                        | ❌ |

Whenever we apply a loop optimization that does not establish Template Assertion Predicates, then all subsequent loop splitting optimizations on that loop cannot establish Template Assertion Predicates, either,  and we fail to emit Initialized Assertion Predicates which can lead to a broken graph.

#### Fixing Unsupported Cases
This patch provides fixes for the remaining unsupported cases as shown in the table above. With all the work done in previous PRs, the fix is quite straight forward: 
- Remove the restriction that we don't clone Template Assertion Predicate in Loop Peeling.
- Remove the restriction that we only clone Template Assertion Predicate when Parse Predicates are available (this prevents, for example, establishing Assertion Predicates when splitting a main loop further).
- Killing old Template Assertion Predicates eagerly after cloning from them is finished (this was missing and is more efficient than waiting for the next round of loop opts to mark them useless).

When I was doing some refactoring work in earlier PRs, I did not want to include semantic changes and thus kept these actually unnecessary restrictions up. Now that the preparation work is complete, I can safely remove the blocking pieces to fix the remaining cases with not changing that many lines of code.

### Testing
I've collected a lot of tests from various JBS reports and fuzzer reports and included them all as test cases with `testJBSNumber()`. I also created a lot of new tests to cover the different loop splitting cases. More advanced tests also cover the chaining of different loop splitting optimizations together. During the development I added more tests to cover some intermediate issues.

Note that I always run the tests with `-XX:+AbortVMOnCompilationFailure` to crash on some compilation bailouts due to a broken graph (there should not be any compilation bailouts).

### Work Left to Do
There are still some tasks left to tackle after this fix goes in:
- Add some verification when eliminating useless Template Assertion Predicates ([JDK-8352418](https://bugs.openjdk.org/browse/JDK-8352418)).
- Do a general pass over all the new predicate code added over the course of many PRs. I've added a lot of intermediate code that became obsolete again. There are probably some opportunities left to clean up the code further now.
- Replace Template Assertion Predicate `If` nodes with a new dedicated `TemplateAssertionPredicate` node. We currently create the following nodes during Loop Predication for a hoisted range check:

![Screenshot from 2025-03-25 16-15-01](https://github.com/user-attachments/assets/4e724bf5-49bc-4b08-be6e-b916cb3680ac)

What we actually need to keep around are both `Bool` nodes in order to create Initialized Assertion Predicates with them. We only used `If` nodes in the past due to easier matching back there with UCTs on the failing path. We got rid of the UCT and replaced it with a `Halt`. But having an `If` node in the first place is not really mandatory since we are always removing the Template Assertion Predicates after loop opts are over which basically means the `If` node a `nop`. What we can actually do is having a single dedicated `TemplateAssertionPredicate` CFG `nop` node instead that is folded after loop opts are over:
![image](https://github.com/user-attachments/assets/31733251-c617-479c-9c81-ab56a8758ee3)
This also allows us to simplify code where we special case `OpaqueTemplateAssertionPredicate` bools for `If` nodes.
- Enable Loop Peeling stress option ([JDK-8286805](https://bugs.openjdk.org/browse/JDK-8286805)). This could not have been done so far due to hitting the remaining Assertion Predicate issues too often.
- Add IR tests to show the usefulness of Assertion Predicates that they can fold dead loops away. This can also be beneficial when https://github.com/openjdk/jdk/pull/23468 is integrated and we break Assertion Predicates in some way not caught by the tests added with this PR or existing tests that only trigger when the graph is broken.

Thank again to @rwestrel, @eme64, and also @vnkozlov, for reviewing and discussing a lot of the work around Assertion Predicates!

Thanks,
Christian

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

Commit messages:
 - 8350577: Fix missing Assertion Predicates when splitting loops

Changes: https://git.openjdk.org/jdk/pull/24246/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=24246&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8350577
  Stats: 1799 lines in 5 files changed: 1700 ins; 34 del; 65 mod
  Patch: https://git.openjdk.org/jdk/pull/24246.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/24246/head:pull/24246

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


More information about the hotspot-compiler-dev mailing list