Skip to content
Merged
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 @@ -126,9 +126,6 @@ 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 @@ -347,69 +344,6 @@ 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() || isConstructor()) {
/*
* Static methods and constructors must always be explicitly declared.
*/
return setIndirectCallTarget(this, true);
}

var dispatchTableMethods = declaringClass.getOrCalculateOpenTypeWorldDispatchTableMethods();

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);
}

/**
* @see PointsToAnalysis#validateFixedPointState
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,6 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
private AnalysisMethod[] declaredMethods;
private Set<AnalysisMethod> dispatchTableMethods;

private AnalysisType[] allInterfaces;

/* isArray is an expensive operation so we eagerly compute it */
private final boolean isArray;
private final boolean isJavaLangObject;
Expand Down Expand Up @@ -377,36 +375,6 @@ 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 @@ -1356,18 +1324,6 @@ public AnalysisMethod[] getDeclaredConstructors(boolean forceLink) {
return universe.lookup(wrapped.getDeclaredConstructors(forceLink));
}

public AnalysisMethod findConstructor(Signature signature) {
if (wrapped instanceof BaseLayerType) {
return null;
}
for (AnalysisMethod ctor : getDeclaredConstructors(false)) {
if (ctor.getSignature().equals(signature)) {
return ctor;
}
}
return null;
}

public boolean isOpenTypeWorldDispatchTableMethodsCalculated() {
return dispatchTableMethods != null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ public void lower(LoadOpenTypeWorldDispatchTableStartingOffset node, LoweringToo
SharedMethod target = node.getTarget();
int vtableStartingOffset = KnownOffsets.singleton().getVTableBaseOffset();
if (target != null) {
/*
* Update target to point to indirect call target. The indirect call target is
* different than the original target when the original target is a method we have
* not placed in any virtual/interface table. See SharedMethod#getIndirectCallTarget
* and HostedMethod#indirectCallVTableIndex for more information.
*/
target = target.getIndirectCallTarget();
/*
* If the target is known, then we know whether to use the class dispatch table or
* an interface dispatch table.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ public interface SharedMethod extends ResolvedJavaMethod {

int getVTableIndex();

/**
* In the open type world, our virtual/interface tables will only contain declared methods.
* However, sometimes JVMCI will expose special methods HotSpot introduces into vtables, such as
* miranda and overpass methods. When these special methods serve as call targets for indirect
* calls, we must switch the call target to an alternative method (with the same resolution)
* that will be present in the open type world virtual/interface tables.
*
* <p>
* Note normally in the open type world {@code indirectCallTarget == this}. Only for special
* HotSpot-specific methods such as miranda and overpass methods will the indirectCallTarget be
* a different method. The logic for setting the indirectCallTarget can be found in
* {@code OpenTypeWorldFeature#calculateIndirectCallTarget}.
*
* <p>
* In the closed type world, this method will always return {@code this}.
*/
SharedMethod getIndirectCallTarget();

/**
* Returns the deopt stub type for the stub methods in {@link Deoptimizer}. Only used when
* compiling the deopt stubs during image generation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,23 +476,33 @@ public void updateSubstrateDataAfterCompilation(HostedUniverse hUniverse, Provid
? providers.getConstantReflection().readFieldValue(hField, null)
: null;
constantValue = SubstrateGraalUtils.hostedToRuntime(constantValue, providers.getConstantReflection());
sField.setSubstrateData(hField.getLocation(), hField.isAccessed(), hField.isWritten() || !hField.isValueAvailable(), constantValue);
sField.setSubstrateDataAfterCompilation(hField.getLocation(), hField.isAccessed(), hField.isWritten() || !hField.isValueAvailable(), constantValue);
}

methods.forEach((aMethod, sMethod) -> {
HostedMethod hMethod = hUniverse.lookup(aMethod);
SubstrateMethod indirectCallTarget = sMethod;
if (!hMethod.getIndirectCallTarget().equals(hMethod)) {
indirectCallTarget = methods.get(hMethod.getIndirectCallTarget().getWrapped());
}
int vTableIndex = (hMethod.hasVTableIndex() ? hMethod.getVTableIndex() : HostedMethod.MISSING_VTABLE_IDX);
sMethod.setSubstrateDataAfterCompilation(indirectCallTarget, vTableIndex);
});

}

public void updateSubstrateDataAfterHeapLayout(HostedUniverse hUniverse) {
for (Map.Entry<AnalysisMethod, SubstrateMethod> entry : methods.entrySet()) {
AnalysisMethod aMethod = entry.getKey();
SubstrateMethod sMethod = entry.getValue();
HostedMethod hMethod = hUniverse.lookup(aMethod);
int vTableIndex = (hMethod.hasVTableIndex() ? hMethod.getVTableIndex() : -1);

/*
* We access the offset of methods in the image code section here. Therefore, this code
* can only run after the heap and code cache layout was done.
*/
int imageCodeOffset = hMethod.isCodeAddressOffsetValid() ? hMethod.getCodeAddressOffset() : 0;
sMethod.setSubstrateData(vTableIndex, imageCodeOffset, hMethod.getImageCodeDeoptOffset());
sMethod.setSubstrateDataAfterHeapLayout(imageCodeOffset, hMethod.getImageCodeDeoptOffset());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void setLinks(SubstrateType type, SubstrateType declaringClass) {
this.declaringClass = declaringClass;
}

public void setSubstrateData(int location, boolean isAccessed, boolean isWritten, JavaConstant constantValue) {
public void setSubstrateDataAfterCompilation(int location, boolean isAccessed, boolean isWritten, JavaConstant constantValue) {
this.location = location;
this.isAccessed = isAccessed;
this.isWritten = isWritten;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.graalvm.nativeimage.c.function.CEntryPoint;

import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.svm.core.BuildPhaseProvider.AfterCompilation;
import com.oracle.svm.core.BuildPhaseProvider.AfterHeapLayout;
import com.oracle.svm.core.BuildPhaseProvider.ReadyForCompilation;
import com.oracle.svm.core.Uninterruptible;
Expand All @@ -50,6 +51,7 @@
import com.oracle.svm.core.graal.phases.SubstrateSafepointInsertionPhase;
import com.oracle.svm.core.heap.UnknownObjectField;
import com.oracle.svm.core.heap.UnknownPrimitiveField;
import com.oracle.svm.core.meta.SharedMethod;
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
import com.oracle.svm.core.util.HostedStringDeduplication;
import com.oracle.svm.core.util.VMError;
Expand Down Expand Up @@ -88,7 +90,8 @@ public class SubstrateMethod implements SharedRuntimeMethod {
private final int hashCode;
private SubstrateType declaringClass;
@UnknownPrimitiveField(availability = ReadyForCompilation.class) private int encodedGraphStartOffset;
@UnknownPrimitiveField(availability = AfterHeapLayout.class) private int vTableIndex;
@UnknownPrimitiveField(availability = AfterCompilation.class) private int vTableIndex;
@UnknownObjectField(availability = AfterCompilation.class) private SubstrateMethod indirectCallTarget;

/**
* A metadata object describing the image code that contains the compiled code of this method.
Expand Down Expand Up @@ -195,8 +198,12 @@ public Object getRawImplementations() {
return implementations;
}

public void setSubstrateData(int vTableIndex, int imageCodeOffset, int imageCodeDeoptOffset) {
public void setSubstrateDataAfterCompilation(SubstrateMethod indirectCallTarget, int vTableIndex) {
this.indirectCallTarget = indirectCallTarget;
this.vTableIndex = vTableIndex;
}

public void setSubstrateDataAfterHeapLayout(int imageCodeOffset, int imageCodeDeoptOffset) {
this.imageCodeOffset = imageCodeOffset;
this.imageCodeDeoptOffset = imageCodeDeoptOffset;
}
Expand Down Expand Up @@ -302,6 +309,11 @@ public int getVTableIndex() {
return vTableIndex;
}

@Override
public SharedMethod getIndirectCallTarget() {
return indirectCallTarget;
}

@Override
public Deoptimizer.StubType getDeoptStubType() {
return Deoptimizer.StubType.NoDeoptStub;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,36 +96,37 @@ struct PersistedAnalysisMethod {
annotationList @20 :List(Annotation);
isVarArgs @21 :Bool;
isBridge @22 :Bool;
analysisGraphLocation @23 :Text;
analysisGraphIsIntrinsic @24 :Bool;
strengthenedGraphLocation @25 :Text;
hostedMethodIndex @26 :HostedMethodIndex;
isDeclared @23 :Bool;
analysisGraphLocation @24 :Text;
analysisGraphIsIntrinsic @25 :Bool;
strengthenedGraphLocation @26 :Text;
hostedMethodIndex @27 :HostedMethodIndex;
wrappedMethod :union {
none @27 :Void; # default
none @28 :Void; # default
factoryMethod :group {
targetConstructorId @28 :MethodId;
throwAllocatedObject @29 :Bool;
instantiatedTypeId @30 :TypeId;
targetConstructorId @29 :MethodId;
throwAllocatedObject @30 :Bool;
instantiatedTypeId @31 :TypeId;
}
outlinedSB :group {
methodTypeReturn @31 :Text;
methodTypeParameters @32 :List(Text);
methodTypeReturn @32 :Text;
methodTypeParameters @33 :List(Text);
}
cEntryPointCallStub :group {
originalMethodId @33 :MethodId;
notPublished @34 :Bool;
originalMethodId @34 :MethodId;
notPublished @35 :Bool;
}
wrappedMember :group {
union {
reflectionExpandSignature @35 :Void;
javaCallVariantWrapper @36 :Void;
reflectionExpandSignature @36 :Void;
javaCallVariantWrapper @37 :Void;
}
name @37 :Text;
declaringClassName @38 :Text;
argumentTypeNames @39 :List(Text);
name @38 :Text;
declaringClassName @39 :Text;
argumentTypeNames @40 :List(Text);
}
polymorphicSignature :group {
callers @40 :List(MethodId);
callers @41 :List(MethodId);
}
}
}
Expand Down
Loading
Loading