C/Wasm: resizable heap

This commit is contained in:
Alexey Andreev 2019-09-05 17:17:41 +03:00
parent f0b6cc2f30
commit fe3436f053
45 changed files with 779 additions and 268 deletions

View File

@ -259,13 +259,18 @@ public final class TSystem extends TObject {
} }
public static long nanoTime() { public static long nanoTime() {
if (PlatformDetector.isLowLevel()) { if (PlatformDetector.isWebAssembly()) {
return (long) (nanoTimeWasm() * 1000000);
} else if (PlatformDetector.isLowLevel()) {
return nanoTimeLowLevel(); return nanoTimeLowLevel();
} else { } else {
return (long) (Performance.now() * 1000000); return (long) (Performance.now() * 1000000);
} }
} }
@Import(module = "teavm", name = "nanoTime")
private static native double nanoTimeWasm();
@Import(name = "teavm_currentTimeNano") @Import(name = "teavm_currentTimeNano")
@Include("time.h") @Include("time.h")
private static native long nanoTimeLowLevel(); private static native long nanoTimeLowLevel();

View File

@ -151,7 +151,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private NullCheckInsertion nullCheckInsertion; private NullCheckInsertion nullCheckInsertion;
private NullCheckTransformation nullCheckTransformation; private NullCheckTransformation nullCheckTransformation;
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener(); 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<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
private List<GeneratorFactory> generatorFactories = new ArrayList<>(); private List<GeneratorFactory> generatorFactories = new ArrayList<>();
private Characteristics characteristics; private Characteristics characteristics;
@ -173,6 +174,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
this.minHeapSize = minHeapSize; this.minHeapSize = minHeapSize;
} }
public void setMaxHeapSize(int maxHeapSize) {
this.maxHeapSize = maxHeapSize;
}
public void setIncremental(boolean incremental) { public void setIncremental(boolean incremental) {
this.incremental = 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(Allocator.class, "<clinit>", void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "fixHeap", 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", dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
Throwable.class, void.class)).use(); 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("int " + mainFunctionName + "(int argc, char** argv) {").indent();
writer.println("teavm_beforeInit();"); writer.println("teavm_beforeInit();");
writer.println("teavm_initHeap(" + minHeapSize + ");"); writer.println("teavm_initHeap(" + minHeapSize + ", " + maxHeapSize + ");");
generateVirtualTableHeaders(context, writer); generateVirtualTableHeaders(context, writer);
writer.println("teavm_initStringPool();"); writer.println("teavm_initStringPool();");
for (ValueType type : types) { for (ValueType type : types) {

View File

@ -34,6 +34,9 @@ public class GCIntrinsic implements Intrinsic {
case "regionMaxCount": case "regionMaxCount":
case "availableBytes": case "availableBytes":
case "regionSize": case "regionSize":
case "minAvailableBytes":
case "maxAvailableBytes":
case "resizeHeap":
return true; return true;
default: default:
return false; return false;
@ -43,6 +46,13 @@ public class GCIntrinsic implements Intrinsic {
@Override @Override
public void apply(IntrinsicContext context, InvocationExpr invocation) { public void apply(IntrinsicContext context, InvocationExpr invocation) {
context.includes().includePath("memory.h"); 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.includes().includePath("heaptrace.h");
context.writer().print("teavm_gc_").print(invocation.getMethod().getName()); context.writer().print("teavm_gc_").print(invocation.getMethod().getName());
} }

View 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;
}
}

View File

@ -24,13 +24,9 @@ import org.teavm.runtime.RuntimeObject;
@StaticInit @StaticInit
@Unmanaged @Unmanaged
public final class WasmRuntime { public final class WasmRuntime {
public static Address stack = initStack();
private WasmRuntime() { private WasmRuntime() {
} }
private static native Address initStack();
public static int compare(int a, int b) { public static int compare(int a, int b) {
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
} }
@ -80,6 +76,14 @@ public final class WasmRuntime {
return Address.fromInt(value); 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") @Import(name = "print", module = "spectest")
public static native void print(int a); public static native void print(int a);
@ -252,20 +256,20 @@ public final class WasmRuntime {
} }
public static Address allocStack(int size) { public static Address allocStack(int size) {
Address result = stack.add(4); Address result = WasmHeap.stack.add(4);
stack = result.add((size << 2) + 4); WasmHeap.stack = result.add((size << 2) + 4);
stack.putInt(size); WasmHeap.stack.putInt(size);
return result; return result;
} }
public static Address getStackTop() { public static Address getStackTop() {
return stack != initStack() ? stack : null; return WasmHeap.stack != WasmHeap.stackAddress ? WasmHeap.stack : null;
} }
public static Address getNextStackFrame(Address stackFrame) { public static Address getNextStackFrame(Address stackFrame) {
int size = stackFrame.getInt() + 2; int size = stackFrame.getInt() + 2;
Address result = stackFrame.add(-size * 4); Address result = stackFrame.add(-size * 4);
if (result == initStack()) { if (result == WasmHeap.stackAddress) {
result = null; result = null;
} }
return result; return result;

View File

@ -64,6 +64,7 @@ import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic;
import org.teavm.backend.wasm.intrinsics.RuntimeClassIntrinsic; import org.teavm.backend.wasm.intrinsics.RuntimeClassIntrinsic;
import org.teavm.backend.wasm.intrinsics.ShadowStackIntrinsic; import org.teavm.backend.wasm.intrinsics.ShadowStackIntrinsic;
import org.teavm.backend.wasm.intrinsics.StructureIntrinsic; 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.WasmIntrinsicFactory;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactoryContext; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactoryContext;
import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic;
@ -149,6 +150,8 @@ import org.teavm.vm.TeaVMTargetController;
import org.teavm.vm.spi.TeaVMHostExtension; import org.teavm.vm.spi.TeaVMHostExtension;
public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { 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 TeaVMTargetController controller;
private boolean debugging; private boolean debugging;
private boolean wastEmitted; private boolean wastEmitted;
@ -159,7 +162,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private ShadowStackTransformer shadowStackTransformer; private ShadowStackTransformer shadowStackTransformer;
private WasmBinaryVersion version = WasmBinaryVersion.V_0x1; private WasmBinaryVersion version = WasmBinaryVersion.V_0x1;
private List<WasmIntrinsicFactory> additionalIntrinsics = new ArrayList<>(); private List<WasmIntrinsicFactory> additionalIntrinsics = new ArrayList<>();
private int minHeapSize; private int minHeapSize = 2 * 1024 * 1024;
private int maxHeapSize = 128 * 1024 * 1024;
@Override @Override
public void setController(TeaVMTargetController controller) { public void setController(TeaVMTargetController controller) {
@ -241,6 +245,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
this.minHeapSize = minHeapSize; this.minHeapSize = minHeapSize;
} }
public void setMaxHeapSize(int maxHeapSize) {
this.maxHeapSize = maxHeapSize;
}
@Override @Override
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) { public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) { 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", dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory",
void.class)).use(); void.class)).use();
dependencyAnalyzer.linkMethod(INIT_HEAP_REF).use();
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate", dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
RuntimeClass.class, Address.class)).use(); RuntimeClass.class, Address.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray", dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
@ -360,6 +370,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
context.addIntrinsic(new ConsoleIntrinsic()); context.addIntrinsic(new ConsoleIntrinsic());
context.addGenerator(new ArrayGenerator()); context.addGenerator(new ArrayGenerator());
context.addIntrinsic(new MemoryTraceIntrinsic()); context.addIntrinsic(new MemoryTraceIntrinsic());
context.addIntrinsic(new WasmHeapIntrinsic());
IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext();
for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) { for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) {
@ -377,9 +388,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter); 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); generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames())); exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
generateIsSupertypeFunctions(tagRegistry, module, classGenerator); generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
@ -391,23 +399,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
dataSegment.setOffset(256); dataSegment.setOffset(256);
module.getSegments().add(dataSegment); module.getSegments().add(dataSegment);
renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic, wasmRuntimeIntrinsic); renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic);
renderClinit(classes, classGenerator, module); renderClinit(classes, classGenerator, module);
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
return; return;
} }
for (String className : classes.getClassNames()) { generateInitFunction(classes, initFunction, names, binaryWriter.getAddress());
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)));
}
module.add(initFunction); module.add(initFunction);
module.setStartFunction(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) { private String getBaseName(String name) {
int index = name.lastIndexOf('.'); int index = name.lastIndexOf('.');
return index < 0 ? name : name.substring(0, index); 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, private void renderMemoryLayout(WasmModule module, int address, GCIntrinsic gcIntrinsic) {
WasmRuntimeIntrinsic runtimeIntrinsic) { module.setMinMemorySize(WasmRuntime.align(address, WasmHeap.PAGE_SIZE) / WasmHeap.PAGE_SIZE);
address = (((address - 1) / 256) + 1) * 256;
runtimeIntrinsic.setStackAddress(address); int newStorageSize = WasmHeap.calculateStorageSize(maxHeapSize);
address += 65536; int newRegionsCount = WasmHeap.calculateRegionsCount(maxHeapSize, WasmHeap.DEFAULT_REGION_SIZE);
int newRegionsSize = WasmHeap.calculateRegionsSize(newRegionsCount);
int gcMemory = module.getMemorySize() * 65536 - address; address = WasmRuntime.align(address + WasmHeap.DEFAULT_STACK_SIZE, 16);
int storageSize = (gcMemory >> 6) >> 2 << 2; address = WasmRuntime.align(address + maxHeapSize, 16);
gcIntrinsic.setGCStorageAddress(address); address = WasmRuntime.align(address + newRegionsSize, 16);
gcIntrinsic.setGCStorageSize(storageSize); address = WasmRuntime.align(address + newStorageSize, 16);
gcIntrinsic.setRegionSize(WasmHeap.DEFAULT_REGION_SIZE);
gcMemory -= storageSize; module.setMaxMemorySize(WasmRuntime.align(address, WasmHeap.PAGE_SIZE) / WasmHeap.PAGE_SIZE);
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);
} }
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {

View File

@ -59,6 +59,7 @@ public class CallSiteBinaryGenerator {
private BinaryWriter writer; private BinaryWriter writer;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private WasmStringPool stringPool; private WasmStringPool stringPool;
private ObjectIntMap<String> stringIndirectPointerCache = new ObjectIntHashMap<>();
public CallSiteBinaryGenerator(BinaryWriter writer, WasmClassGenerator classGenerator, WasmStringPool stringPool) { public CallSiteBinaryGenerator(BinaryWriter writer, WasmClassGenerator classGenerator, WasmStringPool stringPool) {
this.writer = writer; this.writer = writer;
@ -132,15 +133,15 @@ public class CallSiteBinaryGenerator {
methodLocationCache.put(methodLocation, methodLocationAddress); methodLocationCache.put(methodLocation, methodLocationAddress);
if (location.getFileName() != null) { if (location.getFileName() != null) {
binaryMethodLocation.setAddress(METHOD_LOCATION_FILE, binaryMethodLocation.setAddress(METHOD_LOCATION_FILE,
stringPool.getStringPointer(location.getFileName())); getStringIndirectPointer(location.getFileName()));
} }
if (location.getClassName() != null) { if (location.getClassName() != null) {
binaryMethodLocation.setAddress(METHOD_LOCATION_CLASS, binaryMethodLocation.setAddress(METHOD_LOCATION_CLASS,
stringPool.getStringPointer(location.getClassName())); getStringIndirectPointer(location.getClassName()));
} }
if (location.getMethodName() != null) { if (location.getMethodName() != null) {
binaryMethodLocation.setAddress(METHOD_LOCATION_METHOD, binaryMethodLocation.setAddress(METHOD_LOCATION_METHOD,
stringPool.getStringPointer(location.getMethodName())); getStringIndirectPointer(location.getMethodName()));
} }
} }
@ -153,6 +154,16 @@ public class CallSiteBinaryGenerator {
return firstCallSite; 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 static class MethodLocation {
final String file; final String file;
final String className; final String className;

View File

@ -621,7 +621,7 @@ public class WasmClassGenerator {
return cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null; return cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;
} }
private class ClassBinaryData { static class ClassBinaryData {
ValueType type; ValueType type;
int size; int size;
int alignment; int alignment;

View File

@ -64,6 +64,7 @@ import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr; import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement; import org.teavm.ast.WhileStatement;
import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.WasmHeap;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.BinaryWriter;
import org.teavm.backend.wasm.binary.DataPrimitives; import org.teavm.backend.wasm.binary.DataPrimitives;
@ -1015,7 +1016,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
+ "Mutator.allocStack"); + "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); WasmExpression oldValue = new WasmGetLocal(stackVariable);
oldValue = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, oldValue, oldValue = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, oldValue,
new WasmInt32Constant(4)); new WasmInt32Constant(4));
@ -1541,6 +1542,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
public void releaseTemporary(WasmLocal local) { public void releaseTemporary(WasmLocal local) {
WasmGenerationVisitor.this.releaseTemporary(local); WasmGenerationVisitor.this.releaseTemporary(local);
} }
@Override
public int getStaticField(FieldReference field) {
return classGenerator.getFieldOffset(field);
}
}; };
private WasmLocal getTemporary(WasmType type) { private WasmLocal getTemporary(WasmType type) {

View File

@ -18,50 +18,27 @@ package org.teavm.backend.wasm.intrinsics;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmHeap;
import org.teavm.backend.wasm.WasmRuntime; 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.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCall; 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.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant; 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.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.runtime.GC; import org.teavm.runtime.GC;
public class GCIntrinsic implements WasmIntrinsic { public class GCIntrinsic implements WasmIntrinsic {
private static final MethodReference PRINT_OUT_OF_MEMORY = new MethodReference( private static final MethodReference PRINT_OUT_OF_MEMORY = new MethodReference(
WasmRuntime.class, "printOutOfMemory", void.class); WasmRuntime.class, "printOutOfMemory", void.class);
private List<WasmInt32Constant> heapAddressExpressions = new ArrayList<>(); private static final MethodReference RESIZE_HEAP = new MethodReference(
private List<WasmInt64Constant> availableBytesExpressions = new ArrayList<>(); WasmHeap.class, "printOutOfMemory", void.class);
private List<WasmInt32Constant> gcStorageAddressExpressions = new ArrayList<>();
private List<WasmInt32Constant> gcStorageSizeExpressions = new ArrayList<>();
private List<WasmInt32Constant> regionSizeExpressions = new ArrayList<>(); 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) { public void setRegionSize(int regionSize) {
for (WasmInt32Constant constant : regionSizeExpressions) { 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 @Override
public boolean isApplicable(MethodReference methodReference) { public boolean isApplicable(MethodReference methodReference) {
if (!methodReference.getClassName().endsWith(GC.class.getName())) { if (!methodReference.getClassName().equals(GC.class.getName())) {
return false; return false;
} }
@ -96,6 +61,9 @@ public class GCIntrinsic implements WasmIntrinsic {
case "regionMaxCount": case "regionMaxCount":
case "regionSize": case "regionSize":
case "outOfMemory": case "outOfMemory":
case "minAvailableBytes":
case "maxAvailableBytes":
case "resizeHeap":
return true; return true;
default: default:
return false; return false;
@ -104,31 +72,33 @@ public class GCIntrinsic implements WasmIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
List<WasmInt32Constant> list;
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "gcStorageAddress": case "gcStorageAddress":
list = gcStorageAddressExpressions; return getStaticField(manager, "storageAddress");
break;
case "gcStorageSize": case "gcStorageSize":
list = gcStorageSizeExpressions; return getStaticField(manager, "storageSize");
break;
case "heapAddress": case "heapAddress":
list = heapAddressExpressions; return getStaticField(manager, "heapAddress");
break;
case "regionsAddress": case "regionsAddress":
list = regionsAddressExpressions; return getStaticField(manager, "regionsAddress");
break;
case "regionMaxCount": case "regionMaxCount":
list = regionMaxCountExpressions; return getStaticField(manager, "regionsCount");
break; case "minAvailableBytes":
case "regionSize": return intToLong(getStaticField(manager, "minHeapSize"));
list = regionSizeExpressions; case "maxAvailableBytes":
break; return intToLong(getStaticField(manager, "maxHeapSize"));
case "availableBytes": { case "resizeHeap": {
WasmInt64Constant constant = new WasmInt64Constant(0); WasmExpression amount = manager.generate(invocation.getArguments().get(0));
availableBytesExpressions.add(constant); amount = new WasmConversion(WasmType.INT64, WasmType.INT32, false, amount);
return constant; 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": { case "outOfMemory": {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY), true); WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY), true);
@ -139,8 +109,14 @@ public class GCIntrinsic implements WasmIntrinsic {
default: default:
throw new IllegalArgumentException(invocation.getMethod().toString()); 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);
} }
} }

View File

@ -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());
}
}
}

View File

@ -23,6 +23,7 @@ import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.FieldReference;
public interface WasmIntrinsicManager { public interface WasmIntrinsicManager {
WasmExpression generate(Expr expr); WasmExpression generate(Expr expr);
@ -37,5 +38,7 @@ public interface WasmIntrinsicManager {
WasmLocal getTemporary(WasmType type); WasmLocal getTemporary(WasmType type);
int getStaticField(FieldReference field);
void releaseTemporary(WasmLocal local); void releaseTemporary(WasmLocal local);
} }

View File

@ -15,8 +15,6 @@
*/ */
package org.teavm.backend.wasm.intrinsics; package org.teavm.backend.wasm.intrinsics;
import java.util.ArrayList;
import java.util.List;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.generate.WasmGeneratorUtil; 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.WasmFloatBinary;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType; 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.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class WasmRuntimeIntrinsic implements WasmIntrinsic { public class WasmRuntimeIntrinsic implements WasmIntrinsic {
private List<WasmInt32Constant> stackExpressions = new ArrayList<>();
@Override @Override
public boolean isApplicable(MethodReference methodReference) { public boolean isApplicable(MethodReference methodReference) {
if (!methodReference.getClassName().equals(WasmRuntime.class.getName())) { 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 @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
@ -64,11 +53,6 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
case "gt": case "gt":
return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT, return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT,
invocation, manager); invocation, manager);
case "initStack": {
WasmInt32Constant constant = new WasmInt32Constant(0);
stackExpressions.add(constant);
return constant;
}
default: default:
throw new IllegalArgumentException(invocation.getMethod().getName()); throw new IllegalArgumentException(invocation.getMethod().getName());
} }

View File

@ -22,7 +22,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class WasmModule { public class WasmModule {
private int memorySize; private int minMemorySize;
private int maxMemorySize;
private List<WasmMemorySegment> segments = new ArrayList<>(); private List<WasmMemorySegment> segments = new ArrayList<>();
private Map<String, WasmFunction> functions = new LinkedHashMap<>(); private Map<String, WasmFunction> functions = new LinkedHashMap<>();
private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions); private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions);
@ -60,12 +61,20 @@ public class WasmModule {
return segments; return segments;
} }
public int getMemorySize() { public int getMinMemorySize() {
return memorySize; return minMemorySize;
} }
public void setMemorySize(int memorySize) { public void setMinMemorySize(int minMemorySize) {
this.memorySize = memorySize; this.minMemorySize = minMemorySize;
}
public int getMaxMemorySize() {
return maxMemorySize;
}
public void setMaxMemorySize(int maxMemorySize) {
this.maxMemorySize = maxMemorySize;
} }
public WasmFunction getStartFunction() { public WasmFunction getStartFunction() {

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.model.expression; package org.teavm.backend.wasm.model.expression;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -34,6 +35,11 @@ public class WasmCall extends WasmExpression {
this(functionName, false); this(functionName, false);
} }
public WasmCall(String functionName, WasmExpression... arguments) {
this(functionName);
getArguments().addAll(Arrays.asList(arguments));
}
public String getFunctionName() { public String getFunctionName() {
return functionName; return functionName;
} }

View File

@ -180,4 +180,9 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
expression.getIndex().acceptVisitor(this); expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this); expression.getValue().acceptVisitor(this);
} }
@Override
public void visit(WasmMemoryGrow expression) {
expression.getAmount().acceptVisitor(this);
}
} }

View File

@ -73,4 +73,6 @@ public interface WasmExpressionVisitor {
void visit(WasmStoreFloat32 expression); void visit(WasmStoreFloat32 expression);
void visit(WasmStoreFloat64 expression); void visit(WasmStoreFloat64 expression);
void visit(WasmMemoryGrow expression);
} }

View File

@ -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);
}
}

View File

@ -221,4 +221,10 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
expression.getValue().acceptVisitor(this); expression.getValue().acceptVisitor(this);
expression.setValue(mapper.apply(expression.getValue())); expression.setValue(mapper.apply(expression.getValue()));
} }
@Override
public void visit(WasmMemoryGrow expression) {
expression.getAmount().acceptVisitor(this);
expression.setAmount(mapper.apply(expression.getAmount()));
}
} }

View File

@ -179,8 +179,8 @@ public class WasmBinaryRenderer {
section.writeByte(1); section.writeByte(1);
section.writeByte(1); section.writeByte(1);
section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMinMemorySize());
section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMaxMemorySize());
writeSection(SECTION_MEMORY, "memory", section.getData()); writeSection(SECTION_MEMORY, "memory", section.getData());
} }
@ -333,7 +333,7 @@ public class WasmBinaryRenderer {
WasmBinaryWriter functionsSubsection = new WasmBinaryWriter(); WasmBinaryWriter functionsSubsection = new WasmBinaryWriter();
Collection<WasmFunction> functions = module.getFunctions().values(); 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()); functionsSubsection.writeLEB(functions.size());
for (WasmFunction function : functions) { for (WasmFunction function : functions) {

View File

@ -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.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -824,6 +825,13 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
writer.writeLEB(expression.getOffset()); 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) { private int alignment(int value) {
return 31 - Integer.numberOfLeadingZeros(Math.max(1, value)); return 31 - Integer.numberOfLeadingZeros(Math.max(1, value));
} }

View File

@ -72,8 +72,6 @@ public class WasmCRenderer {
line(""); line("");
renderFunctionDeclarations(module); renderFunctionDeclarations(module);
line("static int8_t *wasm_heap;");
line("static int32_t wasm_heap_size;");
renderFunctionTable(module); renderFunctionTable(module);
for (WasmFunction function : module.getFunctions().values()) { for (WasmFunction function : module.getFunctions().values()) {
@ -117,8 +115,8 @@ public class WasmCRenderer {
} }
private void renderHeap(WasmModule module) { private void renderHeap(WasmModule module) {
line("wasm_heap_size = " + 65536 * module.getMemorySize() + ";"); line("wasm_heap_size = " + 65536 * module.getMinMemorySize() + ";");
line("wasm_heap = malloc(" + 65536 * module.getMemorySize() + ");"); line("wasm_heap = malloc(wasm_heap_size);");
for (WasmMemorySegment segment : module.getSegments()) { for (WasmMemorySegment segment : module.getSegments()) {
line("memcpy(wasm_heap + " + segment.getOffset() + ","); line("memcpy(wasm_heap + " + segment.getOffset() + ",");
indent(); indent();

View File

@ -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.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -1030,6 +1031,26 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
value = result; 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) { private CExpression checkAddress(CExpression index) {
if (!memoryAccessChecked) { if (!memoryAccessChecked) {
return index; return index;
@ -1044,7 +1065,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
} else { } else {
var = index.getText(); var = index.getText();
} }
checked.addLine("assert(" + var + " < " + module.getMemorySize() * 65536 + ");"); checked.addLine("assert(" + var + " < " + module.getMinMemorySize() * 65536 + ");");
checked.setText(var); checked.setText(var);
checked.setRelocatable(index.isRelocatable()); checked.setRelocatable(index.isRelocatable());

View File

@ -82,7 +82,7 @@ public class WasmRenderer {
public void renderMemory(WasmModule module) { public void renderMemory(WasmModule module) {
visitor.lf(); 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) { public void renderData(WasmModule module) {

View File

@ -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.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -594,6 +595,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close(); close();
} }
@Override
public void visit(WasmMemoryGrow expression) {
open().append("memory.grow");
line(expression.getAmount());
close();
}
private String type(WasmType type) { private String type(WasmType type) {
switch (type) { switch (type) {
case INT32: case INT32:

View File

@ -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.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -209,6 +210,11 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = null; result = null;
} }
@Override
public void visit(WasmMemoryGrow expression) {
result = WasmType.INT32;
}
private static WasmType map(WasmIntType type) { private static WasmType map(WasmIntType type) {
switch (type) { switch (type) {
case INT32: case INT32:

View File

@ -76,6 +76,8 @@ public class ExceptionHandlingShadowStackContributor {
private boolean hasExceptionHandlers; private boolean hasExceptionHandlers;
private int parameterCount; private int parameterCount;
public int callSiteIdGen;
public ExceptionHandlingShadowStackContributor(Characteristics characteristics, public ExceptionHandlingShadowStackContributor(Characteristics characteristics,
List<CallSiteDescriptor> callSites, MethodReference method, Program program) { List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
this.characteristics = characteristics; this.characteristics = characteristics;
@ -246,7 +248,7 @@ public class ExceptionHandlingShadowStackContributor {
} }
CallSiteLocation location = new CallSiteLocation(fileName, method.getClassName(), method.getName(), CallSiteLocation location = new CallSiteLocation(fileName, method.getClassName(), method.getName(),
lineNumber); lineNumber);
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location); CallSiteDescriptor callSite = new CallSiteDescriptor(callSiteIdGen++, location);
callSites.add(callSite); callSites.add(callSite);
List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite, List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite,

View File

@ -38,6 +38,7 @@ public class ShadowStackTransformer {
private Characteristics characteristics; private Characteristics characteristics;
private GCShadowStackContributor gcContributor; private GCShadowStackContributor gcContributor;
private boolean exceptionHandling; private boolean exceptionHandling;
private int callSiteIdGen;
public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) { public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) {
gcContributor = new GCShadowStackContributor(characteristics); gcContributor = new GCShadowStackContributor(characteristics);
@ -54,8 +55,12 @@ public class ShadowStackTransformer {
boolean exceptions; boolean exceptions;
if (exceptionHandling) { if (exceptionHandling) {
List<CallSiteDescriptor> callSites = new ArrayList<>(); List<CallSiteDescriptor> callSites = new ArrayList<>();
exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites, ExceptionHandlingShadowStackContributor exceptionContributor =
method.getReference(), program).contribute(); new ExceptionHandlingShadowStackContributor(characteristics, callSites,
method.getReference(), program);
exceptionContributor.callSiteIdGen = callSiteIdGen;
exceptions = exceptionContributor.contribute();
callSiteIdGen = exceptionContributor.callSiteIdGen;
CallSiteDescriptor.save(callSites, program.getAnnotations()); CallSiteDescriptor.save(callSites, program.getAnnotations());
} else { } else {
exceptions = false; exceptions = false;

View File

@ -103,6 +103,11 @@ public final class ExceptionHandling {
} }
if (stackFrame == null) { if (stackFrame == null) {
stackFrame = ShadowStack.getStackTop();
while (stackFrame != null) {
ShadowStack.setExceptionHandlerId(stackFrame, ShadowStack.getCallSiteId(stackFrame) + 1);
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
}
printStack(); printStack();
abort(); abort();
} else if (isJumpSupported()) { } else if (isJumpSupported()) {
@ -134,6 +139,7 @@ public final class ExceptionHandling {
public static void fillStackTrace(StackTraceElement[] target) { public static void fillStackTrace(StackTraceElement[] target) {
Address stackFrame = ShadowStack.getStackTop(); Address stackFrame = ShadowStack.getStackTop();
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
int index = 0; int index = 0;
while (stackFrame != null && index < target.length) { while (stackFrame != null && index < target.length) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame); int callSiteId = ShadowStack.getCallSiteId(stackFrame);

View File

@ -34,6 +34,7 @@ public final class GC {
static int freeChunks; static int freeChunks;
static int freeMemory = (int) availableBytes(); static int freeMemory = (int) availableBytes();
static RuntimeReference firstWeakReference; static RuntimeReference firstWeakReference;
static FreeChunk lastChunk;
static RelocationBlock lastRelocationBlock; static RelocationBlock lastRelocationBlock;
@ -49,6 +50,12 @@ public final class GC {
public static native long availableBytes(); 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(); private static native int regionSize();
@Import(name = "teavm_outOfMemory") @Import(name = "teavm_outOfMemory")
@ -86,7 +93,7 @@ public final class GC {
if (getNextChunkIfPossible(size)) { if (getNextChunkIfPossible(size)) {
return; return;
} }
collectGarbage(); collectGarbageImpl(size);
if (currentChunk.size < size && !getNextChunkIfPossible(size)) { if (currentChunk.size < size && !getNextChunkIfPossible(size)) {
ExceptionHandling.printStack(); ExceptionHandling.printStack();
outOfMemory(); outOfMemory();
@ -113,7 +120,13 @@ public final class GC {
return true; return true;
} }
@Export(name = "teavm_gc_collect")
public static void collectGarbage() { public static void collectGarbage() {
fixHeap();
collectGarbageImpl(0);
}
private static void collectGarbageImpl(int size) {
MemoryTrace.gcStarted(); MemoryTrace.gcStarted();
mark(); mark();
processReferences(); processReferences();
@ -121,12 +134,36 @@ public final class GC {
MemoryTrace.sweepCompleted(); MemoryTrace.sweepCompleted();
defragment(); defragment();
MemoryTrace.defragCompleted(); MemoryTrace.defragCompleted();
//sortFreeChunks();
updateFreeMemory(); updateFreeMemory();
long minRequestedSize = 0;
if (!hasAvailableChunk(size)) {
minRequestedSize = computeMinRequestedSize(size);
}
resizeHeapIfNecessary(minRequestedSize);
currentChunk = currentChunkPointer.value; currentChunk = currentChunkPointer.value;
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size); 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") @Export(name = "teavm_gc_fixHeap")
public static void fixHeap() { public static void fixHeap() {
if (freeChunks > 0) { 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() { private static void mark() {
MemoryTrace.initMark(); MemoryTrace.initMark();
firstWeakReference = null; firstWeakReference = null;
@ -295,8 +341,6 @@ public final class GC {
FreeChunk object = heapAddress().toStructure(); FreeChunk object = heapAddress().toStructure();
FreeChunk lastFreeSpace = null; FreeChunk lastFreeSpace = null;
long heapSize = availableBytes(); long heapSize = availableBytes();
long reclaimedSpace = 0;
long maxFreeChunk = 0;
int regionsCount = (int) ((heapSize - 1) / regionSize()) + 1; int regionsCount = (int) ((heapSize - 1) / regionSize()) + 1;
Address currentRegionEnd = null; Address currentRegionEnd = null;
Address limit = heapAddress().add(heapSize); Address limit = heapAddress().add(heapSize);
@ -348,10 +392,6 @@ public final class GC {
freeChunkPtr.value = lastFreeSpace; freeChunkPtr.value = lastFreeSpace;
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
freeChunks++; freeChunks++;
reclaimedSpace += lastFreeSpace.size;
if (maxFreeChunk < lastFreeSpace.size) {
maxFreeChunk = lastFreeSpace.size;
}
lastFreeSpace = null; lastFreeSpace = null;
} }
} }
@ -366,12 +406,7 @@ public final class GC {
lastFreeSpace.size = freeSize; lastFreeSpace.size = freeSize;
MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size); MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size);
freeChunkPtr.value = lastFreeSpace; freeChunkPtr.value = lastFreeSpace;
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
freeChunks++; freeChunks++;
reclaimedSpace += freeSize;
if (maxFreeChunk < freeSize) {
maxFreeChunk = freeSize;
}
} }
currentChunkPointer = gcStorageAddress().toStructure(); currentChunkPointer = gcStorageAddress().toStructure();
@ -425,6 +460,7 @@ public final class GC {
Address relocations = Structure.add(FreeChunk.class, freeChunk, 1).toAddress(); Address relocations = Structure.add(FreeChunk.class, freeChunk, 1).toAddress();
Address relocationsLimit = freeChunk.toAddress().add(freeChunk.size); Address relocationsLimit = freeChunk.toAddress().add(freeChunk.size);
lastChunk = heapAddress().toStructure();
boolean lastWasLocked = false; boolean lastWasLocked = false;
objects: while (object.toAddress().isLessThan(limit)) { objects: while (object.toAddress().isLessThan(limit)) {
@ -461,6 +497,7 @@ public final class GC {
lastRelocationBlock.start = object.toAddress().add(size); lastRelocationBlock.start = object.toAddress().add(size);
lastRelocationBlock.count = 0; lastRelocationBlock.count = 0;
object.classReference &= ~RuntimeObject.GC_MARKED; object.classReference &= ~RuntimeObject.GC_MARKED;
lastChunk = object;
} else { } else {
lastWasLocked = false; lastWasLocked = false;
@ -721,6 +758,9 @@ public final class GC {
while (!lastRelocationBlock.toAddress().isLessThan(relocationBlock.toAddress())) { while (!lastRelocationBlock.toAddress().isLessThan(relocationBlock.toAddress())) {
if (relocationBlock.start.isLessThan(relocationBlock.end)) { if (relocationBlock.start.isLessThan(relocationBlock.end)) {
FreeChunk freeChunk = relocationBlock.start.toStructure(); FreeChunk freeChunk = relocationBlock.start.toStructure();
if (!freeChunk.toAddress().isLessThan(lastChunk.toAddress())) {
lastChunk = freeChunk;
}
freeChunk.size = (int) (relocationBlock.end.toLong() - relocationBlock.start.toLong()); freeChunk.size = (int) (relocationBlock.end.toLong() - relocationBlock.start.toLong());
freeChunk.classReference = 0; freeChunk.classReference = 0;
MemoryTrace.assertFree(freeChunk.toAddress(), freeChunk.size); 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() { private static void updateFreeMemory() {
freeMemory = 0; freeMemory = 0;
FreeChunkHolder freeChunkPtr = currentChunkPointer; FreeChunkHolder freeChunkPtr = currentChunkPointer;
@ -746,63 +781,71 @@ public final class GC {
} }
} }
private static void sortFreeChunks(int lower, int upper) { private static void resizeHeapConsistent(long newSize) {
int start = lower; long oldSize = availableBytes();
int end = upper; if (newSize == oldSize) {
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) {
return; return;
} }
if (lower - start > 0) { if (newSize > oldSize) {
sortFreeChunks(start, lower); resizeHeap(newSize);
} if (lastChunk.classReference == 0) {
if (end - lower - 1 > 0) { lastChunk.size += (int) (newSize - oldSize);
sortFreeChunks(lower + 1, end); } 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) { private static void resizeHeapIfNecessary(long requestedSize) {
return Structure.add(FreeChunkHolder.class, currentChunkPointer, index); 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) { private static int objectSize(FreeChunk object) {

View File

@ -20,68 +20,160 @@ void* teavm_gc_gcStorageAddress = NULL;
int32_t teavm_gc_gcStorageSize = INT32_C(0); int32_t teavm_gc_gcStorageSize = INT32_C(0);
void* teavm_gc_regionsAddress = NULL; void* teavm_gc_regionsAddress = NULL;
int32_t teavm_gc_regionSize = INT32_C(32768); int32_t teavm_gc_regionSize = INT32_C(32768);
int32_t teavm_gc_regionMaxCount = INT32_C(0); int32_t teavm_gc_regionMaxCount;
int64_t teavm_gc_availableBytes = INT64_C(0); int64_t teavm_gc_availableBytes;
int64_t teavm_gc_minAvailableBytes;
int64_t teavm_gc_maxAvailableBytes;
static int64_t teavm_gc_pageSize;
#if TEAVM_UNIX #if TEAVM_UNIX
static void* teavm_virtualAlloc(int size) { static void* teavm_virtualAlloc(int64_t size) {
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 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); return sysconf(_SC_PAGE_SIZE);
} }
#endif #endif
#if TEAVM_WINDOWS #if TEAVM_WINDOWS
static void* teavm_virtualAlloc(int size) { static void* teavm_virtualAlloc(int64_t size) {
#if TEAVM_WINDOWS_UWP #if TEAVM_WINDOWS_UWP
return VirtualAllocFromApp( return VirtualAllocFromApp(
NULL, NULL,
size, size,
MEM_RESERVE | MEM_COMMIT, MEM_RESERVE,
PAGE_READWRITE PAGE_NOACCESS
); );
#else #else
return VirtualAlloc( return VirtualAlloc(
NULL, NULL,
size, 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 PAGE_READWRITE
); );
#endif #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; SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo); GetSystemInfo(&systemInfo);
return systemInfo.dwPageSize; return systemInfo.dwPageSize;
} }
#endif #endif
static int teavm_pageCount(int64_t size, int64_t pageSize) { static int64_t teavm_pageCount(int64_t size) {
return (int) ((size + pageSize + 1) / pageSize * pageSize); return (int64_t) ((size + teavm_gc_pageSize - 1) / teavm_gc_pageSize * teavm_gc_pageSize);
} }
void teavm_initHeap(int64_t heapSize) { static int32_t teavm_gc_calculateWorkSize(int64_t heapSize) {
long workSize = (long) (heapSize / 16); return (int32_t) (heapSize / 16);
long regionsSize = (long) (heapSize / teavm_gc_regionSize) + 1; }
long pageSize = teavm_pageSize();
teavm_gc_heapAddress = teavm_virtualAlloc(teavm_pageCount(heapSize, pageSize)); static int32_t teavm_gc_calculateRegionsSize(int64_t heapSize) {
teavm_gc_gcStorageAddress = teavm_virtualAlloc(teavm_pageCount(workSize, pageSize)); return (int32_t) (heapSize / teavm_gc_regionSize) + 1;
teavm_gc_regionsAddress = teavm_virtualAlloc(teavm_pageCount(regionsSize * 2, pageSize)); }
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 #if TEAVM_MEMORY_TRACE
int64_t heapMapSize = heapSize / sizeof(void*); int64_t heapMapSize = maxHeap / sizeof(void*);
teavm_gc_heapMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize, pageSize)); teavm_gc_heapMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize));
teavm_virtualCommit(teavm_gc_heapMap, teavm_pageCount(heapMapSize));
memset(teavm_gc_heapMap, 0, 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 #endif
teavm_gc_gcStorageSize = (int) workSize; teavm_gc_minAvailableBytes = minHeap;
teavm_gc_regionMaxCount = regionsSize; teavm_gc_maxAvailableBytes = maxHeap;
teavm_gc_availableBytes = heapSize; teavm_gc_gcStorageSize = 0;
teavm_gc_regionMaxCount = 0;
teavm_gc_availableBytes = 0;
teavm_gc_resizeHeap(minHeap);
} }
typedef struct TeaVM_StaticGcRootDescriptor { typedef struct TeaVM_StaticGcRootDescriptor {
@ -136,4 +228,4 @@ void teavm_initStaticGcRoots() {
free(builder); free(builder);
builder = next; builder = next;
} }
} }

View File

@ -8,9 +8,12 @@ extern void* teavm_gc_regionsAddress;
extern int32_t teavm_gc_regionSize; extern int32_t teavm_gc_regionSize;
extern int32_t teavm_gc_regionMaxCount; extern int32_t teavm_gc_regionMaxCount;
extern int64_t teavm_gc_availableBytes; 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_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_registerStaticGcRoots(void***, int);
extern void teavm_initStaticGcRoots(); extern void teavm_initStaticGcRoots();

View File

@ -7,6 +7,10 @@
#include <wchar.h> #include <wchar.h>
#include <wctype.h> #include <wctype.h>
#include <time.h> #include <time.h>
#include <uchar.h>
static int8_t *wasm_heap;
static int32_t wasm_heap_size;
static inline float teavm_getNaN() { static inline float teavm_getNaN() {
return NAN; return NAN;
@ -46,7 +50,15 @@ static void logOutOfMemory() {
abort(); 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) { static void logInt(int32_t v) {
wprintf(L"%" PRId32, v);
} }

View File

@ -52,6 +52,7 @@ TeaVM.wasm = function() {
function importDefaults(obj) { function importDefaults(obj) {
obj.teavm = { obj.teavm = {
currentTimeMillis: currentTimeMillis, currentTimeMillis: currentTimeMillis,
nanoTime: function() { return performance.now(); },
isnan: isNaN, isnan: isNaN,
teavm_getNaN: function() { return NaN; }, teavm_getNaN: function() { return NaN; },
isinf: function(n) { return !isFinite(n) }, isinf: function(n) { return !isFinite(n) },

View File

@ -63,7 +63,8 @@ import org.teavm.vm.TeaVMProgressListener;
public class IncrementalCBuilder { public class IncrementalCBuilder {
private String mainClass; private String mainClass;
private String[] classPath; private String[] classPath;
private int minHeapSize = 32; private int minHeapSize = 4;
private int maxHeapSize = 128;
private boolean longjmpSupported = true; private boolean longjmpSupported = true;
private boolean lineNumbersGenerated; private boolean lineNumbersGenerated;
private String targetPath; private String targetPath;
@ -110,6 +111,10 @@ public class IncrementalCBuilder {
this.minHeapSize = minHeapSize; this.minHeapSize = minHeapSize;
} }
public void setMaxHeapSize(int maxHeapSize) {
this.maxHeapSize = maxHeapSize;
}
public void setLineNumbersGenerated(boolean lineNumbersGenerated) { public void setLineNumbersGenerated(boolean lineNumbersGenerated) {
this.lineNumbersGenerated = lineNumbersGenerated; this.lineNumbersGenerated = lineNumbersGenerated;
} }
@ -333,6 +338,7 @@ public class IncrementalCBuilder {
cTarget.setIncremental(true); cTarget.setIncremental(true);
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024); cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
cTarget.setMaxHeapSize(maxHeapSize * 1024 * 1024);
cTarget.setLineNumbersGenerated(lineNumbersGenerated); cTarget.setLineNumbersGenerated(lineNumbersGenerated);
cTarget.setLongjmpUsed(longjmpSupported); cTarget.setLongjmpUsed(longjmpSupported);
cTarget.setHeapDump(true); cTarget.setHeapDump(true);

View File

@ -60,7 +60,13 @@ public class TeaVMCBuilderRunner {
.withLongOpt("min-heap") .withLongOpt("min-heap")
.withArgName("size") .withArgName("size")
.hasArg() .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()); .create());
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withLongOpt("no-longjmp") .withLongOpt("no-longjmp")
@ -174,6 +180,18 @@ public class TeaVMCBuilderRunner {
} }
builder.setMinHeapSize(size); 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() { private void runAll() {

View File

@ -134,6 +134,12 @@ public final class TeaVMRunner {
.hasArg() .hasArg()
.withDescription("Minimum heap size in megabytes (for C and WebAssembly)") .withDescription("Minimum heap size in megabytes (for C and WebAssembly)")
.create()); .create());
options.addOption(OptionBuilder
.withLongOpt("max-heap")
.withArgName("size")
.hasArg()
.withDescription("Maximum heap size in megabytes (for C and WebAssembly)")
.create());
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withLongOpt("max-toplevel-names") .withLongOpt("max-toplevel-names")
.withArgName("number") .withArgName("number")
@ -339,6 +345,17 @@ public final class TeaVMRunner {
} }
tool.setMinHeapSize(size * 1024 * 1024); 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() { private void setUp() {

View File

@ -102,7 +102,8 @@ public class TeaVMTool {
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1; private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
private CTarget cTarget; private CTarget cTarget;
private Set<File> generatedFiles = new HashSet<>(); 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 ReferenceCache referenceCache;
private boolean longjmpSupported = true; private boolean longjmpSupported = true;
private boolean heapDump; private boolean heapDump;
@ -235,6 +236,10 @@ public class TeaVMTool {
this.minHeapSize = minHeapSize; this.minHeapSize = minHeapSize;
} }
public void setMaxHeapSize(int maxHeapSize) {
this.maxHeapSize = maxHeapSize;
}
public ClassLoader getClassLoader() { public ClassLoader getClassLoader() {
return classLoader; return classLoader;
} }
@ -326,12 +331,14 @@ public class TeaVMTool {
webAssemblyTarget.setWastEmitted(debugInformationGenerated); webAssemblyTarget.setWastEmitted(debugInformationGenerated);
webAssemblyTarget.setVersion(wasmVersion); webAssemblyTarget.setVersion(wasmVersion);
webAssemblyTarget.setMinHeapSize(minHeapSize); webAssemblyTarget.setMinHeapSize(minHeapSize);
webAssemblyTarget.setMinHeapSize(maxHeapSize);
return webAssemblyTarget; return webAssemblyTarget;
} }
private CTarget prepareCTarget() { private CTarget prepareCTarget() {
cTarget = new CTarget(new CNameProvider()); cTarget = new CTarget(new CNameProvider());
cTarget.setMinHeapSize(minHeapSize); cTarget.setMinHeapSize(minHeapSize);
cTarget.setMaxHeapSize(maxHeapSize);
cTarget.setLineNumbersGenerated(debugInformationGenerated); cTarget.setLineNumbersGenerated(debugInformationGenerated);
cTarget.setLongjmpUsed(longjmpSupported); cTarget.setLongjmpUsed(longjmpSupported);
cTarget.setHeapDump(heapDump); cTarget.setHeapDump(heapDump);

View File

@ -72,7 +72,9 @@ public interface BuildStrategy {
void setWasmVersion(WasmBinaryVersion wasmVersion); void setWasmVersion(WasmBinaryVersion wasmVersion);
void setHeapSize(int heapSize); void setMinHeapSize(int minHeapSize);
void setMaxHeapSize(int maxHeapSize);
void setLongjmpSupported(boolean value); void setLongjmpSupported(boolean value);

View File

@ -59,7 +59,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
private String[] transformers = new String[0]; private String[] transformers = new String[0];
private String[] classesToPreserve = new String[0]; private String[] classesToPreserve = new String[0];
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1; 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 final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
private boolean longjmpSupported = true; private boolean longjmpSupported = true;
private boolean heapDump; private boolean heapDump;
@ -194,8 +195,13 @@ public class InProcessBuildStrategy implements BuildStrategy {
} }
@Override @Override
public void setHeapSize(int heapSize) { public void setMinHeapSize(int minHeapSize) {
this.heapSize = heapSize; this.minHeapSize = minHeapSize;
}
@Override
public void setMaxHeapSize(int maxHeapSize) {
this.maxHeapSize = maxHeapSize;
} }
@Override @Override
@ -233,7 +239,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve)); tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve));
tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null); tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null);
tool.setWasmVersion(wasmVersion); tool.setWasmVersion(wasmVersion);
tool.setMinHeapSize(heapSize); tool.setMinHeapSize(minHeapSize);
tool.setMaxHeapSize(maxHeapSize);
tool.setLongjmpSupported(longjmpSupported); tool.setLongjmpSupported(longjmpSupported);
tool.setHeapDump(heapDump); tool.setHeapDump(heapDump);

View File

@ -171,8 +171,13 @@ public class RemoteBuildStrategy implements BuildStrategy {
} }
@Override @Override
public void setHeapSize(int heapSize) { public void setMinHeapSize(int minHeapSize) {
request.heapSize = heapSize; request.minHeapSize = minHeapSize;
}
@Override
public void setMaxHeapSize(int maxHeapSize) {
request.maxHeapSize = maxHeapSize;
} }
@Override @Override

View File

@ -158,7 +158,8 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
tool.setMinifying(request.minifying); tool.setMinifying(request.minifying);
tool.setMaxTopLevelNames(request.maxTopLevelNames); tool.setMaxTopLevelNames(request.maxTopLevelNames);
tool.setWasmVersion(request.wasmVersion); tool.setWasmVersion(request.wasmVersion);
tool.setMinHeapSize(request.heapSize); tool.setMinHeapSize(request.minHeapSize);
tool.setMaxHeapSize(request.maxHeapSize);
tool.setLongjmpSupported(request.longjmpSupported); tool.setLongjmpSupported(request.longjmpSupported);
tool.setHeapDump(request.heapDump); tool.setHeapDump(request.heapDump);

View File

@ -45,7 +45,8 @@ public class RemoteBuildRequest implements Serializable {
public TeaVMOptimizationLevel optimizationLevel; public TeaVMOptimizationLevel optimizationLevel;
public boolean fastDependencyAnalysis; public boolean fastDependencyAnalysis;
public WasmBinaryVersion wasmVersion; public WasmBinaryVersion wasmVersion;
public int heapSize; public int minHeapSize;
public int maxHeapSize;
public boolean longjmpSupported; public boolean longjmpSupported;
public boolean heapDump; public boolean heapDump;
} }

View File

@ -93,7 +93,6 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
@Override @Override
public void apply(WasmTarget target) { public void apply(WasmTarget target) {
target.setMinHeapSize(32 * 1024 * 1024);
target.setWastEmitted(true); target.setWastEmitted(true);
target.setCEmitted(true); target.setCEmitted(true);
target.setDebugging(true); target.setDebugging(true);

View File

@ -137,8 +137,11 @@ public class TeaVMCompileMojo extends AbstractMojo {
@Parameter(property = "teavm.wasmVersion", defaultValue = "V_0x1") @Parameter(property = "teavm.wasmVersion", defaultValue = "V_0x1")
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1; private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
@Parameter(property = "teavm.heapSize", defaultValue = "32") @Parameter(property = "teavm.minHeapSize", defaultValue = "4")
private int heapSize; private int minHeapSize;
@Parameter(property = "teavm.maxHeapSize", defaultValue = "128")
private int maxHeapSize;
@Parameter(property = "teavm.outOfProcess", defaultValue = "false") @Parameter(property = "teavm.outOfProcess", defaultValue = "false")
private boolean outOfProcess; private boolean outOfProcess;
@ -173,7 +176,8 @@ public class TeaVMCompileMojo extends AbstractMojo {
builder.setDebugInformationGenerated(debugInformationGenerated); builder.setDebugInformationGenerated(debugInformationGenerated);
builder.setSourceMapsFileGenerated(sourceMapsGenerated); builder.setSourceMapsFileGenerated(sourceMapsGenerated);
builder.setSourceFilesCopied(sourceFilesCopied); builder.setSourceFilesCopied(sourceFilesCopied);
builder.setHeapSize(heapSize * 1024 * 1024); builder.setMinHeapSize(minHeapSize * 1024 * 1024);
builder.setMaxHeapSize(maxHeapSize * 1024 * 1024);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new MojoExecutionException("Unexpected error occurred", e); throw new MojoExecutionException("Unexpected error occurred", e);
} }