Skip to content

Commit 2a975eb

Browse files
committed
[GR-18799] Share JNI wrapper methods between callees with the same signature.
PullRequest: graal/12019
2 parents d3e1677 + 0e0a4a2 commit 2a975eb

20 files changed

+1003
-616
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public static <U extends SignedWord> U nullHandle() {
4949
return WordFactory.signed(0);
5050
}
5151

52+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
5253
public static <U extends ObjectHandle> boolean isInRange(U handle) {
5354
return handle.rawValue() >= MIN_VALUE && handle.rawValue() <= MAX_VALUE;
5455
}
@@ -90,13 +91,25 @@ private void growFrameStack() {
9091
@SuppressWarnings("unchecked")
9192
public T create(Object obj) {
9293
if (obj == null) {
93-
return (T) nullHandle();
94+
return nullHandle();
9495
}
9596
ensureCapacity(1);
97+
T handle = tryCreateNonNull(obj);
98+
assert !handle.equal(nullHandle());
99+
return handle;
100+
}
101+
102+
@SuppressWarnings("unchecked")
103+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
104+
public T tryCreateNonNull(Object obj) {
105+
assert obj != null;
106+
if (top >= objects.length) {
107+
return nullHandle();
108+
}
96109
int index = top;
97110
objects[index] = obj;
98111
top++;
99-
return (T) WordFactory.signed(index);
112+
return WordFactory.signed(index);
100113
}
101114

102115
@SuppressWarnings("unchecked")

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SimpleSignature.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,35 @@
2424
*/
2525
package com.oracle.svm.hosted.code;
2626

27+
import java.util.Arrays;
2728
import java.util.List;
29+
import java.util.Objects;
2830

31+
import com.oracle.svm.core.SubstrateUtil;
32+
33+
import jdk.vm.ci.meta.JavaKind;
2934
import jdk.vm.ci.meta.JavaType;
35+
import jdk.vm.ci.meta.MetaAccessProvider;
3036
import jdk.vm.ci.meta.ResolvedJavaType;
3137
import jdk.vm.ci.meta.Signature;
3238

3339
/**
3440
* A straightforward implementation of {@link Signature}.
3541
*/
3642
public class SimpleSignature implements Signature {
43+
public static SimpleSignature fromKinds(JavaKind[] paramKinds, JavaKind returnKind, MetaAccessProvider metaAccess) {
44+
ResolvedJavaType[] paramTypes = new ResolvedJavaType[paramKinds.length];
45+
for (int i = 0; i < paramKinds.length; i++) {
46+
paramTypes[i] = SimpleSignature.resolveType(paramKinds[i], metaAccess);
47+
}
48+
JavaType returnType = SimpleSignature.resolveType(returnKind, metaAccess);
49+
return new SimpleSignature(paramTypes, returnType);
50+
}
51+
52+
private static ResolvedJavaType resolveType(JavaKind kind, MetaAccessProvider metaAccess) {
53+
return metaAccess.lookupJavaType(kind.isObject() ? Object.class : kind.toJavaClass());
54+
}
55+
3756
private final JavaType[] parameterTypes;
3857
private final JavaType returnType;
3958

@@ -60,4 +79,38 @@ public JavaType getParameterType(int index, ResolvedJavaType accessingClass) {
6079
public JavaType getReturnType(ResolvedJavaType accessingClass) {
6180
return returnType;
6281
}
82+
83+
public String getIdentifier() {
84+
StringBuilder sb = new StringBuilder(1 + parameterTypes.length);
85+
boolean digest = false;
86+
for (JavaType type : parameterTypes) {
87+
if (type.getJavaKind().isPrimitive() || (type instanceof ResolvedJavaType && ((ResolvedJavaType) type).isJavaLangObject())) {
88+
sb.append(type.getJavaKind().getTypeChar());
89+
} else {
90+
sb.append(type.toClassName());
91+
digest = true;
92+
}
93+
}
94+
sb.append('_').append(returnType.getJavaKind().getTypeChar());
95+
return digest ? SubstrateUtil.digest(sb.toString()) : sb.toString();
96+
}
97+
98+
@Override
99+
public boolean equals(Object obj) {
100+
if (this != obj && obj instanceof SimpleSignature) {
101+
var other = (SimpleSignature) obj;
102+
return Arrays.equals(parameterTypes, other.parameterTypes) && Objects.equals(returnType, other.returnType);
103+
}
104+
return (this == obj);
105+
}
106+
107+
@Override
108+
public int hashCode() {
109+
return Arrays.hashCode(parameterTypes) * 31 + Objects.hashCode(returnType);
110+
}
111+
112+
@Override
113+
public String toString() {
114+
return getIdentifier();
115+
}
63116
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
import com.oracle.svm.util.ReflectionUtil;
4848

4949
@AutomaticFeature
50-
final class KnownOffsetsFeature implements Feature {
50+
public final class KnownOffsetsFeature implements Feature {
5151
@Override
5252
public List<Class<? extends Feature>> getRequiredFeatures() {
5353
if (SubstrateOptions.MultiThreaded.getValue()) {

substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIGeneratedMethodSupport.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.graalvm.word.WordFactory;
3333

3434
import com.oracle.svm.core.StaticFieldsSupport;
35+
import com.oracle.svm.core.annotate.Uninterruptible;
3536
import com.oracle.svm.core.config.ConfigurationValues;
3637
import com.oracle.svm.jni.access.JNIAccessibleField;
3738
import com.oracle.svm.jni.access.JNINativeLinkage;
@@ -64,22 +65,27 @@ static JNIEnvironment environment() {
6465
return JNIThreadLocalEnvironment.getAddress();
6566
}
6667

68+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
6769
static JNIObjectHandle boxObjectInLocalHandle(Object obj) {
6870
return JNIObjectHandles.createLocal(obj);
6971
}
7072

73+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
7174
static Object unboxHandle(JNIObjectHandle handle) {
7275
return JNIObjectHandles.getObject(handle);
7376
}
7477

78+
@Uninterruptible(reason = "Allow inlining from field accessor methods, which are uninterruptible.", mayBeInlined = true)
7579
static WordBase getFieldOffsetFromId(JNIFieldId fieldId) {
7680
return JNIAccessibleField.getOffsetFromId(fieldId);
7781
}
7882

83+
@Uninterruptible(reason = "Allow inlining from field accessor methods, which are uninterruptible.", mayBeInlined = true)
7984
static Object getStaticPrimitiveFieldsArray() {
8085
return StaticFieldsSupport.getStaticPrimitiveFields();
8186
}
8287

88+
@Uninterruptible(reason = "Allow inlining from field accessor methods, which are uninterruptible.", mayBeInlined = true)
8389
static Object getStaticObjectFieldsArray() {
8490
return StaticFieldsSupport.getStaticObjectFields();
8591
}

substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallTrampolines.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
import com.oracle.svm.core.util.VMError;
3131
import com.oracle.svm.jni.hosted.JNICallTrampolineMethod;
32-
import com.oracle.svm.jni.hosted.JNIJavaCallWrapperMethod.CallVariant;
32+
import com.oracle.svm.jni.hosted.JNIJavaCallVariantWrapperMethod.CallVariant;
3333

3434
/** Holder class for generated {@link JNICallTrampolineMethod} code. */
3535
public final class JNIJavaCallTrampolines {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2017, 2021, 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 com.oracle.svm.jni;
26+
27+
import com.oracle.svm.jni.hosted.JNIJavaCallVariantWrapperMethod;
28+
29+
import jdk.vm.ci.meta.ConstantPool;
30+
import jdk.vm.ci.meta.MetaAccessProvider;
31+
32+
/** Holder class for generated {@link JNIJavaCallVariantWrapperMethod} code. */
33+
public final class JNIJavaCallVariantWrappers {
34+
/**
35+
* Generated call wrappers need an actual constant pool, so we provide that of our private
36+
* constructor.
37+
*/
38+
public static ConstantPool getConstantPool(MetaAccessProvider metaAccess) {
39+
return metaAccess.lookupJavaType(JNIJavaCallVariantWrappers.class).getDeclaredConstructors()[0].getConstantPool();
40+
}
41+
42+
private JNIJavaCallVariantWrappers() {
43+
}
44+
}

substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIObjectHandles.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.jni;
2626

2727
import org.graalvm.compiler.api.replacements.Fold;
28+
import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
2829
import org.graalvm.compiler.word.Word;
2930
import org.graalvm.nativeimage.CurrentIsolate;
3031
import org.graalvm.nativeimage.Isolate;
@@ -112,22 +113,27 @@ private static ThreadLocalHandles<ObjectHandle> createLocals() {
112113
}
113114

114115
@SuppressWarnings("unchecked")
116+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
115117
private static ThreadLocalHandles<ObjectHandle> getExistingLocals() {
116118
return handles.get();
117119
}
118120

121+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
119122
private static boolean isInLocalRange(JNIObjectHandle handle) {
120123
return ThreadLocalHandles.isInRange((ObjectHandle) handle);
121124
}
122125

126+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
123127
private static ObjectHandle decodeLocal(JNIObjectHandle handle) {
124128
return (ObjectHandle) handle;
125129
}
126130

131+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
127132
private static JNIObjectHandle encodeLocal(ObjectHandle handle) {
128133
return (JNIObjectHandle) handle;
129134
}
130135

136+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
131137
public static <T> T getObject(JNIObjectHandle handle) {
132138
if (handle.equal(nullHandle())) {
133139
return null;
@@ -138,10 +144,18 @@ public static <T> T getObject(JNIObjectHandle handle) {
138144
if (useImageHeapHandles() && JNIImageHeapHandles.isInRange(handle)) {
139145
return JNIImageHeapHandles.getObject(handle);
140146
}
147+
return getObjectSlow(handle);
148+
}
149+
150+
@Uninterruptible(reason = "Not really, but our caller is to allow inlining and we must be safe at this point.", calleeMustBe = false)
151+
private static <T> T getObjectSlow(JNIObjectHandle handle) {
152+
return getObjectSlow0(handle);
153+
}
154+
155+
private static <T> T getObjectSlow0(JNIObjectHandle handle) {
141156
if (JNIGlobalHandles.isInRange(handle)) {
142157
return JNIGlobalHandles.getObject(handle);
143158
}
144-
145159
throw throwIllegalArgumentException();
146160
}
147161

@@ -163,10 +177,30 @@ public static JNIObjectRefType getHandleType(JNIObjectHandle handle) {
163177
return JNIObjectRefType.Invalid; // intentionally includes the null handle
164178
}
165179

180+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
166181
public static JNIObjectHandle createLocal(Object obj) {
182+
if (obj == null) {
183+
return JNIObjectHandles.nullHandle();
184+
}
167185
if (useImageHeapHandles() && JNIImageHeapHandles.isInImageHeap(obj)) {
168186
return JNIImageHeapHandles.asLocal(obj);
169187
}
188+
ThreadLocalHandles<ObjectHandle> locals = getExistingLocals();
189+
if (BranchProbabilityNode.probability(BranchProbabilityNode.VERY_FAST_PATH_PROBABILITY, locals != null)) {
190+
ObjectHandle handle = locals.tryCreateNonNull(obj);
191+
if (BranchProbabilityNode.probability(BranchProbabilityNode.FAST_PATH_PROBABILITY, handle.notEqual(nullHandle()))) {
192+
return encodeLocal(handle);
193+
}
194+
}
195+
return createLocalSlow(obj);
196+
}
197+
198+
@Uninterruptible(reason = "Not really, but our caller is uninterruptible for inlining and we must be safe at this point.", calleeMustBe = false)
199+
private static JNIObjectHandle createLocalSlow(Object obj) {
200+
return createLocalSlow0(obj);
201+
}
202+
203+
private static JNIObjectHandle createLocalSlow0(Object obj) {
170204
return encodeLocal(getOrCreateLocals().create(obj));
171205
}
172206

@@ -269,6 +303,7 @@ final class JNIGlobalHandles {
269303
private static final SignedWord MSB = WordFactory.signed(1L << 63);
270304
private static final ObjectHandlesImpl globalHandles = new ObjectHandlesImpl(JNIObjectHandles.nullHandle().add(1), HANDLE_BITS_MASK, JNIObjectHandles.nullHandle());
271305

306+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
272307
static boolean isInRange(JNIObjectHandle handle) {
273308
return MIN_VALUE.lessOrEqual((SignedWord) handle) && MAX_VALUE.greaterThan((SignedWord) handle);
274309
}
@@ -355,15 +390,18 @@ final class JNIImageHeapHandles {
355390
assert ENTIRE_RANGE_MAX.lessThan(JNIGlobalHandles.MIN_VALUE) || ENTIRE_RANGE_MIN.greaterThan(JNIGlobalHandles.MAX_VALUE);
356391
}
357392

393+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
358394
static boolean isInImageHeap(Object target) {
359395
return target != null && Heap.getHeap().isInImageHeap(target);
360396
}
361397

398+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
362399
static boolean isInRange(JNIObjectHandle handle) {
363400
SignedWord handleValue = (SignedWord) handle;
364401
return handleValue.greaterOrEqual(ENTIRE_RANGE_MIN) && handleValue.lessOrEqual(ENTIRE_RANGE_MAX);
365402
}
366403

404+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
367405
static JNIObjectHandle asLocal(Object target) {
368406
assert isInImageHeap(target);
369407
SignedWord base = (SignedWord) Isolates.getHeapBase(CurrentIsolate.getIsolate());
@@ -390,6 +428,7 @@ private static JNIObjectHandle toRange(JNIObjectHandle handle, SignedWord rangeM
390428
return (JNIObjectHandle) ((SignedWord) handle).and(OBJ_OFFSET_BITS_MASK).add(rangeMin);
391429
}
392430

431+
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
393432
static <T> T getObject(JNIObjectHandle handle) {
394433
assert isInRange(handle);
395434
UnsignedWord base = (UnsignedWord) Isolates.getHeapBase(CurrentIsolate.getIsolate());

0 commit comments

Comments
 (0)