Skip to content

Implement VM/GC support for JIT constant references#23257

Merged
gacholio merged 2 commits intoeclipse-openj9:masterfrom
nbhuiyan:const-refs-vmgc
Feb 7, 2026
Merged

Implement VM/GC support for JIT constant references#23257
gacholio merged 2 commits intoeclipse-openj9:masterfrom
nbhuiyan:const-refs-vmgc

Conversation

@nbhuiyan
Copy link
Copy Markdown
Member

@nbhuiyan nbhuiyan commented Jan 26, 2026

This PR contributes @jdmpapin's work implementing the necessary VM and GC support for const refs. Please refer to the commit message for a detailed description.

Issue: #16616

@nbhuiyan
Copy link
Copy Markdown
Member Author

nbhuiyan commented Jan 26, 2026

@babsingh / @TobiAjila and @dmitripivkine / @amicic Requesting review of the VM and GC changes.

@0xdaryl @vijaysun-omr @hzongaro FYI

Copy link
Copy Markdown
Contributor

@dmitripivkine dmitripivkine left a comment

Choose a reason for hiding this comment

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

GC part of the change looks good to me

#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */
, _valueTypesIterator(clazz)
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
, _constRefsIterator(clazz)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be moved after line 100 in the existing ifdef.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The reason for this and other cases of multiple J9VM_OPT_OPENJDK_METHODHANDLE ifdef blocks is due to -Werror,-Wreorder-ctor used for GC build phase. This will require some reordering of member declarations as well.

GC_ClassStaticsIterator _classStaticsIterator;
GC_ConstantPoolObjectSlotIterator _constantPoolObjectSlotIterator;
GC_CallSitesIterator _callSitesIterator;
GC_MethodTypesIterator _methodTypesIterator;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unrelated to this PR. Probably an oversight, but this and all other associated code can be placed under the J9VM_OPT_OPENJDK_METHODHANDLE ifdef.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These fields are required in non-OpenJDK MH cases as well. In the case of _methodTypesIterator, there are two ways it is initialized based on the MH implementation:

OpenJDK MH:

, _methodTypesIterator(clazz->romClass->invokeCacheCount, clazz->invokeCache)

OpenJ9 MH:

, _methodTypesIterator(clazz->romClass->methodTypeCount, clazz->methodTypes)

#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */
, _valueTypesIterator(clazz)
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
, _constRefsIterator(clazz)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be moved after line 110 in the existing ifdef.

Copy link
Copy Markdown
Member Author

@nbhuiyan nbhuiyan Feb 5, 2026

Choose a reason for hiding this comment

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

Same explanation #23257 (comment)

Please let me know if I should proceed with changing the member declaration order as necessary to be able to have a single OpenJDK MH ifdef block.

jdmpapin and others added 2 commits February 5, 2026 13:53
Constant references will be analyzed by the JIT compiler at compile time
and each one will be attributed to an owning class. The slots will be
embedded in the JIT body for which they're needed. A run of one or more
slots owned by the same class will be described by a J9ConstRefArray,
which will be attached to the class in a circular doubly-linked list
(J9Class::constRefArrays, J9ConstRefArray::{prev,next}InClass).
Additionally, every J9ConstRefArray for a particular JIT body will be
part of a circular singly-linked list for the JIT body metadata
(J9JITExceptionTable::constRefArrays, J9ConstRefArray::nextInJITBody)
so that they can be destroyed in case of code cache reclamation.
There is a single global mutex that protects the J9ConstRefArray pool
and all of the linked list structures.

From a GC perspective, constant references will appear to be additional
static slots in the class that owns them.

This commit consists only of the changes needed outside of the JIT
compiler. Some context about the JIT part that is coming separately:

- At the end of a compilation that needs constant references, the JIT
  compiler will allocate the needed J9ConstRefArray instances, and add
  them into the appropriate lists.

- J9Class::constRefArrays will point to a sentinel entry rather than to
  an entry that contains actual constant references. This is done to
  simplify deletion and to avoid needing to add a pointer from each
  J9ConstRefArray directly to its owning J9Class. The sentinel will be
  allocated by the JIT compiler.

- To reduce overhead and simplify scanning, the GC scans the constant
  references owned by a class without acquiring the mutex, so it will be
  possible for the GC to be scanning the list for a class while the JIT
  is adding to it. The JIT will use a memory barrier to make the
  addition atomic w.r.t. any concurrent GC scanning.

- The JIT will initialize the slots using a write barrier so that the GC
  can respond appropriately to the new slots. For example, the GC may
  need to re-scan the class if the slots were initialized during
  concurrent marking. As another example, in gencon the GC may need to
  add the class object to the remembered set if any of the slots happens
  to point to an object that's still in the nursery.

- The JIT will avoid calling the write barrier before the GC has been
  made aware of the slots, i.e. before the J9ConstRefArray has been
  added to the per-class list. So the full sequence for initializing
  constant references on the JIT side will be as follows (though note
  that step 4 is atomic w.r.t. GC scanning, as mentioned above):

    1. Allocate the slots in the JIT body (during code generation).
    2. Zero-initialize all of the slots.
    3. Acquire the constant references mutex.
    4. Set up the J9ConstRefArray(s) and add to all applicable lists.
    5. Release the constant references mutex.
    6. Acquire VM access.
    7. Initialize the slots to their correct values via write barrier.
    8. Release VM access.

- Because the GC scans without the mutex, it is not sufficient to hold
  the mutex in order to destroy a J9ConstRefArray. It's also necessary
  to know that no concurrent scanning is ongoing. This is the case
  during class unloading and code cache reclamation (which will be part
  of the upcoming JIT changes). It will also be necessary to destroy
  J9ConstRefArrays on compilation failure in the unlikely event that
  compilation fails after one or more J9ConstRefArrays have already been
  successfully created for the JIT body. In that case, the JIT will
  acquire exclusive VM access to allow for the necessary cleanup.
In ConstRefsIterator, moving JIT write protection toggling from the
constructor/destructor to nextSlot() function reduces the duration
that JIT write protection remains in disabled state. With this,
omrthread_jit_write_protect_disable() is only called the first time
nextSlot() is called for a non-empty set of constant reference slots.
_jitWriteProtectDisabled field is used to keep track of the state of
JIT write protection. When nextSlot() is to return NULL (empty list,
or iteration reached the last slot),
omrthread_jit_write_protect_enable() is called based on whether JIT
write protection is in disabled state or not.

Also includes some minor fixes based on review comments.

Signed-off-by: Nazim Bhuiyan <nubhuiyan@ibm.com>
@gacholio
Copy link
Copy Markdown
Contributor

gacholio commented Feb 6, 2026

jenkins test sanity zlinux jdk25

@gacholio
Copy link
Copy Markdown
Contributor

gacholio commented Feb 6, 2026

jenkins compile win jdk8

@nbhuiyan
Copy link
Copy Markdown
Member Author

nbhuiyan commented Feb 6, 2026

JDK25 sanity.openjdk test failures on zLinux:

TEST RESULT: Failed. Execution failed: `main' threw exception: java.lang.UnsatisfiedLinkError: jdk/test/whitebox/WhiteBox.registerNatives()V
...
#INFO:  AOT header validation failed: AsyncCompilation feature mismatch.
...
TEST: jdk/internal/vm/Continuation/OSRWithManyLocals.java

For this failure, see explanation in #23304 (comment). This failure is unrelated to the changes in this PR.

@gacholio gacholio merged commit 933c710 into eclipse-openj9:master Feb 7, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

7 participants