Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-18799] Share JNI wrapper methods between callees with the same signature. #4686

Merged
merged 11 commits into from
Jul 11, 2022
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static <U extends SignedWord> U nullHandle() {
return WordFactory.signed(0);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static <U extends ObjectHandle> boolean isInRange(U handle) {
return handle.rawValue() >= MIN_VALUE && handle.rawValue() <= MAX_VALUE;
}
Expand Down Expand Up @@ -90,13 +91,25 @@ private void growFrameStack() {
@SuppressWarnings("unchecked")
public T create(Object obj) {
if (obj == null) {
return (T) nullHandle();
return nullHandle();
}
ensureCapacity(1);
T handle = tryCreateNonNull(obj);
assert !handle.equal(nullHandle());
return handle;
}

@SuppressWarnings("unchecked")
@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
public T tryCreateNonNull(Object obj) {
assert obj != null;
if (top >= objects.length) {
return nullHandle();
}
int index = top;
objects[index] = obj;
top++;
return (T) WordFactory.signed(index);
return WordFactory.signed(index);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,35 @@
*/
package com.oracle.svm.hosted.code;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import com.oracle.svm.core.SubstrateUtil;

import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.Signature;

/**
* A straightforward implementation of {@link Signature}.
*/
public class SimpleSignature implements Signature {
public static SimpleSignature fromKinds(JavaKind[] paramKinds, JavaKind returnKind, MetaAccessProvider metaAccess) {
ResolvedJavaType[] paramTypes = new ResolvedJavaType[paramKinds.length];
for (int i = 0; i < paramKinds.length; i++) {
paramTypes[i] = SimpleSignature.resolveType(paramKinds[i], metaAccess);
}
JavaType returnType = SimpleSignature.resolveType(returnKind, metaAccess);
return new SimpleSignature(paramTypes, returnType);
}

private static ResolvedJavaType resolveType(JavaKind kind, MetaAccessProvider metaAccess) {
return metaAccess.lookupJavaType(kind.isObject() ? Object.class : kind.toJavaClass());
}

private final JavaType[] parameterTypes;
private final JavaType returnType;

Expand All @@ -60,4 +79,38 @@ public JavaType getParameterType(int index, ResolvedJavaType accessingClass) {
public JavaType getReturnType(ResolvedJavaType accessingClass) {
return returnType;
}

public String getIdentifier() {
StringBuilder sb = new StringBuilder(1 + parameterTypes.length);
boolean digest = false;
for (JavaType type : parameterTypes) {
if (type.getJavaKind().isPrimitive() || (type instanceof ResolvedJavaType && ((ResolvedJavaType) type).isJavaLangObject())) {
sb.append(type.getJavaKind().getTypeChar());
} else {
sb.append(type.toClassName());
digest = true;
}
}
sb.append('_').append(returnType.getJavaKind().getTypeChar());
return digest ? SubstrateUtil.digest(sb.toString()) : sb.toString();
}

@Override
public boolean equals(Object obj) {
if (this != obj && obj instanceof SimpleSignature) {
var other = (SimpleSignature) obj;
return Arrays.equals(parameterTypes, other.parameterTypes) && Objects.equals(returnType, other.returnType);
}
return (this == obj);
}

@Override
public int hashCode() {
return Arrays.hashCode(parameterTypes) * 31 + Objects.hashCode(returnType);
}

@Override
public String toString() {
return getIdentifier();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import com.oracle.svm.util.ReflectionUtil;

@AutomaticFeature
final class KnownOffsetsFeature implements Feature {
public final class KnownOffsetsFeature implements Feature {
@Override
public List<Class<? extends Feature>> getRequiredFeatures() {
if (SubstrateOptions.MultiThreaded.getValue()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.StaticFieldsSupport;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.jni.access.JNIAccessibleField;
import com.oracle.svm.jni.access.JNINativeLinkage;
Expand Down Expand Up @@ -64,22 +65,27 @@ static JNIEnvironment environment() {
return JNIThreadLocalEnvironment.getAddress();
}

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

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

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

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

@Uninterruptible(reason = "Allow inlining from field accessor methods, which are uninterruptible.", mayBeInlined = true)
static Object getStaticObjectFieldsArray() {
return StaticFieldsSupport.getStaticObjectFields();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

import com.oracle.svm.core.util.VMError;
import com.oracle.svm.jni.hosted.JNICallTrampolineMethod;
import com.oracle.svm.jni.hosted.JNIJavaCallWrapperMethod.CallVariant;
import com.oracle.svm.jni.hosted.JNIJavaCallVariantWrapperMethod.CallVariant;

/** Holder class for generated {@link JNICallTrampolineMethod} code. */
public final class JNIJavaCallTrampolines {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2017, 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.oracle.svm.jni;

import com.oracle.svm.jni.hosted.JNIJavaCallVariantWrapperMethod;

import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.MetaAccessProvider;

/** Holder class for generated {@link JNIJavaCallVariantWrapperMethod} code. */
public final class JNIJavaCallVariantWrappers {
/**
* Generated call wrappers need an actual constant pool, so we provide that of our private
* constructor.
*/
public static ConstantPool getConstantPool(MetaAccessProvider metaAccess) {
return metaAccess.lookupJavaType(JNIJavaCallVariantWrappers.class).getDeclaredConstructors()[0].getConstantPool();
}

private JNIJavaCallVariantWrappers() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.oracle.svm.jni;

import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
import org.graalvm.compiler.word.Word;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.Isolate;
Expand Down Expand Up @@ -112,22 +113,27 @@ private static ThreadLocalHandles<ObjectHandle> createLocals() {
}

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

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

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

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

@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
public static <T> T getObject(JNIObjectHandle handle) {
if (handle.equal(nullHandle())) {
return null;
Expand All @@ -138,10 +144,18 @@ public static <T> T getObject(JNIObjectHandle handle) {
if (useImageHeapHandles() && JNIImageHeapHandles.isInRange(handle)) {
return JNIImageHeapHandles.getObject(handle);
}
return getObjectSlow(handle);
}

@Uninterruptible(reason = "Not really, but our caller is to allow inlining and we must be safe at this point.", calleeMustBe = false)
private static <T> T getObjectSlow(JNIObjectHandle handle) {
return getObjectSlow0(handle);
}

private static <T> T getObjectSlow0(JNIObjectHandle handle) {
if (JNIGlobalHandles.isInRange(handle)) {
return JNIGlobalHandles.getObject(handle);
}

throw throwIllegalArgumentException();
}

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

@Uninterruptible(reason = "Allow inlining from entry points, which are uninterruptible.", mayBeInlined = true)
public static JNIObjectHandle createLocal(Object obj) {
if (obj == null) {
return JNIObjectHandles.nullHandle();
}
if (useImageHeapHandles() && JNIImageHeapHandles.isInImageHeap(obj)) {
return JNIImageHeapHandles.asLocal(obj);
}
ThreadLocalHandles<ObjectHandle> locals = getExistingLocals();
if (BranchProbabilityNode.probability(BranchProbabilityNode.VERY_FAST_PATH_PROBABILITY, locals != null)) {
ObjectHandle handle = locals.tryCreateNonNull(obj);
if (BranchProbabilityNode.probability(BranchProbabilityNode.FAST_PATH_PROBABILITY, handle.notEqual(nullHandle()))) {
return encodeLocal(handle);
}
}
return createLocalSlow(obj);
}

@Uninterruptible(reason = "Not really, but our caller is uninterruptible for inlining and we must be safe at this point.", calleeMustBe = false)
private static JNIObjectHandle createLocalSlow(Object obj) {
return createLocalSlow0(obj);
}

private static JNIObjectHandle createLocalSlow0(Object obj) {
return encodeLocal(getOrCreateLocals().create(obj));
}

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

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

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

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

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

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