RFR: 8373495: C2: Aggressively fold loads from objects that have not escaped [v17]
Daniel Lundén
dlunden at openjdk.org
Fri Jan 16 10:57:47 UTC 2026
On Mon, 12 Jan 2026 15:05:29 GMT, Quan Anh Mai <qamai at openjdk.org> wrote:
>> Hi,
>>
>> This patch is an alternative to #28764 but it does the analysis during IGVN instead.
>>
>> ## The current PR:
>>
>> The current escape analysis mechanism is all-or-nothing: either the object does not escape, or it does. If the object escapes, we lose the ability to analyse the values of its fields completely, even if the object only escapes at return.
>>
>> This PR tries to find the escape status of an object at a load, and if it is decided that the object has not escaped there, we can try folding the load aggressively, ignoring calls and memory barriers to find a corresponding store that the load observes. Implementation-wise, when walking at `find_previous_store`, if we encounter a call or memory barrier, we start looking at all nodes that make the allocation escape. If all such nodes have a control input that is not a transitive control input of the call/barrier we are at, then we can decidedly say that the allocation has not escaped at that call/barrier, and walk past that call/barrier to find a corresponding store.
>>
>> I do not see a noticeable difference in C2 runtime with and without this patch.
>>
>> ## Future work:
>>
>> 1. Fold a memory `Phi`.
>>
>> This is pretty straightforward. We need to create a value `Phi` for each memory `Phi` so that we can handle loop `Phi`s.
>>
>> 2. Fold a pointer `Phi`.
>>
>> Currently, this PR is doing the trivial approach, just give up if we don't encounter a store into that `Phi`. However, we can do better. Consider this case:
>>
>> Point p1 = new Point;
>> Point p2 = new Point;
>> p1.x = v1;
>> p2.x = v2;
>> Point p = Phi(p1, p2);
>> int a = p.x;
>>
>> Then, `a` should be able to be folded to `Phi(v1, v2)` if `p1` and `p2` are known not to alias.
>>
>> Another interesting case:
>>
>> Point p = Phi(p1, p2);
>> p.x = v;
>> p1.x = v1;
>> int a = p.x;
>>
>> Then, theoretically, we can fold `a` to `Phi(v1, v)` if `p1` and `p2` are known not to alias.
>>
>> 3. Nested objects
>>
>> It can be observed that if an object is stored into a memory that has not escaped, then it can be considered that the object has not escaped. For example:
>>
>> Point p = new Point;
>> PointHolder h = new PointHolder;
>> h.p = p;
>> int x = p.x;
>> escape(h);
>>
>> Then, `p` can be considered that it has not escaped until `escape(h)`. To do this, the computation of `_aliases` in the constructor of `LocalEA` needs to be more comprehensive. See the comments in `LocalEA::check_escape_status`.
>>
>> Please...
>
> Quan Anh Mai has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains 19 additional commits since the last revision:
>
> - Merge branch 'master' into loadfoldingigvn
> - Early return when not a heap access
> - Fix escape at store
> - Fix outdated and unclear comments
> - copyright year, return, comments, whitespace
> - Merge branch 'master' into loadfoldingigvn
> - ea of phis and nested objects
> - Add test scenarios
> - Add a flag to turn off the feature
> - Much more comments, refactor the data into a separate class
> - ... and 9 more: https://git.openjdk.org/jdk/compare/2d116c1f...c275e6e6
I haven't started digging into the meat of this changeset yet, but here are some initial comments. Can you please create an RFE for the future work you mention in the PR description (if you have not done so already)?
src/hotspot/share/opto/memnode.cpp line 2191:
> 2189: const TypePtr *addr_t = phase->type(address)->isa_ptr();
> 2190:
> 2191: if (can_reshape && (addr_t != nullptr)) {
We can now remove this can_reshape check, right? If we want to keep it as documentation, better to use an assert.
src/hotspot/share/opto/memnode.cpp line 2224:
> 2222: // anything that is not a load of a field/array element (like
> 2223: // barriers etc.) alone
> 2224: if (in(0) != nullptr && !adr_type()->isa_rawptr() && can_reshape) {
We can now remove this can_reshape check, right? If we want to keep it as documentation, better to use an assert.
src/hotspot/share/opto/memnode.cpp line 2256:
> 2254: // the alias index stuff. So instead, peek through Stores and IFF we can
> 2255: // fold up, do so.
> 2256: Node* prev_mem = find_previous_store(phase);
Previously, we reached here even if `!can_reshape`. We no longer do so due to the additional check above. Is this correct? If so, can you add a brief comment explaining this?
src/hotspot/share/opto/memnode.cpp line 2259:
> 2257: if (prev_mem != nullptr && prev_mem->is_top()) {
> 2258: return prev_mem;
> 2259: }
Please add a comment explaining this addition.
src/hotspot/share/opto/memnode.cpp line 3861:
> 3859: if (prev_mem != nullptr && prev_mem->is_top()) {
> 3860: return prev_mem;
> 3861: }
Please add a comment explaining this addition.
-------------
PR Review: https://git.openjdk.org/jdk/pull/28812#pullrequestreview-3670244656
PR Review Comment: https://git.openjdk.org/jdk/pull/28812#discussion_r2698014261
PR Review Comment: https://git.openjdk.org/jdk/pull/28812#discussion_r2698019143
PR Review Comment: https://git.openjdk.org/jdk/pull/28812#discussion_r2698019025
PR Review Comment: https://git.openjdk.org/jdk/pull/28812#discussion_r2698021042
PR Review Comment: https://git.openjdk.org/jdk/pull/28812#discussion_r2698022539
More information about the hotspot-compiler-dev
mailing list