diff --git a/core/src/main/java/org/teavm/runtime/LaxMalloc.java b/core/src/main/java/org/teavm/runtime/LaxMalloc.java
index fbe3b33fb..94d787c8e 100644
--- a/core/src/main/java/org/teavm/runtime/LaxMalloc.java
+++ b/core/src/main/java/org/teavm/runtime/LaxMalloc.java
@@ -35,519 +35,519 @@ import org.teavm.interop.Unmanaged;
@StaticInit
public final class LaxMalloc {
- private LaxMalloc() {
- }
+ private LaxMalloc() {
+ }
- private static final int SIZEOF_PTR = 4;
- private static final int SIZEOF_PTR_SH = 2;
+ private static final int SIZEOF_PTR = 4;
+ private static final int SIZEOF_PTR_SH = 2;
- private static final int MIN_ALLOC_SIZE = 8;
+ private static final int MIN_ALLOC_SIZE = 8;
- private static final int ADDR_HEAP_OUTER_LIMIT = 0; // Address where we store the WebAssembly.Memory limit (32 bit int)
- private static final int ADDR_HEAP_INNER_LIMIT = 4; // Address where we store the current heap limit (32 bit int)
- private static final int ADDR_HEAP_BUCKETS_FREE_MASK = 8; // Address where we store the bitmask of free chunk lists (64 bit int)
- 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_OUTER_LIMIT = 0; // Address where we store the WebAssembly.Memory limit (32 bit int)
+ private static final int ADDR_HEAP_INNER_LIMIT = 4; // Address where we store the current heap limit (32 bit int)
+ private static final int ADDR_HEAP_BUCKETS_FREE_MASK = 8; // Address where we store the bitmask of free chunk lists (64 bit int)
+ 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_INNER_LIMIT).putInt(ADDR_HEAP_DATA_START);
- Address.fromInt(ADDR_HEAP_OUTER_LIMIT).putInt(0x10000);
- }
+ static {
+ // zero out the control region
+ Allocator.fillZero(Address.fromInt(0), ADDR_HEAP_DATA_START);
+ // initialize heap limit
+ Address.fromInt(ADDR_HEAP_INNER_LIMIT).putInt(ADDR_HEAP_DATA_START);
+ Address.fromInt(ADDR_HEAP_OUTER_LIMIT).putInt(0x10000);
+ }
- /**
- * malloc implementation
- */
- public static Address laxMalloc(int sizeBytes) {
- return laxAlloc(sizeBytes, false);
- }
+ /**
+ * malloc implementation
+ */
+ public static Address laxMalloc(int sizeBytes) {
+ return laxAlloc(sizeBytes, false);
+ }
- /**
- * calloc implementation (zeroed malloc)
- */
- public static Address laxCalloc(int sizeBytes) {
- return laxAlloc(sizeBytes, true);
- }
+ /**
+ * calloc implementation (zeroed malloc)
+ */
+ public static Address laxCalloc(int sizeBytes) {
+ return laxAlloc(sizeBytes, true);
+ }
- private static Address laxAlloc(int sizeBytes, boolean cleared) {
- if(sizeBytes <= 0) {
- // Produce a null pointer if 0 or invalid size is requested
- return Address.fromInt(0);
- }
-
- // Allocation must be large enough to hold the two list pointers when the chunk becomes free again
- if(sizeBytes < MIN_ALLOC_SIZE) {
- sizeBytes = MIN_ALLOC_SIZE;
- }
-
- // Make sure all allocations are at least a multiple of 4 to maintain alignment
- sizeBytes = (sizeBytes + 3) & 0xFFFFFFFC;
-
- // always between 0-63
- int bucket = getListBucket(sizeBytes);
-
- if(bucket == 63) {
- // special bucket for the huge allocations
- // uses a different slower function
- return laxHugeAlloc(sizeBytes, cleared);
- }
-
- // load bitmask of buckets with free chunks
- long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
-
- // mask away the buckets that we know are too small for this allocation
- bucketMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << bucket));
-
- // there are no more buckets with free chunks
- // need to sbrk
- if(bucketMask == 0l) {
- int sizePlusInts = sizeBytes + 8; // size + 2 ints
- Address newChunk = growHeap(sizePlusInts); // sbrk
-
- // Out of memory
- if(newChunk.toInt() == 0) {
- return Address.fromInt(0); //TODO
- }
-
- // provision the new chunk
- newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
- 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 memory
- return newChunk.add(4);
- }
-
- // at least one bucket exists containing a free chunk,
- // quickly determine which bucket it is with bit hacks
- int availableBucket = numberOfTrailingZerosL(bucketMask);
-
- Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(availableBucket << SIZEOF_PTR_SH);
- Address chunkPtr = bucketStartAddr.getAddress();
- int chunkSize = readChunkSizeStatus(chunkPtr);
- Address itrChunkStart = Address.fromInt(0);
-
- // check if the first chunk in the bucket is large enough
- if(chunkSize - 8 < sizeBytes) { // size - 2 ints
-
- // the chunk is not large enough, move the first chunk to the end of the list
- // and then check in the next bucket (where the chunks are definitely large enough)
- // this functionality is present in emmalloc (emscripten)
-
- Address chunkNextPtr = readChunkNextFreeAddr(chunkPtr);
- if(chunkNextPtr.getInt() != chunkPtr.getInt()) {
- bucketStartAddr.putAddress(chunkNextPtr);
- itrChunkStart = chunkNextPtr;
- }
-
- // extend mask to the next bucket
- bucketMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << (bucket + 1)));
-
- if(bucketMask != 0l) {
- // there is a bucket with a larger chunk
- int availableLargerBucket = numberOfTrailingZerosL(bucketMask);
- Address largerBucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(availableLargerBucket << SIZEOF_PTR_SH);
- Address largerChunkPtr = largerBucketStartAddr.getAddress();
- int largerChunkSize = readChunkSizeStatus(largerChunkPtr);
-
- // this will remove the chunk from the free list
- allocateMemoryFromChunk(largerChunkPtr, largerChunkSize, sizeBytes);
-
- // +4 bytes to skip size int
- Address ret = largerChunkPtr.add(4);
-
- // clear if requested
- if(cleared) {
- Allocator.fillZero(ret, sizeBytes);
- }
-
- return ret;
- }
- }else {
- // the first chunk in the bucket is large enough
- // this will remove the chunk from the free list
- allocateMemoryFromChunk(chunkPtr, chunkSize, sizeBytes);
-
- // +4 bytes to skip size int
- Address ret = chunkPtr.add(4);
-
- // clear if requested
- if(cleared) {
- Allocator.fillZero(ret, sizeBytes);
- }
-
- return ret;
- }
-
- if(itrChunkStart.toInt() != 0) {
-
- // if we've reached this point, it means the first chunk in the bucket wasn't large enough
- // and there weren't any chunks in the larger buckets we could split up
- // so we need to look closer
-
- // iterate the (only) bucket of possibly large enough chunks
- Address addrIterator = itrChunkStart;
- do {
- chunkSize = readChunkSizeStatus(addrIterator);
-
- // check if the chunk is large enough
- if(chunkSize - 8 >= sizeBytes) { // size - 2 ints
- // we've found a large enough chunk
- // this will remove the chunk from the free list
- allocateMemoryFromChunk(addrIterator, chunkSize, sizeBytes);
-
- // +4 bytes to skip size int
- Address ret = chunkPtr.add(4);
-
- // clear if requested
- if(cleared) {
- Allocator.fillZero(ret, sizeBytes);
- }
-
- return ret;
- }
- }while((addrIterator = readChunkNextFreeAddr(addrIterator)).getInt() != chunkPtr.getInt());
- }
-
- // no other options, time to sbrk
-
- int sizePlusInts = sizeBytes + 8; // size + 2 ints
- Address newChunk = growHeap(sizePlusInts); // sbrk
-
- // Out of memory
- if(newChunk.toInt() == 0) {
- return Address.fromInt(0); //TODO
- }
-
- // provision the new chunk
- newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
- 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 memory
- return newChunk.add(4);
- }
+ private static Address laxAlloc(int sizeBytes, boolean cleared) {
+ if(sizeBytes <= 0) {
+ // Produce a null pointer if 0 or invalid size is requested
+ return Address.fromInt(0);
+ }
+
+ // Allocation must be large enough to hold the two list pointers when the chunk becomes free again
+ if(sizeBytes < MIN_ALLOC_SIZE) {
+ sizeBytes = MIN_ALLOC_SIZE;
+ }
+
+ // Make sure all allocations are at least a multiple of 4 to maintain alignment
+ sizeBytes = (sizeBytes + 3) & 0xFFFFFFFC;
+
+ // always between 0-63
+ int bucket = getListBucket(sizeBytes);
+
+ if(bucket == 63) {
+ // special bucket for the huge allocations
+ // uses a different slower function
+ return laxHugeAlloc(sizeBytes, cleared);
+ }
+
+ // load bitmask of buckets with free chunks
+ long bucketMask = Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
+
+ // mask away the buckets that we know are too small for this allocation
+ bucketMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << bucket));
+
+ // there are no more buckets with free chunks
+ // need to sbrk
+ if(bucketMask == 0l) {
+ int sizePlusInts = sizeBytes + 8; // size + 2 ints
+ Address newChunk = growHeap(sizePlusInts); // sbrk
+
+ // Out of memory
+ if(newChunk.toInt() == 0) {
+ return Address.fromInt(0); //TODO
+ }
+
+ // provision the new chunk
+ newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
+ 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 memory
+ return newChunk.add(4);
+ }
+
+ // at least one bucket exists containing a free chunk,
+ // quickly determine which bucket it is with bit hacks
+ int availableBucket = numberOfTrailingZerosL(bucketMask);
+
+ Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(availableBucket << SIZEOF_PTR_SH);
+ Address chunkPtr = bucketStartAddr.getAddress();
+ int chunkSize = readChunkSizeStatus(chunkPtr);
+ Address itrChunkStart = Address.fromInt(0);
+
+ // check if the first chunk in the bucket is large enough
+ if(chunkSize - 8 < sizeBytes) { // size - 2 ints
+
+ // the chunk is not large enough, move the first chunk to the end of the list
+ // and then check in the next bucket (where the chunks are definitely large enough)
+ // this functionality is present in emmalloc (emscripten)
+
+ Address chunkNextPtr = readChunkNextFreeAddr(chunkPtr);
+ if(chunkNextPtr.getInt() != chunkPtr.getInt()) {
+ bucketStartAddr.putAddress(chunkNextPtr);
+ itrChunkStart = chunkNextPtr;
+ }
+
+ // extend mask to the next bucket
+ bucketMask = (bucketMask & (0xFFFFFFFFFFFFFFFFL << (bucket + 1)));
+
+ if(bucketMask != 0l) {
+ // there is a bucket with a larger chunk
+ int availableLargerBucket = numberOfTrailingZerosL(bucketMask);
+ Address largerBucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(availableLargerBucket << SIZEOF_PTR_SH);
+ Address largerChunkPtr = largerBucketStartAddr.getAddress();
+ int largerChunkSize = readChunkSizeStatus(largerChunkPtr);
+
+ // this will remove the chunk from the free list
+ allocateMemoryFromChunk(largerChunkPtr, largerChunkSize, sizeBytes);
+
+ // +4 bytes to skip size int
+ Address ret = largerChunkPtr.add(4);
+
+ // clear if requested
+ if(cleared) {
+ Allocator.fillZero(ret, sizeBytes);
+ }
+
+ return ret;
+ }
+ }else {
+ // the first chunk in the bucket is large enough
+ // this will remove the chunk from the free list
+ allocateMemoryFromChunk(chunkPtr, chunkSize, sizeBytes);
+
+ // +4 bytes to skip size int
+ Address ret = chunkPtr.add(4);
+
+ // clear if requested
+ if(cleared) {
+ Allocator.fillZero(ret, sizeBytes);
+ }
+
+ return ret;
+ }
+
+ if(itrChunkStart.toInt() != 0) {
+
+ // if we've reached this point, it means the first chunk in the bucket wasn't large enough
+ // and there weren't any chunks in the larger buckets we could split up
+ // so we need to look closer
+
+ // iterate the (only) bucket of possibly large enough chunks
+ Address addrIterator = itrChunkStart;
+ do {
+ chunkSize = readChunkSizeStatus(addrIterator);
+
+ // check if the chunk is large enough
+ if(chunkSize - 8 >= sizeBytes) { // size - 2 ints
+ // we've found a large enough chunk
+ // this will remove the chunk from the free list
+ allocateMemoryFromChunk(addrIterator, chunkSize, sizeBytes);
+
+ // +4 bytes to skip size int
+ Address ret = chunkPtr.add(4);
+
+ // clear if requested
+ if(cleared) {
+ Allocator.fillZero(ret, sizeBytes);
+ }
+
+ return ret;
+ }
+ }while((addrIterator = readChunkNextFreeAddr(addrIterator)).getInt() != chunkPtr.getInt());
+ }
+
+ // no other options, time to sbrk
+
+ int sizePlusInts = sizeBytes + 8; // size + 2 ints
+ Address newChunk = growHeap(sizePlusInts); // sbrk
+
+ // Out of memory
+ if(newChunk.toInt() == 0) {
+ return Address.fromInt(0); //TODO
+ }
+
+ // provision the new chunk
+ newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
+ 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 memory
+ return newChunk.add(4);
+ }
- private static Address laxHugeAlloc(int sizeBytes, boolean cleared) {
-
- // check the bucket mask if bucket 63 has any chunks
- if((Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong() & 0x8000000000000000L) != 0) {
+ private static Address laxHugeAlloc(int sizeBytes, boolean cleared) {
+
+ // check the bucket mask if bucket 63 has any chunks
+ if((Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).getLong() & 0x8000000000000000L) != 0) {
- // bucket 63 address
- Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(63 << SIZEOF_PTR_SH);
- Address chunkPtr = bucketStartAddr.getAddress();
-
- // iterate all free huge chunks
- Address addrIterator = chunkPtr;
- do {
- int chunkSize = readChunkSizeStatus(addrIterator);
-
- if(chunkSize - 8 >= sizeBytes) { // size - 2 ints
- // we've found a large enough chunk
- // this will remove the chunk from the free list
- allocateMemoryFromChunk(addrIterator, chunkSize, sizeBytes);
-
- // +4 bytes to skip size int
- Address ret = chunkPtr.add(4);
-
- // clear if requested
- if(cleared) {
- Allocator.fillZero(ret, sizeBytes);
- }
-
- return ret;
- }
- }while((addrIterator = readChunkNextFreeAddr(addrIterator)).getInt() != chunkPtr.getInt());
- }
-
- // no free huge chunks found, time to sbrk
-
- int sizePlusInts = sizeBytes + 8; // size + 2 ints
- Address newChunk = growHeap(sizePlusInts); // sbrk
-
- // Out of memory
- if(newChunk.toInt() == 0) {
- return Address.fromInt(0); //TODO
- }
-
- // provision the new chunk
- newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
- 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 memory
- return newChunk.add(4);
- }
+ // bucket 63 address
+ Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(63 << SIZEOF_PTR_SH);
+ Address chunkPtr = bucketStartAddr.getAddress();
+
+ // iterate all free huge chunks
+ Address addrIterator = chunkPtr;
+ do {
+ int chunkSize = readChunkSizeStatus(addrIterator);
+
+ if(chunkSize - 8 >= sizeBytes) { // size - 2 ints
+ // we've found a large enough chunk
+ // this will remove the chunk from the free list
+ allocateMemoryFromChunk(addrIterator, chunkSize, sizeBytes);
+
+ // +4 bytes to skip size int
+ Address ret = chunkPtr.add(4);
+
+ // clear if requested
+ if(cleared) {
+ Allocator.fillZero(ret, sizeBytes);
+ }
+
+ return ret;
+ }
+ }while((addrIterator = readChunkNextFreeAddr(addrIterator)).getInt() != chunkPtr.getInt());
+ }
+
+ // no free huge chunks found, time to sbrk
+
+ int sizePlusInts = sizeBytes + 8; // size + 2 ints
+ Address newChunk = growHeap(sizePlusInts); // sbrk
+
+ // Out of memory
+ if(newChunk.toInt() == 0) {
+ return Address.fromInt(0); //TODO
+ }
+
+ // provision the new chunk
+ newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
+ 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 memory
+ return newChunk.add(4);
+ }
- /**
- * free implementation
- *
- * bad things will happen if you free an address that was never allocated
- */
- public static void laxFree(Address address) {
- if(address.toInt() == 0) {
- return;
- }
-
- // chunk actually starts 4 bytes before
- Address chunkPtr = address.add(-4);
-
- // bring the size of the chunk into the stack
- int chunkSize = chunkPtr.getInt();
- boolean sizeChanged = false;
-
- // set the chunk no longer in use
- chunkSize &= 0x7FFFFFFF;
-
- if(Address.fromInt(ADDR_HEAP_DATA_START).isLessThan(chunkPtr)) {
- // check if we can merge with the previous chunk, and move it to another bucket
- Address prevChunkPtr = chunkPtr.add(-(chunkPtr.add(-4).getInt()));
- int prevChunkSize = readChunkSizeStatus(prevChunkPtr);
- if((prevChunkSize & 0x80000000) == 0) {
- // previous chunk is not in use, merge!
-
- // remove the previous chunk from its list
- unlinkChunkFromFreeList(prevChunkPtr, prevChunkSize);
-
- // resize the current chunk to also contain the previous chunk
- chunkPtr = prevChunkPtr;
- chunkSize += prevChunkSize;
- sizeChanged = true;
- }
- }
-
- Address nextChunkPtr = chunkPtr.add(chunkSize);
- if(Address.fromInt(ADDR_HEAP_INNER_LIMIT).getAddress().isLessThan(nextChunkPtr)) {
- // check if we can merge with the next chunk as well
- int nextChunkSize = readChunkSizeStatus(nextChunkPtr);
- if((nextChunkSize & 0x80000000) == 0) {
- // next chunk is not in use, merge!
+ /**
+ * free implementation
+ *
+ * bad things will happen if you free an address that was never allocated
+ */
+ public static void laxFree(Address address) {
+ if(address.toInt() == 0) {
+ return;
+ }
+
+ // chunk actually starts 4 bytes before
+ Address chunkPtr = address.add(-4);
+
+ // bring the size of the chunk into the stack
+ int chunkSize = chunkPtr.getInt();
+ boolean sizeChanged = false;
+
+ // set the chunk no longer in use
+ chunkSize &= 0x7FFFFFFF;
+
+ if(Address.fromInt(ADDR_HEAP_DATA_START).isLessThan(chunkPtr)) {
+ // check if we can merge with the previous chunk, and move it to another bucket
+ Address prevChunkPtr = chunkPtr.add(-(chunkPtr.add(-4).getInt()));
+ int prevChunkSize = readChunkSizeStatus(prevChunkPtr);
+ if((prevChunkSize & 0x80000000) == 0) {
+ // previous chunk is not in use, merge!
+
+ // remove the previous chunk from its list
+ unlinkChunkFromFreeList(prevChunkPtr, prevChunkSize);
+
+ // resize the current chunk to also contain the previous chunk
+ chunkPtr = prevChunkPtr;
+ chunkSize += prevChunkSize;
+ sizeChanged = true;
+ }
+ }
+
+ Address nextChunkPtr = chunkPtr.add(chunkSize);
+ if(Address.fromInt(ADDR_HEAP_INNER_LIMIT).getAddress().isLessThan(nextChunkPtr)) {
+ // check if we can merge with the next chunk as well
+ 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);
-
- // resize the current chunk to also contain the next chunk
- chunkSize += nextChunkSize;
- sizeChanged = true;
- }
- }
-
- // store the final chunk size (also clears the in use flag)
- chunkPtr.putInt(chunkSize);
-
- if(sizeChanged) {
- // if the size of the chunk changed, we also need to update the chunk's second size integer
- chunkPtr.add(chunkSize - 4).putInt(chunkSize);
- }
-
- // add the final chunk to the free chunks list
- linkChunkInFreeList(chunkPtr, chunkSize);
- }
+ // remove the next chunk from its list
+ unlinkChunkFromFreeList(nextChunkPtr, nextChunkSize);
+
+ // resize the current chunk to also contain the next chunk
+ chunkSize += nextChunkSize;
+ sizeChanged = true;
+ }
+ }
+
+ // store the final chunk size (also clears the in use flag)
+ chunkPtr.putInt(chunkSize);
+
+ if(sizeChanged) {
+ // if the size of the chunk changed, we also need to update the chunk's second size integer
+ chunkPtr.add(chunkSize - 4).putInt(chunkSize);
+ }
+
+ // add the final chunk to the free chunks list
+ linkChunkInFreeList(chunkPtr, chunkSize);
+ }
- /**
- * Allocates memory from a free chunk, if the allocSize is smaller than the chunkSize by
- * enough of a margin then the chunk is split into two smaller chunks, and the upper part
- * of the chunk is returned to a bucket of free chunks
- */
- private static void allocateMemoryFromChunk(Address chunkPtr, int chunkSize, int allocSize) {
- // remove the chunk from its bucket
- unlinkChunkFromFreeList(chunkPtr, chunkSize);
-
- int otherHalfSize = chunkSize - allocSize - 8; // -size - 2 ints
-
- // check if we can split the chunk into two smaller chunks
- // chunk must be large enough to hold the 2 list pointers
- if(otherHalfSize - (2 << SIZEOF_PTR_SH) >= MIN_ALLOC_SIZE) {
- // chunk is large enough to split
-
- // provision the lower part of the chunk, the part we want to use
- int sizePlusInts = allocSize + 8; // size + 2 ints
- chunkPtr.putInt(sizePlusInts | 0x80000000); // size + in use flag
- chunkPtr.add(allocSize + 4).putInt(sizePlusInts); // size integer at the end
-
- // provision the upper part of the chunk that we want to return to the free list
- Address otherChunkPtr = chunkPtr.add(sizePlusInts);
- otherChunkPtr.putInt(otherHalfSize); // size
- otherChunkPtr.add(otherHalfSize - 4).putInt(otherHalfSize); // size (end)
+ /**
+ * Allocates memory from a free chunk, if the allocSize is smaller than the chunkSize by
+ * enough of a margin then the chunk is split into two smaller chunks, and the upper part
+ * of the chunk is returned to a bucket of free chunks
+ */
+ private static void allocateMemoryFromChunk(Address chunkPtr, int chunkSize, int allocSize) {
+ // remove the chunk from its bucket
+ unlinkChunkFromFreeList(chunkPtr, chunkSize);
+
+ int otherHalfSize = chunkSize - allocSize - 8; // -size - 2 ints
+
+ // check if we can split the chunk into two smaller chunks
+ // chunk must be large enough to hold the 2 list pointers
+ if(otherHalfSize - (2 << SIZEOF_PTR_SH) >= MIN_ALLOC_SIZE) {
+ // chunk is large enough to split
+
+ // provision the lower part of the chunk, the part we want to use
+ int sizePlusInts = allocSize + 8; // size + 2 ints
+ chunkPtr.putInt(sizePlusInts | 0x80000000); // size + in use flag
+ chunkPtr.add(allocSize + 4).putInt(sizePlusInts); // size integer at the end
+
+ // provision the upper part of the chunk that we want to return to the free list
+ Address otherChunkPtr = chunkPtr.add(sizePlusInts);
+ otherChunkPtr.putInt(otherHalfSize); // size
+ otherChunkPtr.add(otherHalfSize - 4).putInt(otherHalfSize); // size (end)
- // return the upper part of the chunk to the free chunks list
- linkChunkInFreeList(otherChunkPtr, otherHalfSize);
+ // return the upper part of the chunk to the free chunks list
+ linkChunkInFreeList(otherChunkPtr, otherHalfSize);
- }else {
- // not large enough to split, just take the entire chunk
- chunkPtr.putInt(chunkSize | 0x80000000); // sets the in use flag
- }
- }
+ }else {
+ // not large enough to split, just take the entire chunk
+ chunkPtr.putInt(chunkSize | 0x80000000); // sets the in use flag
+ }
+ }
- /**
- * Adds a free chunk to its corresponding bucket
- */
- 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();
- Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(bucket << SIZEOF_PTR_SH);
-
- // test the bucket mask if the bucket is empty
- if((bucketMask & (1L << bucket)) == 0l) {
-
- // bucket is empty, add the free chunk to the list
- bucketStartAddr.putAddress(chunkPtr);
- writeChunkPrevFreeAddr(chunkPtr, chunkPtr);
- writeChunkNextFreeAddr(chunkPtr, chunkPtr);
-
- // set the free bit in bucket mask
- bucketMask |= (1L << bucket);
- Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).putLong(bucketMask);
-
- }else {
-
- // bucket is not empty, append to the bucket's existing free chunks list
- Address otherBucketStart = bucketStartAddr.getAddress();
- Address otherBucketPrev = readChunkPrevFreeAddr(otherBucketStart);
-
- // link new chunk to the existing chunks in the bucket
- writeChunkPrevFreeAddr(chunkPtr, otherBucketPrev);
- writeChunkNextFreeAddr(chunkPtr, otherBucketStart);
-
- // link the existing chunks in the bucket to the new chunk
- writeChunkPrevFreeAddr(otherBucketStart, chunkPtr);
- writeChunkNextFreeAddr(otherBucketPrev, chunkPtr);
-
- // put the chunk in the bucket
- bucketStartAddr.putAddress(chunkPtr);
-
- }
- }
+ /**
+ * Adds a free chunk to its corresponding bucket
+ */
+ 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();
+ Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(bucket << SIZEOF_PTR_SH);
+
+ // test the bucket mask if the bucket is empty
+ if((bucketMask & (1L << bucket)) == 0l) {
+
+ // bucket is empty, add the free chunk to the list
+ bucketStartAddr.putAddress(chunkPtr);
+ writeChunkPrevFreeAddr(chunkPtr, chunkPtr);
+ writeChunkNextFreeAddr(chunkPtr, chunkPtr);
+
+ // set the free bit in bucket mask
+ bucketMask |= (1L << bucket);
+ Address.fromInt(ADDR_HEAP_BUCKETS_FREE_MASK).putLong(bucketMask);
+
+ }else {
+
+ // bucket is not empty, append to the bucket's existing free chunks list
+ Address otherBucketStart = bucketStartAddr.getAddress();
+ Address otherBucketPrev = readChunkPrevFreeAddr(otherBucketStart);
+
+ // link new chunk to the existing chunks in the bucket
+ writeChunkPrevFreeAddr(chunkPtr, otherBucketPrev);
+ writeChunkNextFreeAddr(chunkPtr, otherBucketStart);
+
+ // link the existing chunks in the bucket to the new chunk
+ writeChunkPrevFreeAddr(otherBucketStart, chunkPtr);
+ writeChunkNextFreeAddr(otherBucketPrev, chunkPtr);
+
+ // put the chunk in the bucket
+ bucketStartAddr.putAddress(chunkPtr);
+
+ }
+ }
- /**
- * Removes a free chunk from its corresponding bucket
- */
- private static void unlinkChunkFromFreeList(Address chunkPtr, int chunkSize) {
- Address prevChunkPtr = readChunkPrevFreeAddr(chunkPtr);
- Address nextChunkPtr = readChunkNextFreeAddr(chunkPtr);
- if(prevChunkPtr.toInt() == nextChunkPtr.toInt()) {
- // 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
-
- // link the next chunk to the previous chunk
- writeChunkNextFreeAddr(prevChunkPtr, nextChunkPtr);
- writeChunkPrevFreeAddr(nextChunkPtr, prevChunkPtr);
-
- int chunkBucket = getListBucket(chunkSize - 8); // size - 2 ints
- Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(chunkBucket << SIZEOF_PTR_SH);
- Address bucketStartChunk = bucketStartAddr.getAddress();
-
- // chunk is the first in the bucket, so we also need to
- // update the bucket to point to the next chunk instead
- if(bucketStartChunk.toInt() == chunkPtr.toInt()) {
- bucketStartAddr.putAddress(nextChunkPtr);
- }
- }
- }
+ /**
+ * Removes a free chunk from its corresponding bucket
+ */
+ private static void unlinkChunkFromFreeList(Address chunkPtr, int chunkSize) {
+ Address prevChunkPtr = readChunkPrevFreeAddr(chunkPtr);
+ Address nextChunkPtr = readChunkNextFreeAddr(chunkPtr);
+ if(prevChunkPtr.toInt() == nextChunkPtr.toInt()) {
+ // 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
+
+ // link the next chunk to the previous chunk
+ writeChunkNextFreeAddr(prevChunkPtr, nextChunkPtr);
+ writeChunkPrevFreeAddr(nextChunkPtr, prevChunkPtr);
+
+ int chunkBucket = getListBucket(chunkSize - 8); // size - 2 ints
+ Address bucketStartAddr = Address.fromInt(ADDR_HEAP_BUCKETS_START).add(chunkBucket << SIZEOF_PTR_SH);
+ Address bucketStartChunk = bucketStartAddr.getAddress();
+
+ // chunk is the first in the bucket, so we also need to
+ // update the bucket to point to the next chunk instead
+ if(bucketStartChunk.toInt() == chunkPtr.toInt()) {
+ bucketStartAddr.putAddress(nextChunkPtr);
+ }
+ }
+ }
- /**
- * https://github.com/emscripten-core/emscripten/blob/16a0bf174cb85f88b6d9dcc8ee7fbca59390185b/system/lib/emmalloc.c#L241
- * (MIT License)
- */
- private static int getListBucket(int allocSize) {
- if (allocSize < 128)
- return (allocSize >> 3) - 1;
-
- int clz = numberOfLeadingZerosI(allocSize);
- int bucketIndex = (clz > 19) ? 110 - (clz << 2) + ((allocSize >> (29 - clz)) ^ 4)
- : min(71 - (clz << 1) + ((allocSize >> (30 - clz)) ^ 2), 63);
+ /**
+ * https://github.com/emscripten-core/emscripten/blob/16a0bf174cb85f88b6d9dcc8ee7fbca59390185b/system/lib/emmalloc.c#L241
+ * (MIT License)
+ */
+ private static int getListBucket(int allocSize) {
+ if (allocSize < 128)
+ return (allocSize >> 3) - 1;
+
+ int clz = numberOfLeadingZerosI(allocSize);
+ int bucketIndex = (clz > 19) ? 110 - (clz << 2) + ((allocSize >> (29 - clz)) ^ 4)
+ : min(71 - (clz << 1) + ((allocSize >> (30 - clz)) ^ 2), 63);
+
+ return bucketIndex;
+ }
- return bucketIndex;
- }
+ /**
+ * This is our sbrk
+ */
+ private static Address growHeap(int amount) {
+ Address heapInnerLimit = Address.fromInt(ADDR_HEAP_INNER_LIMIT).getAddress();
+ Address heapOuterLimit = Address.fromInt(ADDR_HEAP_OUTER_LIMIT).getAddress();
+ Address newHeapInnerLimit = heapInnerLimit.add(amount);
+ if(heapOuterLimit.isLessThan(newHeapInnerLimit)) {
+ int bytesNeeded = newHeapInnerLimit.toInt() - heapOuterLimit.toInt();
+ bytesNeeded = (bytesNeeded + 0xFFFF) & 0xFFFF0000;
+ if(growHeapOuter(bytesNeeded)) {
+ Address.fromInt(ADDR_HEAP_INNER_LIMIT).putAddress(newHeapInnerLimit);
+ Address.fromInt(ADDR_HEAP_OUTER_LIMIT).putAddress(heapOuterLimit.add(bytesNeeded));
+ return newHeapInnerLimit;
+ }else {
+ return Address.fromInt(0);
+ }
+ }else {
+ Address.fromInt(ADDR_HEAP_INNER_LIMIT).putAddress(newHeapInnerLimit);
+ return newHeapInnerLimit;
+ }
+ }
- /**
- * This is our sbrk
- */
- private static Address growHeap(int amount) {
- Address heapInnerLimit = Address.fromInt(ADDR_HEAP_INNER_LIMIT).getAddress();
- Address heapOuterLimit = Address.fromInt(ADDR_HEAP_OUTER_LIMIT).getAddress();
- Address newHeapInnerLimit = heapInnerLimit.add(amount);
- if(heapOuterLimit.isLessThan(newHeapInnerLimit)) {
- int bytesNeeded = newHeapInnerLimit.toInt() - heapOuterLimit.toInt();
- bytesNeeded = (bytesNeeded + 0xFFFF) & 0xFFFF0000;
- if(growHeapOuter(bytesNeeded)) {
- Address.fromInt(ADDR_HEAP_INNER_LIMIT).putAddress(newHeapInnerLimit);
- Address.fromInt(ADDR_HEAP_OUTER_LIMIT).putAddress(heapOuterLimit.add(bytesNeeded));
- return newHeapInnerLimit;
- }else {
- return Address.fromInt(0);
- }
- }else {
- Address.fromInt(ADDR_HEAP_INNER_LIMIT).putAddress(newHeapInnerLimit);
- return newHeapInnerLimit;
- }
- }
+ @Import(name = "teavm_growHeap")
+ private static native boolean growHeapOuter(int bytes);
- @Import(name = "teavm_growHeap")
- private static native boolean growHeapOuter(int bytes);
+ /**
+ * Note that on a free chunk, this is the size, because the status bit is 0
+ */
+ private static int readChunkSizeStatus(Address chunkAddr) {
+ return chunkAddr.getInt();
+ }
- /**
- * Note that on a free chunk, this is the size, because the status bit is 0
- */
- private static int readChunkSizeStatus(Address chunkAddr) {
- return chunkAddr.getInt();
- }
+ private static int readChunkSize(Address chunkAddr) {
+ return chunkAddr.getInt() & 0x7FFFFFFF;
+ }
- private static int readChunkSize(Address chunkAddr) {
- return chunkAddr.getInt() & 0x7FFFFFFF;
- }
+ private static boolean readChunkInUse(Address chunkAddr) {
+ return (chunkAddr.getInt() & 0x80000000) != 0;
+ }
- private static boolean readChunkInUse(Address chunkAddr) {
- return (chunkAddr.getInt() & 0x80000000) != 0;
- }
+ private static void writeChunkSizeStatus(Address chunkAddr, int sizeStatus) {
+ chunkAddr.putInt(sizeStatus);
+ }
- private static void writeChunkSizeStatus(Address chunkAddr, int sizeStatus) {
- chunkAddr.putInt(sizeStatus);
- }
+ private static Address readChunkPrevFreeAddr(Address chunkAddr) {
+ return chunkAddr.add(4).getAddress();
+ }
- private static Address readChunkPrevFreeAddr(Address chunkAddr) {
- return chunkAddr.add(4).getAddress();
- }
+ private static void writeChunkPrevFreeAddr(Address chunkAddr, Address prevFree) {
+ chunkAddr.add(4).putAddress(prevFree);
+ }
- private static void writeChunkPrevFreeAddr(Address chunkAddr, Address prevFree) {
- chunkAddr.add(4).putAddress(prevFree);
- }
+ private static Address readChunkNextFreeAddr(Address chunkAddr) {
+ return chunkAddr.add(4 + (1 << SIZEOF_PTR_SH)).getAddress();
+ }
- private static Address readChunkNextFreeAddr(Address chunkAddr) {
- return chunkAddr.add(4 + (1 << SIZEOF_PTR_SH)).getAddress();
- }
+ private static void writeChunkNextFreeAddr(Address chunkAddr, Address nextFree) {
+ chunkAddr.add(4 + (1 << SIZEOF_PTR_SH)).putAddress(nextFree);
+ }
- private static void writeChunkNextFreeAddr(Address chunkAddr, Address nextFree) {
- chunkAddr.add(4 + (1 << SIZEOF_PTR_SH)).putAddress(nextFree);
- }
+ private static int min(int a, int b) {
+ return a < b ? a : b;
+ }
- private static int min(int a, int 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 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);
- }
+ private static int numberOfLeadingZerosI(int i) {
+ // TODO: Intrinsify this, WASM has dedicated instructions for this operation!!!
+ return Integer.numberOfLeadingZeros(i);
+ }
}