mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C/Wasm: resizable heap
This commit is contained in:
parent
f0b6cc2f30
commit
fe3436f053
|
@ -259,13 +259,18 @@ public final class TSystem extends TObject {
|
|||
}
|
||||
|
||||
public static long nanoTime() {
|
||||
if (PlatformDetector.isLowLevel()) {
|
||||
if (PlatformDetector.isWebAssembly()) {
|
||||
return (long) (nanoTimeWasm() * 1000000);
|
||||
} else if (PlatformDetector.isLowLevel()) {
|
||||
return nanoTimeLowLevel();
|
||||
} else {
|
||||
return (long) (Performance.now() * 1000000);
|
||||
}
|
||||
}
|
||||
|
||||
@Import(module = "teavm", name = "nanoTime")
|
||||
private static native double nanoTimeWasm();
|
||||
|
||||
@Import(name = "teavm_currentTimeNano")
|
||||
@Include("time.h")
|
||||
private static native long nanoTimeLowLevel();
|
||||
|
|
|
@ -151,7 +151,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private NullCheckInsertion nullCheckInsertion;
|
||||
private NullCheckTransformation nullCheckTransformation;
|
||||
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
||||
private int minHeapSize = 32 * 1024 * 1024;
|
||||
private int minHeapSize = 4 * 1024 * 1024;
|
||||
private int maxHeapSize = 128 * 1024 * 1024;
|
||||
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
||||
private List<GeneratorFactory> generatorFactories = new ArrayList<>();
|
||||
private Characteristics characteristics;
|
||||
|
@ -173,6 +174,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public void setMaxHeapSize(int maxHeapSize) {
|
||||
this.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
public void setIncremental(boolean incremental) {
|
||||
this.incremental = incremental;
|
||||
}
|
||||
|
@ -252,6 +257,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "fixHeap", void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "tryShrink", void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "collectGarbage", void.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
||||
Throwable.class, void.class)).use();
|
||||
|
@ -746,7 +753,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
writer.println("int " + mainFunctionName + "(int argc, char** argv) {").indent();
|
||||
|
||||
writer.println("teavm_beforeInit();");
|
||||
writer.println("teavm_initHeap(" + minHeapSize + ");");
|
||||
writer.println("teavm_initHeap(" + minHeapSize + ", " + maxHeapSize + ");");
|
||||
generateVirtualTableHeaders(context, writer);
|
||||
writer.println("teavm_initStringPool();");
|
||||
for (ValueType type : types) {
|
||||
|
|
|
@ -34,6 +34,9 @@ public class GCIntrinsic implements Intrinsic {
|
|||
case "regionMaxCount":
|
||||
case "availableBytes":
|
||||
case "regionSize":
|
||||
case "minAvailableBytes":
|
||||
case "maxAvailableBytes":
|
||||
case "resizeHeap":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -43,6 +46,13 @@ public class GCIntrinsic implements Intrinsic {
|
|||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
context.includes().includePath("memory.h");
|
||||
if (invocation.getMethod().getName().equals("resizeHeap")) {
|
||||
context.writer().print("teavm_gc_resizeHeap(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
return;
|
||||
}
|
||||
|
||||
context.includes().includePath("heaptrace.h");
|
||||
context.writer().print("teavm_gc_").print(invocation.getMethod().getName());
|
||||
}
|
||||
|
|
102
core/src/main/java/org/teavm/backend/wasm/WasmHeap.java
Normal file
102
core/src/main/java/org/teavm/backend/wasm/WasmHeap.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm;
|
||||
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
||||
@StaticInit
|
||||
@Unmanaged
|
||||
public final class WasmHeap {
|
||||
public static final int PAGE_SIZE = 65536;
|
||||
public static final int DEFAULT_STACK_SIZE = PAGE_SIZE * 4;
|
||||
public static final int DEFAULT_REGION_SIZE = 32768;
|
||||
|
||||
public static int minHeapSize;
|
||||
public static int maxHeapSize;
|
||||
public static Address storageAddress;
|
||||
public static int storageSize;
|
||||
public static Address regionsAddress;
|
||||
public static int regionsCount;
|
||||
public static int regionsSize;
|
||||
public static Address heapAddress;
|
||||
public static int heapSize;
|
||||
public static int regionSize = DEFAULT_REGION_SIZE;
|
||||
public static Address memoryLimit;
|
||||
public static Address stackAddress;
|
||||
public static Address stack;
|
||||
public static int stackSize;
|
||||
|
||||
private WasmHeap() {
|
||||
}
|
||||
|
||||
public static int calculateStorageSize(int heapSize) {
|
||||
return heapSize / 16;
|
||||
}
|
||||
|
||||
public static int calculateRegionsCount(int heapSize, int regionSize) {
|
||||
return (heapSize + regionSize - 1) / regionSize;
|
||||
}
|
||||
|
||||
public static int calculateRegionsSize(int regionsCount) {
|
||||
return regionsCount * 2;
|
||||
}
|
||||
|
||||
public static native void growMemory(int amount);
|
||||
|
||||
public static void initHeap(Address start, int minHeap, int maxHeap, int stackSize) {
|
||||
stackAddress = start;
|
||||
stack = start;
|
||||
heapAddress = WasmRuntime.align(stackAddress.add(stackSize), 16);
|
||||
memoryLimit = WasmRuntime.align(start, PAGE_SIZE);
|
||||
minHeapSize = minHeap;
|
||||
maxHeapSize = maxHeap;
|
||||
WasmHeap.stackSize = stackSize;
|
||||
resizeHeap(minHeap);
|
||||
}
|
||||
|
||||
public static void resizeHeap(int newHeapSize) {
|
||||
if (newHeapSize <= heapSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newStorageSize = calculateStorageSize(newHeapSize);
|
||||
int newRegionsCount = calculateRegionsCount(newHeapSize, regionSize);
|
||||
int newRegionsSize = calculateRegionsSize(newRegionsCount);
|
||||
|
||||
Address newRegionsAddress = WasmRuntime.align(heapAddress.add(newHeapSize), 16);
|
||||
Address newStorageAddress = WasmRuntime.align(newRegionsAddress.add(newRegionsSize), 16);
|
||||
Address newMemoryLimit = WasmRuntime.align(newStorageAddress.add(newStorageSize), PAGE_SIZE);
|
||||
if (newMemoryLimit != memoryLimit) {
|
||||
growMemory((int) (newMemoryLimit.toLong() - memoryLimit.toLong()) / PAGE_SIZE);
|
||||
memoryLimit = newMemoryLimit;
|
||||
}
|
||||
if (storageSize > 0) {
|
||||
WasmRuntime.moveMemoryBlock(storageAddress, newStorageAddress, storageSize);
|
||||
}
|
||||
if (regionsSize > 0) {
|
||||
WasmRuntime.moveMemoryBlock(regionsAddress, newRegionsAddress, regionsSize);
|
||||
}
|
||||
|
||||
storageAddress = newStorageAddress;
|
||||
regionsAddress = newRegionsAddress;
|
||||
storageSize = newStorageSize;
|
||||
regionsCount = newRegionsCount;
|
||||
regionsSize = newRegionsSize;
|
||||
heapSize = newHeapSize;
|
||||
}
|
||||
}
|
|
@ -24,13 +24,9 @@ import org.teavm.runtime.RuntimeObject;
|
|||
@StaticInit
|
||||
@Unmanaged
|
||||
public final class WasmRuntime {
|
||||
public static Address stack = initStack();
|
||||
|
||||
private WasmRuntime() {
|
||||
}
|
||||
|
||||
private static native Address initStack();
|
||||
|
||||
public static int compare(int a, int b) {
|
||||
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
||||
}
|
||||
|
@ -80,6 +76,14 @@ public final class WasmRuntime {
|
|||
return Address.fromInt(value);
|
||||
}
|
||||
|
||||
public static int align(int value, int alignment) {
|
||||
if (value == 0) {
|
||||
return value;
|
||||
}
|
||||
value = ((value - 1) / alignment + 1) * alignment;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Import(name = "print", module = "spectest")
|
||||
public static native void print(int a);
|
||||
|
||||
|
@ -252,20 +256,20 @@ public final class WasmRuntime {
|
|||
}
|
||||
|
||||
public static Address allocStack(int size) {
|
||||
Address result = stack.add(4);
|
||||
stack = result.add((size << 2) + 4);
|
||||
stack.putInt(size);
|
||||
Address result = WasmHeap.stack.add(4);
|
||||
WasmHeap.stack = result.add((size << 2) + 4);
|
||||
WasmHeap.stack.putInt(size);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Address getStackTop() {
|
||||
return stack != initStack() ? stack : null;
|
||||
return WasmHeap.stack != WasmHeap.stackAddress ? WasmHeap.stack : null;
|
||||
}
|
||||
|
||||
public static Address getNextStackFrame(Address stackFrame) {
|
||||
int size = stackFrame.getInt() + 2;
|
||||
Address result = stackFrame.add(-size * 4);
|
||||
if (result == initStack()) {
|
||||
if (result == WasmHeap.stackAddress) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic;
|
|||
import org.teavm.backend.wasm.intrinsics.RuntimeClassIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.ShadowStackIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.StructureIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.WasmHeapIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactory;
|
||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactoryContext;
|
||||
import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic;
|
||||
|
@ -149,6 +150,8 @@ import org.teavm.vm.TeaVMTargetController;
|
|||
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||
|
||||
public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
private static final MethodReference INIT_HEAP_REF = new MethodReference(WasmHeap.class, "initHeap",
|
||||
Address.class, int.class, int.class, int.class, void.class);
|
||||
private TeaVMTargetController controller;
|
||||
private boolean debugging;
|
||||
private boolean wastEmitted;
|
||||
|
@ -159,7 +162,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
private ShadowStackTransformer shadowStackTransformer;
|
||||
private WasmBinaryVersion version = WasmBinaryVersion.V_0x1;
|
||||
private List<WasmIntrinsicFactory> additionalIntrinsics = new ArrayList<>();
|
||||
private int minHeapSize;
|
||||
private int minHeapSize = 2 * 1024 * 1024;
|
||||
private int maxHeapSize = 128 * 1024 * 1024;
|
||||
|
||||
@Override
|
||||
public void setController(TeaVMTargetController controller) {
|
||||
|
@ -241,6 +245,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public void setMaxHeapSize(int maxHeapSize) {
|
||||
this.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) {
|
||||
|
@ -282,6 +290,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory",
|
||||
void.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(INIT_HEAP_REF).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||
RuntimeClass.class, Address.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
|
@ -360,6 +370,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
context.addIntrinsic(new ConsoleIntrinsic());
|
||||
context.addGenerator(new ArrayGenerator());
|
||||
context.addIntrinsic(new MemoryTraceIntrinsic());
|
||||
context.addIntrinsic(new WasmHeapIntrinsic());
|
||||
|
||||
IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext();
|
||||
for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) {
|
||||
|
@ -377,9 +388,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
|
||||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter);
|
||||
|
||||
int pageSize = 1 << 16;
|
||||
int pages = (minHeapSize + pageSize - 1) / pageSize;
|
||||
module.setMemorySize(pages);
|
||||
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
|
||||
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
|
@ -391,23 +399,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
dataSegment.setOffset(256);
|
||||
module.getSegments().add(dataSegment);
|
||||
|
||||
renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic, wasmRuntimeIntrinsic);
|
||||
renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic);
|
||||
renderClinit(classes, classGenerator, module);
|
||||
if (controller.wasCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassReader cls = classes.get(className);
|
||||
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) {
|
||||
continue;
|
||||
}
|
||||
MethodReader clinit = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||
if (clinit == null) {
|
||||
continue;
|
||||
}
|
||||
initFunction.getBody().add(new WasmCall(names.forClassInitializer(className)));
|
||||
}
|
||||
generateInitFunction(classes, initFunction, names, binaryWriter.getAddress());
|
||||
module.add(initFunction);
|
||||
module.setStartFunction(initFunction);
|
||||
|
||||
|
@ -475,6 +473,39 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
}
|
||||
}
|
||||
|
||||
private void generateInitFunction(ListableClassReaderSource classes, WasmFunction initFunction,
|
||||
NameProvider names, int heapAddress) {
|
||||
|
||||
for (Class<?> javaCls : new Class<?>[] { WasmRuntime.class, WasmHeap.class }) {
|
||||
ClassReader cls = classes.get(javaCls.getName());
|
||||
MethodReader clinit = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||
if (clinit == null) {
|
||||
continue;
|
||||
}
|
||||
initFunction.getBody().add(new WasmCall(names.forClassInitializer(cls.getName())));
|
||||
}
|
||||
|
||||
initFunction.getBody().add(new WasmCall(names.forMethod(INIT_HEAP_REF),
|
||||
new WasmInt32Constant(heapAddress), new WasmInt32Constant(minHeapSize),
|
||||
new WasmInt32Constant(maxHeapSize), new WasmInt32Constant(WasmHeap.DEFAULT_STACK_SIZE)));
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
if (className.equals(WasmRuntime.class.getName()) || className.equals(WasmHeap.class.getName())) {
|
||||
continue;
|
||||
}
|
||||
ClassReader cls = classes.get(className);
|
||||
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) {
|
||||
continue;
|
||||
}
|
||||
MethodReader clinit = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||
if (clinit == null) {
|
||||
continue;
|
||||
}
|
||||
initFunction.getBody().add(new WasmCall(names.forClassInitializer(className)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getBaseName(String name) {
|
||||
int index = name.lastIndexOf('.');
|
||||
return index < 0 ? name : name.substring(0, index);
|
||||
|
@ -754,32 +785,20 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
}
|
||||
}
|
||||
|
||||
private void renderMemoryLayout(WasmModule module, int address, GCIntrinsic gcIntrinsic,
|
||||
WasmRuntimeIntrinsic runtimeIntrinsic) {
|
||||
address = (((address - 1) / 256) + 1) * 256;
|
||||
private void renderMemoryLayout(WasmModule module, int address, GCIntrinsic gcIntrinsic) {
|
||||
module.setMinMemorySize(WasmRuntime.align(address, WasmHeap.PAGE_SIZE) / WasmHeap.PAGE_SIZE);
|
||||
|
||||
runtimeIntrinsic.setStackAddress(address);
|
||||
address += 65536;
|
||||
int newStorageSize = WasmHeap.calculateStorageSize(maxHeapSize);
|
||||
int newRegionsCount = WasmHeap.calculateRegionsCount(maxHeapSize, WasmHeap.DEFAULT_REGION_SIZE);
|
||||
int newRegionsSize = WasmHeap.calculateRegionsSize(newRegionsCount);
|
||||
|
||||
int gcMemory = module.getMemorySize() * 65536 - address;
|
||||
int storageSize = (gcMemory >> 6) >> 2 << 2;
|
||||
gcIntrinsic.setGCStorageAddress(address);
|
||||
gcIntrinsic.setGCStorageSize(storageSize);
|
||||
address = WasmRuntime.align(address + WasmHeap.DEFAULT_STACK_SIZE, 16);
|
||||
address = WasmRuntime.align(address + maxHeapSize, 16);
|
||||
address = WasmRuntime.align(address + newRegionsSize, 16);
|
||||
address = WasmRuntime.align(address + newStorageSize, 16);
|
||||
gcIntrinsic.setRegionSize(WasmHeap.DEFAULT_REGION_SIZE);
|
||||
|
||||
gcMemory -= storageSize;
|
||||
address += storageSize;
|
||||
int regionSize = 32768;
|
||||
int regionCount = gcMemory / (2 + regionSize) + 1;
|
||||
gcIntrinsic.setRegionSize(regionSize);
|
||||
gcIntrinsic.setRegionsAddress(address);
|
||||
gcIntrinsic.setRegionMaxCount(regionCount);
|
||||
|
||||
address += regionCount * 2;
|
||||
address = (address + 4) >> 2 << 2;
|
||||
|
||||
gcMemory = module.getMemorySize() * 65536 - address;
|
||||
gcIntrinsic.setHeapAddress(address);
|
||||
gcIntrinsic.setAvailableBytes(gcMemory);
|
||||
module.setMaxMemorySize(WasmRuntime.align(address, WasmHeap.PAGE_SIZE) / WasmHeap.PAGE_SIZE);
|
||||
}
|
||||
|
||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||
|
|
|
@ -59,6 +59,7 @@ public class CallSiteBinaryGenerator {
|
|||
private BinaryWriter writer;
|
||||
private WasmClassGenerator classGenerator;
|
||||
private WasmStringPool stringPool;
|
||||
private ObjectIntMap<String> stringIndirectPointerCache = new ObjectIntHashMap<>();
|
||||
|
||||
public CallSiteBinaryGenerator(BinaryWriter writer, WasmClassGenerator classGenerator, WasmStringPool stringPool) {
|
||||
this.writer = writer;
|
||||
|
@ -132,15 +133,15 @@ public class CallSiteBinaryGenerator {
|
|||
methodLocationCache.put(methodLocation, methodLocationAddress);
|
||||
if (location.getFileName() != null) {
|
||||
binaryMethodLocation.setAddress(METHOD_LOCATION_FILE,
|
||||
stringPool.getStringPointer(location.getFileName()));
|
||||
getStringIndirectPointer(location.getFileName()));
|
||||
}
|
||||
if (location.getClassName() != null) {
|
||||
binaryMethodLocation.setAddress(METHOD_LOCATION_CLASS,
|
||||
stringPool.getStringPointer(location.getClassName()));
|
||||
getStringIndirectPointer(location.getClassName()));
|
||||
}
|
||||
if (location.getMethodName() != null) {
|
||||
binaryMethodLocation.setAddress(METHOD_LOCATION_METHOD,
|
||||
stringPool.getStringPointer(location.getMethodName()));
|
||||
getStringIndirectPointer(location.getMethodName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,6 +154,16 @@ public class CallSiteBinaryGenerator {
|
|||
return firstCallSite;
|
||||
}
|
||||
|
||||
private int getStringIndirectPointer(String str) {
|
||||
int result = stringIndirectPointerCache.getOrDefault(str, -1);
|
||||
if (result < 0) {
|
||||
DataValue indirectValue = DataPrimitives.ADDRESS.createValue();
|
||||
result = writer.append(indirectValue);
|
||||
indirectValue.setAddress(0, stringPool.getStringPointer(str));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
final static class MethodLocation {
|
||||
final String file;
|
||||
final String className;
|
||||
|
|
|
@ -621,7 +621,7 @@ public class WasmClassGenerator {
|
|||
return cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;
|
||||
}
|
||||
|
||||
private class ClassBinaryData {
|
||||
static class ClassBinaryData {
|
||||
ValueType type;
|
||||
int size;
|
||||
int alignment;
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.teavm.ast.UnwrapArrayExpr;
|
|||
import org.teavm.ast.VariableExpr;
|
||||
import org.teavm.ast.WhileStatement;
|
||||
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||
import org.teavm.backend.wasm.WasmHeap;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.binary.BinaryWriter;
|
||||
import org.teavm.backend.wasm.binary.DataPrimitives;
|
||||
|
@ -1015,7 +1016,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
+ "Mutator.allocStack");
|
||||
}
|
||||
|
||||
int offset = classGenerator.getFieldOffset(new FieldReference(WasmRuntime.class.getName(), "stack"));
|
||||
int offset = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "stack"));
|
||||
WasmExpression oldValue = new WasmGetLocal(stackVariable);
|
||||
oldValue = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, oldValue,
|
||||
new WasmInt32Constant(4));
|
||||
|
@ -1541,6 +1542,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
public void releaseTemporary(WasmLocal local) {
|
||||
WasmGenerationVisitor.this.releaseTemporary(local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStaticField(FieldReference field) {
|
||||
return classGenerator.getFieldOffset(field);
|
||||
}
|
||||
};
|
||||
|
||||
private WasmLocal getTemporary(WasmType type) {
|
||||
|
|
|
@ -18,50 +18,27 @@ package org.teavm.backend.wasm.intrinsics;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.WasmHeap;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmConversion;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.GC;
|
||||
|
||||
public class GCIntrinsic implements WasmIntrinsic {
|
||||
private static final MethodReference PRINT_OUT_OF_MEMORY = new MethodReference(
|
||||
WasmRuntime.class, "printOutOfMemory", void.class);
|
||||
private List<WasmInt32Constant> heapAddressExpressions = new ArrayList<>();
|
||||
private List<WasmInt64Constant> availableBytesExpressions = new ArrayList<>();
|
||||
private List<WasmInt32Constant> gcStorageAddressExpressions = new ArrayList<>();
|
||||
private List<WasmInt32Constant> gcStorageSizeExpressions = new ArrayList<>();
|
||||
private static final MethodReference RESIZE_HEAP = new MethodReference(
|
||||
WasmHeap.class, "printOutOfMemory", void.class);
|
||||
private List<WasmInt32Constant> regionSizeExpressions = new ArrayList<>();
|
||||
private List<WasmInt32Constant> regionsAddressExpressions = new ArrayList<>();
|
||||
private List<WasmInt32Constant> regionMaxCountExpressions = new ArrayList<>();
|
||||
|
||||
public void setHeapAddress(int address) {
|
||||
for (WasmInt32Constant constant : heapAddressExpressions) {
|
||||
constant.setValue(address);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAvailableBytes(long availableBytes) {
|
||||
for (WasmInt64Constant constant : availableBytesExpressions) {
|
||||
constant.setValue(availableBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public void setGCStorageAddress(int address) {
|
||||
for (WasmInt32Constant constant : gcStorageAddressExpressions) {
|
||||
constant.setValue(address);
|
||||
}
|
||||
}
|
||||
|
||||
public void setGCStorageSize(int storageSize) {
|
||||
for (WasmInt32Constant constant : gcStorageSizeExpressions) {
|
||||
constant.setValue(storageSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRegionSize(int regionSize) {
|
||||
for (WasmInt32Constant constant : regionSizeExpressions) {
|
||||
|
@ -69,21 +46,9 @@ public class GCIntrinsic implements WasmIntrinsic {
|
|||
}
|
||||
}
|
||||
|
||||
public void setRegionsAddress(int address) {
|
||||
for (WasmInt32Constant constant : regionsAddressExpressions) {
|
||||
constant.setValue(address);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRegionMaxCount(int maxCount) {
|
||||
for (WasmInt32Constant constant : regionMaxCountExpressions) {
|
||||
constant.setValue(maxCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
if (!methodReference.getClassName().endsWith(GC.class.getName())) {
|
||||
if (!methodReference.getClassName().equals(GC.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,6 +61,9 @@ public class GCIntrinsic implements WasmIntrinsic {
|
|||
case "regionMaxCount":
|
||||
case "regionSize":
|
||||
case "outOfMemory":
|
||||
case "minAvailableBytes":
|
||||
case "maxAvailableBytes":
|
||||
case "resizeHeap":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -104,31 +72,33 @@ public class GCIntrinsic implements WasmIntrinsic {
|
|||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
List<WasmInt32Constant> list;
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "gcStorageAddress":
|
||||
list = gcStorageAddressExpressions;
|
||||
break;
|
||||
return getStaticField(manager, "storageAddress");
|
||||
case "gcStorageSize":
|
||||
list = gcStorageSizeExpressions;
|
||||
break;
|
||||
return getStaticField(manager, "storageSize");
|
||||
case "heapAddress":
|
||||
list = heapAddressExpressions;
|
||||
break;
|
||||
return getStaticField(manager, "heapAddress");
|
||||
case "regionsAddress":
|
||||
list = regionsAddressExpressions;
|
||||
break;
|
||||
return getStaticField(manager, "regionsAddress");
|
||||
case "regionMaxCount":
|
||||
list = regionMaxCountExpressions;
|
||||
break;
|
||||
case "regionSize":
|
||||
list = regionSizeExpressions;
|
||||
break;
|
||||
case "availableBytes": {
|
||||
WasmInt64Constant constant = new WasmInt64Constant(0);
|
||||
availableBytesExpressions.add(constant);
|
||||
return constant;
|
||||
return getStaticField(manager, "regionsCount");
|
||||
case "minAvailableBytes":
|
||||
return intToLong(getStaticField(manager, "minHeapSize"));
|
||||
case "maxAvailableBytes":
|
||||
return intToLong(getStaticField(manager, "maxHeapSize"));
|
||||
case "resizeHeap": {
|
||||
WasmExpression amount = manager.generate(invocation.getArguments().get(0));
|
||||
amount = new WasmConversion(WasmType.INT64, WasmType.INT32, false, amount);
|
||||
return new WasmCall(manager.getNames().forMethod(RESIZE_HEAP), amount);
|
||||
}
|
||||
case "regionSize": {
|
||||
WasmInt32Constant result = new WasmInt32Constant(0);
|
||||
regionSizeExpressions.add(result);
|
||||
return result;
|
||||
}
|
||||
case "availableBytes":
|
||||
return intToLong(getStaticField(manager, "heapSize"));
|
||||
case "outOfMemory": {
|
||||
WasmBlock block = new WasmBlock(false);
|
||||
WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY), true);
|
||||
|
@ -139,8 +109,14 @@ public class GCIntrinsic implements WasmIntrinsic {
|
|||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||
}
|
||||
WasmInt32Constant result = new WasmInt32Constant(0);
|
||||
list.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static WasmExpression getStaticField(WasmIntrinsicManager manager, String fieldName) {
|
||||
int address = manager.getStaticField(new FieldReference(WasmHeap.class.getName(), fieldName));
|
||||
return new WasmLoadInt32(4, new WasmInt32Constant(address), WasmInt32Subtype.INT32);
|
||||
}
|
||||
|
||||
private static WasmExpression intToLong(WasmExpression expression) {
|
||||
return new WasmConversion(WasmType.INT32, WasmType.INT64, false, expression);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.intrinsics;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.WasmHeap;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmHeapIntrinsic implements WasmIntrinsic {
|
||||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
if (!methodReference.getClassName().equals(WasmHeap.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
switch (methodReference.getName()) {
|
||||
case "growMemory":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "growMemory":
|
||||
return new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0)));
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().getName());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.teavm.backend.wasm.model.WasmLocal;
|
|||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.FieldReference;
|
||||
|
||||
public interface WasmIntrinsicManager {
|
||||
WasmExpression generate(Expr expr);
|
||||
|
@ -37,5 +38,7 @@ public interface WasmIntrinsicManager {
|
|||
|
||||
WasmLocal getTemporary(WasmType type);
|
||||
|
||||
int getStaticField(FieldReference field);
|
||||
|
||||
void releaseTemporary(WasmLocal local);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.intrinsics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.generate.WasmGeneratorUtil;
|
||||
|
@ -25,15 +23,12 @@ import org.teavm.backend.wasm.model.expression.WasmExpression;
|
|||
import org.teavm.backend.wasm.model.expression.WasmFloatBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFloatType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmRuntimeIntrinsic implements WasmIntrinsic {
|
||||
private List<WasmInt32Constant> stackExpressions = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
if (!methodReference.getClassName().equals(WasmRuntime.class.getName())) {
|
||||
|
@ -49,12 +44,6 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
|
|||
}
|
||||
}
|
||||
|
||||
public void setStackAddress(int stackAddress) {
|
||||
for (WasmInt32Constant constant : stackExpressions) {
|
||||
constant.setValue(stackAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
|
@ -64,11 +53,6 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
|
|||
case "gt":
|
||||
return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT,
|
||||
invocation, manager);
|
||||
case "initStack": {
|
||||
WasmInt32Constant constant = new WasmInt32Constant(0);
|
||||
stackExpressions.add(constant);
|
||||
return constant;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().getName());
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
public class WasmModule {
|
||||
private int memorySize;
|
||||
private int minMemorySize;
|
||||
private int maxMemorySize;
|
||||
private List<WasmMemorySegment> segments = new ArrayList<>();
|
||||
private Map<String, WasmFunction> functions = new LinkedHashMap<>();
|
||||
private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions);
|
||||
|
@ -60,12 +61,20 @@ public class WasmModule {
|
|||
return segments;
|
||||
}
|
||||
|
||||
public int getMemorySize() {
|
||||
return memorySize;
|
||||
public int getMinMemorySize() {
|
||||
return minMemorySize;
|
||||
}
|
||||
|
||||
public void setMemorySize(int memorySize) {
|
||||
this.memorySize = memorySize;
|
||||
public void setMinMemorySize(int minMemorySize) {
|
||||
this.minMemorySize = minMemorySize;
|
||||
}
|
||||
|
||||
public int getMaxMemorySize() {
|
||||
return maxMemorySize;
|
||||
}
|
||||
|
||||
public void setMaxMemorySize(int maxMemorySize) {
|
||||
this.maxMemorySize = maxMemorySize;
|
||||
}
|
||||
|
||||
public WasmFunction getStartFunction() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.backend.wasm.model.expression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -34,6 +35,11 @@ public class WasmCall extends WasmExpression {
|
|||
this(functionName, false);
|
||||
}
|
||||
|
||||
public WasmCall(String functionName, WasmExpression... arguments) {
|
||||
this(functionName);
|
||||
getArguments().addAll(Arrays.asList(arguments));
|
||||
}
|
||||
|
||||
public String getFunctionName() {
|
||||
return functionName;
|
||||
}
|
||||
|
|
|
@ -180,4 +180,9 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
|
|||
expression.getIndex().acceptVisitor(this);
|
||||
expression.getValue().acceptVisitor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmMemoryGrow expression) {
|
||||
expression.getAmount().acceptVisitor(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,4 +73,6 @@ public interface WasmExpressionVisitor {
|
|||
void visit(WasmStoreFloat32 expression);
|
||||
|
||||
void visit(WasmStoreFloat64 expression);
|
||||
|
||||
void visit(WasmMemoryGrow expression);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.model.expression;
|
||||
|
||||
public class WasmMemoryGrow extends WasmExpression {
|
||||
private WasmExpression amount;
|
||||
|
||||
public WasmMemoryGrow(WasmExpression amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public WasmExpression getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(WasmExpression amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -221,4 +221,10 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
|
|||
expression.getValue().acceptVisitor(this);
|
||||
expression.setValue(mapper.apply(expression.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmMemoryGrow expression) {
|
||||
expression.getAmount().acceptVisitor(this);
|
||||
expression.setAmount(mapper.apply(expression.getAmount()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,8 +179,8 @@ public class WasmBinaryRenderer {
|
|||
|
||||
section.writeByte(1);
|
||||
section.writeByte(1);
|
||||
section.writeLEB(module.getMemorySize());
|
||||
section.writeLEB(module.getMemorySize());
|
||||
section.writeLEB(module.getMinMemorySize());
|
||||
section.writeLEB(module.getMaxMemorySize());
|
||||
|
||||
writeSection(SECTION_MEMORY, "memory", section.getData());
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ public class WasmBinaryRenderer {
|
|||
|
||||
WasmBinaryWriter functionsSubsection = new WasmBinaryWriter();
|
||||
Collection<WasmFunction> functions = module.getFunctions().values();
|
||||
functions = functions.stream().filter(f -> f.getImportName() != null).collect(Collectors.toList());
|
||||
functions = functions.stream().filter(f -> f.getImportName() == null).collect(Collectors.toList());
|
||||
functionsSubsection.writeLEB(functions.size());
|
||||
|
||||
for (WasmFunction function : functions) {
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
|||
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
|
||||
|
@ -824,6 +825,13 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
|
|||
writer.writeLEB(expression.getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmMemoryGrow expression) {
|
||||
expression.getAmount().acceptVisitor(this);
|
||||
writer.writeByte(0x40);
|
||||
writer.writeByte(0);
|
||||
}
|
||||
|
||||
private int alignment(int value) {
|
||||
return 31 - Integer.numberOfLeadingZeros(Math.max(1, value));
|
||||
}
|
||||
|
|
|
@ -72,8 +72,6 @@ public class WasmCRenderer {
|
|||
line("");
|
||||
|
||||
renderFunctionDeclarations(module);
|
||||
line("static int8_t *wasm_heap;");
|
||||
line("static int32_t wasm_heap_size;");
|
||||
renderFunctionTable(module);
|
||||
|
||||
for (WasmFunction function : module.getFunctions().values()) {
|
||||
|
@ -117,8 +115,8 @@ public class WasmCRenderer {
|
|||
}
|
||||
|
||||
private void renderHeap(WasmModule module) {
|
||||
line("wasm_heap_size = " + 65536 * module.getMemorySize() + ";");
|
||||
line("wasm_heap = malloc(" + 65536 * module.getMemorySize() + ");");
|
||||
line("wasm_heap_size = " + 65536 * module.getMinMemorySize() + ";");
|
||||
line("wasm_heap = malloc(wasm_heap_size);");
|
||||
for (WasmMemorySegment segment : module.getSegments()) {
|
||||
line("memcpy(wasm_heap + " + segment.getOffset() + ",");
|
||||
indent();
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
|||
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
|
||||
|
@ -1030,6 +1031,26 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
|
|||
value = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmMemoryGrow expression) {
|
||||
CExpression result = new CExpression();
|
||||
|
||||
requiredType = WasmType.INT32;
|
||||
expression.getAmount().acceptVisitor(this);
|
||||
CExpression amount = value;
|
||||
|
||||
if (amount.getLines().isEmpty()) {
|
||||
result.addLine("wasm_heap_size += 65536 * (" + amount.getText() + ");", expression.getLocation());
|
||||
} else {
|
||||
result.addLine("wasm_heap_size += 65536 * (" + amount.getText(), expression.getLocation());
|
||||
result.getLines().addAll(amount.getLines());
|
||||
result.addLine(");", expression.getLocation());
|
||||
}
|
||||
|
||||
result.addLine("wasm_heap = realloc(wasm_heap, wasm_heap_size);");
|
||||
value = result;
|
||||
}
|
||||
|
||||
private CExpression checkAddress(CExpression index) {
|
||||
if (!memoryAccessChecked) {
|
||||
return index;
|
||||
|
@ -1044,7 +1065,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
|
|||
} else {
|
||||
var = index.getText();
|
||||
}
|
||||
checked.addLine("assert(" + var + " < " + module.getMemorySize() * 65536 + ");");
|
||||
checked.addLine("assert(" + var + " < " + module.getMinMemorySize() * 65536 + ");");
|
||||
checked.setText(var);
|
||||
checked.setRelocatable(index.isRelocatable());
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ public class WasmRenderer {
|
|||
|
||||
public void renderMemory(WasmModule module) {
|
||||
visitor.lf();
|
||||
visitor.open().append("memory (export \"memory\") " + module.getMemorySize()).close().lf();
|
||||
visitor.open().append("memory (export \"memory\") " + module.getMinMemorySize()).close().lf();
|
||||
}
|
||||
|
||||
public void renderData(WasmModule module) {
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
|||
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
|
||||
|
@ -594,6 +595,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
|
|||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmMemoryGrow expression) {
|
||||
open().append("memory.grow");
|
||||
line(expression.getAmount());
|
||||
close();
|
||||
}
|
||||
|
||||
private String type(WasmType type) {
|
||||
switch (type) {
|
||||
case INT32:
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
|||
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
||||
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
|
||||
|
@ -209,6 +210,11 @@ public class WasmTypeInference implements WasmExpressionVisitor {
|
|||
result = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmMemoryGrow expression) {
|
||||
result = WasmType.INT32;
|
||||
}
|
||||
|
||||
private static WasmType map(WasmIntType type) {
|
||||
switch (type) {
|
||||
case INT32:
|
||||
|
|
|
@ -76,6 +76,8 @@ public class ExceptionHandlingShadowStackContributor {
|
|||
private boolean hasExceptionHandlers;
|
||||
private int parameterCount;
|
||||
|
||||
public int callSiteIdGen;
|
||||
|
||||
public ExceptionHandlingShadowStackContributor(Characteristics characteristics,
|
||||
List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
|
||||
this.characteristics = characteristics;
|
||||
|
@ -246,7 +248,7 @@ public class ExceptionHandlingShadowStackContributor {
|
|||
}
|
||||
CallSiteLocation location = new CallSiteLocation(fileName, method.getClassName(), method.getName(),
|
||||
lineNumber);
|
||||
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location);
|
||||
CallSiteDescriptor callSite = new CallSiteDescriptor(callSiteIdGen++, location);
|
||||
callSites.add(callSite);
|
||||
List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
|
||||
List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite,
|
||||
|
|
|
@ -38,6 +38,7 @@ public class ShadowStackTransformer {
|
|||
private Characteristics characteristics;
|
||||
private GCShadowStackContributor gcContributor;
|
||||
private boolean exceptionHandling;
|
||||
private int callSiteIdGen;
|
||||
|
||||
public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) {
|
||||
gcContributor = new GCShadowStackContributor(characteristics);
|
||||
|
@ -54,8 +55,12 @@ public class ShadowStackTransformer {
|
|||
boolean exceptions;
|
||||
if (exceptionHandling) {
|
||||
List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
|
||||
method.getReference(), program).contribute();
|
||||
ExceptionHandlingShadowStackContributor exceptionContributor =
|
||||
new ExceptionHandlingShadowStackContributor(characteristics, callSites,
|
||||
method.getReference(), program);
|
||||
exceptionContributor.callSiteIdGen = callSiteIdGen;
|
||||
exceptions = exceptionContributor.contribute();
|
||||
callSiteIdGen = exceptionContributor.callSiteIdGen;
|
||||
CallSiteDescriptor.save(callSites, program.getAnnotations());
|
||||
} else {
|
||||
exceptions = false;
|
||||
|
|
|
@ -103,6 +103,11 @@ public final class ExceptionHandling {
|
|||
}
|
||||
|
||||
if (stackFrame == null) {
|
||||
stackFrame = ShadowStack.getStackTop();
|
||||
while (stackFrame != null) {
|
||||
ShadowStack.setExceptionHandlerId(stackFrame, ShadowStack.getCallSiteId(stackFrame) + 1);
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
printStack();
|
||||
abort();
|
||||
} else if (isJumpSupported()) {
|
||||
|
@ -134,6 +139,7 @@ public final class ExceptionHandling {
|
|||
|
||||
public static void fillStackTrace(StackTraceElement[] target) {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
int index = 0;
|
||||
while (stackFrame != null && index < target.length) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
|
|
|
@ -34,6 +34,7 @@ public final class GC {
|
|||
static int freeChunks;
|
||||
static int freeMemory = (int) availableBytes();
|
||||
static RuntimeReference firstWeakReference;
|
||||
static FreeChunk lastChunk;
|
||||
|
||||
static RelocationBlock lastRelocationBlock;
|
||||
|
||||
|
@ -49,6 +50,12 @@ public final class GC {
|
|||
|
||||
public static native long availableBytes();
|
||||
|
||||
public static native long minAvailableBytes();
|
||||
|
||||
public static native long maxAvailableBytes();
|
||||
|
||||
public static native void resizeHeap(long size);
|
||||
|
||||
private static native int regionSize();
|
||||
|
||||
@Import(name = "teavm_outOfMemory")
|
||||
|
@ -86,7 +93,7 @@ public final class GC {
|
|||
if (getNextChunkIfPossible(size)) {
|
||||
return;
|
||||
}
|
||||
collectGarbage();
|
||||
collectGarbageImpl(size);
|
||||
if (currentChunk.size < size && !getNextChunkIfPossible(size)) {
|
||||
ExceptionHandling.printStack();
|
||||
outOfMemory();
|
||||
|
@ -113,7 +120,13 @@ public final class GC {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Export(name = "teavm_gc_collect")
|
||||
public static void collectGarbage() {
|
||||
fixHeap();
|
||||
collectGarbageImpl(0);
|
||||
}
|
||||
|
||||
private static void collectGarbageImpl(int size) {
|
||||
MemoryTrace.gcStarted();
|
||||
mark();
|
||||
processReferences();
|
||||
|
@ -121,12 +134,36 @@ public final class GC {
|
|||
MemoryTrace.sweepCompleted();
|
||||
defragment();
|
||||
MemoryTrace.defragCompleted();
|
||||
//sortFreeChunks();
|
||||
updateFreeMemory();
|
||||
long minRequestedSize = 0;
|
||||
if (!hasAvailableChunk(size)) {
|
||||
minRequestedSize = computeMinRequestedSize(size);
|
||||
}
|
||||
resizeHeapIfNecessary(minRequestedSize);
|
||||
currentChunk = currentChunkPointer.value;
|
||||
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
|
||||
}
|
||||
|
||||
private static boolean hasAvailableChunk(int size) {
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
FreeChunkHolder ptr = currentChunkPointer;
|
||||
for (int i = 0; i < freeChunks; ++i) {
|
||||
if (size <= ptr.value.size) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static long computeMinRequestedSize(int size) {
|
||||
if (lastChunk.classReference == 0) {
|
||||
size -= lastChunk.size;
|
||||
}
|
||||
return availableBytes() + size;
|
||||
}
|
||||
|
||||
@Export(name = "teavm_gc_fixHeap")
|
||||
public static void fixHeap() {
|
||||
if (freeChunks > 0) {
|
||||
|
@ -135,6 +172,15 @@ public final class GC {
|
|||
}
|
||||
}
|
||||
|
||||
@Export(name = "teavm_gc_tryShrink")
|
||||
public static void tryShrink() {
|
||||
long availableBytes = availableBytes();
|
||||
long occupiedMemory = availableBytes - freeMemory;
|
||||
if (occupiedMemory < availableBytes / 4) {
|
||||
collectGarbage();
|
||||
}
|
||||
}
|
||||
|
||||
private static void mark() {
|
||||
MemoryTrace.initMark();
|
||||
firstWeakReference = null;
|
||||
|
@ -295,8 +341,6 @@ public final class GC {
|
|||
FreeChunk object = heapAddress().toStructure();
|
||||
FreeChunk lastFreeSpace = null;
|
||||
long heapSize = availableBytes();
|
||||
long reclaimedSpace = 0;
|
||||
long maxFreeChunk = 0;
|
||||
int regionsCount = (int) ((heapSize - 1) / regionSize()) + 1;
|
||||
Address currentRegionEnd = null;
|
||||
Address limit = heapAddress().add(heapSize);
|
||||
|
@ -348,10 +392,6 @@ public final class GC {
|
|||
freeChunkPtr.value = lastFreeSpace;
|
||||
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
|
||||
freeChunks++;
|
||||
reclaimedSpace += lastFreeSpace.size;
|
||||
if (maxFreeChunk < lastFreeSpace.size) {
|
||||
maxFreeChunk = lastFreeSpace.size;
|
||||
}
|
||||
lastFreeSpace = null;
|
||||
}
|
||||
}
|
||||
|
@ -366,12 +406,7 @@ public final class GC {
|
|||
lastFreeSpace.size = freeSize;
|
||||
MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size);
|
||||
freeChunkPtr.value = lastFreeSpace;
|
||||
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
|
||||
freeChunks++;
|
||||
reclaimedSpace += freeSize;
|
||||
if (maxFreeChunk < freeSize) {
|
||||
maxFreeChunk = freeSize;
|
||||
}
|
||||
}
|
||||
|
||||
currentChunkPointer = gcStorageAddress().toStructure();
|
||||
|
@ -425,6 +460,7 @@ public final class GC {
|
|||
|
||||
Address relocations = Structure.add(FreeChunk.class, freeChunk, 1).toAddress();
|
||||
Address relocationsLimit = freeChunk.toAddress().add(freeChunk.size);
|
||||
lastChunk = heapAddress().toStructure();
|
||||
|
||||
boolean lastWasLocked = false;
|
||||
objects: while (object.toAddress().isLessThan(limit)) {
|
||||
|
@ -461,6 +497,7 @@ public final class GC {
|
|||
lastRelocationBlock.start = object.toAddress().add(size);
|
||||
lastRelocationBlock.count = 0;
|
||||
object.classReference &= ~RuntimeObject.GC_MARKED;
|
||||
lastChunk = object;
|
||||
} else {
|
||||
lastWasLocked = false;
|
||||
|
||||
|
@ -721,6 +758,9 @@ public final class GC {
|
|||
while (!lastRelocationBlock.toAddress().isLessThan(relocationBlock.toAddress())) {
|
||||
if (relocationBlock.start.isLessThan(relocationBlock.end)) {
|
||||
FreeChunk freeChunk = relocationBlock.start.toStructure();
|
||||
if (!freeChunk.toAddress().isLessThan(lastChunk.toAddress())) {
|
||||
lastChunk = freeChunk;
|
||||
}
|
||||
freeChunk.size = (int) (relocationBlock.end.toLong() - relocationBlock.start.toLong());
|
||||
freeChunk.classReference = 0;
|
||||
MemoryTrace.assertFree(freeChunk.toAddress(), freeChunk.size);
|
||||
|
@ -732,11 +772,6 @@ public final class GC {
|
|||
}
|
||||
}
|
||||
|
||||
private static void sortFreeChunks() {
|
||||
currentChunkPointer = gcStorageAddress().toStructure();
|
||||
sortFreeChunks(0, freeChunks - 1);
|
||||
}
|
||||
|
||||
private static void updateFreeMemory() {
|
||||
freeMemory = 0;
|
||||
FreeChunkHolder freeChunkPtr = currentChunkPointer;
|
||||
|
@ -746,63 +781,71 @@ public final class GC {
|
|||
}
|
||||
}
|
||||
|
||||
private static void sortFreeChunks(int lower, int upper) {
|
||||
int start = lower;
|
||||
int end = upper;
|
||||
int mid = (lower + upper) / 2;
|
||||
|
||||
int midSize = getFreeChunk(mid).value.size;
|
||||
int firstSize = getFreeChunk(lower).value.size;
|
||||
int lastSize = getFreeChunk(upper).value.size;
|
||||
if (midSize < firstSize) {
|
||||
int tmp = firstSize;
|
||||
firstSize = midSize;
|
||||
midSize = tmp;
|
||||
}
|
||||
if (lastSize < firstSize) {
|
||||
lastSize = firstSize;
|
||||
}
|
||||
if (midSize > lastSize) {
|
||||
midSize = lastSize;
|
||||
}
|
||||
|
||||
outer: while (true) {
|
||||
while (true) {
|
||||
if (lower == upper) {
|
||||
break outer;
|
||||
}
|
||||
if (getFreeChunk(lower).value.size <= midSize) {
|
||||
break;
|
||||
}
|
||||
++lower;
|
||||
}
|
||||
while (true) {
|
||||
if (lower == upper) {
|
||||
break outer;
|
||||
}
|
||||
if (getFreeChunk(upper).value.size > midSize) {
|
||||
break;
|
||||
}
|
||||
--upper;
|
||||
}
|
||||
FreeChunk tmp = getFreeChunk(lower).value;
|
||||
getFreeChunk(lower).value = getFreeChunk(upper).value;
|
||||
getFreeChunk(upper).value = tmp;
|
||||
}
|
||||
|
||||
if (lower == end || upper == start) {
|
||||
private static void resizeHeapConsistent(long newSize) {
|
||||
long oldSize = availableBytes();
|
||||
if (newSize == oldSize) {
|
||||
return;
|
||||
}
|
||||
if (lower - start > 0) {
|
||||
sortFreeChunks(start, lower);
|
||||
}
|
||||
if (end - lower - 1 > 0) {
|
||||
sortFreeChunks(lower + 1, end);
|
||||
if (newSize > oldSize) {
|
||||
resizeHeap(newSize);
|
||||
if (lastChunk.classReference == 0) {
|
||||
lastChunk.size += (int) (newSize - oldSize);
|
||||
} else {
|
||||
int size = objectSize(lastChunk);
|
||||
lastChunk = lastChunk.toAddress().add(size).toStructure();
|
||||
lastChunk.classReference = 0;
|
||||
lastChunk.size = (int) (newSize - oldSize);
|
||||
Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks).value = lastChunk;
|
||||
freeChunks++;
|
||||
}
|
||||
} else {
|
||||
long minimumSize = lastChunk.toAddress().toLong() - heapAddress().toLong();
|
||||
if (lastChunk.classReference != 0) {
|
||||
minimumSize += objectSize(lastChunk);
|
||||
}
|
||||
if (newSize < minimumSize) {
|
||||
newSize = minimumSize;
|
||||
if (newSize == oldSize) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (newSize == minimumSize) {
|
||||
freeChunks--;
|
||||
} else {
|
||||
lastChunk.size -= (int) (oldSize - newSize);
|
||||
}
|
||||
resizeHeap(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static FreeChunkHolder getFreeChunk(int index) {
|
||||
return Structure.add(FreeChunkHolder.class, currentChunkPointer, index);
|
||||
private static void resizeHeapIfNecessary(long requestedSize) {
|
||||
long availableBytes = availableBytes();
|
||||
long occupiedMemory = availableBytes - freeMemory;
|
||||
if (requestedSize > availableBytes || occupiedMemory > availableBytes / 2) {
|
||||
long newSize = max(requestedSize, (availableBytes - freeMemory) * 2);
|
||||
newSize = min(newSize, maxAvailableBytes());
|
||||
if (newSize != availableBytes) {
|
||||
if (newSize % 8 != 0) {
|
||||
newSize += 8 - newSize % 8;
|
||||
}
|
||||
resizeHeapConsistent(newSize);
|
||||
}
|
||||
} else if (occupiedMemory < availableBytes / 4) {
|
||||
long newSize = occupiedMemory * 3;
|
||||
newSize = max(newSize, minAvailableBytes());
|
||||
if (newSize % 8 != 0) {
|
||||
newSize -= newSize % 8;
|
||||
}
|
||||
resizeHeapConsistent(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static long min(long a, long b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
private static long max(long a, long b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
private static int objectSize(FreeChunk object) {
|
||||
|
|
|
@ -20,68 +20,160 @@ void* teavm_gc_gcStorageAddress = NULL;
|
|||
int32_t teavm_gc_gcStorageSize = INT32_C(0);
|
||||
void* teavm_gc_regionsAddress = NULL;
|
||||
int32_t teavm_gc_regionSize = INT32_C(32768);
|
||||
int32_t teavm_gc_regionMaxCount = INT32_C(0);
|
||||
int64_t teavm_gc_availableBytes = INT64_C(0);
|
||||
int32_t teavm_gc_regionMaxCount;
|
||||
int64_t teavm_gc_availableBytes;
|
||||
int64_t teavm_gc_minAvailableBytes;
|
||||
int64_t teavm_gc_maxAvailableBytes;
|
||||
static int64_t teavm_gc_pageSize;
|
||||
|
||||
#if TEAVM_UNIX
|
||||
static void* teavm_virtualAlloc(int size) {
|
||||
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
static void* teavm_virtualAlloc(int64_t size) {
|
||||
return mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
}
|
||||
|
||||
static long teavm_pageSize() {
|
||||
static void teavm_virtualCommit(void* address, int64_t size) {
|
||||
mprotect(address, size, PROT_READ | PROT_WRITE);
|
||||
}
|
||||
|
||||
static void teavm_virtualUncommit(void* address, int64_t size) {
|
||||
mprotect(address, size, PROT_NONE);
|
||||
}
|
||||
|
||||
static int64_t teavm_pageSize() {
|
||||
return sysconf(_SC_PAGE_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TEAVM_WINDOWS
|
||||
static void* teavm_virtualAlloc(int size) {
|
||||
static void* teavm_virtualAlloc(int64_t size) {
|
||||
#if TEAVM_WINDOWS_UWP
|
||||
return VirtualAllocFromApp(
|
||||
NULL,
|
||||
size,
|
||||
MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_READWRITE
|
||||
MEM_RESERVE,
|
||||
PAGE_NOACCESS
|
||||
);
|
||||
#else
|
||||
return VirtualAlloc(
|
||||
NULL,
|
||||
size,
|
||||
MEM_RESERVE | MEM_COMMIT,
|
||||
MEM_RESERVE,
|
||||
PAGE_NOACCESS
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void teavm_virtualCommit(void* address, int64_t size) {
|
||||
#if TEAVM_WINDOWS_UWP
|
||||
VirtualAllocFromApp(
|
||||
address,
|
||||
size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE
|
||||
);
|
||||
#else
|
||||
VirtualAlloc(
|
||||
address,
|
||||
size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
static long teavm_pageSize() {
|
||||
static void teavm_virtualUncommit(void* address, int64_t size) {
|
||||
VirtualFree(address, size, MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
static int64_t teavm_pageSize() {
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetSystemInfo(&systemInfo);
|
||||
return systemInfo.dwPageSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int teavm_pageCount(int64_t size, int64_t pageSize) {
|
||||
return (int) ((size + pageSize + 1) / pageSize * pageSize);
|
||||
static int64_t teavm_pageCount(int64_t size) {
|
||||
return (int64_t) ((size + teavm_gc_pageSize - 1) / teavm_gc_pageSize * teavm_gc_pageSize);
|
||||
}
|
||||
|
||||
void teavm_initHeap(int64_t heapSize) {
|
||||
long workSize = (long) (heapSize / 16);
|
||||
long regionsSize = (long) (heapSize / teavm_gc_regionSize) + 1;
|
||||
long pageSize = teavm_pageSize();
|
||||
static int32_t teavm_gc_calculateWorkSize(int64_t heapSize) {
|
||||
return (int32_t) (heapSize / 16);
|
||||
}
|
||||
|
||||
teavm_gc_heapAddress = teavm_virtualAlloc(teavm_pageCount(heapSize, pageSize));
|
||||
teavm_gc_gcStorageAddress = teavm_virtualAlloc(teavm_pageCount(workSize, pageSize));
|
||||
teavm_gc_regionsAddress = teavm_virtualAlloc(teavm_pageCount(regionsSize * 2, pageSize));
|
||||
static int32_t teavm_gc_calculateRegionsSize(int64_t heapSize) {
|
||||
return (int32_t) (heapSize / teavm_gc_regionSize) + 1;
|
||||
}
|
||||
|
||||
void teavm_gc_resizeHeap(int64_t newSize) {
|
||||
if (newSize == teavm_gc_availableBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t workSize = teavm_gc_calculateWorkSize(newSize);
|
||||
int32_t regionsSize = teavm_gc_calculateRegionsSize(newSize);
|
||||
|
||||
int64_t newSizeAligned = teavm_pageCount(newSize);
|
||||
int64_t oldSizeAligned = teavm_pageCount(teavm_gc_availableBytes);
|
||||
int64_t newWorkSizeAligned = teavm_pageCount(workSize);
|
||||
int64_t oldWorkSizeAligned = teavm_pageCount(teavm_gc_gcStorageSize);
|
||||
int64_t newRegionsSizeAligned = teavm_pageCount(regionsSize * 2);
|
||||
int64_t oldRegionsSizeAligned = teavm_pageCount(teavm_gc_regionMaxCount * 2);
|
||||
|
||||
if (newSize > teavm_gc_availableBytes) {
|
||||
if (newSizeAligned > oldSizeAligned) {
|
||||
teavm_virtualCommit((char*) teavm_gc_heapAddress + oldSizeAligned, newSizeAligned - oldSizeAligned);
|
||||
}
|
||||
if (newWorkSizeAligned > oldWorkSizeAligned) {
|
||||
teavm_virtualCommit((char*) teavm_gc_gcStorageAddress + oldWorkSizeAligned,
|
||||
newWorkSizeAligned - oldWorkSizeAligned);
|
||||
}
|
||||
if (newRegionsSizeAligned > oldRegionsSizeAligned) {
|
||||
teavm_virtualCommit((char*) teavm_gc_regionsAddress + oldRegionsSizeAligned,
|
||||
newRegionsSizeAligned - oldRegionsSizeAligned);
|
||||
}
|
||||
} else {
|
||||
if (newSizeAligned < oldSizeAligned) {
|
||||
teavm_virtualUncommit((char*) teavm_gc_heapAddress + newSizeAligned, oldSizeAligned - newSizeAligned);
|
||||
}
|
||||
if (newWorkSizeAligned < oldWorkSizeAligned) {
|
||||
teavm_virtualUncommit((char*) teavm_gc_gcStorageAddress + newWorkSizeAligned,
|
||||
oldWorkSizeAligned - newWorkSizeAligned);
|
||||
}
|
||||
if (newRegionsSizeAligned < oldRegionsSizeAligned) {
|
||||
teavm_virtualUncommit((char*) teavm_gc_regionsAddress + newRegionsSizeAligned,
|
||||
oldRegionsSizeAligned - newRegionsSizeAligned);
|
||||
}
|
||||
}
|
||||
|
||||
teavm_gc_gcStorageSize = workSize;
|
||||
teavm_gc_regionMaxCount = regionsSize;
|
||||
teavm_gc_availableBytes = newSize;
|
||||
}
|
||||
|
||||
void teavm_initHeap(int64_t minHeap, int64_t maxHeap) {
|
||||
teavm_gc_pageSize = teavm_pageSize();
|
||||
int32_t workSize = teavm_gc_calculateWorkSize(maxHeap);
|
||||
int32_t regionsSize = teavm_gc_calculateRegionsSize(maxHeap);
|
||||
|
||||
teavm_gc_heapAddress = teavm_virtualAlloc(teavm_pageCount(maxHeap));
|
||||
teavm_gc_gcStorageAddress = teavm_virtualAlloc(teavm_pageCount(workSize));
|
||||
teavm_gc_regionsAddress = teavm_virtualAlloc(teavm_pageCount(regionsSize * 2));
|
||||
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
int64_t heapMapSize = heapSize / sizeof(void*);
|
||||
teavm_gc_heapMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize, pageSize));
|
||||
int64_t heapMapSize = maxHeap / sizeof(void*);
|
||||
teavm_gc_heapMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize));
|
||||
teavm_virtualCommit(teavm_gc_heapMap, teavm_pageCount(heapMapSize));
|
||||
memset(teavm_gc_heapMap, 0, heapMapSize);
|
||||
teavm_gc_markMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize, pageSize));
|
||||
teavm_gc_markMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize));
|
||||
teavm_virtualCommit(teavm_gc_markMap, teavm_pageCount(heapMapSize));
|
||||
#endif
|
||||
|
||||
teavm_gc_gcStorageSize = (int) workSize;
|
||||
teavm_gc_regionMaxCount = regionsSize;
|
||||
teavm_gc_availableBytes = heapSize;
|
||||
teavm_gc_minAvailableBytes = minHeap;
|
||||
teavm_gc_maxAvailableBytes = maxHeap;
|
||||
teavm_gc_gcStorageSize = 0;
|
||||
teavm_gc_regionMaxCount = 0;
|
||||
teavm_gc_availableBytes = 0;
|
||||
teavm_gc_resizeHeap(minHeap);
|
||||
}
|
||||
|
||||
typedef struct TeaVM_StaticGcRootDescriptor {
|
||||
|
@ -136,4 +228,4 @@ void teavm_initStaticGcRoots() {
|
|||
free(builder);
|
||||
builder = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,12 @@ extern void* teavm_gc_regionsAddress;
|
|||
extern int32_t teavm_gc_regionSize;
|
||||
extern int32_t teavm_gc_regionMaxCount;
|
||||
extern int64_t teavm_gc_availableBytes;
|
||||
extern int64_t teavm_gc_minAvailableBytes;
|
||||
extern int64_t teavm_gc_maxAvailableBytes;
|
||||
extern void*** teavm_gc_staticRoots;
|
||||
|
||||
extern void teavm_initHeap(int64_t heapSize);
|
||||
extern void teavm_initHeap(int64_t minHeap, int64_t maxHeap);
|
||||
extern void teavm_gc_resizeHeap(int64_t newSize);
|
||||
|
||||
extern void teavm_registerStaticGcRoots(void***, int);
|
||||
extern void teavm_initStaticGcRoots();
|
|
@ -7,6 +7,10 @@
|
|||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#include <time.h>
|
||||
#include <uchar.h>
|
||||
|
||||
static int8_t *wasm_heap;
|
||||
static int32_t wasm_heap_size;
|
||||
|
||||
static inline float teavm_getNaN() {
|
||||
return NAN;
|
||||
|
@ -46,7 +50,15 @@ static void logOutOfMemory() {
|
|||
abort();
|
||||
}
|
||||
|
||||
static void logString(int32_t v) {
|
||||
static void logString(int32_t string) {
|
||||
uint32_t arrayPtr = *(uint32_t*) (wasm_heap + string + 8);
|
||||
uint32_t length = *(uint32_t*) (wasm_heap + arrayPtr + 8);
|
||||
for (int32_t i = 0; i < length; ++i) {
|
||||
char16_t c = *(char16_t*) (wasm_heap + i * 2 + arrayPtr + 12);
|
||||
putwchar(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void logInt(int32_t v) {
|
||||
wprintf(L"%" PRId32, v);
|
||||
}
|
|
@ -52,6 +52,7 @@ TeaVM.wasm = function() {
|
|||
function importDefaults(obj) {
|
||||
obj.teavm = {
|
||||
currentTimeMillis: currentTimeMillis,
|
||||
nanoTime: function() { return performance.now(); },
|
||||
isnan: isNaN,
|
||||
teavm_getNaN: function() { return NaN; },
|
||||
isinf: function(n) { return !isFinite(n) },
|
||||
|
|
|
@ -63,7 +63,8 @@ import org.teavm.vm.TeaVMProgressListener;
|
|||
public class IncrementalCBuilder {
|
||||
private String mainClass;
|
||||
private String[] classPath;
|
||||
private int minHeapSize = 32;
|
||||
private int minHeapSize = 4;
|
||||
private int maxHeapSize = 128;
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean lineNumbersGenerated;
|
||||
private String targetPath;
|
||||
|
@ -110,6 +111,10 @@ public class IncrementalCBuilder {
|
|||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public void setMaxHeapSize(int maxHeapSize) {
|
||||
this.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
public void setLineNumbersGenerated(boolean lineNumbersGenerated) {
|
||||
this.lineNumbersGenerated = lineNumbersGenerated;
|
||||
}
|
||||
|
@ -333,6 +338,7 @@ public class IncrementalCBuilder {
|
|||
|
||||
cTarget.setIncremental(true);
|
||||
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
|
||||
cTarget.setMaxHeapSize(maxHeapSize * 1024 * 1024);
|
||||
cTarget.setLineNumbersGenerated(lineNumbersGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(true);
|
||||
|
|
|
@ -60,7 +60,13 @@ public class TeaVMCBuilderRunner {
|
|||
.withLongOpt("min-heap")
|
||||
.withArgName("size")
|
||||
.hasArg()
|
||||
.withDescription("Minimum heap size in bytes")
|
||||
.withDescription("Minimum heap size in megabytes")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("max-heap")
|
||||
.withArgName("size")
|
||||
.hasArg()
|
||||
.withDescription("Minimum heap size in megabytes")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("no-longjmp")
|
||||
|
@ -174,6 +180,18 @@ public class TeaVMCBuilderRunner {
|
|||
}
|
||||
builder.setMinHeapSize(size);
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("max-heap")) {
|
||||
int size;
|
||||
try {
|
||||
size = Integer.parseInt(commandLine.getOptionValue("max-heap"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.print("Wrong heap size");
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
builder.setMaxHeapSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void runAll() {
|
||||
|
|
|
@ -134,6 +134,12 @@ public final class TeaVMRunner {
|
|||
.hasArg()
|
||||
.withDescription("Minimum heap size in megabytes (for C and WebAssembly)")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("max-heap")
|
||||
.withArgName("size")
|
||||
.hasArg()
|
||||
.withDescription("Maximum heap size in megabytes (for C and WebAssembly)")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("max-toplevel-names")
|
||||
.withArgName("number")
|
||||
|
@ -339,6 +345,17 @@ public final class TeaVMRunner {
|
|||
}
|
||||
tool.setMinHeapSize(size * 1024 * 1024);
|
||||
}
|
||||
if (commandLine.hasOption("max-heap")) {
|
||||
int size;
|
||||
try {
|
||||
size = Integer.parseInt(commandLine.getOptionValue("max-heap"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.print("Wrong heap size");
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
tool.setMaxHeapSize(size * 1024 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUp() {
|
||||
|
|
|
@ -102,7 +102,8 @@ public class TeaVMTool {
|
|||
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
private CTarget cTarget;
|
||||
private Set<File> generatedFiles = new HashSet<>();
|
||||
private int minHeapSize = 32 * (1 << 20);
|
||||
private int minHeapSize = 4 * (1 << 20);
|
||||
private int maxHeapSize = 128 * (1 << 20);
|
||||
private ReferenceCache referenceCache;
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean heapDump;
|
||||
|
@ -235,6 +236,10 @@ public class TeaVMTool {
|
|||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public void setMaxHeapSize(int maxHeapSize) {
|
||||
this.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
@ -326,12 +331,14 @@ public class TeaVMTool {
|
|||
webAssemblyTarget.setWastEmitted(debugInformationGenerated);
|
||||
webAssemblyTarget.setVersion(wasmVersion);
|
||||
webAssemblyTarget.setMinHeapSize(minHeapSize);
|
||||
webAssemblyTarget.setMinHeapSize(maxHeapSize);
|
||||
return webAssemblyTarget;
|
||||
}
|
||||
|
||||
private CTarget prepareCTarget() {
|
||||
cTarget = new CTarget(new CNameProvider());
|
||||
cTarget.setMinHeapSize(minHeapSize);
|
||||
cTarget.setMaxHeapSize(maxHeapSize);
|
||||
cTarget.setLineNumbersGenerated(debugInformationGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(heapDump);
|
||||
|
|
|
@ -72,7 +72,9 @@ public interface BuildStrategy {
|
|||
|
||||
void setWasmVersion(WasmBinaryVersion wasmVersion);
|
||||
|
||||
void setHeapSize(int heapSize);
|
||||
void setMinHeapSize(int minHeapSize);
|
||||
|
||||
void setMaxHeapSize(int maxHeapSize);
|
||||
|
||||
void setLongjmpSupported(boolean value);
|
||||
|
||||
|
|
|
@ -59,7 +59,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
private String[] transformers = new String[0];
|
||||
private String[] classesToPreserve = new String[0];
|
||||
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
private int heapSize = 32;
|
||||
private int minHeapSize = 4 * 1024 * 1204;
|
||||
private int maxHeapSize = 128 * 1024 * 1024;
|
||||
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean heapDump;
|
||||
|
@ -194,8 +195,13 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setHeapSize(int heapSize) {
|
||||
this.heapSize = heapSize;
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxHeapSize(int maxHeapSize) {
|
||||
this.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -233,7 +239,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve));
|
||||
tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null);
|
||||
tool.setWasmVersion(wasmVersion);
|
||||
tool.setMinHeapSize(heapSize);
|
||||
tool.setMinHeapSize(minHeapSize);
|
||||
tool.setMaxHeapSize(maxHeapSize);
|
||||
tool.setLongjmpSupported(longjmpSupported);
|
||||
tool.setHeapDump(heapDump);
|
||||
|
||||
|
|
|
@ -171,8 +171,13 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setHeapSize(int heapSize) {
|
||||
request.heapSize = heapSize;
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
request.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxHeapSize(int maxHeapSize) {
|
||||
request.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -158,7 +158,8 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
|
|||
tool.setMinifying(request.minifying);
|
||||
tool.setMaxTopLevelNames(request.maxTopLevelNames);
|
||||
tool.setWasmVersion(request.wasmVersion);
|
||||
tool.setMinHeapSize(request.heapSize);
|
||||
tool.setMinHeapSize(request.minHeapSize);
|
||||
tool.setMaxHeapSize(request.maxHeapSize);
|
||||
tool.setLongjmpSupported(request.longjmpSupported);
|
||||
tool.setHeapDump(request.heapDump);
|
||||
|
||||
|
|
|
@ -45,7 +45,8 @@ public class RemoteBuildRequest implements Serializable {
|
|||
public TeaVMOptimizationLevel optimizationLevel;
|
||||
public boolean fastDependencyAnalysis;
|
||||
public WasmBinaryVersion wasmVersion;
|
||||
public int heapSize;
|
||||
public int minHeapSize;
|
||||
public int maxHeapSize;
|
||||
public boolean longjmpSupported;
|
||||
public boolean heapDump;
|
||||
}
|
||||
|
|
|
@ -93,7 +93,6 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
|
|||
|
||||
@Override
|
||||
public void apply(WasmTarget target) {
|
||||
target.setMinHeapSize(32 * 1024 * 1024);
|
||||
target.setWastEmitted(true);
|
||||
target.setCEmitted(true);
|
||||
target.setDebugging(true);
|
||||
|
|
|
@ -137,8 +137,11 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
|||
@Parameter(property = "teavm.wasmVersion", defaultValue = "V_0x1")
|
||||
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
|
||||
@Parameter(property = "teavm.heapSize", defaultValue = "32")
|
||||
private int heapSize;
|
||||
@Parameter(property = "teavm.minHeapSize", defaultValue = "4")
|
||||
private int minHeapSize;
|
||||
|
||||
@Parameter(property = "teavm.maxHeapSize", defaultValue = "128")
|
||||
private int maxHeapSize;
|
||||
|
||||
@Parameter(property = "teavm.outOfProcess", defaultValue = "false")
|
||||
private boolean outOfProcess;
|
||||
|
@ -173,7 +176,8 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
|||
builder.setDebugInformationGenerated(debugInformationGenerated);
|
||||
builder.setSourceMapsFileGenerated(sourceMapsGenerated);
|
||||
builder.setSourceFilesCopied(sourceFilesCopied);
|
||||
builder.setHeapSize(heapSize * 1024 * 1024);
|
||||
builder.setMinHeapSize(minHeapSize * 1024 * 1024);
|
||||
builder.setMaxHeapSize(maxHeapSize * 1024 * 1024);
|
||||
} catch (RuntimeException e) {
|
||||
throw new MojoExecutionException("Unexpected error occurred", e);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user