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() {
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();

View File

@ -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) {

View File

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

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

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.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) {

View File

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

View File

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

View File

@ -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) {

View File

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

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

View File

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

View File

@ -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() {

View File

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

View File

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

View File

@ -73,4 +73,6 @@ public interface WasmExpressionVisitor {
void visit(WasmStoreFloat32 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.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.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) {

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

View File

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

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.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());

View File

@ -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) {

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.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:

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.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:

View File

@ -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,

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {

View File

@ -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() {

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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