Skip to content

Commit 5823a62

Browse files
committed
[GR-54910] GuestGraal: Add support for truffle run-time compilation - method handles for upcalls
PullRequest: graal/18449
2 parents 151ad5b + 9ed0a9d commit 5823a62

28 files changed

+3205
-93
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/guestgraal/BuildTime.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ public static Map<String, MethodHandle> getRuntimeHandles() {
205205
boolean.class, boolean.class, boolean.class, boolean.class,
206206
long.class, int.class, int.class,
207207
String.class, BiConsumer.class, Supplier.class)),
208+
"hashConstantOopFields", MHL.findStatic(RunTime.class, "hashConstantOopFields",
209+
methodType(long.class, long.class, boolean.class, int.class, int.class,
210+
boolean.class, Runnable.class)),
208211
"getJNIEnv", MHL.findStatic(RunTime.class, "getJNIEnv",
209212
methodType(long.class)),
210213
"attachCurrentThread", MHL.findStatic(RunTime.class, "attachCurrentThread",

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/guestgraal/RunTime.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,14 @@
2929
import java.util.function.BiConsumer;
3030
import java.util.function.Supplier;
3131

32+
import jdk.vm.ci.hotspot.HotSpotResolvedJavaType;
3233
import jdk.vm.ci.hotspot.HotSpotVMConfigAccess;
3334
import jdk.vm.ci.hotspot.HotSpotVMConfigStore;
35+
import jdk.vm.ci.meta.ConstantReflectionProvider;
36+
import jdk.vm.ci.meta.JavaConstant;
37+
import jdk.vm.ci.meta.JavaKind;
38+
import jdk.vm.ci.meta.ResolvedJavaField;
39+
import jdk.vm.ci.runtime.JVMCIBackend;
3440
import org.graalvm.collections.EconomicMap;
3541

3642
import jdk.graal.compiler.debug.GlobalMetrics;
@@ -144,6 +150,59 @@ public static long compileMethod(long methodHandle, boolean useProfilingInfo,
144150
}
145151
}
146152

153+
@SuppressWarnings({"unused", "try"})
154+
public static long hashConstantOopFields(long typeHandle, boolean useScope, int iterations,
155+
int oopsPerIteration, boolean verbose, Runnable doReferenceHandling) {
156+
HotSpotJVMCIRuntime runtime = HotSpotJVMCIRuntime.runtime();
157+
JVMCIBackend backend = runtime.getHostJVMCIBackend();
158+
ConstantReflectionProvider constantReflection = backend.getConstantReflection();
159+
HotSpotResolvedJavaType type = runtime.unhand(HotSpotResolvedJavaType.class, typeHandle);
160+
ResolvedJavaField[] staticFields = type.getStaticFields();
161+
JavaConstant receiver = null;
162+
long hash = 13;
163+
164+
Object scopeDescription = "TestingOopHandles";
165+
166+
int remainingIterations = iterations;
167+
while (remainingIterations-- > 0) {
168+
ResolvedJavaField lastReadField = null;
169+
try (CompilationContext scope = useScope ? HotSpotGraalServices.openLocalCompilationContext(scopeDescription) : null) {
170+
if (verbose && useScope) {
171+
System.out.println("Opened " + scopeDescription);
172+
}
173+
int remainingOops = oopsPerIteration;
174+
while (remainingOops-- > 0) {
175+
for (ResolvedJavaField field : staticFields) {
176+
if (field.getType().getJavaKind() == JavaKind.Object) {
177+
JavaConstant value = constantReflection.readFieldValue(field, receiver);
178+
if (value != null) {
179+
lastReadField = field;
180+
hash = hash ^ value.hashCode();
181+
}
182+
}
183+
}
184+
}
185+
}
186+
if (!useScope) {
187+
System.gc();
188+
if (verbose) {
189+
System.out.println("calling reference handling");
190+
}
191+
doReferenceHandling.run();
192+
if (verbose) {
193+
System.out.println("called reference handling");
194+
}
195+
// Need one more remote oop creation to trigger releasing
196+
// of remote oops that were wrapped in weakly reachable
197+
// IndirectHotSpotObjectConstantImpl objects just collected.
198+
constantReflection.readFieldValue(lastReadField, receiver);
199+
} else if (verbose) {
200+
System.out.println(" Closed " + scopeDescription);
201+
}
202+
}
203+
return hash;
204+
}
205+
147206
private static long jniEnvironmentOffset = Integer.MAX_VALUE;
148207

149208
private static long getJniEnvironmentOffset() {
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.hotspot.guestgraal.truffle;
26+
27+
import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal.Id;
28+
import jdk.graal.compiler.truffle.host.TruffleHostEnvironment;
29+
import jdk.vm.ci.services.Services;
30+
31+
import java.lang.invoke.MethodHandle;
32+
import java.lang.invoke.MethodHandles;
33+
import java.lang.invoke.MethodHandles.Lookup;
34+
import java.lang.reflect.Method;
35+
import java.lang.reflect.Modifier;
36+
import java.util.Arrays;
37+
import java.util.HashMap;
38+
import java.util.HashSet;
39+
import java.util.Map;
40+
import java.util.Map.Entry;
41+
import java.util.NoSuchElementException;
42+
import java.util.Objects;
43+
import java.util.Set;
44+
45+
/**
46+
* Class used to initialize the Truffle extensions to the Graal compiler in the image build time.
47+
*/
48+
public class BuildTime {
49+
50+
private static Lookup hostLookup;
51+
private static Class<?> truffleFromLibGraalStartPoint;
52+
private static Class<?> nativeImageHostEntryPoint;
53+
private static Map<String, MethodHandle> hostMethods;
54+
55+
/**
56+
* Configures Truffle services to the Graal compiler in the image build time.
57+
*/
58+
public static void configureGraalForLibGraal() {
59+
TruffleHostEnvironment.overrideLookup(new LibGraalTruffleHostEnvironmentLookup());
60+
}
61+
62+
/**
63+
* Obtains a {@link Lookup} instance for resolving method handles to invoke Graal and JVMCI
64+
* methods.
65+
* <p>
66+
* This method is invoked reflectively by {@code GuestGraalFeature.initializeTruffle()} in the
67+
* native-image classloader to facilitate the exchange of lookup instances between the
68+
* native-image classloader and the guest Graal classloader.
69+
* </p>
70+
*
71+
* @param lookup a {@link Lookup} instance used to resolve handles for calling into the
72+
* native-image host.
73+
* @param fromLibGraal a class that contains methods for making calls to HotSpot using the JNI
74+
* API.
75+
* @param nativeImageSupport a class that provides native-image and JNI helper methods.
76+
* @return a {@link Entry} containing a {@link Lookup} instance and a class with compiler entry
77+
* methods. The {@link Lookup} instance can be used to resolve the compiler entry
78+
* methods within the provided class.
79+
*/
80+
public static Entry<Lookup, Class<?>> initializeLookup(Lookup lookup, Class<?> fromLibGraal, Class<?> nativeImageSupport) {
81+
if (hostLookup != null) {
82+
throw new IllegalStateException("Host lookup has already been initialized. BuildTime.initializeLookup should only be called once during the native image build process.");
83+
}
84+
hostLookup = Objects.requireNonNull(lookup, "lookup must be non null");
85+
truffleFromLibGraalStartPoint = Objects.requireNonNull(fromLibGraal, "fromLibGraal must be non null");
86+
nativeImageHostEntryPoint = Objects.requireNonNull(nativeImageSupport, "nativeImageSupport must be non null");
87+
return Map.entry(MethodHandles.lookup(), GraalEntryPoints.class);
88+
}
89+
90+
static MethodHandle getHostMethodHandleOrFail(Id id) {
91+
return getHostMethodHandleOrFail(id.getMethodName());
92+
}
93+
94+
static MethodHandle getHostMethodHandleOrFail(String name) {
95+
if (Services.IS_BUILDING_NATIVE_IMAGE) {
96+
/*
97+
* Native-image initializes BuildTime also in the platform classloader. In this case we
98+
* return null.
99+
*/
100+
ClassLoader myLoader = BuildTime.class.getClassLoader();
101+
if (myLoader == null || myLoader == ClassLoader.getPlatformClassLoader() || myLoader == ClassLoader.getSystemClassLoader()) {
102+
return null;
103+
}
104+
if (hostMethods == null) {
105+
hostMethods = initializeHostMethods();
106+
}
107+
MethodHandle handle = hostMethods.get(name);
108+
if (handle != null) {
109+
return handle;
110+
} else {
111+
throw new NoSuchElementException(name);
112+
}
113+
} else if (Services.IS_IN_NATIVE_IMAGE) {
114+
/*
115+
* The getHostMethodHandleOrFail should never be called in the native-image execution
116+
* time.
117+
*/
118+
throw new IllegalStateException("Should not be reachable at libgraal runtime");
119+
} else {
120+
/*
121+
* HS proxy classes and BuildTime are not used in Jargraal, but the CheckGraalInvariants
122+
* test eagerly initializes these proxy classes, leading to a call to
123+
* getHostMethodHandleOrFail. In this scenario, we return null to prevent the test from
124+
* crashing.
125+
*/
126+
return null;
127+
}
128+
}
129+
130+
private static Map<String, MethodHandle> initializeHostMethods() {
131+
try {
132+
Map<String, MethodHandle> result = new HashMap<>();
133+
Set<String> methodNames = new HashSet<>();
134+
Arrays.stream(Id.values()).map(Id::getMethodName).forEach(methodNames::add);
135+
for (Method m : truffleFromLibGraalStartPoint.getDeclaredMethods()) {
136+
if (Modifier.isStatic(m.getModifiers()) && Modifier.isPublic(m.getModifiers())) {
137+
String methodName = m.getName();
138+
if (methodNames.remove(methodName)) {
139+
result.put(methodName, hostLookup.unreflect(m));
140+
}
141+
}
142+
}
143+
if (!methodNames.isEmpty()) {
144+
throw new RuntimeException(String.format("Cannot find methods for following ids %s in %s", methodNames, truffleFromLibGraalStartPoint.getName()));
145+
}
146+
Arrays.stream(NativeImageHostCalls.class.getDeclaredMethods()).map(Method::getName).forEach(methodNames::add);
147+
for (Method m : nativeImageHostEntryPoint.getDeclaredMethods()) {
148+
if (Modifier.isStatic(m.getModifiers()) && Modifier.isPublic(m.getModifiers())) {
149+
String methodName = m.getName();
150+
if (methodNames.remove(methodName)) {
151+
if (result.put(methodName, hostLookup.unreflect(m)) != null) {
152+
throw new RuntimeException(String.format("Duplicate methods for name %s in %s", methodName, nativeImageHostEntryPoint));
153+
}
154+
}
155+
}
156+
}
157+
if (!methodNames.isEmpty()) {
158+
throw new RuntimeException(String.format("Cannot find following methods %s in %s", methodNames, nativeImageHostEntryPoint));
159+
}
160+
return result;
161+
} catch (IllegalAccessException e) {
162+
throw new RuntimeException(e);
163+
}
164+
}
165+
}

0 commit comments

Comments
 (0)