diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index ec4d469da..94a6e92fe 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -321,16 +321,14 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { customGenerators.contributeToModule(module); generateExceptionExports(declarationsGenerator); if (enableDirectMallocSupport) { - var heapSegment = new WasmMemorySegment(); + var heapSegmentStart = 0; if (!module.getSegments().isEmpty()) { var lastSegment = module.getSegments().get(module.getSegments().size() - 1); - heapSegment.setOffset(WasmRuntime.align(lastSegment.getOffset() - + lastSegment.getLength(), WasmHeap.PAGE_SIZE)); + heapSegmentStart = WasmRuntime.align(lastSegment.getOffset() + + lastSegment.getLength(), WasmHeap.PAGE_SIZE); } - heapSegment.setLength(WasmHeap.PAGE_SIZE); - module.getSegments().add(heapSegment); - intrinsics.setupLaxMallocHeap(heapSegment.getOffset(), heapSegment.getOffset() + directMallocMinHeapSize, - heapSegment.getOffset() + directMallocMaxHeapSize); + intrinsics.setupLaxMallocHeap(heapSegmentStart, heapSegmentStart + directMallocMinHeapSize, + heapSegmentStart + directMallocMaxHeapSize); } adjustModuleMemory(module); @@ -439,7 +437,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { if (enableDirectMallocSupport) { var minPages = (memorySize - 1) / WasmHeap.PAGE_SIZE + 1; - var maxPages = (memorySize + directMallocMaxHeapSize - WasmHeap.PAGE_SIZE - 1) / WasmHeap.PAGE_SIZE + 1; + var maxPages = minPages + (directMallocMaxHeapSize - 1) / WasmHeap.PAGE_SIZE + 1; module.setMinMemorySize(minPages); module.setMaxMemorySize(maxPages); } else { diff --git a/core/src/main/java/org/teavm/runtime/LaxMalloc.java b/core/src/main/java/org/teavm/runtime/LaxMalloc.java index deffaffe6..4f432e270 100644 --- a/core/src/main/java/org/teavm/runtime/LaxMalloc.java +++ b/core/src/main/java/org/teavm/runtime/LaxMalloc.java @@ -76,17 +76,19 @@ public final class LaxMalloc { private static native void notifyHeapResized(); static { + int initialGrowAmount = getHeapMinAddr().toInt() >>> 16; + if (growHeapOuter(initialGrowAmount) == -1) { + initialGrowAmount = 1; + if (growHeapOuter(initialGrowAmount) == -1) { + //TODO: Handle failure to initialize fallback 64KiB heap + } + } + // zero out the control region DirectMalloc.zmemset(addrHeap(0), ADDR_HEAP_DATA_START); // initialize heap limit addrHeap(ADDR_HEAP_INNER_LIMIT).putAddress(addrHeap(ADDR_HEAP_DATA_START)); - addrHeap(ADDR_HEAP_OUTER_LIMIT).putAddress(getHeapMinAddr()); - - int initialGrowAmount = getHeapMinAddr().toInt() >>> 16 - 1; - if (initialGrowAmount > 0) { - //TODO: Need to handle error setting the heap to its initial size - growHeapOuter(initialGrowAmount); - } + addrHeap(ADDR_HEAP_OUTER_LIMIT).putAddress(addrHeap(initialGrowAmount << 16)); } /** @@ -136,7 +138,7 @@ public final class LaxMalloc { // need to sbrk if (bucketMask == 0L) { int sizePlusInts = sizeBytes + 8; // size + 2 ints - Address newChunk = growHeap(sizePlusInts); // sbrk + Address newChunk = growLastChunk(sizePlusInts); // Out of memory if (newChunk.toInt() == 0) { @@ -248,7 +250,7 @@ public final class LaxMalloc { // no other options, time to sbrk int sizePlusInts = sizeBytes + 8; // size + 2 ints - Address newChunk = growHeap(sizePlusInts); // sbrk + Address newChunk = growLastChunk(sizePlusInts); // Out of memory if (newChunk.toInt() == 0) { @@ -300,7 +302,7 @@ public final class LaxMalloc { // no free huge chunks found, time to sbrk int sizePlusInts = sizeBytes + 8; // size + 2 ints - Address newChunk = growHeap(sizePlusInts); // sbrk + Address newChunk = growLastChunk(sizePlusInts); // Out of memory if (newChunk.toInt() == 0) { @@ -514,6 +516,46 @@ public final class LaxMalloc { return bucketIndex; } + /** + * Removes the last chunk from the heap (if free), then grows the heap by amount minus + * the length of the last chunk, this shouldn't be called unless the program has failed + * to find a free chunk that is larger than the requested amount + */ + private static Address growLastChunk(int amount) { + Address lastAddr = addrHeap(ADDR_HEAP_INNER_LIMIT).getAddress(); + + // make sure it doesn't crash if the heap is empty + if (!addrHeap(ADDR_HEAP_DATA_START).isLessThan(lastAddr)) { + return growHeap(amount); + } + + // get the length and address of the last chunk + int lastLen = lastAddr.add(-4).getInt(); + Address lastChunk = lastAddr.add(-lastLen); + lastLen = lastChunk.getInt(); + + // check if the last chunk is free + if ((lastLen & 0x80000000) == 0) { + + // chunk is free, attempt to resize the heap first + // so errors can be handled + if (growHeap(amount - lastLen).toInt() == 0) { + // out of memory + return Address.fromInt(0); + } + + // unlink last chunk from free list + unlinkChunkFromFreeList(lastChunk, lastLen); + + // return the start of the last chunk + return lastChunk; + } else { + // no free chunk at the end of the heap + // just grow the heap by the full amount + return growHeap(amount); + } + } + /** * This is our sbrk */