Implementing simple mark&sweep GC

This commit is contained in:
Alexey Andreev 2016-09-08 19:08:52 +03:00
parent ae5d701aac
commit 3370898a54
10 changed files with 485 additions and 70 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
default:
throw new IllegalArgumentException(type.toString());
}
return null;
}
}

View File

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

View File

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

View File

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

View File

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