From 89e21b4b9ecfcc026a4f3abbe416488c21f63e47 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 3 Dec 2025 09:49:08 -0600 Subject: [PATCH] Fix problem identified by Jorn --- .../classes/java/lang/invoke/VarHandle.java | 40 ++++++++++++++----- .../java/lang/foreign/VarHandleExact.java | 11 +++-- .../java/lang/invoke/VarHandleExact.java | 2 +- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandle.java b/src/java.base/share/classes/java/lang/invoke/VarHandle.java index c7792299359..dcd6efc3928 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandle.java @@ -2016,10 +2016,24 @@ public abstract sealed class VarHandle implements Constable final Class returnType; final int type; final int mode; - // The cache for last adapted MH if the access site has a VH that does - // not match the erased type for. It can be safely reused if VH is - // discovered to be a constant during C2 compilation. - @Stable MethodHandle lastAdaption; + + // Adaption mechanism to reduce overhead for non-exact access. + // This heuristic assumes that each sigpoly VH call site usually sees + // exactly one VarHandle instance. Each sigpoly VH call site already + // has a dedicated AccessDescriptor. + // (See MethodHandleNatives::varHandleOperationLinkerMethod) + // However, for correctness, we must verify the incoming VarHandle; + // adaptedMethodHandle may be inlined by different callers. + // In the long run, we wish to put a specific-type invoker that converts + // from one fixed type (symbolicMethodTypeInvoker) to another (the + // invocation type of the underlying MemberName, or MH for indirect VH), + // perform a foldable lookup with a hash table, and hope C2 inline it + // all. + + // Object indirection is the only way to ensure the vh and mh are not + // from two writes (they must not be tearable) + private record Adaption(VarHandle vh, MethodHandle mh) {} + private @Stable Adaption adaption; AccessDescriptor(MethodType symbolicMethodType, int type, int mode) { this.symbolicMethodTypeExact = symbolicMethodType; @@ -2032,15 +2046,19 @@ public abstract sealed class VarHandle implements Constable @ForceInline MethodHandle adaptedMethodHandle(VarHandle vh) { - if (MethodHandleImpl.isCompileConstant(vh)) { - var cache = lastAdaption; - if (cache != null) { - return cache; - } + var cache = adaption; + if (cache != null && cache.vh == vh) { + return cache.mh; } - // Keep capturing - vh may suddenly get promoted to a constant by C2 - return lastAdaption = vh.getMethodHandle(mode).asType(symbolicMethodTypeInvoker); + var mh = vh.getMethodHandle(mode).asType(symbolicMethodTypeInvoker); + if (cache == null) { + // Reduce costly object allocation - if our assumption stands, + // the first adaption works, and we don't want allocations for + // every VH invocation. + adaption = new Adaption(vh, mh); + } + return mh; } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java index b1e9bdf2987..ec322253503 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java @@ -74,16 +74,21 @@ public class VarHandleExact { @Benchmark public void exact_exactInvocation() { - exact.set(data, (long) 0, 42); + var _ = (int) exact.getAndAdd(data, (long) 0, 42); } @Benchmark public void generic_genericInvocation() { - generic.set(data, 0, 42); + generic.getAndAdd(data, 0, 42); + } + + @Benchmark + public void generic_returnDroppingInvocation() { + generic.getAndAdd(data, (long) 0, 42); } @Benchmark public void generic_exactInvocation() { - generic.set(data, (long) 0, 42); + var _ = (int) generic.getAndAdd(data, (long) 0, 42); } } diff --git a/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java index 1a56fdc4d6a..35714130bf0 100644 --- a/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java @@ -74,7 +74,7 @@ public class VarHandleExact { @Benchmark public void generic_genericInvocation() { - var _ = (long) generic.getAndAdd(data, 42); + generic.getAndAdd(data, 42); } @Benchmark