RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15]
Emanuel Peter
epeter at openjdk.org
Fri Oct 24 13:42:27 UTC 2025
On Mon, 29 Sep 2025 08:44:51 GMT, Roland Westrelin <roland at openjdk.org> wrote:
>> An `Initialize` node for an `Allocate` node is created with a memory
>> `Proj` of adr type raw memory. In order for stores to be captured, the
>> memory state out of the allocation is a `MergeMem` with slices for the
>> various object fields/array element set to the raw memory `Proj` of
>> the `Initialize` node. If `Phi`s need to be created during later
>> transformations from this memory state, The `Phi` for a particular
>> slice gets its adr type from the type of the `Proj` which is raw
>> memory. If during macro expansion, the `Allocate` is found to have no
>> use and so can be removed, the `Proj` out of the `Initialize` is
>> replaced by the memory state on input to the `Allocate`. A `Phi` for
>> some slice for a field of an object will end up with the raw memory
>> state on input to the `Allocate` node. As a result, memory state at
>> the `Phi` is incorrect and incorrect execution can happen.
>>
>> The fix I propose is, rather than have a single `Proj` for the memory
>> state out of the `Initialize` with adr type raw memory, to use one
>> `Proj` per slice added to the memory state after the `Initalize`. Each
>> of the `Proj` should return the right adr type for its slice. For that
>> I propose having a new type of `Proj`: `NarrowMemProj` that captures
>> the right adr type.
>>
>> Logic for the construction of the `Allocate`/`Initialize` subgraph is
>> tweaked so the right adr type captured in is own `NarrowMemProj` is
>> added to the memory sugraph. Code that removes an allocation or moves
>> it also has to be changed so it correctly takes the multiple memory
>> projections out of the `Initialize` node into account.
>>
>> One tricky issue is that when EA split types for a scalar replaceable
>> `Allocate` node:
>>
>> 1- the adr type captured in the `NarrowMemProj` becomes out of sync
>> with the type of the slices for the allocation
>>
>> 2- before EA, the memory state for one particular field out of the
>> `Initialize` node can be used for a `Store` to the just allocated
>> object or some other. So we can have a chain of `Store`s, some to
>> the newly allocated object, some to some other objects, all of them
>> using the state of `NarrowMemProj` out of the `Initialize`. After
>> split unique types, the `NarrowMemProj` is for the slice of a
>> particular allocation. So `Store`s to some other objects shouldn't
>> use that memory state but the memory state before the `Allocate`.
>>
>> For that, I added logic to update the adr type of `NarrowMemProj`
>> during split uni...
>
> Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision:
>
> - review
> - Roberto's patches
Last batch of comments for this round.
I'm quite happy with the solution @rwestrel , thanks for coming up with this 😊
I think really all my suggestions / requests are about the complexity / implementation around the `apply_...` methods. It seems to me that it makes sense to internally implement it with the `apply_...` functionality that take callbacks which return a `ApplyToProjs`. But I think many of the `public` API methods could have a simpler semantic, for example many could just be `for_each_...` calls that just take a callback with void return / no return.
src/hotspot/share/opto/memnode.cpp line 5476:
> 5474:
> 5475:
> 5476: template<class Callback> ProjNode* InitializeNode::apply_to_narrow_mem_projs(Callback callback, const TypePtr* adr_type) const {
Another nit: we will only ever return a `NarrowMemProj`, so you might as well make the return value more precise ;)
src/hotspot/share/opto/multnode.cpp line 273:
> 271: ProjNode::dump_compact_spec(st);
> 272: MemNode::dump_adr_type(_adr_type, st);
> 273: }
Can you show us an example out put of `dump`? I'm just wondering if there maybe needs to be a space between the two, and if it is immediately readable :)
src/hotspot/share/opto/multnode.hpp line 215:
> 213: }
> 214: public:
> 215: NarrowMemProjNode(Node* src, const TypePtr* adr_type)
Can you feed it any other `src` than a `InitializeNode*`?
Suggestion:
NarrowMemProjNode(InitializeNode* src, const TypePtr* adr_type)
src/hotspot/share/opto/multnode.hpp line 232:
> 230: };
> 231:
> 232: template <class Callback> ProjNode* MultiNode::apply_to_projs(DUIterator_Fast& imax, DUIterator_Fast& i, Callback callback, uint which_proj) const {
Does this not belong right after the `MultiNode`? Or even in `multnode.cpp`?
src/hotspot/share/opto/multnode.hpp line 234:
> 232: template <class Callback> ProjNode* MultiNode::apply_to_projs(DUIterator_Fast& imax, DUIterator_Fast& i, Callback callback, uint which_proj) const {
> 233: auto filter = [&](ProjNode* proj) {
> 234: if (proj->_con == which_proj && callback(proj)) {
Implicit zero check?
src/hotspot/share/opto/phaseX.cpp line 2598:
> 2596: add_users_to_worklist0(proj, worklist);
> 2597: return MultiNode::CONTINUE;
> 2598: };
`for_each` call below would mean we would not need to return anything.
-------------
PR Review: https://git.openjdk.org/jdk/pull/24570#pullrequestreview-3376663539
PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460499158
PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460487363
PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460422959
PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460434657
PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460443335
PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460448870
More information about the hotspot-compiler-dev
mailing list