mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-09 00:14:10 -08:00
Implementing simple mark&sweep GC
This commit is contained in:
parent
ae5d701aac
commit
3370898a54
|
@ -249,4 +249,22 @@ public final class WasmRuntime {
|
|||
stack.add(-4).putInt(size);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Address getStackGcRoots() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
public static Address getNextStackRoots(Address address) {
|
||||
int size = address.getInt() + 1;
|
||||
return address.add(-size * 4);
|
||||
}
|
||||
|
||||
public static int getStackRootCount(Address address) {
|
||||
return address.getInt();
|
||||
}
|
||||
|
||||
public static Address getStackRootPointer(Address address) {
|
||||
int size = address.getInt();
|
||||
return address.add(-size * 4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
|
|||
import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.ClassIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.PlatformIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic;
|
||||
|
@ -101,6 +102,7 @@ import org.teavm.model.instructions.CloneArrayInstruction;
|
|||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.MutatorIntrinsic;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
import org.teavm.model.lowlevel.GcRootMaintainingTransformer;
|
||||
|
@ -201,6 +203,14 @@ public class WasmTarget implements TeaVMTarget {
|
|||
Address.class, int.class, void.class), null).use();
|
||||
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "allocStack",
|
||||
int.class, Address.class), null).use();
|
||||
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "getStackGcRoots", Address.class),
|
||||
null) .use();
|
||||
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "getNextStackRoots", Address.class,
|
||||
Address.class), null).use();
|
||||
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "getStackRootCount", Address.class,
|
||||
int.class), null).use();
|
||||
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "getStackRootPointer", Address.class,
|
||||
Address.class), null).use();
|
||||
|
||||
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||
RuntimeClass.class, Address.class), null).use();
|
||||
|
@ -270,12 +280,17 @@ public class WasmTarget implements TeaVMTarget {
|
|||
context.addIntrinsic(new AddressIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new StructureIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new FunctionIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new WasmRuntimeIntrinsic());
|
||||
WasmRuntimeIntrinsic wasmRuntimeIntrinsic = new WasmRuntimeIntrinsic();
|
||||
context.addIntrinsic(wasmRuntimeIntrinsic);
|
||||
context.addIntrinsic(new AllocatorIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new PlatformIntrinsic());
|
||||
context.addIntrinsic(new PlatformClassIntrinsic());
|
||||
context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new ClassIntrinsic());
|
||||
GCIntrinsic gcIntrinsic = new GCIntrinsic();
|
||||
context.addIntrinsic(gcIntrinsic);
|
||||
MutatorIntrinsic mutatorIntrinsic = new MutatorIntrinsic();
|
||||
context.addIntrinsic(mutatorIntrinsic);
|
||||
|
||||
WasmGenerator generator = new WasmGenerator(decompiler, classes,
|
||||
context, classGenerator);
|
||||
|
@ -284,14 +299,14 @@ public class WasmTarget implements TeaVMTarget {
|
|||
generateMethods(classes, context, generator, module);
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
classGenerator.postProcess();
|
||||
mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress());
|
||||
|
||||
WasmMemorySegment dataSegment = new WasmMemorySegment();
|
||||
dataSegment.setData(binaryWriter.getData());
|
||||
dataSegment.setOffset(256);
|
||||
module.getSegments().add(dataSegment);
|
||||
|
||||
int address = renderStackInit(module, binaryWriter.getAddress());
|
||||
renderAllocatorInit(module, address);
|
||||
renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic, wasmRuntimeIntrinsic);
|
||||
renderClinit(classes, classGenerator, module);
|
||||
if (controller.wasCancelled()) {
|
||||
return;
|
||||
|
@ -370,14 +385,6 @@ public class WasmTarget implements TeaVMTarget {
|
|||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getOwnerName().equals(Allocator.class.getName())
|
||||
&& method.getName().equals("initialize")) {
|
||||
continue;
|
||||
}
|
||||
if (method.getOwnerName().equals(WasmRuntime.class.getName())
|
||||
&& method.getName().equals("initStack")) {
|
||||
continue;
|
||||
}
|
||||
if (context.getIntrinsic(method.getReference()) != null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -599,24 +606,30 @@ public class WasmTarget implements TeaVMTarget {
|
|||
}
|
||||
}
|
||||
|
||||
private void renderAllocatorInit(WasmModule module, int address) {
|
||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference(
|
||||
Allocator.class, "initialize", Address.class)));
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getBody().add(new WasmReturn(new WasmInt32Constant(address)));
|
||||
module.add(function);
|
||||
}
|
||||
|
||||
private int renderStackInit(WasmModule module, int address) {
|
||||
private void renderMemoryLayout(WasmModule module, int address, GCIntrinsic gcIntrinsic,
|
||||
WasmRuntimeIntrinsic runtimeIntrinsic) {
|
||||
address = (((address - 1) / 256) + 1) * 256;
|
||||
|
||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference(
|
||||
WasmRuntime.class, "initStack", Address.class)));
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getBody().add(new WasmReturn(new WasmInt32Constant(address)));
|
||||
module.add(function);
|
||||
runtimeIntrinsic.setStackAddress(address);
|
||||
address += 65536;
|
||||
|
||||
return address + 65536;
|
||||
int gcMemory = module.getMemorySize() * 65536 - address;
|
||||
int storageSize = (gcMemory >> 6) >> 2 << 2;
|
||||
gcIntrinsic.setGCStorageAddress(address);
|
||||
gcIntrinsic.setGCStorageSize(storageSize);
|
||||
|
||||
gcMemory -= storageSize;
|
||||
address += storageSize;
|
||||
int regionSize = 32768;
|
||||
int regionCount = gcMemory / (2 + regionSize) + 1;
|
||||
gcIntrinsic.setRegionSize(regionSize);
|
||||
gcIntrinsic.setRegionsAddress(address);
|
||||
gcIntrinsic.setRegionMaxCount(regionCount);
|
||||
|
||||
gcMemory -= regionCount * 2;
|
||||
address += regionCount * 2;
|
||||
gcIntrinsic.setHeapAddress(address);
|
||||
gcIntrinsic.setAvailableBytes(address);
|
||||
}
|
||||
|
||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||
|
|
|
@ -113,6 +113,7 @@ import org.teavm.model.ValueType;
|
|||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableEntry;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.Mutator;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
|
||||
|
@ -781,7 +782,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(InvocationExpr expr) {
|
||||
if (expr.getMethod().getClassName().equals(Allocator.class.getName())) {
|
||||
if (expr.getMethod().getClassName().equals(Mutator.class.getName())) {
|
||||
switch (expr.getMethod().getName()) {
|
||||
case "allocStack":
|
||||
generateAllocStack(expr.getArguments().get(0));
|
||||
|
@ -885,7 +886,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
private void generateAllocStack(Expr sizeExpr) {
|
||||
if (stackVariable != null) {
|
||||
throw new IllegalStateException("Call to Allocator.allocStack must be done only once");
|
||||
throw new IllegalStateException("Call to Mutator.allocStack must be done only once");
|
||||
}
|
||||
stackVariable = getTemporary(WasmType.INT32);
|
||||
stackVariable.setName("__stack__");
|
||||
|
@ -900,8 +901,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
private void generateReleaseStack() {
|
||||
if (stackVariable == null) {
|
||||
throw new IllegalStateException("Call to Allocator.releaseStack must be dominated by "
|
||||
+ "Allocator.allocStack");
|
||||
throw new IllegalStateException("Call to Mutator.releaseStack must be dominated by "
|
||||
+ "Mutator.allocStack");
|
||||
}
|
||||
|
||||
int offset = classGenerator.getFieldOffset(new FieldReference(WasmRuntime.class.getName(), "stack"));
|
||||
|
@ -911,8 +912,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) {
|
||||
if (stackVariable == null) {
|
||||
throw new IllegalStateException("Call to Allocator.registerGcRoot must be dominated by "
|
||||
+ "Allocator.allocStack");
|
||||
throw new IllegalStateException("Call to Mutator.registerGcRoot must be dominated by "
|
||||
+ "Mutator.allocStack");
|
||||
}
|
||||
|
||||
slotExpr.acceptVisitor(this);
|
||||
|
@ -928,8 +929,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
private void generateRemoveGcRoot(Expr slotExpr) {
|
||||
if (stackVariable == null) {
|
||||
throw new IllegalStateException("Call to Allocator.removeGcRoot must be dominated by "
|
||||
+ "Allocator.allocStack");
|
||||
throw new IllegalStateException("Call to Mutator.removeGcRoot must be dominated by "
|
||||
+ "Mutator.allocStack");
|
||||
}
|
||||
|
||||
slotExpr.acceptVisitor(this);
|
||||
|
|
|
@ -102,6 +102,7 @@ public class AddressIntrinsic implements WasmIntrinsic {
|
|||
case "getChar":
|
||||
return new WasmLoadInt32(2, manager.generate(invocation.getArguments().get(0)),
|
||||
WasmInt32Subtype.UINT16);
|
||||
case "getAddress":
|
||||
case "getInt":
|
||||
return new WasmLoadInt32(4, manager.generate(invocation.getArguments().get(0)),
|
||||
WasmInt32Subtype.INT32);
|
||||
|
@ -122,6 +123,7 @@ public class AddressIntrinsic implements WasmIntrinsic {
|
|||
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||
return new WasmStoreInt32(2, address, value, WasmInt32Subtype.INT16);
|
||||
}
|
||||
case "putAddress":
|
||||
case "putChar": {
|
||||
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||
|
@ -158,6 +160,11 @@ public class AddressIntrinsic implements WasmIntrinsic {
|
|||
.collect(Collectors.toSet()));
|
||||
return call;
|
||||
}
|
||||
case "isLessThan": {
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_UNSIGNED,
|
||||
manager.generate(invocation.getArguments().get(0)),
|
||||
manager.generate(invocation.getArguments().get(1)));
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
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.model.MethodReference;
|
||||
import org.teavm.runtime.GC;
|
||||
|
||||
public class GCIntrinsic implements WasmIntrinsic {
|
||||
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 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) {
|
||||
constant.setValue(regionSize);
|
||||
}
|
||||
}
|
||||
|
||||
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())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (methodReference.getName()) {
|
||||
case "gcStorageAddress":
|
||||
case "gcStorageSize":
|
||||
case "heapAddress":
|
||||
case "availableBytes":
|
||||
case "regionsAddress":
|
||||
case "regionMaxCount":
|
||||
case "regionSize":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
List<WasmInt32Constant> list;
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "gcStorageAddress":
|
||||
list = gcStorageAddressExpressions;
|
||||
break;
|
||||
case "gcStorageSize":
|
||||
list = gcStorageSizeExpressions;
|
||||
break;
|
||||
case "heapAddress":
|
||||
list = heapAddressExpressions;
|
||||
break;
|
||||
case "regionsAddress":
|
||||
list = regionsAddressExpressions;
|
||||
break;
|
||||
case "regionMaxCount":
|
||||
list = regionMaxCountExpressions;
|
||||
break;
|
||||
case "regionSize":
|
||||
list = regionSizeExpressions;
|
||||
break;
|
||||
case "availableBytes": {
|
||||
WasmInt64Constant constant = new WasmInt64Constant(0);
|
||||
availableBytesExpressions.add(constant);
|
||||
return constant;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||
}
|
||||
WasmInt32Constant result = new WasmInt32Constant(0);
|
||||
list.add(result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
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;
|
||||
|
@ -23,40 +25,62 @@ 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) {
|
||||
return methodReference.getClassName().equals(WasmRuntime.class.getName())
|
||||
&& (methodReference.getName().equals("lt") || methodReference.getName().equals("gt"));
|
||||
if (!methodReference.getClassName().equals(WasmRuntime.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
switch (methodReference.getName()) {
|
||||
case "gt":
|
||||
case "lt":
|
||||
case "initStack":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setStackAddress(int stackAddress) {
|
||||
for (WasmInt32Constant constant : stackExpressions) {
|
||||
constant.setValue(stackAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "lt":
|
||||
return comparison(WasmIntBinaryOperation.LT_SIGNED, WasmFloatBinaryOperation.LT,
|
||||
invocation, manager);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
private static WasmExpression comparison(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp,
|
||||
InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
WasmType type = WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(0));
|
||||
|
||||
WasmExpression first = manager.generate(invocation.getArguments().get(0));
|
||||
WasmExpression second = manager.generate(invocation.getArguments().get(1));
|
||||
|
||||
WasmIntBinaryOperation intOp;
|
||||
WasmFloatBinaryOperation floatOp;
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "lt":
|
||||
intOp = WasmIntBinaryOperation.LT_SIGNED;
|
||||
floatOp = WasmFloatBinaryOperation.LT;
|
||||
break;
|
||||
case "gt":
|
||||
intOp = WasmIntBinaryOperation.GT_SIGNED;
|
||||
floatOp = WasmFloatBinaryOperation.GT;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().getName());
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case INT32:
|
||||
return new WasmIntBinary(WasmIntType.INT32, intOp, first, second);
|
||||
|
@ -66,8 +90,8 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
|
|||
return new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second);
|
||||
case FLOAT64:
|
||||
return new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second);
|
||||
}
|
||||
|
||||
return null;
|
||||
default:
|
||||
throw new IllegalArgumentException(type.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.model.instructions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.ast.*;
|
||||
import org.teavm.ast.InvocationType;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.Mutator;
|
||||
|
||||
public class MutatorIntrinsic implements WasmIntrinsic {
|
||||
private List<WasmInt32Constant> staticGcRootsExpressions = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
if (!methodReference.getClassName().equals(Mutator.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
switch (methodReference.getName()) {
|
||||
case "getStaticGcRoots":
|
||||
case "getStackGcRoots":
|
||||
case "getNextStackRoots":
|
||||
case "getStackRootCount":
|
||||
case "getStackRootPointer":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setStaticGcRootsAddress(int address) {
|
||||
for (WasmInt32Constant constant : staticGcRootsExpressions) {
|
||||
constant.setValue(address);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getStaticGcRoots": {
|
||||
WasmInt32Constant constant = new WasmInt32Constant(0);
|
||||
staticGcRootsExpressions.add(constant);
|
||||
return constant;
|
||||
}
|
||||
default: {
|
||||
InvocationExpr expr = new InvocationExpr();
|
||||
MethodReference method = new MethodReference(WasmRuntime.class.getName(),
|
||||
invocation.getMethod().getDescriptor());
|
||||
expr.setMethod(method);
|
||||
expr.setType(InvocationType.SPECIAL);
|
||||
expr.getArguments().addAll(invocation.getArguments());
|
||||
return manager.generate(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ import org.teavm.model.util.LivenessAnalyzer;
|
|||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.TypeInferer;
|
||||
import org.teavm.model.util.VariableType;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.Mutator;
|
||||
|
||||
public class GcRootMaintainingTransformer {
|
||||
private ClassReaderSource classSource;
|
||||
|
@ -158,7 +158,7 @@ public class GcRootMaintainingTransformer {
|
|||
|
||||
InvokeInstruction registerInvocation = new InvokeInstruction();
|
||||
registerInvocation.setType(InvocationType.SPECIAL);
|
||||
registerInvocation.setMethod(new MethodReference(Allocator.class, "registerGcRoot", int.class,
|
||||
registerInvocation.setMethod(new MethodReference(Mutator.class, "registerGcRoot", int.class,
|
||||
Object.class, void.class));
|
||||
registerInvocation.getArguments().add(slotVar);
|
||||
registerInvocation.getArguments().add(program.variableAt(liveVar));
|
||||
|
@ -177,7 +177,7 @@ public class GcRootMaintainingTransformer {
|
|||
|
||||
InvokeInstruction clearInvocation = new InvokeInstruction();
|
||||
clearInvocation.setType(InvocationType.SPECIAL);
|
||||
clearInvocation.setMethod(new MethodReference(Allocator.class, "removeGcRoot", int.class, void.class));
|
||||
clearInvocation.setMethod(new MethodReference(Mutator.class, "removeGcRoot", int.class, void.class));
|
||||
clearInvocation.getArguments().add(slotVar);
|
||||
clearInvocation.setLocation(callInstruction.getLocation());
|
||||
instructionsToAdd.add(clearInvocation);
|
||||
|
@ -200,7 +200,7 @@ public class GcRootMaintainingTransformer {
|
|||
|
||||
InvokeInstruction invocation = new InvokeInstruction();
|
||||
invocation.setType(InvocationType.SPECIAL);
|
||||
invocation.setMethod(new MethodReference(Allocator.class, "allocStack", int.class, void.class));
|
||||
invocation.setMethod(new MethodReference(Mutator.class, "allocStack", int.class, void.class));
|
||||
invocation.getArguments().add(sizeVariable);
|
||||
instructionsToAdd.add(invocation);
|
||||
|
||||
|
@ -261,7 +261,7 @@ public class GcRootMaintainingTransformer {
|
|||
|
||||
InvokeInstruction invocation = new InvokeInstruction();
|
||||
invocation.setType(InvocationType.SPECIAL);
|
||||
invocation.setMethod(new MethodReference(Allocator.class, "releaseStack", int.class, void.class));
|
||||
invocation.setMethod(new MethodReference(Mutator.class, "releaseStack", int.class, void.class));
|
||||
invocation.getArguments().add(sizeVariable);
|
||||
instructionsToAdd.add(invocation);
|
||||
|
||||
|
|
|
@ -31,7 +31,9 @@ public final class GC {
|
|||
static FreeChunkHolder currentChunkPointer;
|
||||
static int freeChunks;
|
||||
|
||||
private static native Address gcStorageAddress();
|
||||
static native Address gcStorageAddress();
|
||||
|
||||
static native int gcStorageSize();
|
||||
|
||||
private static native Address heapAddress();
|
||||
|
||||
|
@ -39,14 +41,14 @@ public final class GC {
|
|||
|
||||
private static native int regionMaxCount();
|
||||
|
||||
private static native int availableBytes();
|
||||
private static native long availableBytes();
|
||||
|
||||
private static native int regionSize();
|
||||
|
||||
static {
|
||||
currentChunk = heapAddress().toStructure();
|
||||
currentChunk.classReference = 0;
|
||||
currentChunk.size = availableBytes();
|
||||
currentChunk.size = (int) availableBytes();
|
||||
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
|
||||
currentChunkPointer = gcStorageAddress().toStructure();
|
||||
currentChunkPointer.value = currentChunk;
|
||||
|
@ -90,7 +92,8 @@ public final class GC {
|
|||
|
||||
private static boolean collectGarbage(int size) {
|
||||
mark();
|
||||
return false;
|
||||
sweep();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void mark() {
|
||||
|
@ -159,6 +162,150 @@ public final class GC {
|
|||
}
|
||||
}
|
||||
|
||||
private static void sweep() {
|
||||
FreeChunkHolder freeChunkPtr = gcStorageAddress().toStructure();
|
||||
freeChunks = 0;
|
||||
|
||||
RuntimeObject object = heapAddress().toStructure();
|
||||
FreeChunk lastFreeSpace = null;
|
||||
long heapSize = availableBytes();
|
||||
long reclaimedSpace = 0;
|
||||
long maxFreeChunk = 0;
|
||||
int currentRegionIndex = 0;
|
||||
int regionsCount = (int) ((heapSize - 1) / regionSize()) + 1;
|
||||
Address currentRegionEnd = object.toAddress().add(regionSize());
|
||||
Address limit = heapAddress().add(heapSize);
|
||||
|
||||
loop: while (object.toAddress().isLessThan(limit)) {
|
||||
int tag = object.classReference;
|
||||
boolean free;
|
||||
if (tag == 0) {
|
||||
free = true;
|
||||
} else {
|
||||
free = (tag & RuntimeObject.GC_MARKED) == 0;
|
||||
if (!free) {
|
||||
tag &= ~RuntimeObject.GC_MARKED;
|
||||
}
|
||||
object.classReference = tag;
|
||||
}
|
||||
|
||||
if (free) {
|
||||
if (lastFreeSpace == null) {
|
||||
lastFreeSpace = (FreeChunk) object;
|
||||
}
|
||||
|
||||
if (!object.toAddress().isLessThan(currentRegionEnd)) {
|
||||
currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize());
|
||||
Region currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex)
|
||||
.toStructure();
|
||||
if (currentRegion.start == 0) {
|
||||
do {
|
||||
if (++currentRegionIndex == regionsCount) {
|
||||
object = limit.toStructure();
|
||||
break loop;
|
||||
}
|
||||
currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex)
|
||||
.toStructure();
|
||||
} while (currentRegion.start == 0);
|
||||
}
|
||||
currentRegionEnd = currentRegion.toAddress().add(regionSize());
|
||||
}
|
||||
} else {
|
||||
if (lastFreeSpace != null) {
|
||||
lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
||||
freeChunkPtr.value = lastFreeSpace;
|
||||
freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure();
|
||||
freeChunks++;
|
||||
reclaimedSpace += lastFreeSpace.size;
|
||||
if (maxFreeChunk < lastFreeSpace.size) {
|
||||
maxFreeChunk = lastFreeSpace.size;
|
||||
}
|
||||
lastFreeSpace = null;
|
||||
}
|
||||
}
|
||||
|
||||
int size = objectSize(object);
|
||||
object = object.toAddress().add(size).toStructure();
|
||||
}
|
||||
|
||||
if (lastFreeSpace != null) {
|
||||
int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
||||
lastFreeSpace.size = freeSize;
|
||||
freeChunkPtr.value = lastFreeSpace;
|
||||
freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure();
|
||||
freeChunks++;
|
||||
reclaimedSpace += freeSize;
|
||||
if (maxFreeChunk < freeSize) {
|
||||
maxFreeChunk = freeSize;
|
||||
}
|
||||
}
|
||||
|
||||
currentChunkPointer = heapAddress().toStructure();
|
||||
sortFreeChunks(0, freeChunks - 1);
|
||||
}
|
||||
|
||||
private static void sortFreeChunks(int lower, int upper) {
|
||||
int start = lower;
|
||||
int end = upper;
|
||||
int mid = (lower + upper) / 2;
|
||||
|
||||
FreeChunk midChunk = getFreeChunk(mid).value;
|
||||
outer: while (true) {
|
||||
while (true) {
|
||||
if (lower == upper) {
|
||||
break outer;
|
||||
}
|
||||
if (getFreeChunk(lower).value.size <= midChunk.size) {
|
||||
break;
|
||||
}
|
||||
++lower;
|
||||
}
|
||||
while (true) {
|
||||
if (lower == upper) {
|
||||
break outer;
|
||||
}
|
||||
if (getFreeChunk(upper).value.size > midChunk.size) {
|
||||
break;
|
||||
}
|
||||
--upper;
|
||||
}
|
||||
FreeChunk tmp = getFreeChunk(lower).value;
|
||||
getFreeChunk(lower).value = getFreeChunk(upper).value;
|
||||
getFreeChunk(upper).value = tmp;
|
||||
}
|
||||
|
||||
if (lower - start > 0) {
|
||||
sortFreeChunks(start, lower);
|
||||
}
|
||||
if (end - lower - 1 > 0) {
|
||||
sortFreeChunks(lower + 1, end);
|
||||
}
|
||||
}
|
||||
|
||||
private static FreeChunkHolder getFreeChunk(int index) {
|
||||
return currentChunkPointer.toAddress().add(FreeChunkHolder.class, index).toStructure();
|
||||
}
|
||||
|
||||
private static int objectSize(RuntimeObject object) {
|
||||
if (object.classReference == 0) {
|
||||
return ((FreeChunk) object).size;
|
||||
} else {
|
||||
RuntimeClass cls = RuntimeClass.getClass(object);
|
||||
if (cls.itemType == null) {
|
||||
return cls.size;
|
||||
} else {
|
||||
int itemSize = (cls.itemType.flags & RuntimeClass.PRIMITIVE) == 0
|
||||
? Address.sizeOf()
|
||||
: cls.itemType.size;
|
||||
RuntimeArray array = (RuntimeArray) object;
|
||||
Address address = Address.fromInt(Structure.sizeOf(RuntimeArray.class));
|
||||
address = Address.align(address, itemSize);
|
||||
address = address.add(itemSize * array.size);
|
||||
return address.toInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMarked(RuntimeObject object) {
|
||||
return (object.classReference & RuntimeObject.GC_MARKED) != 0;
|
||||
}
|
||||
|
|
|
@ -23,26 +23,24 @@ final class MarkQueue {
|
|||
|
||||
private static int head;
|
||||
private static int tail;
|
||||
private static int limit;
|
||||
|
||||
static void init() {
|
||||
head = 0;
|
||||
tail = 0;
|
||||
limit = GC.gcStorageSize() / Address.sizeOf();
|
||||
}
|
||||
|
||||
private static native Address getBase();
|
||||
|
||||
private static native int getSize();
|
||||
|
||||
static void enqueue(RuntimeObject object) {
|
||||
getBase().add(Address.sizeOf() * tail).putAddress(object.toAddress());
|
||||
if (++tail >= getSize()) {
|
||||
GC.gcStorageAddress().add(Address.sizeOf() * tail).putAddress(object.toAddress());
|
||||
if (++tail >= limit) {
|
||||
tail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static RuntimeObject dequeue() {
|
||||
Address result = getBase().add(Address.sizeOf() * head).getAddress();
|
||||
if (++head >= getSize()) {
|
||||
Address result = GC.gcStorageAddress().add(Address.sizeOf() * head).getAddress();
|
||||
if (++head >= limit) {
|
||||
head = 0;
|
||||
}
|
||||
return result.toStructure();
|
||||
|
|
Loading…
Reference in New Issue
Block a user