You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fold MemorySegment primitive get/set into direct accesses on JDK 21+
MemorySegment.get and MemorySegment.set methods for byte, char, short,
int, long, float and double data types dispatch through a deep VarHandle /
MethodHandle / LambdaForm / VarHandleSegmentAsX / ScopedMemoryAccess
chain. Even when such call chains inline fully, they leave a sequence of guards,
such as LambdaForm dispatch, segment accessors and session checks, each of
which cost a compare and branch per access and cannot be hoisted out of loops.
This commit implements direct lowering of these accessors in
J9RecognizedCallTransformer during its early pass (part of ILGen opts)
to a guarded direct load or store at segment.min + offset whenever the layout
argument is known at compile time. The per-access runtime overhead of the slow
chain is replaced by compile-time folding of the layout's byte order and
alignment constraint plus a single combined guard.
The guard ORs together every condition the interpreted path checks and
branches to the original call when any of them fails:
- receiver is NativeMemorySegmentImpl or MappedMemorySegmentImpl (heap
segments take the slow path)
- 0 <= offset <= length - accessSize (bounds check)
- min + offset aligned to the layout's byteAlignment, for
alignment-constrained layouts (folded away for *_UNALIGNED layouts)
- scope state is read through a volatile load, so the check is neither
hoisted nor commoned across iterations
- owner == null || owner == currentThread
- !readOnly (for stores)
The slow path is the unmodified original call, so each failing guard
preserves the exact semantics and throws exceptions as necessary.
Reversed-order layouts are handled by a compile-time endian conversion
(a byte swap on the value, float and double go through their integer bit
patterns).
MemorySegment.get/set are given fine-grained recognized method ids so the
transformation can match each primitive overload, and J9EstimateCodeSize keeps
treating them as MemorySegment methods so the InterpreterEmulator's stateful
bytecode iteration still folds accessHandle().
Two VM frontend queries, TR_J9VMBase::getLayoutByteOrder andgetLayoutByteAlignment,
that read the known layout object's order and byteAlignment have been implemented
for this transformation. Corresponding JITServer changes have also been implemented.
This transformation requires additional work for AOT support, so it is currently
skipped during AOT compilations. A new env option TR_disableFFMDirectLowering can
be used to disable the transformation.
Correctness of the fast path rests on three invariants:
* the scope-state load is volatile
* there is no yield point between that load and the access
* native segment memory is freed only after ScopedMemoryAccess.closeScope0 has taken
exclusive VM access
A racing close therefore either lets an in-flight access complete before the thread
gets to a yield point, or is observed as a closed state just prior the next
access, which results in fallback to the slow path. This also covers Cleaner-managed
implicit arenas without a reachabilityFence on the fast path.
Signed-off-by: Nazim Bhuiyan <nubhuiyan@ibm.com>
0 commit comments