Skip to content

Conversation

@merykitty
Copy link
Member

@merykitty merykitty commented Jul 28, 2025

Hi,

Flat accesses prevent scalar replacement because they are mismatched accesses. It is also generally not possible to look through them, because the payload may contain an oop in the form of raw bits. As a result, this PR adds LoadFlatNode and StoreFlatNode, which act as high-level abstractions for atomic accesses on flat fields. When it is determined that there is no racing access on the flat field (e.g. because the holder object does not escape), these flat access nodes can be expanded into multiple accesses to each flattened fields, otherwise, they will be expanded into a sequence of inferring a payload and accessing memory with that payload.

I also fix an issue with deoptimization reallocation where we miss assigning the null marker of elements in a nullable flat array.

Please take a look and leave your reviews, thanks a lot.


Progress

  • Change must not contain extraneous whitespace

Issue

  • JDK-8364191: [lworld] Accesses to atomic flat fields prevent scalar replacement (Bug - P4)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/valhalla.git pull/1518/head:pull/1518
$ git checkout pull/1518

Update a local copy of the PR:
$ git checkout pull/1518
$ git pull https://git.openjdk.org/valhalla.git pull/1518/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 1518

View PR using the GUI difftool:
$ git pr show -t 1518

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/valhalla/pull/1518.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Jul 28, 2025

👋 Welcome back qamai! A progress list of the required criteria for merging this PR into lworld will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jul 28, 2025

@merykitty This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8364191: [lworld] Accesses to atomic flat fields prevent scalar replacement

Reviewed-by: thartmann

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been no new commits pushed to the lworld branch. If another commit should be pushed before you perform the /integrate command, your PR will be automatically rebased. If you prefer to avoid any potential automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the lworld branch, type /integrate in a new comment.

@openjdk openjdk bot added ready Pull request is ready to be integrated rfr Pull request is ready for review labels Jul 28, 2025
@mlbridge
Copy link

mlbridge bot commented Jul 28, 2025

Webrevs

@TobiHartmann
Copy link
Member

Thanks for working on this @merykitty! I haven't reviewed this in yet but noticed a build failure:

[2025-08-04T17:23:31,905Z] /opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/open/src/hotspot/share/opto/escape.cpp: In member function 'void ConnectionGraph::add_final_edges(Node*)':
[2025-08-04T17:23:31,905Z] /opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/open/src/hotspot/share/opto/escape.cpp:1874:25: error: no matching function for call to 'ConnectionGraph::set_escape_state(PointsToNode*&, PointsToNode::EscapeState, const char [24])'
[2025-08-04T17:23:31,905Z]  1874 |         set_escape_state(field_value_ptn, PointsToNode::GlobalEscape, "store into a flat field");
[2025-08-04T17:23:31,905Z]       |         ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[2025-08-04T17:23:31,905Z] In file included from /opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/open/src/hotspot/share/opto/escape.cpp:38:
[2025-08-04T17:23:31,905Z] /opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/open/src/hotspot/share/opto/escape.hpp:434:8: note: candidate: 'void ConnectionGraph::set_escape_state(PointsToNode*, PointsToNode::EscapeState)'
[2025-08-04T17:23:31,905Z]   434 |   void set_escape_state(PointsToNode* ptn, PointsToNode::EscapeState esc
[2025-08-04T17:23:31,905Z]       |        ^~~~~~~~~~~~~~~~
[2025-08-04T17:23:31,905Z] /opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/open/src/hotspot/share/opto/escape.hpp:434:8: note:   candidate expects 2 arguments, 3 provided
[2025-08-04T17:23:32,241Z] lib/CompileJvm.gmk:172: recipe for target '/opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/build/linux-x64/hotspot/variant-server/libjvm/objs/static/escape.o' failed
[2025-08-04T17:23:32,241Z] make[3]: *** [/opt/mach5/mesos/work_dir/slaves/f7f8bd65-a387-4a2b-b519-702f2fefaf87-S170287/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/8d44f2d4-5a02-4ac8-95c1-3a37a1d82705/runs/1b653695-e82f-4430-a41f-d099d041486e/workspace/build/linux-x64/hotspot/variant-server/libjvm/objs/static/escape.o] Error 1

@merykitty
Copy link
Member Author

@TobiHartmann Ah dummy mistake, I missed the NOT_PRODUCT guard, fixed it now.

@merykitty merykitty marked this pull request as draft August 18, 2025 17:32
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Aug 18, 2025
@TobiHartmann
Copy link
Member

Hi @merykitty Sorry for the delay in reviewing this, I was busy with the the array metadata refactoring and hunting down nasty AArch64 crashes (JDK-8364579 / JDK-8365996). This is great work! I see that you converted the PR back to draft, is it still ready to review?

@merykitty
Copy link
Member Author

@TobiHartmann Hi Tobias, I am having difficulties understanding how our current flat accesses deal with GC barriers. IIUC, every time an access is made to a memory region that may contain an oop, we need a GC barrier (which itself may be a nop). However, looking at our current implementation, I only see that being handled for G1GC with StoreLSpecialNode. For Serial, Parralel, and Shenandoah, the access is made as if the memory is a long, which I assume would not trigger any GC barrier. When I try to add an assertion that a flat access that contains an oop cannot be made for anything other than G1GC, the assert fires.

@TobiHartmann
Copy link
Member

TobiHartmann commented Oct 15, 2025

@merykitty Hi Quan-Anh, sorry for the delay, I missed this message.

I only see that being handled for G1GC with StoreLSpecialNode

G1 is handled specially because of late barrier expansion:

// Convert to a payload value <= 64-bit and write atomically.
// The payload might contain at most two oop fields that must be narrow because otherwise they would be 64-bit
// in size and would then be written by a "normal" oop store. If the payload contains oops, its size is always
// 64-bit because the next smaller (power-of-two) size would be 32-bit which could only hold one narrow oop that
// would then be written by a normal narrow oop store. These properties are asserted in 'convert_to_payload'.
assert(!immutable_memory, "immutable memory does not need explicit atomic access");
BasicType store_bt = vk->atomic_size_to_basic_type(null_free);
Node* payload = (store_bt == T_LONG) ? kit->longcon(0) : kit->intcon(0);
int oop_off_1 = -1;
int oop_off_2 = -1;
payload = convert_to_payload(kit, store_bt, payload, 0, null_free, vk->null_marker_offset_in_payload(), oop_off_1, oop_off_2);
if (!UseG1GC || oop_off_1 == -1) {
// No oop fields or no late barrier expansion. Emit an atomic store of the payload and add GC barriers if needed.
assert(oop_off_2 == -1 || !UseG1GC, "sanity");
// ZGC does not support compressed oops, so only one oop can be in the payload which is written by a "normal" oop store.
assert((oop_off_1 == -1 && oop_off_2 == -1) || !UseZGC, "ZGC does not support embedded oops in flat fields");
const Type* val_type = Type::get_const_basic_type(store_bt);
kit->insert_mem_bar(Op_MemBarCPUOrder);
kit->access_store_at(base, ptr, TypeRawPtr::BOTTOM, payload, val_type, store_bt, decorators | C2_MISMATCHED, true, this);
kit->insert_mem_bar(Op_MemBarCPUOrder);
} else {
// Contains oops and requires late barrier expansion. Emit a special store node that allows to emit GC barriers in the backend.
assert(UseG1GC, "Unexpected GC");
assert(store_bt == T_LONG, "Unexpected payload type");
// If one oop, set the offset (if no offset is set, two oops are assumed by the backend)
Node* oop_offset = (oop_off_2 == -1) ? kit->intcon(oop_off_1) : nullptr;
kit->insert_mem_bar(Op_MemBarCPUOrder);
Node* mem = kit->reset_memory();
kit->set_all_memory(mem);
Node* st = kit->gvn().transform(new StoreLSpecialNode(kit->control(), mem, ptr, TypeRawPtr::BOTTOM, payload, oop_offset, MemNode::unordered));
kit->set_memory(st, TypeRawPtr::BOTTOM);
kit->insert_mem_bar(Op_MemBarCPUOrder);
}
}

For the other GCs, we emit the barriers already during parsing (the information is propagated via C2ParseAccess):

// Is this a flat, atomic access that might require gc barriers on oop fields?
ciInlineKlass* vk = access.vk();
if (vk != nullptr && vk->has_object_fields()) {
// Add pre-barriers for oop fields
for (int i = 0; i < vk->nof_nonstatic_fields(); i++) {
ciField* field = vk->nonstatic_field_at(i);
if (!field->type()->is_primitive_type()) {
int off = access.offset().opr().as_jint() + field->offset_in_bytes() - vk->payload_offset();
LIRAccess inner_access(access.gen(), decorators, access.base(), LIR_OprFact::intConst(off), field->type()->basic_type(), access.patch_emit_info(), access.access_emit_info());
pre_barrier(inner_access, resolve_address(inner_access, false),
LIR_OprFact::illegalOpr /* pre_val */, inner_access.patch_emit_info());
}
}
}

and
if (vt != nullptr) {
for (uint i = 0; i < vt->field_count(); ++i) {
ciType* type = vt->field_type(i);
if (!type->is_primitive_type()) {
ciInlineKlass* vk = vt->bottom_type()->inline_klass();
int field_offset = vt->field_offset(i) - vk->payload_offset();
Node* value = vt->field_value(i);
Node* field_adr = kit->basic_plus_adr(access.base(), adr, field_offset);
post_barrier(kit, access.base(), field_adr, value, use_precise);
}
}
} else {

I added this with #1318 and as mentioned in the PR, this is a bit of a hacky first implementation. We should refactor / improve this later (tracked by JDK-8350865).

Does that help?

If you get a chance, could you merge this PR with latest lworld? We see some issues that look similar to this and would like to verify if your patch helps. Thanks!

@merykitty
Copy link
Member Author

@TobiHartmann Thanks a lot for your help, I am working on this. However, I think that would mean Shenandoah is broken as I see no similar handling in ShenandoahBarrierSetC2?

@TobiHartmann
Copy link
Member

@merykitty Yes, I think that's true. I only implemented Oracle-supported builds/configurations for now. Thank you!

@merykitty merykitty marked this pull request as ready for review October 18, 2025 19:26
@merykitty
Copy link
Member Author

@TobiHartmann Please review this PR, thanks a lot.

@openjdk openjdk bot added ready Pull request is ready to be integrated rfr Pull request is ready for review labels Oct 18, 2025
@TobiHartmann
Copy link
Member

I did some quick testing and I'm seeing this failure:

runtime/valhalla/inlinetypes/verifier/RedefineStrictFieldsTest.java
-Xcomp -XX:-TieredCompilation

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f195cbd8da5, pid=1416875, tid=1416894
#
# JRE version: Java(TM) SE Runtime Environment (26.0) (fastdebug build 26-jep401ea2-2025-10-20-0458161.tobias.hartmann.valhalla2)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (fastdebug 26-jep401ea2-2025-10-20-0458161.tobias.hartmann.valhalla2, compiled mode, sharing, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0xdcada5]  ConnectionGraph::has_ea_local_in_scope(SafePointNode*)+0x115

Current CompileTask:
C2:7013 1605    b        java.lang.VersionProps::parseVersionNumbers (154 bytes)

Stack: [0x00007f1948721000,0x00007f1948821000],  sp=0x00007f194881ba70,  free space=1002k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xdcada5]  ConnectionGraph::has_ea_local_in_scope(SafePointNode*)+0x115  (callnode.hpp:383)
V  [libjvm.so+0xde6e68]  ConnectionGraph::compute_escape()+0x2e88  (escape.cpp:469)
V  [libjvm.so+0xde734a]  ConnectionGraph::do_analysis(Compile*, PhaseIterGVN*)+0x15a  (escape.cpp:120)
V  [libjvm.so+0xbcbe4d]  Compile::Optimize()+0x65d  (compile.cpp:2942)
V  [libjvm.so+0xbcf86f]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x207f  (compile.cpp:878)
V  [libjvm.so+0x9dc0b3]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x483  (c2compiler.cpp:149)
V  [libjvm.so+0xbdee38]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0xb48  (compileBroker.cpp:2345)
V  [libjvm.so+0xbdffc0]  CompileBroker::compiler_thread_loop()+0x530  (compileBroker.cpp:1989)
V  [libjvm.so+0x11701db]  JavaThread::thread_main_inner()+0x13b  (javaThread.cpp:776)
V  [libjvm.so+0x1c23c56]  Thread::call_run()+0xb6  (thread.cpp:243)
V  [libjvm.so+0x185eb58]  thread_native_entry(Thread*)+0x128  (os_linux.cpp:898)

The serviceability/jvmti/valhalla/ tests also fail with the same SIGSEGV when executed with -Xcomp -XX:-TieredCompilation.

@merykitty
Copy link
Member Author

@TobiHartmann Thanks, the issue seems to be due to ConnectionGraph::has_ea_local_in_scope trying to process a dead node, it should be fixed now.

Copy link
Member

@TobiHartmann TobiHartmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, Quan-Anh! This looks good to me. Testing is still running but looks good so far (we had some breakage in our CI). I'll get back to you once it passed.

// element. The order of the Proj node is the same as that of _vk->_nonstatic_fields, and the null
// marker if existing will be the last Proj output. This node acts as if the load happens
// atomically and will be expanded to loading the whole payload and extracting the flattened fields
// from the loaded payload. In special cases, such as when the object from which this load read
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// from the loaded payload. In special cases, such as when the object from which this load read
// from the loaded payload. In special cases, such as when the object from which this load reads

// from the loaded payload. In special cases, such as when the object from which this load read
// does not escape, this node can be expanded to multiple loads from each flattened field.
// This node allows us to replace its results with the value from a matching store because the
// payload value cannot be directly propagated if it contains oops. This effect, in turns, allows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// payload value cannot be directly propagated if it contains oops. This effect, in turns, allows
// payload value cannot be directly propagated if it contains oops. This effect, in turn, allows

};

// Store an InlineTypeNode to a flat element, the store acts as if it is atomic. Similar to
// LoadFlatNode, this node is expanded to storing a payload creating from the field values of the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// LoadFlatNode, this node is expanded to storing a payload creating from the field values of the
// LoadFlatNode, this node is expanded to storing a payload created from the field values of the

}
}

// Atomic flat accesses on non-escape objects can be optimized to non-atomic accesses
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Atomic flat accesses on non-escape objects can be optimized to non-atomic accesses
// Atomic flat accesses on non-escaping objects can be optimized to non-atomic accesses

@TobiHartmann
Copy link
Member

All testing passed! I just see a few timeouts with TestTearing.java with -XX:+UnlockDiagnosticVMOptions -XX:+SafepointALot -XX:+DeoptimizeALot -XX:+DeoptimizeNMethodBarriersALot -XX:MaxNodeLimit=100000 but I think they also happen without your changes.

When working on #1672, I also hit and fixed the issue you fixed in src/hotspot/share/runtime/deoptimization.cpp. I tried to cherry-pick your fix but missed the changes to the reassign_fields_by_klass arguments, so there's a little merge conflict now.

@merykitty
Copy link
Member Author

Thanks very much, I have resolved the merge conflict.

Copy link
Member

@TobiHartmann TobiHartmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, still looks good!

@merykitty
Copy link
Member Author

/integrate

@openjdk
Copy link

openjdk bot commented Oct 22, 2025

Going to push as commit 2f89dea.

@openjdk openjdk bot added the integrated Pull request has been integrated label Oct 22, 2025
@openjdk openjdk bot closed this Oct 22, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Oct 22, 2025
@openjdk
Copy link

openjdk bot commented Oct 22, 2025

@merykitty Pushed as commit 2f89dea.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

2 participants