work on LaxMalloc

This commit is contained in:
lax1dude 2024-10-31 00:13:40 -07:00
parent 2cd9e7f3df
commit 2b73f658d0

View File

@ -16,6 +16,7 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
/** /**
@ -30,6 +31,7 @@ import org.teavm.interop.Unmanaged;
* @author lax1dude * @author lax1dude
*/ */
@Unmanaged @Unmanaged
@StaticInit
public final class LaxMalloc { public final class LaxMalloc {
private 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_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 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 * malloc implementation
*/ */
@ -60,8 +69,8 @@ public final class LaxMalloc {
} }
private static Address laxAlloc(int sizeBytes, boolean cleared) { private static Address laxAlloc(int sizeBytes, boolean cleared) {
if(sizeBytes == 0) { if(sizeBytes <= 0) {
// Produce a null pointer if 0 size if requested // Produce a null pointer if 0 or invalid size is requested
return Address.fromInt(0); return Address.fromInt(0);
} }
@ -73,26 +82,61 @@ public final class LaxMalloc {
// always between 0-63 // always between 0-63
int bucket = getListBucket(sizeBytes); 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(); long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
// test the bucket mask if the bucket has any free chunks // test the bucket mask if the bucket has any free chunks
if((bucketMask & (1L << bucket)) == 0l) { 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 // chunks in the larger buckets we can split
long largerBucketsMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << (bucket + 1))); long largerBucketsMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << (bucket + 1)));
if(largerBucketsMask != 0l) { if(largerBucketsMask != 0l) {
// at least one larger chunk exists // 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 bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(largerBucket << SIZEOF_PTR_SH);
Address chunkPtr = bucketStartAddr.getAddress(); Address chunkPtr = bucketStartAddr.getAddress();
//TODO: remove chunk from old bucket
int chunkSize = readChunkSize(chunkPtr); 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 { }else {
// No larger chunks already exist that we can split, // No larger chunks already exist that we can split,
// time to sbrk // time to sbrk
@ -109,7 +153,7 @@ public final class LaxMalloc {
newChunk.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end newChunk.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end
// return the chunk, +4 bytes to skip size int // 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); return newChunk.add(4);
} }
}else { }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<br><br> * free implementation<br><br>
* *
@ -174,7 +222,14 @@ public final class LaxMalloc {
if((prevChunkSize & 0x80000000) == 0) { if((prevChunkSize & 0x80000000) == 0) {
// previous chunk is not in use, merge! // 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);
} }
} }
@ -185,10 +240,21 @@ public final class LaxMalloc {
if((nextChunkSize & 0x80000000) == 0) { if((nextChunkSize & 0x80000000) == 0) {
// next chunk is not in use, merge! // next chunk is not in use, merge!
//TODO // remove the next chunk from its list
unlinkChunkFromFreeList(nextChunkPtr, nextChunkSize);
// 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 int bucket = getListBucket(chunkSize - 8); // size - 2 ints
long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong(); long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
@ -211,7 +277,53 @@ public final class LaxMalloc {
writeChunkPrevFreeAddr(chunkPtr, Address.fromInt(0)); writeChunkPrevFreeAddr(chunkPtr, Address.fromInt(0));
bucketStartAddr.putAddress(chunkPtr); 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; private static final int NUM_FREE_BUCKETS = 64;
@ -224,7 +336,7 @@ public final class LaxMalloc {
if (allocSize < 128) if (allocSize < 128)
return (allocSize >> 3) - 1; return (allocSize >> 3) - 1;
int clz = Integer.numberOfLeadingZeros(allocSize); int clz = numberOfLeadingZerosI(allocSize);
int bucketIndex = (clz > 19) ? 110 - (clz << 2) + ((allocSize >> (29 - clz)) ^ 4) int bucketIndex = (clz > 19) ? 110 - (clz << 2) + ((allocSize >> (29 - clz)) ^ 4)
: min(71 - (clz << 1) + ((allocSize >> (30 - clz)) ^ 2), NUM_FREE_BUCKETS - 1); : 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; 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);
}
} }