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:44:19 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.

Before this PR, the casts under the elided range checks are not pinned during range check smearing, which is incorrect, This means that both the `CastII`s under `checkIndex(i, length)` are the same and can be GVN-ed.

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

PR Review Comment: https://git.openjdk.org/jdk/pull/29158#discussion_r2680777278


More information about the hotspot-compiler-dev mailing list