RFR: 8303737: C2: Load can bypass subtype check that enforces it's from the right object type [v4]

Tobias Hartmann thartmann at openjdk.org
Wed Sep 20 11:10:40 UTC 2023


On Fri, 15 Sep 2023 10:33:13 GMT, Roland Westrelin <roland at openjdk.org> wrote:

>> This patch has 3 test cases:
>> 
>> TestAddPChainMismatchedBase.java
>> TestAddPChainMismatchedBase2.java
>> 
>> Both fail with a "Base pointers must match" assert
>> 
>> TestLoadBypassesClassCast.java
>> 
>> fails with: assert(Universe::is_in_heap(result)) failed: object not in heap
>> 
>> All failures are related to 8139771 (Eliminating CastPP nodes at Phis
>> when they all come from a unique input may cause crash). The problem
>> there was that if a Phi merges say:
>> 
>> 
>> (Phi (CastPP in) in)
>> 
>> 
>> it was transformed to `in`. Doing that would cause the control
>> dependency from the `Phi` on the null check that guards the `CastPP`
>> to be dropped. As demonstrated by the test case of 8139771, a load
>> could then float above the null check that should have guaranteed the
>> object being read from was non null.
>> 
>> The fix for 8139771 was to transform:
>> 
>> 
>> (Phi (CastPP in) in)
>> 
>> 
>> to:
>> 
>> 
>> (CastPP in)
>> 
>> 
>> That `CastPP` inherits the type of the `Phi` and is flagged as a
>> special kind of cast node so it's not eliminated if the `CastPP`
>> doesn't narrow the type of its input. As a result some depending load
>> cannot float above the cast.
>> 
>> Because that was found to be too conservative, code was added to try
>> to eliminate cast node added as replacement of a `Phi` by looking for
>> a dominating cast with the same input and a narrower type that the
>> cast. The idea was that if there was some dominating null check then
>> we would not need the cast. As TestLoadBypassesClassCast.java shows,
>> this is flawed.
>> 
>> TestLoadBypassesClassCast.java demonstrates that what used to happen
>> with a null check (a load that bypasses a null check) can still happen
>> with a type check: in the case of the test, the load of `objectField`
>> from `o` happens before `o` is checked to be of type `A` (the holder
>> of the field). When `o` is not of type `A`, the result of the load is
>> never used by compiled code but if the gc runs while the result of the
>> load is live (as it does in the test case), gc code sees something
>> that's not a valid oop and crashes.
>> 
>> In the test case, the same logic is duplicated in 2 branches of
>> ifs. There are 2 loads of `objectField`, one in each copy of the
>> logic. After transformation, the `objectField` loads lose their
>> dependency on type checks for `A` and common above a safepoint causing
>> a load from something that may not be a `A` to be live across a
>> safepoint.
>> 
>> Now looking at one copy of the logic:
>> 
>> - right above the `ob...
>
> Roland Westrelin has updated the pull request incrementally with one additional commit since the last revision:
> 
>   whitespace

That looks good to me. Correctness and performance testing also passed.

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

Marked as reviewed by thartmann (Reviewer).

PR Review: https://git.openjdk.org/jdk/pull/15595#pullrequestreview-1635381283


More information about the hotspot-compiler-dev mailing list