From 870571334ddabab88e8807194182cf11dfaca138 Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Thu, 30 Oct 2025 19:23:35 +0100 Subject: [PATCH] Clean up vtable dispatch. --- .../OpenTypeWorldDispatchTableSnippets.java | 15 ++++--- .../graal/snippets/OpenTypeWorldSnippets.java | 10 ++--- ...ypeCheckUtil.java => DynamicHubUtils.java} | 26 ++++++++++--- .../svm/hosted/meta/TypeCheckBuilder.java | 8 ++-- .../svm/hosted/meta/UniverseBuilder.java | 6 +-- .../oracle/svm/hosted/meta/VTableBuilder.java | 11 ++++-- .../svm/interpreter/CremaSupportImpl.java | 8 ++-- .../svm/interpreter/InterpreterToVM.java | 39 ++++++------------- .../svm/jdwp/resident/impl/ResidentJDWP.java | 3 +- 9 files changed, 64 insertions(+), 62 deletions(-) rename substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/{DynamicHubTypeCheckUtil.java => DynamicHubUtils.java} (89%) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldDispatchTableSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldDispatchTableSnippets.java index 608a05ed6cb0..c7054289d28a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldDispatchTableSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldDispatchTableSnippets.java @@ -24,9 +24,9 @@ */ package com.oracle.svm.core.graal.snippets; -import static com.oracle.svm.core.hub.DynamicHubTypeCheckUtil.HASHING_INTERFACE_MASK; -import static com.oracle.svm.core.hub.DynamicHubTypeCheckUtil.HASHING_ITABLE_SHIFT; -import static com.oracle.svm.core.hub.DynamicHubTypeCheckUtil.HASHING_SHIFT_OFFSET; +import static com.oracle.svm.core.hub.DynamicHubUtils.HASHING_INTERFACE_MASK; +import static com.oracle.svm.core.hub.DynamicHubUtils.HASHING_ITABLE_SHIFT; +import static com.oracle.svm.core.hub.DynamicHubUtils.HASHING_SHIFT_OFFSET; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.unknownProbability; @@ -40,7 +40,7 @@ import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.graal.nodes.LoadOpenTypeWorldDispatchTableStartingOffset; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.DynamicHubTypeCheckUtil; +import com.oracle.svm.core.hub.DynamicHubUtils; import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.meta.SharedType; @@ -119,9 +119,8 @@ private static long determineITableStartingOffsetIterative( * If {@link SubstrateOptions#useInterfaceHashing()} is enabled, interfaceIDs and itable * starting offsets are stored in a hash table (see TypeCheckBuilder for a general * documentation). This snippet handles the lookup in the hash table and returns the itable - * starting offset for the given interfaceID. See - * {@link DynamicHubTypeCheckUtil#hashParam(int[])} for details on the hashing function and - * hashing parameter. + * starting offset for the given interfaceID. See {@link DynamicHubUtils#hashParam(int[])} for + * details on the hashing function and hashing parameter. */ private static int determineITableStartingOffsetHashed( DynamicHub checkedHub, @@ -149,7 +148,7 @@ private static int determineITableStartingOffsetHashed( public static long determineITableStartingOffset( DynamicHub checkedHub, int interfaceID) { - if (SubstrateOptions.useInterfaceHashing()) { + if (SubstrateOptions.useInterfaceHashing() && interfaceID <= SubstrateOptions.interfaceHashingMaxId()) { // Use the non-snippet version which contains no snippet asserts. return determineITableStartingOffsetHashedNonSnippet(checkedHub, interfaceID); } else { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldSnippets.java index 3494659dc6cd..77e2e9372f13 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/OpenTypeWorldSnippets.java @@ -26,8 +26,8 @@ import static com.oracle.svm.core.graal.snippets.SubstrateIntrinsics.loadHub; import static com.oracle.svm.core.graal.snippets.SubstrateIntrinsics.loadHubOrNull; -import static com.oracle.svm.core.hub.DynamicHubTypeCheckUtil.HASHING_INTERFACE_MASK; -import static com.oracle.svm.core.hub.DynamicHubTypeCheckUtil.HASHING_SHIFT_OFFSET; +import static com.oracle.svm.core.hub.DynamicHubUtils.HASHING_INTERFACE_MASK; +import static com.oracle.svm.core.hub.DynamicHubUtils.HASHING_SHIFT_OFFSET; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.unknownProbability; @@ -39,7 +39,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.DynamicHubTypeCheckUtil; +import com.oracle.svm.core.hub.DynamicHubUtils; import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.util.DuplicatedInNativeCode; @@ -235,8 +235,8 @@ protected static SubstrateIntrinsics.Any iterativeInterfaceTypeCheck( * starting offsets are stored in a hash table (see TypeCheckBuilder for a general * documentation). This snippet does a hash table lookup and returns true if the provided * interfaceID matches the interfaceID in the hash table, false otherwise. See - * {@link DynamicHubTypeCheckUtil#hashParam(int[])} for details on the hashing function and - * hashing parameter. + * {@link DynamicHubUtils#hashParam(int[])} for details on the hashing function and hashing + * parameter. */ @DuplicatedInNativeCode protected static SubstrateIntrinsics.Any hashedInterfaceTypeCheck( diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubTypeCheckUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubUtils.java similarity index 89% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubTypeCheckUtil.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubUtils.java index 78344ee26714..29b37ece4d77 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubTypeCheckUtil.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubUtils.java @@ -28,17 +28,18 @@ import java.util.Set; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.graal.meta.KnownOffsets; +import com.oracle.svm.core.graal.snippets.OpenTypeWorldDispatchTableSnippets; import com.oracle.svm.core.util.DuplicatedInNativeCode; import jdk.graal.compiler.core.common.NumUtil; import jdk.graal.compiler.debug.GraalError; /** - * Contains utility for computing type check info for DynamicHubs, such as constants and functions - * needed for {@link SubstrateOptions#useInterfaceHashing()}. - * + * Contains utilities for interacting with DynamicHubs, such as converting vtable indexes to + * dispatch table offsets and calculating metadata needed for typecheck computations. */ -public final class DynamicHubTypeCheckUtil { +public final class DynamicHubUtils { // Constants for interface hashing @@ -135,7 +136,7 @@ public static TypeCheckData computeOpenTypeWorldTypeCheckData(boolean implements } // calculate hash parameter for hashable interfaces - hashParam = DynamicHubTypeCheckUtil.hashParam(hashedInterfaces); + hashParam = DynamicHubUtils.hashParam(hashedInterfaces); hashTable = new int[(hashParam & HASHING_PARAM_MASK) + 1]; } @@ -152,7 +153,7 @@ public static TypeCheckData computeOpenTypeWorldTypeCheckData(boolean implements if (useInterfaceHashing && interfaceID <= SubstrateOptions.interfaceHashingMaxId()) { int offset = implementsMethods ? Math.toIntExact(vTableBaseOffset + iTableStartingOffsets[interfaceIdx] * vTableEntrySize) : Short.MIN_VALUE; GraalError.guarantee(NumUtil.isShort(offset), "ItableDynamicHubOffset cannot be encoded as a short. Try -H:-UseInterfaceHashing."); - hashTable[DynamicHubTypeCheckUtil.hash(interfaceID, hashParam)] = ((offset << HASHING_ITABLE_SHIFT) | interfaceID); + hashTable[DynamicHubUtils.hash(interfaceID, hashParam)] = ((offset << HASHING_ITABLE_SHIFT) | interfaceID); } else { int offset = implementsMethods ? Math.toIntExact(vTableBaseOffset + iTableStartingOffsets[interfaceIdx] * vTableEntrySize) : 0xBADD0D1D; openTypeWorldTypeCheckSlots[iterableInterfaceIdx * 2 + numClassTypes] = interfaceID; @@ -255,4 +256,17 @@ private static boolean isValidHashParam(int[] vals, int hashParam, Set } return true; } + + /** + * Calculates the offset of a virtual call when the receiver is of type {@code thisHub} and the + * target method's declaring class is of type {@code callTargetHub}. + */ + public static int determineDispatchTableOffset(DynamicHub thisHub, DynamicHub callTargetHub, int vTableIndex) { + if (SubstrateOptions.useClosedTypeWorldHubLayout() || !callTargetHub.isInterface()) { + return KnownOffsets.singleton().getVTableOffset(vTableIndex, true); + } else { + int indexOffset = KnownOffsets.singleton().getVTableOffset(vTableIndex, false); + return NumUtil.safeToInt(indexOffset + OpenTypeWorldDispatchTableSnippets.determineITableStartingOffset(thisHub, callTargetHub.getInterfaceID())); + } + } } 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 bb34e2682801..5a68c3f293f0 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 @@ -52,7 +52,7 @@ import com.oracle.svm.core.graal.snippets.OpenTypeWorldSnippets; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.DynamicHubSupport; -import com.oracle.svm.core.hub.DynamicHubTypeCheckUtil; +import com.oracle.svm.core.hub.DynamicHubUtils; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.OpenTypeWorldFeature; @@ -145,7 +145,7 @@ * entries encode the following: * *
- * hashTable[hash(interfaceID)] = (iTableOffset << {@link DynamicHubTypeCheckUtil#HASHING_ITABLE_SHIFT HASHING_ITABLE_OFFSET}) | interfaceID
+ * hashTable[hash(interfaceID)] = (iTableOffset << {@link DynamicHubUtils#HASHING_ITABLE_SHIFT HASHING_ITABLE_OFFSET}) | interfaceID
  * 
* * Thus, interfaceIDs encoded in hash tables must be > 0, to properly distinguish them from empty @@ -154,8 +154,8 @@ * The implementation of the open-world typechecks can be found in {@link OpenTypeWorldSnippets}, * the loading of interface methods can be found in {@link OpenTypeWorldDispatchTableSnippets}. The * type check data for dynamic hubs is computed in - * {@link DynamicHubTypeCheckUtil#computeOpenTypeWorldTypeCheckData} with the hashing function being - * defined in {@link DynamicHubTypeCheckUtil#hash}. + * {@link DynamicHubUtils#computeOpenTypeWorldTypeCheckData} with the hashing function being defined + * in {@link DynamicHubUtils#hash}. */ public final class TypeCheckBuilder { public static final int UNINITIALIZED_TYPECHECK_SLOTS = -1; 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 0e521ca551c5..d80e10ff1cea 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 @@ -86,7 +86,7 @@ import com.oracle.svm.core.heap.SubstrateReferenceMap; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.DynamicHubSupport; -import com.oracle.svm.core.hub.DynamicHubTypeCheckUtil; +import com.oracle.svm.core.hub.DynamicHubUtils; import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; @@ -1004,7 +1004,7 @@ private void buildHubs() { } /** - * See {@link DynamicHubTypeCheckUtil#computeOpenTypeWorldTypeCheckData} for details on the + * See {@link DynamicHubUtils#computeOpenTypeWorldTypeCheckData} for details on the * {@link DynamicHub} type check layout in the open type world. */ private static void setOpenTypeWorldData(HostedType type, DynamicHubLayout dynamicHubLayout, DynamicHub hub, boolean useOffsets) { @@ -1020,7 +1020,7 @@ private static void setOpenTypeWorldData(HostedType type, DynamicHubLayout dynam long vTableOffset = dynamicHubLayout.vTableOffset(); long vTableSlotSize = dynamicHubLayout.vTableSlotSize; - DynamicHubTypeCheckUtil.TypeCheckData typeCheckData = DynamicHubTypeCheckUtil.computeOpenTypeWorldTypeCheckData(implementsMethods, typeHierarchy, interfaceIDs, iTableOffsets, vTableOffset, + DynamicHubUtils.TypeCheckData typeCheckData = DynamicHubUtils.computeOpenTypeWorldTypeCheckData(implementsMethods, typeHierarchy, interfaceIDs, iTableOffsets, vTableOffset, vTableSlotSize); MethodRef[] vtable = createVTable(type.openTypeWorldDispatchTables, useOffsets); 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 f57408279bc1..80f5a4c6cca8 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 @@ -501,9 +501,13 @@ private static Set collectSubtypes(HostedType type, Set return allSubtypes; } - private void buildVTable(HostedClass clazz, Map> vtablesMap, Map usedSlotsMap, Map> vtablesSlots) { + private void assignImplementationsAndBuildVTable(HostedClass clazz, Map> vtablesMap, Map usedSlotsMap, + Map> vtablesSlots) { assignImplementations(clazz, vtablesMap, usedSlotsMap, vtablesSlots); + buildVTable(clazz, vtablesMap, usedSlotsMap, vtablesSlots); + } + private void buildVTable(HostedClass clazz, Map> vtablesMap, Map usedSlotsMap, Map> vtablesSlots) { ArrayList vtable = vtablesMap.get(clazz); HostedMethod[] vtableArray = vtable.toArray(new HostedMethod[vtable.size()]); assert vtableArray.length == 0 || vtableArray[vtableArray.length - 1] != null : "Unnecessary entry at end of vtable"; @@ -511,7 +515,7 @@ private void buildVTable(HostedClass clazz, Map clazz) throws SemanticJavaExc } } - static CFunctionPointer peekAtSVMVTable(Class seedClass, Class thisClass, int vTableIndex, boolean isInvokeInterface) { - DynamicHub seedHub = DynamicHub.fromClass(seedClass); + static CFunctionPointer peekAtSVMVTable(Class callTargetClass, Class thisClass, int vTableIndex, boolean isInvokeInterface) { + DynamicHub callTargetHub = DynamicHub.fromClass(callTargetClass); DynamicHub thisHub = DynamicHub.fromClass(thisClass); - - int vtableOffset = KnownOffsets.singleton().getVTableOffset(vTableIndex, false); - - if (SubstrateOptions.useClosedTypeWorldHubLayout()) { - vtableOffset += KnownOffsets.singleton().getVTableBaseOffset(); - } else { - VMError.guarantee(seedHub.isInterface() == isInvokeInterface); - - if (!seedHub.isInterface()) { - vtableOffset += KnownOffsets.singleton().getVTableBaseOffset(); - } else { - vtableOffset += (int) OpenTypeWorldDispatchTableSnippets.determineITableStartingOffset(thisHub, seedHub.getInterfaceID()); - } - } + VMError.guarantee(callTargetHub.isInterface() == isInvokeInterface); + int vtableOffset = DynamicHubUtils.determineDispatchTableOffset(thisHub, callTargetHub, vTableIndex); MethodRef vtableEntry = Word.objectToTrackedPointer(thisHub).readWord(vtableOffset); return getSVMVTableCodePointer(vtableEntry); } @@ -722,20 +711,16 @@ private static InterpreterResolvedJavaMethod peekAtInterpreterVTable(Class se VMError.guarantee(vTable != null); DynamicHub seedHub = DynamicHub.fromClass(seedClass); + VMError.guarantee(isInvokeInterface == seedHub.isInterface()); - if (SubstrateOptions.useClosedTypeWorldHubLayout()) { - VMError.guarantee(vTableIndex > 0 && vTableIndex < vTable.length); - return vTable[vTableIndex]; + int idx; + if (SubstrateOptions.useClosedTypeWorldHubLayout() || !seedHub.isInterface()) { + idx = vTableIndex; } else { - VMError.guarantee(seedHub.isInterface() == isInvokeInterface); - - if (!seedHub.isInterface()) { - return vTable[vTableIndex]; - } else { - int iTableStartingIndex = determineITableStartingIndex(DynamicHub.fromClass(thisClass), seedHub.getInterfaceID()); - return vTable[iTableStartingIndex + vTableIndex]; - } + idx = vTableIndex + determineITableStartingIndex(DynamicHub.fromClass(thisClass), seedHub.getInterfaceID()); } + VMError.guarantee(idx >= 0 && idx < vTable.length); + return vTable[idx]; } private static int determineITableStartingIndex(DynamicHub thisHub, int interfaceID) { diff --git a/substratevm/src/com.oracle.svm.jdwp.resident/src/com/oracle/svm/jdwp/resident/impl/ResidentJDWP.java b/substratevm/src/com.oracle.svm.jdwp.resident/src/com/oracle/svm/jdwp/resident/impl/ResidentJDWP.java index d06dbfa0ca53..4b68769ffe25 100644 --- a/substratevm/src/com.oracle.svm.jdwp.resident/src/com/oracle/svm/jdwp/resident/impl/ResidentJDWP.java +++ b/substratevm/src/com.oracle.svm.jdwp.resident/src/com/oracle/svm/jdwp/resident/impl/ResidentJDWP.java @@ -1920,7 +1920,8 @@ static Result fromThrowable(Throwable throwable) { static Result ofInvoke(boolean isVirtual, InterpreterResolvedJavaMethod method, Object... args) { try { - return fromValue(InterpreterToVM.dispatchInvocation(method, args, isVirtual, false, false, false, false)); + boolean isInvokeInterface = method.getDeclaringClass().isInterface(); + return fromValue(InterpreterToVM.dispatchInvocation(method, args, isVirtual, false, false, isInvokeInterface, false)); } catch (SemanticJavaException e) { return fromThrowable(e.getCause()); } catch (StackOverflowError | OutOfMemoryError error) {