RFR: 8373495: C2: Aggressively fold loads from objects that have not escaped [v14]
Quan Anh Mai
qamai at openjdk.org
Sun Dec 28 07:31:46 UTC 2025
> 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. Nested object:
>
> Consider this case:
>
> Holder h = new Holder();
> Object o = new Object();
> h.o = o;
>
> Currently, `o` will be considered escaped at `h.o = o`. However, it can be seen that `o` has not actually escaped because `h` has not escaped. Luckily, with the current approach, this can be easily achieved, notice how this loop is just "if anything escapes, consider `base` escapes", currently, the "anything" here includes `base` and its aliases. if we include the base of the object at which `o` is stored, then we can correctly determine if `o` has escaped.
>
> // Find all nodes that may escape alloc, and decide that it is provable that they must be
> // executed after ctl
> EscapeStatus res = NOT_ESCAPED;
> aliases.push(base);
> for (uint idx = 0; idx < aliases.size(); idx++) {
> Node* n = aliases.at(idx);
>
> 2. 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.
>
> 3. Fold a pointer `Phi`.
>
> This can be easy, 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 i...
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 15 additional commits since the last revision:
- 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
- Cheaper and stronger assert, add test for devirtualization
- consistently use phase->value during IGVN
- safepoints do not have a memory output
- be even more rigorous
- ... and 5 more: https://git.openjdk.org/jdk/compare/beebaaef...b2b2e5c2
-------------
Changes:
- all: https://git.openjdk.org/jdk/pull/28812/files
- new: https://git.openjdk.org/jdk/pull/28812/files/c546d216..b2b2e5c2
Webrevs:
- full: https://webrevs.openjdk.org/?repo=jdk&pr=28812&range=13
- incr: https://webrevs.openjdk.org/?repo=jdk&pr=28812&range=12-13
Stats: 20616 lines in 1604 files changed: 12143 ins; 2944 del; 5529 mod
Patch: https://git.openjdk.org/jdk/pull/28812.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/28812/head:pull/28812
PR: https://git.openjdk.org/jdk/pull/28812
More information about the hotspot-compiler-dev
mailing list