Skip to content

Commit 5c69def

Browse files
committed
[GR-59590] Reuse dispatch table slots from parents in open type world hub layout.
PullRequest: graal/21703
2 parents cef3604 + 8bce569 commit 5c69def

File tree

3 files changed

+79
-28
lines changed

3 files changed

+79
-28
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ private static HostedType[] getAllInterfaces(Map<HostedType, HostedType[]> allIn
194194
return result;
195195
}
196196

197+
public static boolean matchingSignature(HostedMethod o1, HostedMethod o2) {
198+
return matchingSignature(o1.wrapped, o2.wrapped);
199+
}
200+
197201
private static boolean matchingSignature(AnalysisMethod o1, AnalysisMethod o2) {
198202
if (o1.equals(o2)) {
199203
return true;

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDispatchTableFeature.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Set;
3737
import java.util.concurrent.ConcurrentHashMap;
3838
import java.util.function.Supplier;
39+
import java.util.stream.IntStream;
3940
import java.util.stream.Stream;
4041
import java.util.stream.StreamSupport;
4142

@@ -49,7 +50,6 @@
4950
import com.oracle.graal.pointsto.meta.AnalysisType;
5051
import com.oracle.objectfile.ObjectFile;
5152
import com.oracle.svm.core.InvalidMethodPointerHandler;
52-
import com.oracle.svm.core.SubstrateOptions;
5353
import com.oracle.svm.core.config.ConfigurationValues;
5454
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
5555
import com.oracle.svm.core.feature.InternalFeature;
@@ -145,6 +145,8 @@ public static final class Options {
145145

146146
static final int INVALID_HOSTED_METHOD_INDEX = -1;
147147

148+
private static final String UNRESOLVED_VTABLE_ENTRY_SYMBOL = "__svm_vtableSym_";
149+
148150
@Override
149151
public boolean isInConfiguration(Feature.IsInConfigurationAccess access) {
150152
return ImageLayerBuildingSupport.buildingImageLayer();
@@ -485,7 +487,7 @@ private void markRelocsWordInBitmap(BitSet bitmap, int offsetInHeap, ImageHeapLa
485487
bitmap.set(offsetInHeapRelocs / wordSize);
486488
}
487489

488-
private static String computeUnresolvedMethodSymbol(HostedDispatchSlot slotInfo, Map<ResolvedJavaMethod, String> methodToSymbolMap) {
490+
private static String computeUnresolvedMethodSymbol(HostedDispatchSlot slotInfo, Map<ResolvedJavaMethod, String> methodToSymbolMap, Supplier<String> symbolNameSupplier) {
489491
/*
490492
* First try to determine the resolved method. This is useful for deduplication.
491493
*/
@@ -503,10 +505,9 @@ private static String computeUnresolvedMethodSymbol(HostedDispatchSlot slotInfo,
503505

504506
String unresolvedTableSymbol;
505507
if (resolvedMethod != null) {
506-
unresolvedTableSymbol = methodToSymbolMap.computeIfAbsent(resolvedMethod, k -> String.format("%s_unresolvedVTableSym", NativeImage.localSymbolNameForMethod(k)));
508+
unresolvedTableSymbol = methodToSymbolMap.computeIfAbsent(resolvedMethod, k -> symbolNameSupplier.get());
507509
} else {
508-
unresolvedTableSymbol = SubstrateOptions.ImageSymbolsPrefix.getValue() +
509-
String.format("unresolvedVTableSym_typeid%s_slot%s", slotInfo.dispatchTable.type.getWrapped().getId(), slotInfo.slotIndex);
510+
unresolvedTableSymbol = symbolNameSupplier.get();
510511
}
511512
return unresolvedTableSymbol;
512513
}
@@ -523,6 +524,11 @@ public void defineDispatchTableSlotSymbols(ObjectFile objectFile, ObjectFile.Sec
523524
Set<String> unresolvedVTableSymbolNames = generateUnresolvedSymbolNames ? new HashSet<>() : null;
524525

525526
Map<ResolvedJavaMethod, String> deduplicatedMethodMap = new HashMap<>();
527+
final var unresolvedSlotCount = IntStream.iterate(0, i -> i + 1).iterator();
528+
Supplier<String> symbolNameSupplier = () -> {
529+
int count = unresolvedSlotCount.nextInt();
530+
return String.format("%s_%s", UNRESOLVED_VTABLE_ENTRY_SYMBOL, count);
531+
};
526532

527533
/*
528534
* First calculate symbols for all slots
@@ -543,9 +549,9 @@ public void defineDispatchTableSlotSymbols(ObjectFile objectFile, ObjectFile.Sec
543549
* We need to make a symbol for this method which can be resolved in a later
544550
* build if the target is compiled.
545551
*/
546-
symbol = computeUnresolvedMethodSymbol(slotInfo, deduplicatedMethodMap);
552+
symbol = computeUnresolvedMethodSymbol(slotInfo, deduplicatedMethodMap, symbolNameSupplier);
547553
if (unresolvedVTableSymbolNames.add(symbol)) {
548-
objectFile.createUndefinedSymbol(symbol, 0, true);
554+
objectFile.createUndefinedSymbol(symbol, wordSize, true);
549555
}
550556
}
551557
} else {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.oracle.svm.core.SubstrateUtil;
4343
import com.oracle.svm.core.hub.RuntimeClassLoading;
4444
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
45+
import com.oracle.svm.hosted.OpenTypeWorldFeature;
4546
import com.oracle.svm.hosted.imagelayer.LayeredDispatchTableFeature;
4647

4748
import jdk.graal.compiler.debug.Assertions;
@@ -182,15 +183,40 @@ private boolean verifyOpenTypeWorldDispatchTables() {
182183
}
183184

184185
private List<HostedMethod> generateITable(HostedType type) {
185-
return generateDispatchTable(type, 0);
186+
return generateDispatchTable(type, List.of());
186187
}
187188

188-
private List<HostedMethod> generateDispatchTable(HostedType type, int startingIndex) {
189+
/**
190+
* Tries to find an existing parent slot with an identical signature. If successful, this
191+
* method's index can be assigned to the same slot and no new dispatch slot is needed.
192+
*/
193+
private static boolean findAndLinkToParentSlot(HostedMethod hMethod, List<HostedMethod> parentSlots) {
194+
for (int i = 0; i < parentSlots.size(); i++) {
195+
HostedMethod candidate = parentSlots.get(i);
196+
if (OpenTypeWorldFeature.matchingSignature(hMethod, candidate)) {
197+
assert candidate.computedVTableIndex == i : candidate.computedVTableIndex;
198+
installVTableIndex(hMethod, candidate.computedVTableIndex);
199+
return true;
200+
}
201+
}
202+
return false;
203+
}
204+
205+
private static void installVTableIndex(HostedMethod hMethod, int index) {
206+
assert hMethod.computedVTableIndex == HostedMethod.MISSING_VTABLE_IDX : hMethod.computedVTableIndex;
207+
hMethod.computedVTableIndex = index;
208+
}
209+
210+
private List<HostedMethod> generateDispatchTable(HostedType type, List<HostedMethod> parentClassTable) {
189211
Predicate<HostedMethod> includeMethod;
190212
if (openHubUtils.filterVTableMethods(type)) {
191213
// include only methods which will be indirect calls
192214
includeMethod = m -> {
193215
assert !m.isConstructor() : Assertions.errorMessage("Constructors should never be in dispatch tables", m);
216+
if (findAndLinkToParentSlot(m, parentClassTable)) {
217+
// a prior slot has been found
218+
return false;
219+
}
194220
if (m.implementations.length > 1) {
195221
return true;
196222
} else {
@@ -204,6 +230,10 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
204230
} else {
205231
includeMethod = m -> {
206232
assert !m.isConstructor() : Assertions.errorMessage("Constructors should never be in dispatch tables", m);
233+
if (findAndLinkToParentSlot(m, parentClassTable)) {
234+
// a prior slot has been found
235+
return false;
236+
}
207237
/*
208238
* We have to use the analysis method's canBeStaticallyBound implementation because
209239
* within HostedMethod we sometimes do additional pruning when operating under the
@@ -214,11 +244,10 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
214244
}
215245
var table = type.getWrapped().getOpenTypeWorldDispatchTableMethods().stream().map(hUniverse::lookup).filter(includeMethod).sorted(HostedUniverse.METHOD_COMPARATOR).toList();
216246

217-
int index = startingIndex;
247+
int index = parentClassTable.size();
218248
for (HostedMethod typeMethod : table) {
219249
assert typeMethod.getDeclaringClass().equals(type) : typeMethod;
220-
assert typeMethod.computedVTableIndex == HostedMethod.MISSING_VTABLE_IDX : typeMethod.computedVTableIndex;
221-
typeMethod.computedVTableIndex = index;
250+
installVTableIndex(typeMethod, index);
222251
index++;
223252
}
224253

@@ -229,40 +258,42 @@ private List<HostedMethod> generateDispatchTable(HostedType type, int startingIn
229258
return table;
230259
}
231260

232-
private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<HostedType, List<HostedMethod>> dispatchTablesMap, HostedMethod invalidDispatchTableEntryHandler) {
261+
private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<HostedType, List<HostedMethod>> classTablesMap, HostedMethod invalidDispatchTableEntryHandler) {
233262
var superClass = type.getSuperclass();
234-
List<HostedMethod> parentClassTable = superClass == null ? List.of() : dispatchTablesMap.get(superClass);
235-
List<HostedMethod> classTableWithoutSuper = generateDispatchTable(type, parentClassTable.size());
236-
List<HostedMethod> resultClassTableMethods;
263+
List<HostedMethod> parentClassTable = superClass == null ? List.of() : classTablesMap.get(superClass);
264+
List<HostedMethod> classTableWithoutSuper = generateDispatchTable(type, parentClassTable);
265+
List<HostedMethod> classTableMethods;
237266
if (!classTableWithoutSuper.isEmpty()) {
238-
resultClassTableMethods = new ArrayList<>(parentClassTable);
239-
resultClassTableMethods.addAll(classTableWithoutSuper);
267+
classTableMethods = new ArrayList<>(parentClassTable);
268+
classTableMethods.addAll(classTableWithoutSuper);
240269
} else {
241270
/*
242271
* If the type doesn't declare any new methods, then we can use the parent's class
243272
* table.
244273
*/
245-
resultClassTableMethods = parentClassTable;
274+
classTableMethods = parentClassTable;
246275
}
247-
dispatchTablesMap.put(type, resultClassTableMethods);
276+
classTablesMap.put(type, classTableMethods);
248277

249278
if (!type.isAbstract()) {
250279
// create concrete dispatch classes
251-
List<HostedMethod> aggregatedTable = new ArrayList<>(resultClassTableMethods);
280+
List<HostedMethod> aggregatedTable = new ArrayList<>(classTableMethods);
252281
HostedType[] interfaces = type.typeCheckInterfaceOrder;
253282
type.itableStartingOffsets = new int[interfaces.length];
254-
int currentITableOffset = resultClassTableMethods.size();
283+
int currentITableOffset = classTableMethods.size();
255284
for (int i = 0; i < interfaces.length; i++) {
256285
HostedType interfaceType = interfaces[i];
257-
List<HostedMethod> interfaceMethods = dispatchTablesMap.get(interfaceType);
286+
List<HostedMethod> interfaceMethods = classTablesMap.get(interfaceType);
258287

259288
type.itableStartingOffsets[i] = currentITableOffset;
260289
aggregatedTable.addAll(interfaceMethods);
261290
currentITableOffset += interfaceMethods.size();
262291
}
263292
type.openTypeWorldDispatchTables = new HostedMethod[aggregatedTable.size()];
264293
type.openTypeWorldDispatchTableSlotTargets = aggregatedTable.toArray(HostedMethod[]::new);
294+
265295
boolean[] validTarget = new boolean[aggregatedTable.size()];
296+
Set<HostedMethod> seenResolvedMethods = SubstrateUtil.assertionsEnabled() ? new HashSet<>() : null;
266297
for (int i = 0; i < aggregatedTable.size(); i++) {
267298
HostedMethod method = aggregatedTable.get(i);
268299
/*
@@ -275,6 +306,13 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
275306
if (resolvedMethod != null) {
276307
targetMethod = resolvedMethod;
277308
validTarget[i] = true;
309+
if (seenResolvedMethods != null && i < classTableMethods.size()) {
310+
/*
311+
* Check that each resolved method within the class table is unique
312+
*/
313+
var added = seenResolvedMethods.add(resolvedMethod);
314+
assert added : Assertions.errorMessage("Multiple slots with same resolution method", resolvedMethod);
315+
}
278316
}
279317
}
280318

@@ -289,7 +327,7 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
289327
assert !type.isInterface();
290328
List<HostedMethod> sourceTable;
291329
if (type.isAbstract()) {
292-
sourceTable = resultClassTableMethods;
330+
sourceTable = classTableMethods;
293331
} else {
294332
sourceTable = Arrays.asList(type.openTypeWorldDispatchTableSlotTargets);
295333
}
@@ -306,13 +344,16 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map<Ho
306344

307345
for (HostedType subType : type.subTypes) {
308346
if (subType instanceof HostedInstanceClass instanceClass && openHubUtils.shouldIncludeType(subType)) {
309-
generateOpenTypeWorldDispatchTable(instanceClass, dispatchTablesMap, invalidDispatchTableEntryHandler);
347+
generateOpenTypeWorldDispatchTable(instanceClass, classTablesMap, invalidDispatchTableEntryHandler);
310348
}
311349
}
312350
}
313351

314352
private void buildOpenTypeWorldDispatchTables() {
315-
Map<HostedType, List<HostedMethod>> dispatchTablesMap = new HashMap<>();
353+
/*
354+
* Map from type to class table (i.e. the type's vtable w/o any appended itables).
355+
*/
356+
Map<HostedType, List<HostedMethod>> classTablesMap = new HashMap<>();
316357

317358
for (HostedType type : hUniverse.getTypes()) {
318359
/*
@@ -321,7 +362,7 @@ private void buildOpenTypeWorldDispatchTables() {
321362
*/
322363
if (type.isInterface() && openHubUtils.shouldIncludeType(type)) {
323364
List<HostedMethod> itable = generateITable(type);
324-
dispatchTablesMap.put(type, itable);
365+
classTablesMap.put(type, itable);
325366
if (RuntimeClassLoading.isSupported()) {
326367
type.cremaOpenTypeWorldDispatchTables = new HostedMethod[itable.size()];
327368
for (int i = 0; i < itable.size(); i++) {
@@ -332,7 +373,7 @@ private void buildOpenTypeWorldDispatchTables() {
332373
}
333374

334375
HostedMethod invalidDispatchTableEntryHandler = hMetaAccess.lookupJavaMethod(InvalidMethodPointerHandler.INVALID_VTABLE_ENTRY_HANDLER_METHOD);
335-
generateOpenTypeWorldDispatchTable((HostedInstanceClass) hUniverse.objectType(), dispatchTablesMap, invalidDispatchTableEntryHandler);
376+
generateOpenTypeWorldDispatchTable((HostedInstanceClass) hUniverse.objectType(), classTablesMap, invalidDispatchTableEntryHandler);
336377

337378
int[] emptyITableOffsets = new int[0];
338379
var objectType = hUniverse.getObjectClass();

0 commit comments

Comments
 (0)