RFR: 8345067: C2: enable implicit null checks for ZGC reads [v3]

Emanuel Peter epeter at openjdk.org
Thu May 15 13:28:58 UTC 2025


On Tue, 13 May 2025 17:40:40 GMT, Roberto Castañeda Lozano <rcastanedalo at openjdk.org> wrote:

>> Currently, C2 cannot exploit late-expanded GC memory accesses as implicit null checks because of their use of temporary operands (`MachTemp`), which prevents `PhaseCFG::implicit_null_check` from [hoisting the memory accesses to the test basic block](https://github.com/openjdk/jdk/blob/f88c1c6ff86b8f29a71647e46136b6432bb67619/src/hotspot/share/opto/lcm.cpp#L319-L335).
>> 
>> This changeset extends the scope of the implicit null check optimization so that it can exploit ZGC object loads. It introduces a platform-dependent predicate (`MachNode::is_late_expanded_null_check_candidate`) to mark late-expanded instructions that emit a suitable memory access as a first instruction as candidates, and extends the optimization to recognize and hoist candidate memory accesses that use temporary operands:
>> 
>> ![example](https://github.com/user-attachments/assets/b5f9bbc8-d75d-4cf3-841e-73db3dbae753)
>> 
>> ZGC object loads are marked as late-expanded null-check candidates unconditionally on all ZGC-supported platforms except on aarch64, where only loads that do not require an initial `lea` instruction (due to [address legitimization](https://github.com/openjdk/jdk/blob/ddd07b107e814ec846579a66d4f2005b7db9bb2f/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp#L132-L144)) are marked as candidates. Fortunately, most aarch64 loads seen in practice use small offsets and can be marked as candidates.
>> 
>> Exploiting ZGC loads increases the effectiveness of the implicit null check optimization (percent of explicit null checks turned into implicit ones at compile time) by around 10% in the DaCapo23 benchmarks. This results in slight performance improvements (in the 1-2% range) in a few DaCapo and SPECjvm2008 benchmarks and an overall slight improvement across Renaissance benchmarks.
>> 
>> #### Testing
>> - tier1-5, compiler stress test (linux-x64, macosx-x64, windows-x64, linux-aarch64, macosx-aarch64; release and debug mode).
>
> Roberto Castañeda Lozano has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Extend comments in zLoadP implementations to explain role of reload

Two small nits/questions, but otherwise ready from my side :)

src/hotspot/share/opto/lcm.cpp line 80:

> 78: 
> 79: void PhaseCFG::move_node_and_its_projections_to_block(Node* n, Block* b) {
> 80:   assert(n->bottom_type() != Type::CONTROL, "cannot move control node");

I usually check `n->is_CFG()`.

What is the bottom type of an `IfNode`?
`virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; }`
Are you aware of that?

src/hotspot/share/opto/lcm.cpp line 97:

> 95: 
> 96: void PhaseCFG::ensure_node_is_at_block_or_above(Node* n, Block* b) {
> 97:   assert(n->bottom_type() != Type::CONTROL, "cannot move control node");

Same question here

test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java line 40:

> 38:             different GC memory accesses.
> 39:  * @library /test/lib /
> 40:  * @run driver compiler.gcbarriers.TestImplicitNullChecks

I suppose you could still have a special run with `ZGC` and one with `G1GC`. But not sure if that is worth it, or if we do that in higher tiers anyway?

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

Marked as reviewed by epeter (Reviewer).

PR Review: https://git.openjdk.org/jdk/pull/25066#pullrequestreview-2843684261
PR Review Comment: https://git.openjdk.org/jdk/pull/25066#discussion_r2091166322
PR Review Comment: https://git.openjdk.org/jdk/pull/25066#discussion_r2091166890
PR Review Comment: https://git.openjdk.org/jdk/pull/25066#discussion_r2091170584


More information about the hotspot-gc-dev mailing list