2424 */
2525package com .oracle .svm .core .graal .snippets ;
2626
27+ import static com .oracle .svm .core .hub .DynamicHubTypeCheckUtil .HASHING_INTERFACE_MASK ;
28+ import static com .oracle .svm .core .hub .DynamicHubTypeCheckUtil .HASHING_ITABLE_SHIFT ;
29+ import static com .oracle .svm .core .hub .DynamicHubTypeCheckUtil .HASHING_SHIFT_OFFSET ;
30+ import static jdk .graal .compiler .nodes .extended .BranchProbabilityNode .probability ;
2731import static jdk .graal .compiler .nodes .extended .BranchProbabilityNode .unknownProbability ;
2832
2933import java .util .Map ;
3034
3135import org .graalvm .nativeimage .ImageSingletons ;
3236import org .graalvm .word .LocationIdentity ;
3337
38+ import com .oracle .svm .core .SubstrateOptions ;
3439import com .oracle .svm .core .config .ObjectLayout ;
3540import com .oracle .svm .core .graal .meta .KnownOffsets ;
3641import com .oracle .svm .core .graal .nodes .LoadOpenTypeWorldDispatchTableStartingOffset ;
3742import com .oracle .svm .core .hub .DynamicHub ;
43+ import com .oracle .svm .core .hub .DynamicHubTypeCheckUtil ;
3844import com .oracle .svm .core .meta .SharedMethod ;
3945import com .oracle .svm .core .meta .SharedType ;
4046
4147import jdk .graal .compiler .api .replacements .Snippet ;
48+ import jdk .graal .compiler .core .common .NumUtil ;
4249import jdk .graal .compiler .graph .Node ;
4350import jdk .graal .compiler .nodes .ConstantNode ;
4451import jdk .graal .compiler .nodes .NamedLocationIdentity ;
4552import jdk .graal .compiler .nodes .UnreachableNode ;
53+ import jdk .graal .compiler .nodes .extended .BranchProbabilityNode ;
4654import jdk .graal .compiler .nodes .spi .LoweringTool ;
4755import jdk .graal .compiler .options .OptionValues ;
4856import jdk .graal .compiler .phases .util .Providers ;
57+ import jdk .graal .compiler .replacements .ReplacementsUtil ;
4958import jdk .graal .compiler .replacements .SnippetTemplate ;
5059import jdk .graal .compiler .replacements .Snippets ;
5160import jdk .graal .compiler .word .ObjectAccess ;
@@ -56,28 +65,40 @@ public final class OpenTypeWorldDispatchTableSnippets extends SubstrateTemplates
5665 @ Snippet
5766 private static long loadITableStartingOffset (
5867 @ Snippet .NonNullParameter DynamicHub hub ,
59- int interfaceTypeID ) {
60- return determineITableStartingOffset (hub , interfaceTypeID );
68+ int interfaceID ,
69+ @ Snippet .ConstantParameter boolean useInterfaceHashing ) {
70+ if (useInterfaceHashing && probability (BranchProbabilityNode .FAST_PATH_PROBABILITY , interfaceID <= SubstrateOptions .interfaceHashingMaxId ())) {
71+ return determineITableStartingOffsetHashed (hub , interfaceID );
72+ }
73+ return determineITableStartingOffsetIterative (hub , interfaceID );
6174 }
6275
6376 @ Snippet
6477 private static long loadDispatchTableStartingOffset (
6578 @ Snippet .NonNullParameter DynamicHub hub ,
66- int interfaceTypeID , @ Snippet .ConstantParameter int vtableStartingOffset ) {
67- if (unknownProbability (interfaceTypeID >= 0 )) {
68- return determineITableStartingOffset (hub , interfaceTypeID );
79+ int interfaceID , @ Snippet .ConstantParameter int vtableStartingOffset ,
80+ @ Snippet .ConstantParameter boolean useInterfaceHashing ) {
81+ if (unknownProbability (interfaceID >= 0 )) {
82+ if (useInterfaceHashing && probability (BranchProbabilityNode .FAST_PATH_PROBABILITY , interfaceID <= SubstrateOptions .interfaceHashingMaxId ())) {
83+ return determineITableStartingOffsetHashed (hub , interfaceID );
84+ }
85+ return determineITableStartingOffsetIterative (hub , interfaceID );
6986 } else {
7087 // the class dispatch table is always first
7188 return vtableStartingOffset ;
7289 }
7390 }
7491
75- public static long determineITableStartingOffset (
92+ /**
93+ * Iterative lookup of itable starting offsets used if
94+ * {@link SubstrateOptions#useInterfaceHashing()} is disabled or if the interfaceID exceeds
95+ * {@link SubstrateOptions#interfaceHashingMaxId()}.
96+ */
97+ private static long determineITableStartingOffsetIterative (
7698 DynamicHub checkedHub ,
7799 int interfaceID ) {
78-
79100 int numClassTypes = checkedHub .getNumClassTypes ();
80- int numInterfaceTypes = checkedHub .getNumInterfaceTypes ();
101+ int numInterfaceTypes = checkedHub .getNumIterableInterfaceTypes ();
81102 int [] checkedTypeIds = checkedHub .getOpenTypeWorldTypeCheckSlots ();
82103 for (int i = 0 ; i < numInterfaceTypes * 2 ; i += 2 ) {
83104 // int checkedInterfaceId = checkedTypeIds[numClassTypes + i];
@@ -94,6 +115,76 @@ public static long determineITableStartingOffset(
94115 throw UnreachableNode .unreachable ();
95116 }
96117
118+ /**
119+ * If {@link SubstrateOptions#useInterfaceHashing()} is enabled, interfaceIDs and itable
120+ * starting offsets are stored in a hash table (see TypeCheckBuilder for a general
121+ * documentation). This snippet handles the lookup in the hash table and returns the itable
122+ * starting offset for the given interfaceID. See
123+ * {@link DynamicHubTypeCheckUtil#hashParam(int[])} for details on the hashing function and
124+ * hashing parameter.
125+ */
126+ private static int determineITableStartingOffsetHashed (
127+ DynamicHub checkedHub ,
128+ int interfaceID ) {
129+ ReplacementsUtil .dynamicAssert (NumUtil .isUShort (interfaceID ), "InterfaceIDs must fit in a short to be used for hashing." );
130+
131+ // The upper byte of the hashParam holds the shift value, the lower three bytes hold p
132+ // which is used for bitwise "and": hashParam = shift << HASHING_SHIFT_OFFSET | p.
133+ int hashParam = checkedHub .getOpenTypeWorldInterfaceHashParam ();
134+ int shift = hashParam >>> HASHING_SHIFT_OFFSET ;
135+ int [] hashTable = checkedHub .getOpenTypeWorldInterfaceHashTable ();
136+
137+ // No need to mask hashParam to get "p". interfaceID fits in a short -> the two upper
138+ // bytes are 0.
139+ int hash = (interfaceID >>> shift ) & hashParam ;
140+ int offset = (int ) ImageSingletons .lookup (ObjectLayout .class ).getArrayElementOffset (JavaKind .Int , hash );
141+ int hashTableEntry = ObjectAccess .readInt (hashTable , offset , NamedLocationIdentity .FINAL_LOCATION );
142+
143+ // Hashtable entries contain integers which hold the iTableOffset and the interfaceID:
144+ // hashTableEntry = iTableOffset << HASHING_ITABLE_SHIFT | interfaceID
145+ ReplacementsUtil .dynamicAssert (interfaceID == (hashTableEntry & HASHING_INTERFACE_MASK ), "InterfaceIDs do not match." );
146+ return (hashTableEntry >>> HASHING_ITABLE_SHIFT );
147+ }
148+
149+ public static long determineITableStartingOffset (
150+ DynamicHub checkedHub ,
151+ int interfaceID ) {
152+ if (SubstrateOptions .useInterfaceHashing ()) {
153+ // Use the non-snippet version which contains no snippet asserts.
154+ return determineITableStartingOffsetHashedNonSnippet (checkedHub , interfaceID );
155+ } else {
156+ return determineITableStartingOffsetIterative (checkedHub , interfaceID );
157+ }
158+ }
159+
160+ /**
161+ * IMPORTANT: Has to be identical to {@link #determineITableStartingOffsetHashed} but with
162+ * "real" {@code assert}s instead of {@link ReplacementsUtil#dynamicAssert}. Required for being
163+ * called outside of snippets.
164+ */
165+ private static int determineITableStartingOffsetHashedNonSnippet (
166+ DynamicHub checkedHub ,
167+ int interfaceID ) {
168+ assert NumUtil .isUShort (interfaceID ) : "InterfaceIDs must fit in a short to be used for hashing." ;
169+
170+ // The upper byte of the hashParam holds the shift value, the lower three bytes hold p
171+ // which is used for bitwise "and": hashParam = shift << HASHING_SHIFT_OFFSET | p.
172+ int hashParam = checkedHub .getOpenTypeWorldInterfaceHashParam ();
173+ int shift = hashParam >>> HASHING_SHIFT_OFFSET ;
174+ int [] hashTable = checkedHub .getOpenTypeWorldInterfaceHashTable ();
175+
176+ // No need to mask hashParam to get "p". interfaceID fits in a short -> the two upper
177+ // bytes are 0.
178+ int hash = (interfaceID >>> shift ) & hashParam ;
179+ int offset = (int ) ImageSingletons .lookup (ObjectLayout .class ).getArrayElementOffset (JavaKind .Int , hash );
180+ int hashTableEntry = ObjectAccess .readInt (hashTable , offset , NamedLocationIdentity .FINAL_LOCATION );
181+
182+ // Hashtable entries contain integers which hold the iTableOffset and the interfaceID:
183+ // hashTableEntry = iTableOffset << HASHING_ITABLE_SHIFT | interfaceID
184+ assert interfaceID == (hashTableEntry & HASHING_INTERFACE_MASK ) : "InterfaceIDs do not match." ;
185+ return (hashTableEntry >>> HASHING_ITABLE_SHIFT );
186+ }
187+
97188 private final SnippetTemplate .SnippetInfo loadITableStartingOffset ;
98189 private final SnippetTemplate .SnippetInfo loadDispatchTableStartingOffset ;
99190
@@ -136,7 +227,8 @@ public void lower(LoadOpenTypeWorldDispatchTableStartingOffset node, LoweringToo
136227 if (target .getDeclaringClass ().isInterface ()) {
137228 SnippetTemplate .Arguments args = new SnippetTemplate .Arguments (loadITableStartingOffset , node .graph (), tool .getLoweringStage ());
138229 args .add ("hub" , node .getHub ());
139- args .add ("interfaceTypeID" , ((SharedType ) target .getDeclaringClass ()).getTypeID ());
230+ args .add ("interfaceID" , ((SharedType ) target .getDeclaringClass ()).getInterfaceID ());
231+ args .add ("useInterfaceHashing" , SubstrateOptions .useInterfaceHashing ());
140232 template (tool , node , args ).instantiate (tool .getMetaAccess (), node , SnippetTemplate .DEFAULT_REPLACER , args );
141233
142234 } else {
@@ -150,8 +242,9 @@ public void lower(LoadOpenTypeWorldDispatchTableStartingOffset node, LoweringToo
150242 */
151243 SnippetTemplate .Arguments args = new SnippetTemplate .Arguments (loadDispatchTableStartingOffset , node .graph (), tool .getLoweringStage ());
152244 args .add ("hub" , node .getHub ());
153- args .add ("interfaceTypeID " , node .getInterfaceTypeID ());
245+ args .add ("interfaceID " , node .getInterfaceID ());
154246 args .add ("vtableStartingOffset" , vtableStartingOffset );
247+ args .add ("useInterfaceHashing" , SubstrateOptions .useInterfaceHashing ());
155248 template (tool , node , args ).instantiate (tool .getMetaAccess (), node , SnippetTemplate .DEFAULT_REPLACER , args );
156249 }
157250 }
0 commit comments