diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index 5b8a4478be579..02e3b7e233beb 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -622,6 +622,8 @@ static boolean profileBoolean(boolean result, int[] counters) { } // Intrinsified by C2. Returns true if obj is a compile-time constant. + // Note that a non-constant value may be subsequently promoted to a constant, + // so a false return value does not indicate obj is definitely not a constant. @Hidden @jdk.internal.vm.annotation.IntrinsicCandidate static boolean isCompileConstant(Object obj) { 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 9246fdc039571..dcd6efc3928de 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2017,7 +2017,25 @@ static final class AccessDescriptor { final int type; final int mode; - public AccessDescriptor(MethodType symbolicMethodType, int type, int mode) { + // 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; this.symbolicMethodTypeErased = symbolicMethodType.erase(); this.symbolicMethodTypeInvoker = symbolicMethodType.insertParameterTypes(0, VarHandle.class); @@ -2025,6 +2043,23 @@ public AccessDescriptor(MethodType symbolicMethodType, int type, int mode) { this.type = type; this.mode = mode; } + + @ForceInline + MethodHandle adaptedMethodHandle(VarHandle vh) { + var cache = adaption; + if (cache != null && cache.vh == vh) { + return cache.mh; + } + + 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/hotspot/jtreg/compiler/c2/irTests/TestGetAndAdd.java b/test/hotspot/jtreg/compiler/c2/irTests/TestGetAndAdd.java index 905a11eb49457..50b1abe12678c 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestGetAndAdd.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestGetAndAdd.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ /* * @test - * bug 8308444 + * @bug 8308444 * @summary verify that the correct node is matched for atomic getAndAdd * @requires os.simpleArch == "x64" * @requires vm.compiler2.enabled diff --git a/test/hotspot/jtreg/compiler/c2/irTests/constantFold/VarHandleMismatchedTypeFold.java b/test/hotspot/jtreg/compiler/c2/irTests/constantFold/VarHandleMismatchedTypeFold.java new file mode 100644 index 0000000000000..2530bded4f6b2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/constantFold/VarHandleMismatchedTypeFold.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.c2.irTests.constantFold; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + +import compiler.lib.ir_framework.Check; +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Run; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +/* + * @test + * @bug 8160821 + * @summary Verify constant folding is possible for mismatched VarHandle access + * @library /test/lib / + * @requires vm.compiler2.enabled + * @run driver compiler.c2.irTests.constantFold.VarHandleMismatchedTypeFold + */ +public class VarHandleMismatchedTypeFold { + + public static void main(String[] args) { + TestFramework.runWithFlags( + "-XX:+UnlockExperimentalVMOptions" + ); + } + + static final int a = 5; + + static final VarHandle vh; + + static { + try { + vh = MethodHandles.lookup().findStaticVarHandle(VarHandleMismatchedTypeFold.class, + "a", int.class); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + @Test + @IR(failOn = {IRNode.ADD_L, IRNode.LOAD_L}) + public long testSum() { + return 2L + (long) vh.get(); + } + + @Check(test = "testSum") + public void runTestSum() { + long sum = testSum(); + if (sum != 2L + 5L) { + throw new IllegalStateException("Failed, unexpected sum " + sum); + } + } + +} diff --git a/test/jdk/java/lang/invoke/VarHandles/PolymorphicCallSiteInlineTest.java b/test/jdk/java/lang/invoke/VarHandles/PolymorphicCallSiteInlineTest.java new file mode 100644 index 0000000000000..568d132f2840b --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/PolymorphicCallSiteInlineTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8160821 + * @summary Ensures a polymorphic call site with non-exact invocation won't + * be incorrectly inlined/optimized + * @requires vm.compiler2.enabled + * @run main/othervm -XX:CompileCommand=PrintCompilation,Main::* + * -XX:CompileCommand=dontinline,Main::payload* + * -Xbatch + * PolymorphicCallSiteInlineTest + */ + +import java.lang.invoke.*; +import java.util.concurrent.CountDownLatch; + +public class PolymorphicCallSiteInlineTest { + + // C2 should inline m into payload1/payload2 in this many runs + static final int RUNS = 0x10000; + + static final int X = 0; + static final long Y = 0L; + + static final VarHandle VH_X; + static final VarHandle VH_Y; + + static { + try { + var lookup = MethodHandles.lookup(); + VH_X = lookup.findStaticVarHandle(PolymorphicCallSiteInlineTest.class, "X", int.class); + VH_Y = lookup.findStaticVarHandle(PolymorphicCallSiteInlineTest.class, "Y", long.class); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + }; + + public static void main(String[] args) { + + CountDownLatch latch = new CountDownLatch(2); + + Thread.ofPlatform().start(() -> { + latch.countDown(); + try { + latch.await(); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + System.out.println("T1 running"); + for (int i = 0; i < RUNS; i++) { + payload1(); + } + }); + + Thread.ofPlatform().start(() -> { + latch.countDown(); + try { + latch.await(); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + System.out.println("T2 running"); + for (int i = 0; i < RUNS; i++) { + payload2(); + } + }); + } + + public static int payload1() { + return (int) m(VH_X); + } + + public static long payload2() { + return (long) m(VH_Y); + } + + public static Object m(VarHandle vh) { + // This is a polymorphic site that sees many VarHandle, but each VH + // is considered "constant" when inlined into payload1/payload2 + // payload1/payload2 will throw exceptions if the incorrect VH gets inlined + return vh.get(); + } +} 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 b1e9bdf298784..204a723d8310c 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,16 +74,21 @@ public void tearDown() { @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 1b5e4f776011d..35714130bf0bb 100644 --- a/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,16 +69,21 @@ public void setup() { @Benchmark public void exact_exactInvocation() { - exact.set(data, (long) 42); + var _ = (long) exact.getAndAdd(data, (long) 42); } @Benchmark public void generic_genericInvocation() { - generic.set(data, 42); + generic.getAndAdd(data, 42); + } + + @Benchmark + public void generic_returnDroppingInvocation() { + generic.getAndAdd(data, (long) 42); } @Benchmark public void generic_exactInvocation() { - generic.set(data, (long) 42); + var _ = (long) generic.getAndAdd(data, (long) 42); } }