RFR: 8347365: C2: Fix the handling of depends_only_on_test
Quan Anh Mai
qamai at openjdk.org
Mon Jan 12 03:49:34 UTC 2026
On Mon, 12 Jan 2026 03:30:35 GMT, Quan Anh Mai <qamai at openjdk.org> wrote:
> Hi,
>
> This PR fixes the handling of `depends_only_on_test` when the control graph is transformed. It has to do with the theoretical idea of `depends_only_on_test`, copy from the JBS issue description:
>
> To start with, what is `depends_only_on_test`? Given a node `n` with the control input `c`, if `c` can be deduced from `c'` and `n->depends_only_on_test() == true`, then we can rewire the control input of `n` to `c'`. This means that `depends_only_on_test` does not mean that the node depends on a test, it means that the node depends on the test that is its control input.
>
> For example:
>
> if (y != 0) {
> if (x > 0) {
> if (y != 0) {
> x / y;
> }
> }
> }
>
> Then `x/y` `depends_only_on_test` because its control input is the test `y != 0`. Then, we can rewire the control input of the division to the outer `y != 0`, resulting in:
>
> if (y != 0) {
> x / y;
> if (x > 0) {
> }
> }
>
> On the other hand, consider this case:
>
> if (x > 0) {
> if (y != 0) {
> if (x > 0) {
> x / y;
> }
> }
> }
>
> Then `x/y` does not `depends_only_on_test` because its control input is the test `x > 0` which is unrelated, we can see that if we rewire the division to the outer `x > 0` test, the division floats above the actual test `y != 0`. This means that `depends_only_on_test` is a dynamic property of a node, and not a static property of the division operation. It can change when we transform the graph and it can be different for different nodes of the same kind.
>
> More details can be found in the description of `Node::depends_only_on_test` and `Node::pin_node_under_control` in this change.
>
> Please take a look and leave your reviews, thanks a lot.
test/hotspot/jtreg/compiler/c2/irTests/TestPushAddThruCast.java line 89:
> 87: @Test
> 88: @IR(phase = CompilePhase.ITER_GVN1, counts = { IRNode.CAST_II, "4" })
> 89: @IR(phase = CompilePhase.OPTIMIZE_FINISHED, counts = { IRNode.CAST_II, "3" })
This test needs changing because of range check smearing, just after loop opts, the graph looks like this:
checkIndex(i - 3, length);
j = cast(i, [3, length + 2]) - 3;
checkIndex(i, length);
j += cast(i, [0, length - 1]);
checkIndex(i - 2, length);
j += cast(i, [2, length + 1]) - 2;
checkIndex(i - 1, length);
j += cast(i, [1, length]) - 1;
Range check smearing removes the last 2 range checks, this results in their dependent casts moved to the range check `checkIndex(i, length)`. However, since they now depends on both the first and the second range checks, they are pinned and become non-floating. Furthermore, after loop opts, `CastII`s are widen and become `[0, max_int]`. As a result, the optimized_finish graph has 3 `CastII`, 1 floating non-narrowing under `checkIndex(i - 3, length)`, 1 floating non-narrowing under `checkIndex(i, length)` and 1 non-floating non-narrowing under `checkIndex(i, length)`.
Maybe it's possible to optimize even further by canonicalize the `_dependency` of all `CastII` to the strongest one later during compilation.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/29158#discussion_r2680775488
More information about the hotspot-compiler-dev
mailing list