Skip to content

Commit b98d414

Browse files
teshullstooke
authored andcommitted
Improve native-image class id assignment algorithm.
1 parent 6782f4e commit b98d414

File tree

1 file changed

+76
-57
lines changed

1 file changed

+76
-57
lines changed

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

Lines changed: 76 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,13 @@ private void generateTypeCheckSlots() {
278278
int[] slots = classIDMap.get(type);
279279
for (int i = 0; i < slots.length; i++) {
280280
typeCheckSlots[i] = getShortValue(slots[i]);
281+
assert typeCheckSlots[i] < SLOT_CAPACITY;
281282
}
282283
slots = interfaceIDMap.get(type);
283284
if (slots != null) {
284285
for (int i = 0; i < slots.length; i++) {
285286
typeCheckSlots[numClassSlots + i] = getShortValue(slots[i]);
287+
assert typeCheckSlots[numClassSlots + i] < SLOT_CAPACITY;
286288
}
287289
}
288290

@@ -373,23 +375,25 @@ private static boolean shouldIncludeType(HostedType type) {
373375
* against non-interface types can be accomplished through a range check.
374376
* <p>
375377
* In our algorithm, in order to guarantee ID information can fit into two bytes, the type ids
376-
* are spread out into multiple slots when the two byte capacity is exceeded.
378+
* are spread out into multiple slots when the two byte capacity is exceeded. To do so, the
379+
* concept of a reservedID is introduced. ReservedIDs are assigned backwards from the slot's
380+
* capacity and are used to guarantee subtyping works correct when a type's subtypes will
381+
* overfill the current slot.
377382
*/
378383
private void calculateClassIDs() {
379384
ArrayList<Integer> currentIDs = new ArrayList<>();
380-
ArrayList<Integer> reservedIDs = new ArrayList<>();
381-
currentIDs.add(-1);
382-
reservedIDs.add(SLOT_CAPACITY);
385+
ArrayList<Integer> numReservedIDs = new ArrayList<>();
386+
currentIDs.add(0);
387+
numReservedIDs.add(0);
383388
for (HostedType root : allReachableRoots) {
384-
assert !isInterface(root);
385-
classIdHelper(root, currentIDs, reservedIDs);
389+
classIdHelper(root, currentIDs, numReservedIDs);
386390
}
387391

388392
/* Recording the number of slots reserved for class IDs. */
389393
assert numClassSlots == -1;
390394
numClassSlots = currentIDs.size();
391395

392-
/* Setting class slot for interfaces - will integrate this with classIDHelper eventually. */
396+
/* Setting class slot for interfaces to be the same as the object type. */
393397
for (HostedType type : allReachableTypes) {
394398
if (isInterface(type)) {
395399
int dim = type.getArrayDimension();
@@ -414,15 +418,13 @@ private ClassIDState(int reservedID, int slotNum, int assignedID, int maxSubtype
414418
}
415419

416420
/**
417-
* This method assigns ids to class types. interfaces are performed using the information
421+
* This method assigns ids to class types. Interfaces are performed using the information
418422
* calculated in {@link #computeInterfaceSlots()}.
419423
*/
420-
private void classIdHelper(HostedType type, ArrayList<Integer> currentIDs, ArrayList<Integer> reservedIDs) {
421-
assert shouldIncludeType(type);
422-
boolean isTypeInterface = isInterface(type);
423-
assert !isTypeInterface;
424+
private void classIdHelper(HostedType type, ArrayList<Integer> currentIDs, ArrayList<Integer> numReservedIDs) {
425+
assert shouldIncludeType(type) && !isInterface(type);
424426

425-
ClassIDState state = generateClassIDState(type, currentIDs, reservedIDs);
427+
ClassIDState state = generateClassIDState(type, currentIDs, numReservedIDs);
426428
int reservedID = state.reservedID;
427429
int slotNum = state.slotNum;
428430
int assignedID = state.assignedID;
@@ -441,67 +443,84 @@ private void classIdHelper(HostedType type, ArrayList<Integer> currentIDs, Array
441443
*/
442444
continue;
443445
}
444-
classIdHelper(subtype, currentIDs, reservedIDs);
446+
classIdHelper(subtype, currentIDs, numReservedIDs);
445447

446448
assert currentIDs.get(slotNum) >= assignedID; // IDs should always be increasing.
447449
}
448450

449-
/* Determining range of values assigned to subtypes. */
450-
if (!isTypeInterface) {
451-
type.setTypeCheckSlot(getShortValue(slotNum));
452-
int currentID = currentIDs.get(slotNum);
453-
assert currentID == maxSubtypeID;
454-
type.setTypeCheckRange(getShortValue(assignedID), getShortValue(currentID - assignedID + 1));
455-
}
456-
if (reservedID != -1) {
457-
currentIDs.set(slotNum, reservedID);
458-
reservedIDs.set(slotNum, reservedID + 1); // setting back to original value
451+
/* Validating calculation of maxSubtypeID. */
452+
assert currentIDs.get(slotNum) == maxSubtypeID;
453+
454+
/* Record type's slot and range. */
455+
type.setTypeCheckSlot(getShortValue(slotNum));
456+
type.setTypeCheckRange(getShortValue(assignedID), getShortValue(maxSubtypeID - assignedID + 1));
457+
if (reservedID != 0) {
458+
/* Must distinguish subsequent ID assignments from this type. */
459+
assert numReservedIDs.get(slotNum) == reservedID;
460+
int newNumReservedIDs = reservedID - 1;
461+
numReservedIDs.set(slotNum, newNumReservedIDs);
462+
currentIDs.set(slotNum, newNumReservedIDs == 0 ? 0 : SLOT_CAPACITY - newNumReservedIDs);
459463
}
460464
}
461465

462-
private ClassIDState generateClassIDState(HostedType type, ArrayList<Integer> currentIDs, ArrayList<Integer> reservedIDs) {
463-
int reservedID = -1;
466+
private ClassIDState generateClassIDState(HostedType type, ArrayList<Integer> currentIDs, ArrayList<Integer> numReservedIDs) {
467+
/*
468+
* A reserved ID is assigned when this type's slot will overflow while assigning IDs to its
469+
* subtypes.
470+
*/
471+
int reservedID = 0;
464472
int slotNum = currentIDs.size() - 1;
465473
int numDescendants = numClassDescendants.getOrDefault(type, 0);
466-
int assignedID = currentIDs.get(slotNum) + 1; // need start at the next free stop
467-
int currentReservedID = reservedIDs.get(slotNum); // max value allowed at this spot
468-
assert assignedID < currentReservedID;
469-
470-
if (assignedID + 1 == currentReservedID) {
471-
/* No more space left. Making filled slot's value different than predecessor. */
472-
currentIDs.set(slotNum, assignedID);
474+
/* first trying to assign next sequential id. */
475+
int assignedID = currentIDs.get(slotNum) + 1;
476+
/* Number of slot currently reserved. This effectively lowers the slot's capacity. */
477+
int currentNumReservedIDs = numReservedIDs.get(slotNum);
478+
int currentCapacity = SLOT_CAPACITY - currentNumReservedIDs;
479+
assert assignedID <= currentCapacity;
480+
481+
if (assignedID == currentCapacity) {
482+
/*
483+
* No more space left. Assigning overflowed slot appropriate "end" value.
484+
*/
485+
currentIDs.set(slotNum, currentNumReservedIDs == 0 ? 0 : SLOT_CAPACITY - currentNumReservedIDs);
473486
slotNum++;
474-
assignedID = 1;
475487
currentIDs.add(0);
476-
currentReservedID = SLOT_CAPACITY;
477-
reservedIDs.add(currentReservedID);
488+
currentNumReservedIDs = 0;
489+
currentCapacity = SLOT_CAPACITY;
490+
numReservedIDs.add(currentNumReservedIDs);
491+
assignedID = 1;
478492
}
479493
int maxSubtypeID = assignedID + numDescendants;
480-
if (maxSubtypeID >= currentReservedID) {
481-
/* Means this types descendants will overfill this type. */
482-
reservedID = currentReservedID - 1;
483-
if (assignedID + 1 == reservedID) {
494+
if (maxSubtypeID >= currentCapacity) {
495+
/*
496+
* Means this types descendants will overfill this slot. In this case, need to reserved
497+
* an ID and force all descendants to have values between the current assignable and the
498+
* reserved ID (inclusive). Non-descendants are then assigned the next ID (mod
499+
* capacity).
500+
*/
501+
if (assignedID + 1 == currentCapacity) {
484502
/*
485-
* Not enough space for reserved ID + new-slot filler -- move on to next slot. Also,
486-
* making filled slot's value different than predecessor.
503+
* Not enough space to add a reserved slot at end of the list, so must move to next
504+
* slot.
487505
*/
488-
currentIDs.set(slotNum, reservedID);
506+
currentIDs.set(slotNum, currentNumReservedIDs == 0 ? 0 : SLOT_CAPACITY - currentNumReservedIDs);
489507
slotNum++;
490-
assignedID = 1;
491508
currentIDs.add(0);
509+
currentNumReservedIDs = 0;
510+
currentCapacity = SLOT_CAPACITY;
511+
numReservedIDs.add(currentNumReservedIDs);
512+
assignedID = 1;
492513
maxSubtypeID = assignedID + numDescendants;
493-
currentReservedID = SLOT_CAPACITY;
494-
reservedIDs.add(currentReservedID);
495-
if (maxSubtypeID >= currentReservedID) {
496-
reservedID = currentReservedID - 1;
497-
reservedIDs.set(slotNum, reservedID);
498-
maxSubtypeID = reservedID - 1;
499-
} else {
500-
reservedID = -1;
501-
}
502-
} else {
503-
reservedIDs.set(slotNum, reservedID);
504-
maxSubtypeID = reservedID - 1;
514+
}
515+
516+
/*
517+
* Have to recheck whether a reservedID is needed since a new slot may have been added.
518+
*/
519+
if (maxSubtypeID >= currentCapacity) {
520+
currentNumReservedIDs++;
521+
reservedID = currentNumReservedIDs;
522+
maxSubtypeID = SLOT_CAPACITY - reservedID;
523+
numReservedIDs.set(slotNum, currentNumReservedIDs);
505524
}
506525
}
507526

@@ -515,7 +534,7 @@ private ClassIDState generateClassIDState(HostedType type, ArrayList<Integer> cu
515534
* the slot capacity.
516535
*/
517536
private static short getShortValue(int intValue) {
518-
assert SLOT_CAPACITY <= 1 << 16 && intValue < SLOT_CAPACITY;
537+
assert intValue < (1 << 16);
519538
return (short) intValue;
520539
}
521540

0 commit comments

Comments
 (0)