diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/PhaseSuite.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/PhaseSuite.java index f1f3b9d15826..ddcd71d2bd47 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/PhaseSuite.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/PhaseSuite.java @@ -35,12 +35,12 @@ import java.util.function.Supplier; import jdk.graal.compiler.core.common.util.PhasePlan; +import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.debug.TTY; import jdk.graal.compiler.nodes.GraphState; import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.options.Option; import jdk.graal.compiler.options.OptionKey; - import jdk.graal.compiler.serviceprovider.GraalServices; /** @@ -102,6 +102,21 @@ public final void addBeforeLast(BasePhase phase) { last.add(phase); } + /** The new phase is inserted before the first position the target phase is found at. */ + public final void insertBeforePhase(Class> phaseClass, BasePhase newPhase) { + ListIterator> position = findPhase(phaseClass); + GraalError.guarantee(position != null, "Phase %s not found in suite %s.", phaseClass.getName(), this.getName()); + position.previous(); + position.add(newPhase); + } + + /** The new phase is inserted after the first position the target phase is found at. */ + public final void insertAfterPhase(Class> phaseClass, BasePhase newPhase) { + ListIterator> position = findPhase(phaseClass); + GraalError.guarantee(position != null, "Phase %s not found in suite %s.", phaseClass.getName(), this.getName()); + position.add(newPhase); + } + /** * Insert a new phase at the specified index and shifts the element currently at that position * and any subsequent elements to the right. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/virtual/phases/ea/PartialEscapeClosure.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/virtual/phases/ea/PartialEscapeClosure.java index 5b0866c4d796..0b41f16deb04 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/virtual/phases/ea/PartialEscapeClosure.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/virtual/phases/ea/PartialEscapeClosure.java @@ -69,6 +69,7 @@ import jdk.graal.compiler.nodes.ValuePhiNode; import jdk.graal.compiler.nodes.ValueProxyNode; import jdk.graal.compiler.nodes.VirtualState; +import jdk.graal.compiler.nodes.WithExceptionNode; import jdk.graal.compiler.nodes.cfg.HIRBlock; import jdk.graal.compiler.nodes.java.AbstractNewObjectNode; import jdk.graal.compiler.nodes.java.AccessMonitorNode; @@ -311,7 +312,11 @@ private boolean processVirtualizable(ValueNode node, FixedNode insertBefore, Blo if (state.hasObjectState(id)) { FixedNode materializeBefore = insertBefore; if (insertBefore == node && tool.isDeleted()) { - materializeBefore = ((FixedWithNextNode) insertBefore).next(); + if (insertBefore instanceof FixedWithNextNode withNextNode) { + materializeBefore = withNextNode.next(); + } else { + materializeBefore = ((WithExceptionNode) insertBefore).next(); + } } ensureMaterialized(state, id, materializeBefore, effects, COUNTER_MATERIALIZATIONS); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index d33dced7261c..6c49f491a393 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -332,6 +332,10 @@ public boolean isFieldIncluded(BigBang bb, Field field) { return true; } + public boolean isClosedTypeWorld() { + return true; + } + /** * Helpers to determine what analysis actions should be taken for a given Multi-Method version. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index d0bc5626faf1..bc0a65e751f6 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -911,6 +911,10 @@ public ResolvedJavaMethod unwrapTowardsOriginalMethod() { } public Executable getJavaMethod() { + if (wrapped instanceof BaseLayerMethod) { + /* We don't know the corresponding Java method. */ + return null; + } return OriginalMethodProvider.getJavaMethod(this); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java index b19681634af5..536044aa2b76 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java @@ -36,6 +36,7 @@ import java.util.stream.Stream; import org.graalvm.collections.EconomicSet; +import org.graalvm.nativeimage.AnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.PointsToAnalysis; @@ -53,6 +54,8 @@ import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.typestate.PrimitiveConstantTypeState; import com.oracle.graal.pointsto.typestate.TypeState; +import com.oracle.graal.pointsto.util.AnalysisError; +import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.util.ImageBuildStatistics; import jdk.graal.compiler.core.common.type.AbstractObjectStamp; @@ -182,6 +185,9 @@ public boolean equals(Object obj) { private final StrengthenGraphsCounters beforeCounters; private final StrengthenGraphsCounters afterCounters; + /** Used to avoid aggressive optimizations for open type world analysis. */ + private final boolean isClosedTypeWorld; + public StrengthenGraphs(BigBang bb, Universe converter) { this.bb = bb; this.converter = converter; @@ -197,6 +203,7 @@ public StrengthenGraphs(BigBang bb, Universe converter) { beforeCounters = null; afterCounters = null; } + this.isClosedTypeWorld = converter.hostVM().isClosedTypeWorld(); } private static void reportNeverNullInstanceFields(BigBang bb) { @@ -291,7 +298,7 @@ public final void applyResults(AnalysisMethod method) { protected abstract boolean simplifyDelegate(Node n, SimplifierTool tool); /* Wrapper to clearly identify phase in IGV graph dumps. */ - class AnalysisStrengthenGraphsPhase extends BasePhase { + public class AnalysisStrengthenGraphsPhase extends BasePhase { final CanonicalizerPhase phase; AnalysisStrengthenGraphsPhase(AnalysisMethod method, StructuredGraph graph) { @@ -554,7 +561,8 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) { FixedNode node = invoke.asFixedNode(); MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); - if (callTarget.invokeKind().isDirect() && !((AnalysisMethod) callTarget.targetMethod()).isSimplyImplementationInvoked()) { + AnalysisMethod targetMethod = (AnalysisMethod) callTarget.targetMethod(); + if (callTarget.invokeKind().isDirect() && !targetMethod.isSimplyImplementationInvoked()) { /* * This is a direct call to a method that the static analysis did not see as * invoked. This can happen when the receiver is always null. In most cases, the @@ -619,27 +627,66 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) { } } - if (callees.size() == 1) { + if (callTarget.invokeKind().isDirect()) { + /* + * Note: A direct invoke doesn't necessarily imply that the analysis should have + * discovered a single callee. When dealing with interfaces it is in fact possible + * that the Graal stamps are more accurate than the analysis results. So an + * interface call may have already been optimized to a special call by stamp + * strengthening of the receiver object, hence the invoke kind is direct, whereas + * the points-to analysis inaccurately concluded there can be more than one callee. + * + * Below we just check that if there is a direct invoke *and* the analysis + * discovered a single callee, then the callee should match the target method. + */ + if (callees.size() == 1) { + AnalysisMethod singleCallee = callees.iterator().next(); + assert targetMethod.equals(singleCallee) : "Direct invoke target mismatch: " + targetMethod + " != " + singleCallee + ". Called from " + graph.method().format("%H.%n"); + } + } else if (AnnotationAccess.isAnnotationPresent(targetMethod, Delete.class)) { + /* We de-virtualize invokes to deleted methods since the callee must be unique. */ + AnalysisError.guarantee(callees.size() == 1, "@Delete methods should have a single callee."); AnalysisMethod singleCallee = callees.iterator().next(); - if (callTarget.invokeKind().isDirect()) { - assert callTarget.targetMethod().equals(singleCallee) : "Direct invoke target mismatch: " + callTarget.targetMethod() + " != " + singleCallee; - } else { + devirtualizeInvoke(singleCallee, invoke); + } else if (targetMethod.canBeStaticallyBound() || isClosedTypeWorld) { + /* + * We only de-virtualize invokes if we run a closed type world analysis or the + * target method can be trivially statically bound. + */ + if (callees.size() == 1) { + AnalysisMethod singleCallee = callees.iterator().next(); devirtualizeInvoke(singleCallee, invoke); - } + } else { + TypeState receiverTypeState = null; + /* If the receiver flow is saturated, its exact type state does not matter. */ + if (invokeFlow.getTargetMethod().hasReceiver() && !methodFlow.isSaturated((PointsToAnalysis) bb, invokeFlow.getReceiver())) { + receiverTypeState = methodFlow.foldTypeFlow((PointsToAnalysis) bb, invokeFlow.getReceiver()); + } - } else { - TypeState receiverTypeState = null; - /* If the receiver flow is saturated, its exact type state does not matter. */ - if (invokeFlow.getTargetMethod().hasReceiver() && !methodFlow.isSaturated((PointsToAnalysis) bb, invokeFlow.getReceiver())) { - receiverTypeState = methodFlow.foldTypeFlow((PointsToAnalysis) bb, invokeFlow.getReceiver()); - } + /* + * In an open type world we cannot trust the type state of the receiver for + * virtual calls as new subtypes could be added later. + * + * Note: MethodFlowsGraph.saturateAllParameters() does saturate the receiver in + * many cases, so the check above would also lead to a null typeProfile, but we + * cannot guarantee that we cover all cases. + */ + JavaTypeProfile typeProfile = makeTypeProfile(receiverTypeState); + /* + * In a closed type world analysis the method profile of an invoke is complete + * and contains all the callees reachable at that invocation location. Even if + * that invoke is saturated it is still correct as it contains all the reachable + * implementations of the target method. However, in an open type world the + * method profile of an invoke, saturated or not, is incomplete, as there can be + * implementations that we haven't yet seen. + */ + JavaMethodProfile methodProfile = makeMethodProfile(callees); - JavaTypeProfile typeProfile = makeTypeProfile(receiverTypeState); - JavaMethodProfile methodProfile = makeMethodProfile(callees); - assert typeProfile == null || typeProfile.getTypes().length > 1 : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile; - assert methodProfile == null || methodProfile.getMethods().length > 1 : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile; + assert typeProfile == null || typeProfile.getTypes().length > 1 : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile; + assert methodProfile == null || methodProfile.getMethods().length > 1 : "Should devirtualize with typeProfile=" + typeProfile + " and methodProfile=" + methodProfile; - setInvokeProfiles(invoke, typeProfile, methodProfile); + setInvokeProfiles(invoke, typeProfile, methodProfile); + } } if (allowOptimizeReturnParameter) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 0135165fcf27..f61467b0a3af 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1264,10 +1264,23 @@ public static boolean includeAll() { public static final HostedOptionKey ForeignAPISupport = new HostedOptionKey<>(false); @Option(help = "Assume new types cannot be added after analysis", type = OptionType.Expert) // - public static final HostedOptionKey ClosedTypeWorld = new HostedOptionKey<>(true); + public static final HostedOptionKey ClosedTypeWorld = new HostedOptionKey<>(true) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { + ClosedTypeWorldHubLayout.update(values, newValue); + } + }; + + @Option(help = "Use the closed type world dynamic hub representation. This is only allowed when the option ClosedTypeWorld is also set to true.", type = OptionType.Expert) // + public static final HostedOptionKey ClosedTypeWorldHubLayout = new HostedOptionKey<>(true); + + @Fold + public static boolean useClosedTypeWorldHubLayout() { + return ClosedTypeWorldHubLayout.getValue(); + } @Fold - public static boolean closedTypeWorld() { + public static boolean useClosedTypeWorld() { return ClosedTypeWorld.getValue(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java index 3b671896f2dd..cc4d3a2ffc49 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java @@ -125,7 +125,7 @@ public int getVTableOffset(int vTableIndex, boolean fromDynamicHubStart) { } public int getTypeIDSlotsOffset() { - assert isFullyInitialized() && SubstrateOptions.closedTypeWorld(); + assert isFullyInitialized() && SubstrateOptions.useClosedTypeWorldHubLayout(); return typeIDSlotsOffset; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java index c751d9c90323..598fbc3f6e2e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java @@ -162,7 +162,7 @@ private void lowerLoadMethodNode(LoadMethodNode loadMethodNode, LoweringTool too SharedMethod method = (SharedMethod) loadMethodNode.getMethod(); ValueNode hub = loadMethodNode.getHub(); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex(), true); assert vtableEntryOffset > 0; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java index 69be8161cd6c..4a3383574fbf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java @@ -293,11 +293,13 @@ public static class InvokeLowering implements NodeLoweringProvider { protected final RuntimeConfiguration runtimeConfig; protected final boolean verifyTypes; protected final KnownOffsets knownOffsets; + private final boolean isClosedTypeWorld; public InvokeLowering(RuntimeConfiguration runtimeConfig, boolean verifyTypes, KnownOffsets knownOffsets) { this.runtimeConfig = runtimeConfig; this.verifyTypes = verifyTypes; this.knownOffsets = knownOffsets; + isClosedTypeWorld = SubstrateOptions.useClosedTypeWorld(); } @Override @@ -366,7 +368,7 @@ public void lower(FixedNode node, LoweringTool tool) { } CallTargetNode loweredCallTarget; - if (invokeKind.isDirect() || implementations.length == 1) { + if (invokeKind.isDirect() || (implementations.length == 1 && (isClosedTypeWorld || method.canBeStaticallyBound()))) { SharedMethod targetMethod = method; if (!invokeKind.isDirect()) { /* @@ -374,6 +376,7 @@ public void lower(FixedNode node, LoweringTool tool) { * emit a direct call to the unique implementation. */ targetMethod = implementations[0]; + assert targetMethod != null : "Expecting a unique callee for target method " + method; } if (SubstrateUtil.HOSTED && targetMethod.forceIndirectCall()) { @@ -421,7 +424,7 @@ public void lower(FixedNode node, LoweringTool tool) { address, parameters.toArray(new ValueNode[parameters.size()]), callTarget.returnStamp(), signature, targetMethod, callType, invokeKind)); graph.addBeforeFixed(node, codeStart); } - } else if (implementations.length == 0) { + } else if (implementations.length == 0 && isClosedTypeWorld) { /* * We are calling an abstract method with no implementation, i.e., the * closed-world analysis showed that there is no concrete receiver ever @@ -437,7 +440,7 @@ public void lower(FixedNode node, LoweringTool tool) { LoadHubNode hub = graph.unique(new LoadHubNode(runtimeConfig.getProviders().getStampProvider(), graph.addOrUnique(PiNode.create(receiver, nullCheck)))); nodesToLower.add(hub); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex(), true); AddressNode address = graph.unique(new OffsetAddressNode(hub, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), vtableEntryOffset, graph))); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java index 9dc59d8e3a8c..4202d27012bc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java @@ -128,7 +128,7 @@ CodePointer getCallWrapperAddress() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) CodePointer getJavaCallAddress(Object instance, boolean nonVirtual) { if (!nonVirtual) { - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { assert vtableOffset != JNIAccessibleMethod.VTABLE_OFFSET_NOT_YET_COMPUTED; if (vtableOffset != JNIAccessibleMethod.STATICALLY_BOUND_METHOD) { return BarrieredAccess.readWord(instance.getClass(), vtableOffset, NamedLocationIdentity.FINAL_LOCATION); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java index 2143c03764fe..5e23fd94927d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java @@ -106,7 +106,7 @@ private CFunctionPointer invokeTarget(Object obj) { * In case we have both a vtableOffset and a directTarget, the vtable lookup wins. For such * methods, the directTarget is only used when doing an invokeSpecial. */ - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { CFunctionPointer target; if (vtableOffset == OFFSET_NOT_YET_COMPUTED) { throw VMError.shouldNotReachHere("Missed vtableOffset recomputation at image build time"); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java index c6bf47ac5f34..9937c546c2d8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java @@ -187,7 +187,7 @@ private static DynamicHubLayout createDynamicHubLayout(HostedMetaAccess hMetaAcc int closedTypeWorldTypeCheckSlotSize; Set ignoredFields; - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { closedTypeWorldTypeCheckSlotsOffset = layout.getArrayLengthOffset() + layout.sizeInBytes(JavaKind.Int); closedTypeWorldTypeCheckSlotSize = layout.sizeInBytes(closedTypeWorldTypeCheckSlotsField.getType().getComponentType().getStorageKind()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index ef37b443687d..c73378d625a4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -1540,7 +1540,7 @@ public static void registerReplacements(DebugContext debug, FeatureHandler featu Map, NodeLoweringProvider> lowerings = lowerer.getLowerings(); lowerer.setConfiguration(runtimeConfig, options, providers); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { TypeSnippets.registerLowerings(options, providers, lowerings); } else { OpenTypeWorldSnippets.registerLowerings(options, providers, lowerings); @@ -1922,7 +1922,7 @@ private void printTypes(PrintWriter writer) { writer.print("reachable "); } - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { writer.format("type check start %d range %d slot # %d ", type.getTypeCheckStart(), type.getTypeCheckRange(), type.getTypeCheckSlot()); writer.format("type check slots %s ", slotsToString(type.getClosedTypeWorldTypeCheckSlots())); } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java index 9f1a90cd44c7..a68cde8126ef 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java @@ -79,7 +79,7 @@ public static final class Options { @Override public boolean isInConfiguration(Feature.IsInConfigurationAccess access) { - return !SubstrateOptions.closedTypeWorld(); + return !SubstrateOptions.useClosedTypeWorldHubLayout(); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 9e617c1e1d0d..1d0758971b4c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -201,6 +201,7 @@ public enum UsageKind { private Set excludedFields; private final Boolean optionAllowUnsafeAllocationOfAllInstantiatedTypes = SubstrateOptions.AllowUnsafeAllocationOfAllInstantiatedTypes.getValue(); + private final boolean isClosedTypeWorld = SubstrateOptions.useClosedTypeWorld(); @SuppressWarnings("this-escape") public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializationSupport classInitializationSupport, AnnotationSubstitutionProcessor annotationSubstitutions, @@ -691,7 +692,7 @@ public void methodAfterParsingHook(BigBang bb, AnalysisMethod method, Structured } protected void optimizeAfterParsing(BigBang bb, AnalysisMethod method, StructuredGraph graph) { - if (!SubstrateOptions.closedTypeWorld()) { + if (!SubstrateOptions.useClosedTypeWorldHubLayout()) { new OpenTypeWorldConvertCallTargetPhase().apply(graph, getProviders(method.getMultiMethodKey())); } @@ -936,6 +937,11 @@ public boolean isFieldIncluded(BigBang bb, Field field) { return super.isFieldIncluded(bb, field); } + @Override + public boolean isClosedTypeWorld() { + return isClosedTypeWorld; + } + private final List> neverInlineTrivialHandlers = new CopyOnWriteArrayList<>(); public void registerNeverInlineTrivialHandler(BiPredicate handler) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/DynamicHubLayout.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/DynamicHubLayout.java index 87a32d63773e..dcff6ccdc621 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/DynamicHubLayout.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/DynamicHubLayout.java @@ -144,12 +144,12 @@ public int getVTableSlotOffset(int index) { } public int getClosedTypeWorldTypeCheckSlotsOffset() { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); return closedTypeWorldTypeCheckSlotsOffset; } public int getClosedTypeWorldTypeCheckSlotsOffset(int index) { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); return closedTypeWorldTypeCheckSlotsOffset + index * closedTypeWorldTypeCheckSlotSize; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index 456a526f36ae..5e0b4651ba11 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -506,7 +506,7 @@ private void addObjectToImageHeap(final JavaConstant constant, boolean immutable * can never be duplicated, i.e. written as a separate object. We use the blacklist * to check this. */ - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { Object typeIDSlots = readInlinedField(dynamicHubLayout.closedTypeWorldTypeCheckSlotsField, constant); assert typeIDSlots != null : "Cannot read value for field " + dynamicHubLayout.closedTypeWorldTypeCheckSlotsField.format("%H.%n"); blacklist.add(typeIDSlots); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index beb5056c6e6c..2b199efe8a22 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -368,7 +368,7 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { * Write typeID slots for closed world. In the open world configuration information * is stored in a separate array since it has a variable length. */ - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { short[] typeIDSlots = (short[]) heap.readInlinedField(dynamicHubLayout.closedTypeWorldTypeCheckSlotsField, con); int typeIDSlotsLength = typeIDSlots.length; for (int i = 0; i < typeIDSlotsLength; i++) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index ba1b22e6c41b..1faf781e3323 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -579,7 +579,7 @@ private static void finishMethodBeforeCompilation(JNICallableJavaMethod method, HostedMethod hTarget = hUniverse.lookup(aUniverse.lookup(method.targetMethod)); int vtableOffset; int interfaceTypeID; - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { interfaceTypeID = JNIAccessibleMethod.INTERFACE_TYPEID_UNNEEDED; if (hTarget.canBeStaticallyBound()) { vtableOffset = JNIAccessibleMethod.STATICALLY_BOUND_METHOD; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java index 86ae80063d48..5f892ad3f623 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java @@ -26,8 +26,10 @@ import com.oracle.graal.pointsto.meta.AnalysisType; +import jdk.vm.ci.meta.Assumptions.AssumptionResult; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; public class HostedInstanceClass extends HostedClass { @@ -138,4 +140,16 @@ public void setIdentityHashOffset(int offset) { assert offset > 0; this.identityHashOffset = offset; } + + @Override + public AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod m) { + if (m.canBeStaticallyBound() || universe.hostVM().isClosedTypeWorld()) { + return super.findUniqueConcreteMethod(m); + } + /* + * With an open type world analysis we cannot make assumptions for methods that cannot be + * trivially statically bound. + */ + return null; + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index f94376e25086..860eaaafb95c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -111,6 +111,10 @@ public final class HostedMethod extends HostedElement implements SharedMethod, W /** * All concrete methods that can actually be called when calling this method. This includes all * overridden methods in subclasses, as well as this method if it is non-abstract. + *

+ * With an open type world analysis the list of implementations is incomplete, i.e., no + * aggressive optimizations should be performed based on the contents of this list as one must + * assume that additional implementations can be discovered later. */ HostedMethod[] implementations; @@ -340,7 +344,7 @@ public boolean hasVTableIndex() { @Override public int getVTableIndex() { - assert vtableIndex != -1; + assert vtableIndex != -1 : "Missing vtable index for method " + this.format("%H.%n(%p)"); return vtableIndex; } @@ -451,7 +455,14 @@ public boolean isConstructor() { @Override public boolean canBeStaticallyBound() { - return implementations.length == 1 && implementations[0].equals(this); + if (holder.universe.hostVM().isClosedTypeWorld()) { + return implementations.length == 1 && implementations[0].equals(this); + } + /* + * In open type world analysis we cannot make assumptions based on discovered + * implementations. + */ + return wrapped.canBeStaticallyBound(); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index b0056849bc7b..1e6fc9b2315d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -167,7 +167,7 @@ protected HostedMethod[] getOpenTypeWorldDispatchTables() { } public HostedMethod[] getVTable() { - return SubstrateOptions.closedTypeWorld() ? getClosedTypeWorldVTable() : getOpenTypeWorldDispatchTables(); + return SubstrateOptions.useClosedTypeWorldHubLayout() ? getClosedTypeWorldVTable() : getOpenTypeWorldDispatchTables(); } @Override @@ -177,79 +177,79 @@ public int getTypeID() { } public void setTypeCheckRange(short typeCheckStart, short typeCheckRange) { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); this.typeCheckStart = typeCheckStart; this.typeCheckRange = typeCheckRange; } public void setTypeCheckSlot(short typeCheckSlot) { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); this.typeCheckSlot = typeCheckSlot; } public void setClosedTypeWorldTypeCheckSlots(short[] closedTypeWorldTypeCheckSlots) { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); this.closedTypeWorldTypeCheckSlots = closedTypeWorldTypeCheckSlots; } public short getTypeCheckStart() { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); return typeCheckStart; } public short getTypeCheckRange() { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); return typeCheckRange; } public short getTypeCheckSlot() { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); return typeCheckSlot; } public short[] getClosedTypeWorldTypeCheckSlots() { - assert SubstrateOptions.closedTypeWorld(); + assert SubstrateOptions.useClosedTypeWorldHubLayout(); assert closedTypeWorldTypeCheckSlots != null; return closedTypeWorldTypeCheckSlots; } public void setTypeIDDepth(int typeIDDepth) { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); this.typeIDDepth = typeIDDepth; } public void setNumClassTypes(int numClassTypes) { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); this.numClassTypes = numClassTypes; } public void setNumInterfaceTypes(int numInterfaceTypes) { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); this.numInterfaceTypes = numInterfaceTypes; } public void setOpenTypeWorldTypeCheckSlots(int[] openTypeWorldTypeCheckSlots) { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); this.openTypeWorldTypeCheckSlots = openTypeWorldTypeCheckSlots; } public int getTypeIDDepth() { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); return typeIDDepth; } public int getNumClassTypes() { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); return numClassTypes; } public int getNumInterfaceTypes() { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); return numInterfaceTypes; } public int[] getOpenTypeWorldTypeCheckSlots() { - assert !SubstrateOptions.closedTypeWorld(); + assert !SubstrateOptions.useClosedTypeWorldHubLayout(); assert openTypeWorldTypeCheckSlots != null : this; return openTypeWorldTypeCheckSlots; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java index e105654e5edf..bdce9d509380 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java @@ -70,7 +70,7 @@ public void beforeCompilation(BeforeCompilationAccess a) { DynamicHubLayout dynamicHubLayout = DynamicHubLayout.singleton(); int vtableBaseOffset = dynamicHubLayout.vTableOffset(); int vtableEntrySize = dynamicHubLayout.vTableSlotSize; - int typeIDSlotsOffset = SubstrateOptions.closedTypeWorld() ? dynamicHubLayout.getClosedTypeWorldTypeCheckSlotsOffset() : -1; + int typeIDSlotsOffset = SubstrateOptions.useClosedTypeWorldHubLayout() ? dynamicHubLayout.getClosedTypeWorldTypeCheckSlotsOffset() : -1; int componentHubOffset = findFieldOffset(access, DynamicHub.class, "componentType"); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java index 969065b49435..663e6d735db4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java @@ -153,10 +153,11 @@ public final class TypeCheckBuilder { VMError.guarantee(result != 0, "Unexpected match of types %s %s", o1, o2); return result; }; + private final boolean isClosedTypeWorld; public static int buildTypeMetadata(HostedUniverse hUniverse, Collection types, HostedType objectType, HostedType cloneableType, HostedType serializableType) { - var builder = new TypeCheckBuilder(types, objectType, cloneableType, serializableType); - if (SubstrateOptions.closedTypeWorld()) { + var builder = new TypeCheckBuilder(types, objectType, cloneableType, serializableType, hUniverse.hostVM().isClosedTypeWorld()); + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { builder.buildTypeInformation(hUniverse, 0); builder.calculateClosedTypeWorldTypeMetadata(); return builder.getNumTypeCheckSlots(); @@ -169,7 +170,7 @@ public static int buildTypeMetadata(HostedUniverse hUniverse, Collection types, HostedType objectType, HostedType cloneableType, HostedType serializableType) { + private TypeCheckBuilder(Collection types, HostedType objectType, HostedType cloneableType, HostedType serializableType, boolean isClosedTypeWorld) { this.allTypes = types; this.objectType = objectType; this.cloneableType = cloneableType; @@ -187,6 +188,7 @@ private TypeCheckBuilder(Collection types, HostedType objectType, Ho allIncludedRoots = allIncludedTypes.stream().filter(t -> !hasParent.contains(t)).toList(); heightOrderedTypes = generateHeightOrder(allIncludedRoots, subtypeMap); + this.isClosedTypeWorld = isClosedTypeWorld; } private int getNumTypeCheckSlots() { @@ -472,6 +474,16 @@ public void buildTypeInformation(HostedUniverse hUniverse, int startingTypeID) { for (int i = heightOrderedTypes.size() - 1; i >= 0; i--) { HostedType type = heightOrderedTypes.get(i); + if (!type.isLeaf() && !isClosedTypeWorld) { + /* + * With an open type world analysis we have to assume that a non-final type can have + * multiple instantiated subtypes. + */ + type.strengthenStampType = type; + type.uniqueConcreteImplementation = null; + continue; + } + HostedType subtypeStampType = null; for (HostedType child : subtypeMap.get(type)) { if (child.strengthenStampType != null) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index efac61f459e7..5c1a802569aa 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -525,7 +525,7 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super assert fieldBytes >= intSize; reserve(usedBytes, minimumFirstFieldOffset, fieldBytes); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { /* Each type check id slot is 2 bytes. */ assert numTypeCheckSlots != TypeCheckBuilder.UNINITIALIZED_TYPECHECK_SLOTS : "numTypeCheckSlots is uninitialized"; int slotsSize = numTypeCheckSlots * 2; @@ -916,7 +916,7 @@ private void buildHubs() { DynamicHub hub = type.getHub(); hub.setSharedData(layoutHelper, monitorOffset, identityHashOffset, referenceMapIndex, type.isInstantiated()); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { CFunctionPointer[] vtable = new CFunctionPointer[type.closedTypeWorldVTable.length]; for (int idx = 0; idx < type.closedTypeWorldVTable.length; idx++) { /* diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java index a69bd4a0f75e..de557ab43469 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java @@ -39,7 +39,6 @@ import com.oracle.svm.core.InvalidMethodPointerHandler; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.hosted.OpenTypeWorldFeature; import jdk.graal.compiler.debug.Assertions; @@ -55,7 +54,7 @@ private VTableBuilder(HostedUniverse hUniverse, HostedMetaAccess hMetaAccess) { public static void buildTables(HostedUniverse hUniverse, HostedMetaAccess hMetaAccess) { VTableBuilder builder = new VTableBuilder(hUniverse, hMetaAccess); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { builder.buildClosedTypeWorldVTables(); } else { builder.buildOpenTypeWorldDispatchTables(); @@ -119,12 +118,12 @@ private List generateITable(HostedType type) { private List generateDispatchTable(HostedType type, int startingIndex) { Predicate includeMethod; - if (ImageLayerBuildingSupport.buildingImageLayer()) { - // include all methods - includeMethod = m -> true; - } else { + if (hUniverse.hostVM().isClosedTypeWorld()) { // include only methods which will be indirect calls includeMethod = m -> m.implementations.length > 1 || m.wrapped.isVirtualRootMethod(); + } else { + // include all methods + includeMethod = m -> true; } var table = type.getWrapped().getOpenTypeWorldDispatchTableMethods().stream().map(hUniverse::lookup).filter(includeMethod).sorted(HostedUniverse.METHOD_COMPARATOR).toList(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index 708eac92ff08..54b54b7c3823 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -334,7 +334,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { * to see SubstrateMethodAccessor.vtableOffset before we register the transformer. */ access.registerFieldValueTransformer(ReflectionUtil.lookupField(SubstrateMethodAccessor.class, "vtableOffset"), new ComputeVTableOffset()); - if (!SubstrateOptions.closedTypeWorld()) { + if (!SubstrateOptions.useClosedTypeWorldHubLayout()) { access.registerFieldValueTransformer(ReflectionUtil.lookupField(SubstrateMethodAccessor.class, "interfaceTypeID"), new ComputeInterfaceTypeID()); } @@ -471,7 +471,7 @@ public Object transform(Object receiver, Object originalValue) { if (accessor.getVTableOffset() == SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED) { SharedMethod member = ImageSingletons.lookup(ReflectionFeature.class).hostedMetaAccess().lookupJavaMethod(accessor.getMember()); - if (SubstrateOptions.closedTypeWorld()) { + if (SubstrateOptions.useClosedTypeWorldHubLayout()) { return KnownOffsets.singleton().getVTableOffset(member.getVTableIndex(), true); } else { return KnownOffsets.singleton().getVTableOffset(member.getVTableIndex(), false);