RFR: 8323972: C2 compilation fails with assert(!x->as_Loop()->is_loop_nest_inner_loop()) failed: loop was transformed

Roland Westrelin roland at openjdk.org
Wed Mar 6 14:00:45 UTC 2024


On Wed, 28 Feb 2024 08:58:23 GMT, Christian Hagedorn <chagedorn at openjdk.org> wrote:

>> Long counted loop are transformed into a loop nest of 2 "regular"
>> loops and in a subsequent loop opts round, the inner loop is
>> transformed into a counted loop. The limit for the inner loop is set,
>> when the loop nest is created, so it's expected there's no need for a
>> loop limit check when the counted loop is created. The assert fires
>> because, when the counted loop is created, it is found that it needs a
>> loop limit check. The reason for that is that the limit is
>> transformed, between nest creation and counted loop creation, in a way
>> that the range of values of the inner loop's limit becomes
>> unknown. The limit when the nest is created is:
>> 
>> 
>>  111  ConL  === 0  [[ 112 ]]  #long:-9223372034707292158
>>  106  Phi  === 105 20 94  [[ 112 ]]  #long:9223372034707292160..9223372034707292164:www !orig=72 !jvms: TestInaccurateInnerLoopLimit::test @ bci:12 (line 40)
>>  112  AddL  === _ 106 111  [[ 122 ]]  !orig=[110]
>>  122  ConvL2I  === _ 112  [[ ]]  #int
>> 
>> 
>> The type of 122 is `2..6` but it is then transformed to:
>> 
>> 
>>  106  Phi  === 105 20 154  [[ 191 130 137 ]]  #long:9223372034707292160..9223372034707292164:www !orig=[72] !jvms: TestInaccurateInnerLoopLimit::test @ bci:12 (line 40)
>>  191  ConvL2I  === _ 106  [[ 196 ]]  #int
>>  195  ConI  === 0  [[ 196 ]]  #int:max-1
>>  196  SubI  === _ 195 191  [[ 201 127 ]]  !orig=[123]
>> 
>> 
>> That is the `(ConvL2I (AddL ...))` is transformed into a `(SubI
>> (ConvL2I ))`. `ConvL2I` for an input that's out of the int range of
>> values returns TypeInt::INT and the bounds of the limit are lost. I
>> propose adding a `CastII` after the `ConvL2I` so the range of values
>> of the limit doesn't get lost.
>
> src/hotspot/share/opto/loopnode.cpp line 955:
> 
>> 953:     // opts pass, an accurate range of values for the limits is found.
>> 954:     const TypeInt* inner_iters_actual_int_range = TypeInt::make(0, iters_limit, Type::WidenMin);
>> 955:     inner_iters_actual_int = new CastIINode(outer_head, inner_iters_actual_int, inner_iters_actual_int_range, ConstraintCastNode::UnconditionalDependency);
> 
> The fix idea looks reasonable to me. I have two questions:
> - Do we really need to pin the `CastII` here? We have not pinned the `ConvL2I` before. And here I think we just want to ensure that the type is not lost.
> - Related to the first question, could we just use a normal dependency instead?
> 
> I was also wondering if we should try to improve the type of `ConvL2I` and of `Add/Sub` (and possibly also `Mul`) nodes in general? For `ConvL2I`, we could set a better type if we know that `(int)lo <= (int)hi` and `abs(hi - lo) <= 2^32`. We still have a problem to set a better type if we have a narrow range of inputs that includes `min` and `max` (e.g. `min+1, min, max, max-1`). In this case, `ConvL2I` just uses `int` as type. Then we could go a step further and do the same type optimization for `Add/Sub` nodes by directly looking through a convert/cast node at the input type. The resulting `Add/Sub` range could maybe be represented by something better than `int`:
> 
> Example:
> input type to `ConvL2I`: `[2147483647L, 2147483648L]` -> type of `ConvL2I` is `int` since we cannot represent "`[max_int, min_int]`" with two intervals otherwise.
> `AddI` = `ConvL2I` + 2 -> type could be improved to `[min_int+1,min_int+2]`.
> 
> 
> But that might succeed the scope of this fix. Going with `CastII` for now seems to be the least risk.

Thanks for reviewing this.

> The fix idea looks reasonable to me. I have two questions:
> 
>     * Do we really need to pin the `CastII` here? We have not pinned the `ConvL2I` before. And here I think we just want to ensure that the type is not lost.

I think it's good practice to set the control of a cast node. It probably doesn't make much of a difference here but we had so many issues with cast nodes that not setting control on cast makes me nervous now.

>     * Related to the first question, could we just use a normal dependency instead?

The problem with a normal dependency is that initially the cast and its non transformed input have the same types. So, there is a chance the cast is processed by igvn before its input changes and if that happens, the cast would then be removed. 

> I was also wondering if we should try to improve the type of `ConvL2I` and of `Add/Sub` (and possibly also `Mul`) nodes in general? For `ConvL2I`, we could set a better type if we know that `(int)lo <= (int)hi` and `abs(hi - lo) <= 2^32`. We still have a problem to set a better type if we have a narrow range of inputs that includes `min` and `max` (e.g. `min+1, min, max, max-1`). In this case, `ConvL2I` just uses `int` as type. Then we could go a step further and do the same type optimization for `Add/Sub` nodes by directly looking through a convert/cast node at the input type. The resulting `Add/Sub` range could maybe be represented by something better than `int`:
> 
> Example: input type to `ConvL2I`: `[2147483647L, 2147483648L]` -> type of `ConvL2I` is `int` since we cannot represent "`[max_int, min_int]`" with two intervals otherwise. `AddI` = `ConvL2I` + 2 -> type could be improved to `[min_int+1,min_int+2]`.
> 
> But that might succeed the scope of this fix. Going with `CastII` for now seems to be the least risk.

I thought about that too (I didn't go as far as you did though) and my conclusion is that the change I propose should be more robust (what if the improved type computation still misses some cases that we later find are required) and less risky.

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

PR Review Comment: https://git.openjdk.org/jdk/pull/17965#discussion_r1514532046


More information about the hotspot-compiler-dev mailing list