Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.function.Function;
Expand Down Expand Up @@ -436,4 +437,8 @@ public boolean allowConstantFolding(AnalysisMethod method) {
*/
return method.isOriginalMethod();
}

public Set<AnalysisMethod> loadOpenTypeWorldDispatchTableMethods(@SuppressWarnings("unused") AnalysisType type) {
return Set.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ public void persistImageHeapSize(long imageHeapSize) {
jsonMap.put(IMAGE_HEAP_SIZE_TAG, String.valueOf(imageHeapSize));
}

protected boolean shouldPersistMethod(AnalysisMethod method) {
return method.isReachable();
}

public void persistAnalysisInfo() {
persistHook();

Expand All @@ -223,7 +227,7 @@ public void persistAnalysisInfo() {
}
jsonMap.put(TYPES_TAG, typesMap);

for (AnalysisMethod method : aUniverse.getMethods().stream().filter(AnalysisMethod::isReachable).toList()) {
for (AnalysisMethod method : aUniverse.getMethods().stream().filter(this::shouldPersistMethod).toList()) {
persistMethod(method);
}
jsonMap.put(METHODS_TAG, methodsMap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public record Signature(String name, AnalysisType[] parameterTypes) {

public final ResolvedJavaMethod wrapped;

private AnalysisMethod indirectCallTarget = null;
public boolean invalidIndirectCallTarget = false;

private final int id;
/** Marks a method loaded from a base layer. */
private final boolean isInBaseLayer;
Expand Down Expand Up @@ -329,6 +332,76 @@ public AnalysisUniverse getUniverse() {
return declaringClass.getUniverse();
}

private static boolean matchingSignature(AnalysisMethod o1, AnalysisMethod o2) {
if (o1.equals(o2)) {
return true;
}

if (!o1.getName().equals(o2.getName())) {
return false;
}

return o1.getSignature().equals(o2.getSignature());
}

private AnalysisMethod setIndirectCallTarget(AnalysisMethod method, boolean foundMatch) {
indirectCallTarget = method;
invalidIndirectCallTarget = !foundMatch;
return indirectCallTarget;
}

/**
* For methods where its {@link #getDeclaringClass()} does not explicitly declare the method,
* find an alternative explicit declaration for the method which can be used as an indirect call
* target. This logic is currently used for deciding the target of virtual/interface calls when
* using the open type world.
*/
public AnalysisMethod getIndirectCallTarget() {
if (indirectCallTarget != null) {
return indirectCallTarget;
}
if (isStatic()) {
/*
* Static methods must always be explicitly declared.
*/
return setIndirectCallTarget(this, true);
}

var dispatchTableMethods = declaringClass.getOrCalculateOpenTypeWorldDispatchTableMethods();

if (isConstructor()) {
/*
* Constructors can only be found in their declaring class.
*/
return setIndirectCallTarget(this, dispatchTableMethods.contains(this));
}

if (dispatchTableMethods.contains(this)) {
return setIndirectCallTarget(this, true);
}

for (AnalysisType interfaceType : declaringClass.getAllInterfaces()) {
if (interfaceType.equals(declaringClass)) {
// already checked
continue;
}
dispatchTableMethods = interfaceType.getOrCalculateOpenTypeWorldDispatchTableMethods();
for (AnalysisMethod candidate : dispatchTableMethods) {
if (matchingSignature(candidate, this)) {
return setIndirectCallTarget(candidate, true);
}
}
}

/*
* For some methods (e.g., methods labeled as @PolymorphicSignature or @Delete), we
* currently do not find matches. However, these methods will not be indirect calls within
* our generated code, so it is not necessary to determine an accurate virtual/interface
* call target.
*/
return setIndirectCallTarget(this, false);
}

public void cleanupAfterAnalysis() {
if (parsedGraphCacheState.get() instanceof AnalysisParsedGraph) {
parsedGraphCacheState.set(GRAPH_CACHE_CLEARED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav

private final AnalysisType[] interfaces;
private AnalysisMethod[] declaredMethods;
private Set<AnalysisMethod> dispatchTableMethods;

private AnalysisType[] allInterfaces;

/* isArray is an expensive operation so we eagerly compute it */
private final boolean isArray;
Expand Down Expand Up @@ -364,6 +367,36 @@ public int getArrayDimension() {
return dimension;
}

/**
* @return All interfaces this type inherits (including itself if it is an interface).
*/
public AnalysisType[] getAllInterfaces() {
if (allInterfaces != null) {
return allInterfaces;
}

Set<AnalysisType> allInterfaceSet = new HashSet<>();

if (isInterface()) {
allInterfaceSet.add(this);
}

if (this.superClass != null) {
allInterfaceSet.addAll(Arrays.asList(this.superClass.getAllInterfaces()));
}

for (AnalysisType i : interfaces) {
allInterfaceSet.addAll(Arrays.asList(i.getAllInterfaces()));
}

var result = allInterfaceSet.toArray(AnalysisType[]::new);

// ensure result is fully visible across threads
VarHandle.storeStoreFence();
allInterfaces = result;
return allInterfaces;
}

public void cleanupAfterAnalysis() {
instantiatedTypes = null;
instantiatedTypesNonNull = null;
Expand Down Expand Up @@ -1270,6 +1303,64 @@ public AnalysisMethod findConstructor(Signature signature) {
return null;
}

public Set<AnalysisMethod> getOpenTypeWorldDispatchTableMethods() {
AnalysisError.guarantee(dispatchTableMethods != null);
return dispatchTableMethods;
}

/*
* Calculates all methods in this class which should be included in its dispatch table.
*/
public Set<AnalysisMethod> getOrCalculateOpenTypeWorldDispatchTableMethods() {
if (dispatchTableMethods != null) {
return dispatchTableMethods;
}
if (isPrimitive()) {
dispatchTableMethods = Set.of();
return dispatchTableMethods;
}
if (getWrapped() instanceof BaseLayerType) {
var result = universe.hostVM.loadOpenTypeWorldDispatchTableMethods(this);

// ensure result is fully visible across threads
VarHandle.storeStoreFence();
dispatchTableMethods = result;
return dispatchTableMethods;
}

Set<ResolvedJavaMethod> wrappedMethods = new HashSet<>();
wrappedMethods.addAll(Arrays.asList(getWrapped().getDeclaredMethods(false)));
wrappedMethods.addAll(Arrays.asList(getWrapped().getDeclaredConstructors(false)));

var resultSet = new HashSet<AnalysisMethod>();
for (ResolvedJavaMethod m : wrappedMethods) {
if (m.isStatic()) {
/* Only looking at member methods and constructors */
continue;
}
try {
AnalysisMethod aMethod = universe.lookup(m);
resultSet.add(aMethod);
} catch (UnsupportedFeatureException t) {
/*
* Methods which are deleted or not available on this platform will throw an error
* during lookup - ignore and continue execution
*
* Note it is not simple to create a check to determine whether calling
* universe#lookup will trigger an error by creating an analysis object for a type
* not supported on this platform, as creating a method requires, in addition to the
* types of its return type and parameters, all of the super types of its return and
* parameters to be created as well.
*/
}
}

// ensure result is fully visible across threads
VarHandle.storeStoreFence();
dispatchTableMethods = resultSet;
return dispatchTableMethods;
}

@Override
public AnalysisMethod findMethod(String name, Signature signature) {
for (AnalysisMethod method : getDeclaredMethods(false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,24 @@
*/
package com.oracle.svm.core.config;

import jdk.vm.ci.meta.UnresolvedJavaType;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.function.Predicate;

import org.graalvm.nativeimage.AnnotationAccess;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.constant.CEnum;
import org.graalvm.word.WordBase;

import com.oracle.svm.core.SubstrateTargetDescription;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.api.directives.GraalDirectives;
import jdk.graal.compiler.core.common.NumUtil;
Expand All @@ -41,6 +52,7 @@
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.UnresolvedJavaType;

/**
* Immutable class that holds all sizes and offsets that contribute to the object layout.
Expand Down Expand Up @@ -92,6 +104,29 @@ public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int ob
this.arrayBaseOffset = arrayBaseOffset;
this.objectHeaderIdentityHashOffset = headerIdentityHashOffset;
this.identityHashMode = identityHashMode.value;

if (ImageLayerBuildingSupport.buildingImageLayer()) {
int[] currentValues = {
/* this.target, */
this.referenceSize,
this.objectAlignment,
this.alignmentMask,
this.hubOffset,
this.firstFieldOffset,
this.arrayLengthOffset,
this.arrayBaseOffset,
this.objectHeaderIdentityHashOffset,
this.identityHashMode,
};
var numFields = Arrays.stream(ObjectLayout.class.getDeclaredFields()).filter(Predicate.not(Field::isSynthetic)).count();
VMError.guarantee(numFields - 1 == currentValues.length, "Missing fields");

if (ImageLayerBuildingSupport.buildingInitialLayer()) {
ImageSingletons.add(PriorObjectLayout.class, new PriorObjectLayout(currentValues));
} else {
VMError.guarantee(Arrays.equals(currentValues, ImageSingletons.lookup(PriorObjectLayout.class).priorValues));
}
}
}

/** The minimum alignment of objects (instances and arrays). */
Expand Down Expand Up @@ -276,4 +311,29 @@ public enum IdentityHashMode {
this.value = value;
}
}

static class PriorObjectLayout implements LayeredImageSingleton {
final int[] priorValues;

PriorObjectLayout(int[] priorValues) {
this.priorValues = priorValues;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY;
}

@Override
public PersistFlags preparePersist(ImageSingletonWriter writer) {
writer.writeIntList("priorValues", Arrays.stream(priorValues).boxed().toList());
return PersistFlags.CREATE;
}

@SuppressWarnings("unused")
public static Object createFromLoader(ImageSingletonLoader loader) {
int[] priorValues = loader.readIntList("priorValues").stream().mapToInt(e -> e).toArray();
return new PriorObjectLayout(priorValues);
}
}
}
Loading