From 2b73f658d093adc778aea64a3cecd782b2cb1088 Mon Sep 17 00:00:00 2001 From: lax1dude Date: Thu, 31 Oct 2024 00:13:40 -0700 Subject: [PATCH] work on LaxMalloc --- .../java/org/teavm/runtime/LaxMalloc.java | 152 ++++++++++++++++-- 1 file changed, 137 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/teavm/runtime/LaxMalloc.java b/core/src/main/java/org/teavm/runtime/LaxMalloc.java index cb5b94e34..30be7a894 100644 --- a/core/src/main/java/org/teavm/runtime/LaxMalloc.java +++ b/core/src/main/java/org/teavm/runtime/LaxMalloc.java @@ -16,6 +16,7 @@ package org.teavm.runtime; import org.teavm.interop.Address; +import org.teavm.interop.StaticInit; import org.teavm.interop.Unmanaged; /** @@ -30,6 +31,7 @@ import org.teavm.interop.Unmanaged; * @author lax1dude */ @Unmanaged +@StaticInit public final class LaxMalloc { private LaxMalloc() { @@ -45,6 +47,13 @@ public final class LaxMalloc { private static final int ADDR_HEAP_BUCKETS_START = 16; // Address to the list of 64 pointers to the beginnings of the 64 buckets private static final int ADDR_HEAP_DATA_START = 272; // Beginning of the first chunk of the heap + static { + // zero out the control region + Allocator.fillZero(Address.fromInt(0), ADDR_HEAP_DATA_START); + // initialize heap limit + Address.fromInt(ADDR_HEAP_LIMIT).putInt(ADDR_HEAP_DATA_START); + } + /** * malloc implementation */ @@ -60,8 +69,8 @@ public final class LaxMalloc { } private static Address laxAlloc(int sizeBytes, boolean cleared) { - if(sizeBytes == 0) { - // Produce a null pointer if 0 size if requested + if(sizeBytes <= 0) { + // Produce a null pointer if 0 or invalid size is requested return Address.fromInt(0); } @@ -73,26 +82,61 @@ public final class LaxMalloc { // always between 0-63 int bucket = getListBucket(sizeBytes); + if(bucket == 63) { + // special bucket for the huge allocations + // uses a different function + return laxHugeAlloc(sizeBytes, cleared); + } + long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong(); // test the bucket mask if the bucket has any free chunks if((bucketMask & (1L << bucket)) == 0l) { - // no more free chunks, let us first check if there is are any + // no more free chunks, let us first check if there are any // chunks in the larger buckets we can split long largerBucketsMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << (bucket + 1))); if(largerBucketsMask != 0l) { // at least one larger chunk exists - int largerBucket = Long.numberOfTrailingZeros(largerBucketsMask); + // we can quickly find it using the bitmask + int largerBucket = numberOfTrailingZerosL(largerBucketsMask); + Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(largerBucket << SIZEOF_PTR_SH); Address chunkPtr = bucketStartAddr.getAddress(); - //TODO: remove chunk from old bucket int chunkSize = readChunkSize(chunkPtr); - int chunkOtherHalfNewSize = chunkSize - sizeBytes; - //TODO - //TODO - //TODO - return null; //TODO + int chunkNewSize = chunkSize - sizeBytes - 8; // -size - 2 ints - 2 more ints + + // if the other half of the new chunk is too small, check an even larger bucket + // we should only need to look at an even larger bucket one more time before giving up + if(chunkNewSize - 8 < MIN_ALLOC_SIZE) { + //TODO + } + + // remove the large chunk from its bucket + unlinkChunkFromFreeList(chunkPtr, chunkSize); + + // provision the part of the large chunk we want + int sizePlusInts = sizeBytes + 8; // size + 2 ints + chunkPtr.putInt(sizePlusInts | 0x80000000); // size + in use flag + chunkPtr.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end + + // provision the other part of the chunk that we want to return to the free list + Address otherChunkPtr = chunkPtr.add(sizePlusInts); + otherChunkPtr.putInt(chunkNewSize); // size + otherChunkPtr.add(chunkNewSize - 4).putInt(chunkNewSize); // size (end) + + // return the other part of the chunk to the free chunks list + linkChunkInFreeList(otherChunkPtr, chunkNewSize); + + // +4 bytes to skip size + Address ret = chunkPtr.add(4); + + // clear if requested + if(cleared) { + Allocator.fillZero(ret, sizeBytes); + } + + return ret; }else { // No larger chunks already exist that we can split, // time to sbrk @@ -109,7 +153,7 @@ public final class LaxMalloc { newChunk.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end // return the chunk, +4 bytes to skip size int - // we don't need to clear it because its new + // we don't need to clear it because its new memory return newChunk.add(4); } }else { @@ -148,6 +192,10 @@ public final class LaxMalloc { } } + private static Address laxHugeAlloc(int sizeBytes, boolean cleared) { + return null; //TODO: bucket number 63 + } + /** * free implementation

* @@ -174,7 +222,14 @@ public final class LaxMalloc { if((prevChunkSize & 0x80000000) == 0) { // previous chunk is not in use, merge! - //TODO + // remove the previous chunk from its list + unlinkChunkFromFreeList(prevChunkPtr, prevChunkSize); + + // resize the current chunk to also contain the previous chunk + chunkPtr = prevChunkPtr; + chunkSize += prevChunkSize; + chunkPtr.putInt(chunkSize); + chunkPtr.add(chunkSize).putInt(chunkSize); } } @@ -184,11 +239,22 @@ public final class LaxMalloc { int nextChunkSize = readChunkSizeStatus(nextChunkPtr); if((nextChunkSize & 0x80000000) == 0) { // next chunk is not in use, merge! + + // remove the next chunk from its list + unlinkChunkFromFreeList(nextChunkPtr, nextChunkSize); - //TODO + // resize the current chunk to also contain the next chunk + chunkSize += nextChunkSize; + chunkPtr.putInt(chunkSize); + chunkPtr.add(chunkSize).putInt(chunkSize); } } + // add the final chunk to the free chunks list + linkChunkInFreeList(chunkPtr, chunkSize); + } + + private static void linkChunkInFreeList(Address chunkPtr, int chunkSize) { int bucket = getListBucket(chunkSize - 8); // size - 2 ints long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong(); @@ -211,7 +277,53 @@ public final class LaxMalloc { writeChunkPrevFreeAddr(chunkPtr, Address.fromInt(0)); bucketStartAddr.putAddress(chunkPtr); } - + } + + private static void unlinkChunkFromFreeList(Address chunkPtr, int chunkSize) { + Address prevChunkPtr = readChunkPrevFreeAddr(chunkPtr); + Address nextChunkPtr = readChunkNextFreeAddr(chunkPtr); + if((prevChunkPtr.toInt() | nextChunkPtr.toInt()) == 0) { + // chunk is the only one currently in its bucket + + int chunkBucket = getListBucket(chunkSize - 8); // size - 2 ints + + Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(chunkBucket << SIZEOF_PTR_SH); + bucketStartAddr.putAddress(Address.fromInt(0)); // remove chunk from the bucket + + // clear the bit in the free buckets bitmask + long bucketsFreeMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong(); + bucketsFreeMask &= ~(1L << chunkBucket); + Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).putLong(bucketsFreeMask); + + }else { + // there are other chunks in this bucket + + if(prevChunkPtr.toInt() != 0) { + // previous chunk exists + + if(nextChunkPtr.toInt() != 0) { + // next chunk exits, link it to the previous chunk + writeChunkNextFreeAddr(prevChunkPtr, nextChunkPtr); + writeChunkPrevFreeAddr(nextChunkPtr, prevChunkPtr); + }else { + // there is no next chunk + writeChunkNextFreeAddr(prevChunkPtr, Address.fromInt(0)); + } + + }else { + // no previous chunk, this must be the first chunk in the bucket + + int chunkBucket = getListBucket(chunkSize - 8); // size - 2 ints + Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(chunkBucket << SIZEOF_PTR_SH); + + // we already know the next chunk exists + // make the next chunk the first chunk in the bucket + bucketStartAddr.putAddress(nextChunkPtr); + + // unlink the next chunk from the current chunk + writeChunkPrevFreeAddr(nextChunkPtr, Address.fromInt(0)); + } + } } private static final int NUM_FREE_BUCKETS = 64; @@ -224,7 +336,7 @@ public final class LaxMalloc { if (allocSize < 128) return (allocSize >> 3) - 1; - int clz = Integer.numberOfLeadingZeros(allocSize); + int clz = numberOfLeadingZerosI(allocSize); int bucketIndex = (clz > 19) ? 110 - (clz << 2) + ((allocSize >> (29 - clz)) ^ 4) : min(71 - (clz << 1) + ((allocSize >> (30 - clz)) ^ 2), NUM_FREE_BUCKETS - 1); @@ -282,4 +394,14 @@ public final class LaxMalloc { return a < b ? a : b; } + private static int numberOfTrailingZerosL(long i) { + //TODO: Intrinsify this, WASM has dedicated instructions for this operation!!! + return Long.numberOfTrailingZeros(i); + } + + private static int numberOfLeadingZerosI(int i) { + //TODO: Intrinsify this, WASM has dedicated instructions for this operation!!! + return Integer.numberOfLeadingZeros(i); + } + }