diff --git a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageChunk.java b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageChunk.java index 529f5e136..3c0383e3f 100644 --- a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageChunk.java +++ b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageChunk.java @@ -162,9 +162,9 @@ public SqueakImageChunk getClassChunk() { final int classIndex = getClassIndex(); final int majorIndex = SqueakImageConstants.majorClassIndexOf(classIndex); final int minorIndex = SqueakImageConstants.minorClassIndexOf(classIndex); - final SqueakImageChunk classTablePage = reader.getChunk(reader.hiddenRootsChunk.getWord(majorIndex)); + final SqueakImageChunk classTablePage = reader.chunkMap.get(reader.hiddenRootsChunk.getWord(majorIndex)); assert !classTablePage.isNil() : "Class page does not exist"; - final SqueakImageChunk classChunk = reader.getChunk(classTablePage.getWord(minorIndex)); + final SqueakImageChunk classChunk = reader.chunkMap.get(classTablePage.getWord(minorIndex)); assert classChunk != null : "Unable to find class chunk."; return classChunk; } @@ -197,7 +197,7 @@ public Object[] getPointers(final int end) { private Object decodePointer(final long ptr) { switch ((int) (ptr & 7)) { case SqueakImageConstants.OBJECT_TAG: - final SqueakImageChunk chunk = reader.getChunk(ptr); + final SqueakImageChunk chunk = reader.chunkMap.get(ptr); if (chunk == null) { logBogusPointer(ptr); return ptr >>> SqueakImageConstants.NUM_TAG_BITS; diff --git a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageReader.java b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageReader.java index bd77233a7..cc4d10201 100644 --- a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageReader.java +++ b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/image/SqueakImageReader.java @@ -9,8 +9,8 @@ import java.io.BufferedInputStream; import java.io.IOException; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; +import java.util.logging.Level; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleFile; @@ -29,16 +29,17 @@ import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.SPECIAL_OBJECT_TAG; import de.hpi.swa.trufflesqueak.nodes.accessing.ArrayObjectNodes.ArrayObjectReadNode; import de.hpi.swa.trufflesqueak.util.ArrayUtils; +import de.hpi.swa.trufflesqueak.util.LogUtils; import de.hpi.swa.trufflesqueak.util.MiscUtils; import de.hpi.swa.trufflesqueak.util.VarHandleUtils; public final class SqueakImageReader { private static final byte[] EMPTY_BYTES = new byte[0]; - protected SqueakImageChunk hiddenRootsChunk; + public SqueakImageChunk hiddenRootsChunk; - private final HashMap chunktable = new HashMap<>(750000); - protected final SqueakImageContext image; + public final AddressToChunkMap chunkMap = new AddressToChunkMap(); + public final SqueakImageContext image; private final byte[] byteArrayBuffer = new byte[Long.BYTES]; private long oldBaseAddress; @@ -176,7 +177,7 @@ private void readBody(final BufferedInputStream stream) throws IOException { while (position < segmentEnd) { while (position < segmentEnd - SqueakImageConstants.IMAGE_BRIDGE_SIZE) { final SqueakImageChunk chunk = readObject(stream); - putChunk(chunk); + chunkMap.put(chunk.getPosition() + currentAddressSwizzle, chunk); } assert hiddenRootsChunk != null : "hiddenRootsChunk must be known from now on."; final long bridge = nextLong(stream); @@ -195,10 +196,6 @@ private void readBody(final BufferedInputStream stream) throws IOException { assert dataSize == position; } - private void putChunk(final SqueakImageChunk chunk) { - chunktable.put(chunk.getPosition() + currentAddressSwizzle, chunk); - } - private SqueakImageChunk readObject(final BufferedInputStream stream) throws IOException { int pos = position; assert pos % SqueakImageConstants.WORD_SIZE == 0 : "every object must be 64-bit aligned: " + pos % SqueakImageConstants.WORD_SIZE; @@ -254,7 +251,7 @@ protected static boolean isObjectStack(final int classIndex, final int size) { } private SqueakImageChunk specialObjectChunk(final SqueakImageChunk specialObjectsChunk, final int idx) { - return getChunk(specialObjectsChunk.getWord(idx)); + return chunkMap.get(specialObjectsChunk.getWord(idx)); } private void setPrebuiltObject(final SqueakImageChunk specialObjectsChunk, final int idx, final Object object) { @@ -262,7 +259,7 @@ private void setPrebuiltObject(final SqueakImageChunk specialObjectsChunk, final } private void initPrebuiltConstant() { - final SqueakImageChunk specialChunk = getChunk(specialObjectsPointer); + final SqueakImageChunk specialChunk = chunkMap.get(specialObjectsPointer); specialChunk.setObject(image.specialObjectsArray); // first we find the Metaclass, we need it to correctly instantiate @@ -328,19 +325,19 @@ private void fillInClassObjects() { /** Find all metaclasses and instantiate their singleton instances as class objects. */ int highestKnownClassIndex = -1; for (int p = 0; p < SqueakImageConstants.CLASS_TABLE_ROOT_SLOTS; p++) { - final SqueakImageChunk classTablePage = getChunk(hiddenRootsChunk.getWord(p)); + final SqueakImageChunk classTablePage = chunkMap.get(hiddenRootsChunk.getWord(p)); if (classTablePage.isNil()) { break; /* End of classTable reached (pages are consecutive). */ } for (int i = 0; i < SqueakImageConstants.CLASS_TABLE_PAGE_SIZE; i++) { final long potentialClassPtr = classTablePage.getWord(i); assert potentialClassPtr != 0; - final SqueakImageChunk classChunk = getChunk(potentialClassPtr); + final SqueakImageChunk classChunk = chunkMap.get(potentialClassPtr); if (classChunk.getSqueakClass() == image.metaClass) { /* Derive classIndex from current position in class table. */ highestKnownClassIndex = p << SqueakImageConstants.CLASS_TABLE_MAJOR_INDEX_SHIFT | i; assert classChunk.getWordSize() == METACLASS.INST_SIZE; - final SqueakImageChunk classInstance = getChunk(classChunk.getWord(METACLASS.THIS_CLASS)); + final SqueakImageChunk classInstance = chunkMap.get(classChunk.getWord(METACLASS.THIS_CLASS)); final ClassObject metaClassObject = classChunk.asClassObject(image.metaClass); metaClassObject.setInstancesAreClasses(); classInstance.asClassObject(metaClassObject); @@ -351,7 +348,7 @@ private void fillInClassObjects() { image.classTableIndex = highestKnownClassIndex; /** Fill in metaClass. */ - final SqueakImageChunk specialObjectsChunk = getChunk(specialObjectsPointer); + final SqueakImageChunk specialObjectsChunk = chunkMap.get(specialObjectsPointer); final SqueakImageChunk sqArray = specialObjectsChunk.getClassChunk(); final SqueakImageChunk sqArrayClass = sqArray.getClassChunk(); final SqueakImageChunk sqMetaclass = sqArrayClass.getClassChunk(); @@ -367,17 +364,17 @@ private void fillInClassObjects() { inst.add(classDescriptionClass); for (int p = 0; p < SqueakImageConstants.CLASS_TABLE_ROOT_SLOTS; p++) { - final SqueakImageChunk classTablePage = getChunk(hiddenRootsChunk.getWord(p)); + final SqueakImageChunk classTablePage = chunkMap.get(hiddenRootsChunk.getWord(p)); if (classTablePage.isNil()) { break; /* End of classTable reached (pages are consecutive). */ } for (int i = 0; i < SqueakImageConstants.CLASS_TABLE_PAGE_SIZE; i++) { final long potentialClassPtr = classTablePage.getWord(i); assert potentialClassPtr != 0; - final SqueakImageChunk classChunk = getChunk(potentialClassPtr); + final SqueakImageChunk classChunk = chunkMap.get(potentialClassPtr); if (classChunk.getSqueakClass() == image.metaClass) { assert classChunk.getWordSize() == METACLASS.INST_SIZE; - final SqueakImageChunk classInstance = getChunk(classChunk.getWord(METACLASS.THIS_CLASS)); + final SqueakImageChunk classInstance = chunkMap.get(classChunk.getWord(METACLASS.THIS_CLASS)); final ClassObject metaClassObject = classChunk.asClassObject(image.metaClass); final ClassObject classObject = classInstance.asClassObject(metaClassObject); classObject.fillin(classInstance); @@ -397,7 +394,10 @@ private void fillInClassObjects() { } private void fillInObjects() { - for (final SqueakImageChunk chunk : chunktable.values()) { + for (final SqueakImageChunk chunk : chunkMap.getChunks()) { + if (chunk == null) { + continue; + } final Object chunkObject = chunk.asObject(); if (chunkObject instanceof final AbstractSqueakObjectWithClassAndHash obj) { // FIXME: @@ -413,7 +413,10 @@ private void fillInObjects() { } private void fillInContextObjects() { - for (final SqueakImageChunk chunk : chunktable.values()) { + for (final SqueakImageChunk chunk : chunkMap.getChunks()) { + if (chunk == null) { + continue; + } final Object chunkObject = chunk.asObject(); if (chunkObject instanceof final ContextObject contextObject) { assert !contextObject.hasTruffleFrame(); @@ -432,15 +435,11 @@ private void fillInClassesFromCompactClassList() { private ClassObject lookupClassInCompactClassList(final int compactIndex) { final int majorIndex = SqueakImageConstants.majorClassIndexOf(compactIndex); final int minorIndex = SqueakImageConstants.minorClassIndexOf(compactIndex); - final ArrayObject classTablePage = (ArrayObject) getChunk(hiddenRootsChunk.getWord(majorIndex)).asObject(); + final ArrayObject classTablePage = (ArrayObject) chunkMap.get(hiddenRootsChunk.getWord(majorIndex)).asObject(); final Object result = ArrayObjectReadNode.executeUncached(classTablePage, minorIndex); return result instanceof final ClassObject c ? c : null; } - protected SqueakImageChunk getChunk(final long ptr) { - return chunktable.get(ptr); - } - /* Calculate odd bits (see Behavior>>instSpec). */ public static int calculateObjectPadding(final int format) { if (16 <= format && format <= 31) { @@ -456,4 +455,64 @@ public static int calculateObjectPadding(final int format) { return 0; } } + + public static class AddressToChunkMap { + private static final int INITIAL_CAPACITY = 1_000_000; + private static final float THRESHOLD_PERCENTAGE = 0.75f; + private static final float RESIZE_FACTOR = 1.5f; + private static final int COLLISION_OFFSET = 31; + + private int capacity = INITIAL_CAPACITY; + private int threshold = (int) (capacity * THRESHOLD_PERCENTAGE); + private long[] addresses = new long[capacity]; + private SqueakImageChunk[] chunks = new SqueakImageChunk[capacity]; + private int size; + + public void put(final long address, final SqueakImageChunk chunk) { + if (size > threshold) { + resize(); + } + int slot = (int) (address % capacity); + while (true) { + if (chunks[slot] == null) { + addresses[slot] = address; + chunks[slot] = chunk; + size++; + return; + } + slot = (slot + COLLISION_OFFSET) % capacity; + } + } + + public SqueakImageChunk get(final long address) { + int slot = (int) (address % capacity); + while (true) { + if (addresses[slot] == address) { + return chunks[slot]; + } + slot = (slot + COLLISION_OFFSET) % capacity; + } + } + + private SqueakImageChunk[] getChunks() { + return chunks; + } + + private void resize() { + capacity = (int) (capacity * RESIZE_FACTOR); + threshold = (int) (capacity * THRESHOLD_PERCENTAGE); + LogUtils.READER.log(Level.FINE, "Resizing chunk map to {0}", capacity); + final long[] oldAddresses = addresses; + final SqueakImageChunk[] oldChunks = chunks; + addresses = new long[capacity]; + chunks = new SqueakImageChunk[capacity]; + size = 0; + for (int i = 0; i < oldChunks.length; i++) { + final SqueakImageChunk chunk = oldChunks[i]; + if (chunk != null) { + put(oldAddresses[i], chunk); + } + } + } + } } diff --git a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/LogUtils.java b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/LogUtils.java index 19f75f2d1..203bdc0c0 100644 --- a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/LogUtils.java +++ b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/LogUtils.java @@ -24,6 +24,7 @@ public final class LogUtils { public static final TruffleLogger IO = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "io"); public static final TruffleLogger ITERATE_FRAMES = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "iterate-frames"); public static final TruffleLogger PRIMITIVES = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "primitives"); + public static final TruffleLogger READER = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "reader"); public static final TruffleLogger SCHEDULING = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "scheduling"); public static final TruffleLogger SOCKET = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "socket");