From 3370898a54d276b29dba5e6f685d79dc7f2314a4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 8 Sep 2016 19:08:52 +0300 Subject: [PATCH] Implementing simple mark&sweep GC --- .../org/teavm/backend/wasm/WasmRuntime.java | 18 ++ .../org/teavm/backend/wasm/WasmTarget.java | 65 +++++--- .../wasm/generate/WasmGenerationVisitor.java | 17 +- .../wasm/intrinsics/AddressIntrinsic.java | 7 + .../backend/wasm/intrinsics/GCIntrinsic.java | 132 +++++++++++++++ .../wasm/intrinsics/WasmRuntimeIntrinsic.java | 62 ++++--- .../model/instructions/MutatorIntrinsic.java | 75 +++++++++ .../GcRootMaintainingTransformer.java | 10 +- core/src/main/java/org/teavm/runtime/GC.java | 155 +++++++++++++++++- .../java/org/teavm/runtime/MarkQueue.java | 14 +- 10 files changed, 485 insertions(+), 70 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java create mode 100644 core/src/main/java/org/teavm/model/instructions/MutatorIntrinsic.java diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index adca7bc6f..2e38dfb56 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -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); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index ebf929013..d80815bf8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -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) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 9a7898349..a4894073b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -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); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java index c528da0b6..e8d1859c8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java @@ -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()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java new file mode 100644 index 000000000..ca75f222d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java @@ -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 heapAddressExpressions = new ArrayList<>(); + private List availableBytesExpressions = new ArrayList<>(); + private List gcStorageAddressExpressions = new ArrayList<>(); + private List gcStorageSizeExpressions = new ArrayList<>(); + private List regionSizeExpressions = new ArrayList<>(); + private List regionsAddressExpressions = new ArrayList<>(); + private List 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 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; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java index 68e07c8c6..6f07b6685 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java @@ -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 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); + default: + throw new IllegalArgumentException(type.toString()); } - - return null; } } diff --git a/core/src/main/java/org/teavm/model/instructions/MutatorIntrinsic.java b/core/src/main/java/org/teavm/model/instructions/MutatorIntrinsic.java new file mode 100644 index 000000000..68b8e47d5 --- /dev/null +++ b/core/src/main/java/org/teavm/model/instructions/MutatorIntrinsic.java @@ -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 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); + } + } + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java index a2f67eb4b..21b7226bc 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java @@ -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); diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index af9ae69df..e4e15e98c 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -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; } diff --git a/core/src/main/java/org/teavm/runtime/MarkQueue.java b/core/src/main/java/org/teavm/runtime/MarkQueue.java index 3712d7cea..4148c244b 100644 --- a/core/src/main/java/org/teavm/runtime/MarkQueue.java +++ b/core/src/main/java/org/teavm/runtime/MarkQueue.java @@ -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();