Support generations in GC

This commit is contained in:
Alexey Andreev 2019-11-07 19:05:31 +03:00
parent 0ff17fc2fb
commit 16f4d2571b
31 changed files with 1599 additions and 214 deletions

View File

@ -121,6 +121,7 @@ public final class TSystem extends TObject {
int itemSize = type.itemType.size;
if ((type.itemType.flags & RuntimeClass.PRIMITIVE) == 0) {
itemSize = Address.sizeOf();
GC.writeBarrier(dest);
}
Address srcAddress = Address.align(src.toAddress().add(RuntimeArray.class, 1), itemSize);
@ -253,7 +254,7 @@ public final class TSystem extends TObject {
}
private static void gcLowLevel() {
GC.collectGarbage();
GC.collectGarbageFull();
}
public static void runFinalization() {

View File

@ -118,6 +118,7 @@ import org.teavm.model.lowlevel.ClassInitializerTransformer;
import org.teavm.model.lowlevel.ExportDependencyListener;
import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
import org.teavm.model.lowlevel.ShadowStackTransformer;
import org.teavm.model.lowlevel.WriteBarrierInsertion;
import org.teavm.model.optimization.InliningFilterFactory;
import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.ClassPatch;
@ -155,6 +156,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private ClassInitializerEliminator classInitializerEliminator;
private ClassInitializerTransformer classInitializerTransformer;
private ShadowStackTransformer shadowStackTransformer;
private WriteBarrierInsertion writeBarrierInsertion;
private NullCheckInsertion nullCheckInsertion;
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
private CheckInstructionTransformation checkTransformation;
@ -235,6 +237,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
checkTransformation = new CheckInstructionTransformation();
writeBarrierInsertion = new WriteBarrierInsertion(characteristics);
controller.addVirtualMethods(VIRTUAL_METHODS::contains);
}
@ -272,6 +275,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "fixHeap", void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "tryShrink", void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "collectGarbage", void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "collectGarbageFull", void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
Throwable.class, void.class)).use();
@ -350,6 +354,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
? this.shadowStackTransformer
: new ShadowStackTransformer(characteristics, !longjmpUsed);
shadowStackTransformer.apply(program, method);
writeBarrierInsertion.apply(program);
}
@Override
@ -392,6 +397,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
stringPool = new SimpleStringPool();
boolean vmAssertions = Boolean.parseBoolean(System.getProperty("teavm.c.vmAssertions", "false"));
boolean gcStats = Boolean.parseBoolean(System.getProperty("teavm.c.gcStats", "false"));
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
intrinsics, generators, asyncMethods::contains, buildTarget,
@ -417,6 +423,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
if (obfuscated) {
configHeaderWriter.println("#define TEAVM_OBFUSCATED 1");
}
if (gcStats) {
configHeaderWriter.println("#define TEAVM_GC_STATS 1");
}
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,
controller.getCacheStatus());

View File

@ -32,6 +32,7 @@ public class AllocatorIntrinsic implements Intrinsic {
switch (method.getName()) {
case "fillZero":
case "fill":
case "moveMemoryBlock":
case "isInitialized":
return true;
@ -51,6 +52,16 @@ public class AllocatorIntrinsic implements Intrinsic {
context.emit(invocation.getArguments().get(1));
context.writer().print(")");
break;
case "fill":
context.includes().addInclude("<string.h>");
context.writer().print("memset(");
context.emit(invocation.getArguments().get(0));
context.writer().print(", ");
context.emit(invocation.getArguments().get(1));
context.writer().print(", ");
context.emit(invocation.getArguments().get(2));
context.writer().print(")");
break;
case "moveMemoryBlock":
context.includes().addInclude("<string.h>");
context.writer().print("memmove(");

View File

@ -37,6 +37,8 @@ public class GCIntrinsic implements Intrinsic {
case "minAvailableBytes":
case "maxAvailableBytes":
case "resizeHeap":
case "cardTable":
case "writeBarrier":
return true;
default:
return false;
@ -45,15 +47,23 @@ public class GCIntrinsic implements Intrinsic {
@Override
public void apply(IntrinsicContext context, InvocationExpr invocation) {
context.includes().includePath("memory.h");
if (invocation.getMethod().getName().equals("resizeHeap")) {
context.writer().print("teavm_gc_resizeHeap(");
context.emit(invocation.getArguments().get(0));
context.writer().print(")");
return;
}
switch (invocation.getMethod().getName()) {
case "resizeHeap":
context.writer().print("teavm_gc_resizeHeap(");
context.emit(invocation.getArguments().get(0));
context.writer().print(")");
break;
context.includes().includePath("heaptrace.h");
context.writer().print("teavm_gc_").print(invocation.getMethod().getName());
case "writeBarrier":
context.writer().print("teavm_gc_writeBarrier(");
context.emit(invocation.getArguments().get(0));
context.writer().print(")");
break;
default:
context.includes().includePath("heaptrace.h");
context.writer().print("teavm_gc_").print(invocation.getMethod().getName());
break;
}
}
}

View File

@ -27,6 +27,7 @@ public class MemoryTraceIntrinsic implements Intrinsic {
@Override
public void apply(IntrinsicContext context, InvocationExpr invocation) {
context.includes().includePath("heaptrace.h");
context.writer().print("teavm_gc_").print(invocation.getMethod().getName()).print("(");
if (!invocation.getArguments().isEmpty()) {
context.emit(invocation.getArguments().get(0));

View File

@ -24,7 +24,7 @@ import org.teavm.interop.Unmanaged;
public final class WasmHeap {
public static final int PAGE_SIZE = 65536;
public static final int DEFAULT_STACK_SIZE = PAGE_SIZE * 4;
public static final int DEFAULT_REGION_SIZE = 32768;
public static final int DEFAULT_REGION_SIZE = 4096;
public static int minHeapSize;
public static int maxHeapSize;
@ -33,6 +33,7 @@ public final class WasmHeap {
public static Address regionsAddress;
public static int regionsCount;
public static int regionsSize;
public static Address cardTable;
public static Address heapAddress;
public static int heapSize;
public static int regionSize = DEFAULT_REGION_SIZE;
@ -79,7 +80,8 @@ public final class WasmHeap {
int newRegionsSize = calculateRegionsSize(newRegionsCount);
Address newRegionsAddress = WasmRuntime.align(heapAddress.add(newHeapSize), 16);
Address newStorageAddress = WasmRuntime.align(newRegionsAddress.add(newRegionsSize), 16);
Address newCardTable = WasmRuntime.align(newRegionsAddress.add(newRegionsCount), 16);
Address newStorageAddress = WasmRuntime.align(newCardTable.add(newRegionsSize), 16);
Address newMemoryLimit = WasmRuntime.align(newStorageAddress.add(newStorageSize), PAGE_SIZE);
if (newMemoryLimit != memoryLimit) {
growMemory((int) (newMemoryLimit.toLong() - memoryLimit.toLong()) / PAGE_SIZE);
@ -89,11 +91,13 @@ public final class WasmHeap {
WasmRuntime.moveMemoryBlock(storageAddress, newStorageAddress, storageSize);
}
if (regionsSize > 0) {
WasmRuntime.moveMemoryBlock(cardTable, newCardTable, regionsCount);
WasmRuntime.moveMemoryBlock(regionsAddress, newRegionsAddress, regionsSize);
}
storageAddress = newStorageAddress;
regionsAddress = newRegionsAddress;
cardTable = newCardTable;
storageSize = newStorageSize;
regionsCount = newRegionsCount;
regionsSize = newRegionsSize;

View File

@ -97,23 +97,30 @@ public final class WasmRuntime {
public static native void printOutOfMemory();
public static void fillZero(Address address, int count) {
fill(address, (byte) 0, count);
}
public static void fill(Address address, byte value, int count) {
int value4 = (value & 0xFF << 24) | (value & 0xFF << 16) | (value & 0xFF << 8) | (value & 0xFF);
int start = address.toInt();
int alignedStart = start >>> 2 << 2;
address = Address.fromInt(alignedStart);
switch (start - alignedStart) {
case 0:
address.putInt(0);
address.putInt(value4);
break;
case 1:
address.add(1).putByte((byte) 0);
address.add(2).putShort((short) 0);
address.add(1).putByte(value);
address.add(2).putByte(value);
address.add(3).putByte(value);
break;
case 2:
address.add(2).putShort((short) 0);
address.add(2).putByte(value);
address.add(3).putByte(value);
break;
case 3:
address.add(3).putByte((byte) 0);
address.add(3).putByte(value);
break;
}
@ -124,19 +131,21 @@ public final class WasmRuntime {
case 0:
break;
case 1:
address.putByte((byte) 0);
address.putByte(value);
break;
case 2:
address.putShort((short) 0);
address.putByte(value);
address.add(1).putByte(value);
break;
case 3:
address.putShort((short) 0);
address.add(2).putByte((byte) 0);
address.putByte(value);
address.add(1).putByte(value);
address.add(2).putByte(value);
break;
}
for (address = Address.fromInt(alignedStart + 4); address.toInt() < alignedEnd; address = address.add(4)) {
address.putInt(0);
address.putInt(value4);
}
}

View File

@ -272,6 +272,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "align", Address.class, int.class,
Address.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "fill", Address.class, int.class,
int.class, void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "fillZero", Address.class, int.class,
void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "moveMemoryBlock", Address.class,
@ -821,6 +823,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
address = WasmRuntime.align(address + WasmHeap.DEFAULT_STACK_SIZE, 16);
address = WasmRuntime.align(address + maxHeapSize, 16);
address = WasmRuntime.align(address + newRegionsSize, 16);
address = WasmRuntime.align(address + newRegionsCount, 16);
address = WasmRuntime.align(address + newStorageSize, 16);
gcIntrinsic.setRegionSize(WasmHeap.DEFAULT_REGION_SIZE);

View File

@ -46,6 +46,7 @@ public class AllocatorIntrinsic implements WasmIntrinsic {
return false;
}
switch (methodReference.getName()) {
case "fill":
case "fillZero":
case "moveMemoryBlock":
case "isInitialized":
@ -58,6 +59,7 @@ public class AllocatorIntrinsic implements WasmIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) {
case "fill":
case "fillZero":
case "moveMemoryBlock": {
MethodReference delegateMethod = new MethodReference(WasmRuntime.class.getName(),

View File

@ -27,7 +27,11 @@ import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
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.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
@ -38,6 +42,8 @@ public class GCIntrinsic implements WasmIntrinsic {
WasmRuntime.class, "printOutOfMemory", void.class);
private static final MethodReference RESIZE_HEAP = new MethodReference(
WasmHeap.class, "resizeHeap", int.class, void.class);
private static final FieldReference CARD_TABLE = new FieldReference(WasmHeap.class.getName(), "cardTable");
private static final FieldReference HEAP_ADDRESS = new FieldReference(WasmHeap.class.getName(), "heapAddress");
private List<WasmInt32Constant> regionSizeExpressions = new ArrayList<>();
public void setRegionSize(int regionSize) {
@ -58,12 +64,14 @@ public class GCIntrinsic implements WasmIntrinsic {
case "heapAddress":
case "availableBytes":
case "regionsAddress":
case "cardTable":
case "regionMaxCount":
case "regionSize":
case "outOfMemory":
case "minAvailableBytes":
case "maxAvailableBytes":
case "resizeHeap":
case "writeBarrier":
return true;
default:
return false;
@ -81,6 +89,8 @@ public class GCIntrinsic implements WasmIntrinsic {
return getStaticField(manager, "heapAddress");
case "regionsAddress":
return getStaticField(manager, "regionsAddress");
case "cardTable":
return getStaticField(manager, "cardTable");
case "regionMaxCount":
return getStaticField(manager, "regionsCount");
case "minAvailableBytes":
@ -106,6 +116,21 @@ public class GCIntrinsic implements WasmIntrinsic {
block.getBody().add(new WasmUnreachable());
return block;
}
case "writeBarrier": {
WasmExpression cardTableField = new WasmInt32Constant(manager.getStaticField(CARD_TABLE));
WasmExpression cardTable = new WasmLoadInt32(4, cardTableField, WasmInt32Subtype.INT32);
WasmExpression heapAddressField = new WasmInt32Constant(manager.getStaticField(HEAP_ADDRESS));
WasmExpression heapAddress = new WasmLoadInt32(4, heapAddressField, WasmInt32Subtype.INT32);
WasmInt32Constant regionSize = new WasmInt32Constant(0);
regionSizeExpressions.add(regionSize);
WasmExpression offsetInHeap = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
manager.generate(invocation.getArguments().get(0)), heapAddress);
WasmExpression cardIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.DIV_SIGNED,
offsetInHeap, regionSize);
WasmExpression card = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, cardTable,
cardIndex);
return new WasmStoreInt32(1, card, new WasmInt32Constant(0), WasmInt32Subtype.INT8);
}
default:
throw new IllegalArgumentException(invocation.getMethod().toString());
}

View File

@ -23,23 +23,23 @@ import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.transformation.ClassInitInsertion;
public class Linker {
private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
private DependencyInfo dependency;
private ClassInitInsertion classInitInsertion;
public Linker(DependencyInfo dependency) {
this.dependency = dependency;
classInitInsertion = new ClassInitInsertion(dependency);
}
public void link(ClassHolder cls) {
@ -55,7 +55,7 @@ public class Linker {
method.setProgram(null);
}
} else if (method.getProgram() != null) {
link(method.getReference(), method.getProgram());
link(method, method.getProgram());
}
}
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
@ -66,7 +66,7 @@ public class Linker {
}
}
public void link(MethodReference method, Program program) {
public void link(MethodReader method, Program program) {
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block) {
@ -111,36 +111,16 @@ public class Linker {
getField.setField(linkedField.getReference());
}
FieldReference fieldRef = getField.getField();
if (getField.getInstance() == null) {
insertClinit(dependency, fieldRef.getClassName(), method, insn);
}
} else if (insn instanceof PutFieldInstruction) {
PutFieldInstruction putField = (PutFieldInstruction) insn;
FieldDependencyInfo linkedField = dependency.getField(putField.getField());
if (linkedField != null) {
putField.setField(linkedField.getReference());
}
FieldReference fieldRef = putField.getField();
if (putField.getInstance() == null) {
insertClinit(dependency, fieldRef.getClassName(), method, insn);
}
}
}
}
}
private void insertClinit(DependencyInfo dependency, String className, MethodReference method, Instruction insn) {
if (className.equals(method.getClassName())) {
return;
}
ClassReader cls = dependency.getClassSource().get(className);
if (cls == null || cls.getMethod(clinitDescriptor) != null) {
InitClassInstruction initInsn = new InitClassInstruction();
initInsn.setClassName(className);
initInsn.setLocation(insn.getLocation());
insn.insertPrevious(initInsn);
}
classInitInsertion.apply(program, method);
}
}

View File

@ -33,14 +33,13 @@ import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder;
import org.teavm.common.GraphUtils;
import org.teavm.model.BasicBlock;
import org.teavm.model.Incoming;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.Variable;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
@ -48,6 +47,7 @@ import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction;
import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.GraphColorer;
import org.teavm.model.util.LivenessAnalyzer;
@ -98,28 +98,38 @@ public class GCShadowStackContributor {
// for this phi.
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
DominatorTree dom = GraphUtils.buildDominatorTree(cfg);
boolean[] autoSpilled = new SpilledPhisFinder(liveInInformation, dom, program).find();
boolean[] autoSpilled = new SpilledPhisFinder(liveInInformation, dom, program, variableClasses, colors).find();
List<Map<Instruction, int[]>> liveInStores = reduceGCRootStores(dom, program, usedColors, liveInInformation,
colors, autoSpilled);
colors, autoSpilled, variableClasses);
putLiveInGCRoots(program, liveInStores);
return usedColors;
}
private int[] getVariableClasses(Program program) {
DisjointSet variableClasses = new DisjointSet();
DisjointSet disjointSet = new DisjointSet();
for (int i = 0; i < program.variableCount(); ++i) {
variableClasses.create();
disjointSet.create();
}
for (BasicBlock block : program.getBasicBlocks()) {
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
variableClasses.union(phi.getReceiver().getIndex(), incoming.getValue().getIndex());
for (Instruction instruction : block) {
if (instruction instanceof AssignInstruction) {
AssignInstruction assign = (AssignInstruction) instruction;
disjointSet.union(assign.getAssignee().getIndex(), assign.getReceiver().getIndex());
} else if (instruction instanceof NullCheckInstruction) {
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
disjointSet.union(nullCheck.getValue().getIndex(), nullCheck.getReceiver().getIndex());
} else if (instruction instanceof CastInstruction) {
CastInstruction cast = (CastInstruction) instruction;
disjointSet.union(cast.getValue().getIndex(), cast.getReceiver().getIndex());
} else if (instruction instanceof UnwrapArrayInstruction) {
UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) instruction;
disjointSet.union(unwrapArray.getArray().getIndex(), unwrapArray.getReceiver().getIndex());
}
}
}
return variableClasses.pack(program.variableCount());
return disjointSet.pack(program.variableCount());
}
private List<Map<Instruction, BitSet>> findCallSiteLiveIns(Program program, MethodReader method) {
@ -207,7 +217,8 @@ public class GCShadowStackContributor {
}
private List<Map<Instruction, int[]>> reduceGCRootStores(DominatorTree dom, Program program, int usedColors,
List<Map<Instruction, BitSet>> liveInInformation, int[] colors, boolean[] autoSpilled) {
List<Map<Instruction, BitSet>> liveInInformation, int[] colors, boolean[] autoSpilled,
int[] variableClasses) {
class Step {
private final int node;
private final int[] slotStates = new int[usedColors];
@ -228,7 +239,6 @@ public class GCShadowStackContributor {
Step start = new Step(0);
Arrays.fill(start.slotStates, program.variableCount());
stack[head++] = start;
int[] definitionClasses = getDefinitionClasses(program);
while (head > 0) {
Step step = stack[--head];
@ -250,7 +260,7 @@ public class GCShadowStackContributor {
}
}
int[] updates = compareStates(previousStates, states, autoSpilled, definitionClasses);
int[] updates = compareStates(previousStates, states, autoSpilled, variableClasses);
updatesByCallSite.put(callSiteLocation, updates);
previousStates = states;
states = states.clone();
@ -266,22 +276,6 @@ public class GCShadowStackContributor {
return slotsToUpdate;
}
private int[] getDefinitionClasses(Program program) {
DisjointSet disjointSet = new DisjointSet();
for (int i = 0; i < program.variableCount(); ++i) {
disjointSet.create();
}
for (BasicBlock block : program.getBasicBlocks()) {
for (Instruction instruction : block) {
if (instruction instanceof NullCheckInstruction) {
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
disjointSet.union(nullCheck.getValue().getIndex(), nullCheck.getReceiver().getIndex());
}
}
}
return disjointSet.pack(program.variableCount());
}
private List<Instruction> sortInstructions(Collection<Instruction> instructions, BasicBlock block) {
ObjectIntMap<Instruction> indexes = new ObjectIntHashMap<>();
int index = 0;

View File

@ -37,26 +37,33 @@ class SpilledPhisFinder {
int[][] variableSpilledBlocks;
Phi[] definingPhis;
int variableCount;
private int[] variableClasses;
private int[] colors;
SpilledPhisFinder(List<Map<Instruction, BitSet>> liveInInformation, DominatorTree dom, Program program) {
SpilledPhisFinder(List<Map<Instruction, BitSet>> liveInInformation, DominatorTree dom, Program program,
int[] variableClasses, int[] colors) {
this.dom = dom;
variableCount = program.variableCount();
autoSpilled = new boolean[variableCount];
status = new byte[variableCount];
variableSpilledBlocks = variableSpilledBlocks(liveInInformation, variableCount);
definingPhis = findPhis(program);
variableSpilledBlocks = variableSpilledBlocks(liveInInformation, variableCount, variableClasses);
definingPhis = findPhis(program, variableClasses);
this.variableClasses = variableClasses;
this.colors = colors;
}
private static int[][] variableSpilledBlocks(List<Map<Instruction, BitSet>> liveInInformation, int count) {
private static int[][] variableSpilledBlocks(List<Map<Instruction, BitSet>> liveInInformation, int count,
int[] variableClasses) {
IntSet[] builder = new IntSet[count];
for (int b = 0; b < liveInInformation.size(); b++) {
Map<Instruction, BitSet> blockLiveIn = liveInInformation.get(b);
for (BitSet liveVarsSet : blockLiveIn.values()) {
for (int v = liveVarsSet.nextSetBit(0); v >= 0; v = liveVarsSet.nextSetBit(v + 1)) {
if (builder[v] == null) {
builder[v] = new IntHashSet();
int cls = variableClasses[v];
if (builder[cls] == null) {
builder[cls] = new IntHashSet();
}
builder[v].add(b);
builder[cls].add(b);
}
}
}
@ -71,11 +78,11 @@ class SpilledPhisFinder {
return result;
}
private static Phi[] findPhis(Program program) {
private static Phi[] findPhis(Program program, int[] variableClasses) {
Phi[] result = new Phi[program.variableCount()];
for (BasicBlock block : program.getBasicBlocks()) {
for (Phi phi : block.getPhis()) {
result[phi.getReceiver().getIndex()] = phi;
result[variableClasses[phi.getReceiver().getIndex()]] = phi;
}
}
return result;
@ -89,6 +96,8 @@ class SpilledPhisFinder {
}
private boolean isAutoSpilled(int v) {
v = variableClasses[v];
if (status[v] == VISITED) {
return autoSpilled[v];
}
@ -107,6 +116,10 @@ class SpilledPhisFinder {
boolean result = true;
for (Incoming incoming : definingPhi.getIncomings()) {
if (colors[incoming.getValue().getIndex()] != colors[definingPhi.getReceiver().getIndex()]) {
result = false;
break;
}
if (!isAutoSpilled(incoming.getValue().getIndex())) {
int[] spilledAt = variableSpilledBlocks[incoming.getValue().getIndex()];
result = false;

View File

@ -0,0 +1,236 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.lowlevel;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.cursors.IntCursor;
import org.teavm.model.BasicBlock;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.AbstractInstructionVisitor;
import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction;
import org.teavm.model.instructions.MonitorExitInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.util.DominatorWalker;
import org.teavm.model.util.DominatorWalkerCallback;
import org.teavm.model.util.DominatorWalkerContext;
import org.teavm.runtime.GC;
import org.teavm.runtime.RuntimeObject;
public class WriteBarrierInsertion {
private static final MethodReference BARRIER_METHOD = new MethodReference(GC.class, "writeBarrier",
RuntimeObject.class, void.class);
private Characteristics characteristics;
public WriteBarrierInsertion(Characteristics characteristics) {
this.characteristics = characteristics;
}
public void apply(Program program) {
if (program.basicBlockCount() == 0) {
return;
}
new DominatorWalker(program).walk(new WalkerCallbackImpl());
}
class WalkerCallbackImpl extends AbstractInstructionVisitor implements DominatorWalkerCallback<State> {
private DominatorWalkerContext context;
IntHashSet installedBarriers = new IntHashSet();
State state;
@Override
public void setContext(DominatorWalkerContext context) {
this.context = context;
}
@Override
public State visit(BasicBlock block) {
state = new State();
if (context.isExceptionHandler(block.getIndex()) || !context.allPredecessorsVisited(block.getIndex())) {
invalidateBarriers();
} else {
for (Phi phi : block.getPhis()) {
if (phi.getIncomings().stream().allMatch(
incoming -> installedBarriers.contains(incoming.getValue().getIndex()))) {
markAsInstalled(phi.getReceiver().getIndex());
}
}
}
for (Instruction instruction : block) {
instruction.acceptVisitor(this);
}
return state;
}
@Override
public void endVisit(BasicBlock block, State state) {
if (state.oldBarriers != null) {
installedBarriers.clear();
installedBarriers.addAll(state.oldBarriers);
} else {
for (IntCursor cursor : state.newBarriers) {
installedBarriers.remove(cursor.value);
}
}
}
@Override
public void visit(PutFieldInstruction insn) {
if (insn.getInstance() != null && isManagedReferenceType(insn.getFieldType())) {
installBarrier(insn, insn.getInstance());
}
}
@Override
public void visit(InvokeInstruction insn) {
invalidateBarriers();
}
@Override
public void visit(ConstructInstruction insn) {
invalidateBarriers();
markAsInstalled(insn.getReceiver().getIndex());
}
@Override
public void visit(InitClassInstruction insn) {
invalidateBarriers();
}
@Override
public void visit(CloneArrayInstruction insn) {
invalidateBarriers();
}
@Override
public void visit(ConstructArrayInstruction insn) {
invalidateBarriers();
}
@Override
public void visit(ConstructMultiArrayInstruction insn) {
invalidateBarriers();
}
@Override
public void visit(MonitorEnterInstruction insn) {
invalidateBarriers();
}
@Override
public void visit(MonitorExitInstruction insn) {
invalidateBarriers();
}
private boolean isManagedReferenceType(ValueType type) {
if (type instanceof ValueType.Array) {
return true;
}
if (type instanceof ValueType.Object) {
return characteristics.isManaged(((ValueType.Object) type).getClassName());
}
return false;
}
@Override
public void visit(PutElementInstruction insn) {
if (insn.getType() == ArrayElementType.OBJECT) {
installBarrier(insn, insn.getArray());
}
}
@Override
public void visit(AssignInstruction insn) {
assign(insn.getAssignee(), insn.getReceiver());
}
@Override
public void visit(CastInstruction insn) {
assign(insn.getValue(), insn.getReceiver());
}
@Override
public void visit(NullCheckInstruction insn) {
assign(insn.getValue(), insn.getReceiver());
}
private void assign(Variable from, Variable to) {
if (installedBarriers.contains(from.getIndex())) {
markAsInstalled(to.getIndex());
}
}
private void installBarrier(Instruction instruction, Variable variable) {
if (markAsInstalled(variable.getIndex())) {
InvokeInstruction invoke = new InvokeInstruction();
invoke.setType(InvocationType.SPECIAL);
invoke.setMethod(BARRIER_METHOD);
invoke.setArguments(variable);
invoke.setLocation(instruction.getLocation());
instruction.insertPrevious(invoke);
}
}
private boolean markAsInstalled(int index) {
if (!installedBarriers.add(index)) {
return false;
}
if (state.newBarriers != null) {
state.newBarriers.add(index);
}
return true;
}
private void invalidateBarriers() {
if (state.newBarriers != null) {
state.oldBarriers = new IntArrayList();
for (IntCursor cursor : installedBarriers) {
if (!state.newBarriers.contains(cursor.value)) {
state.oldBarriers.add(cursor.value);
}
}
state.newBarriers = null;
}
installedBarriers.clear();
}
}
static class State {
IntSet newBarriers = new IntHashSet();
IntArrayList oldBarriers;
}
}

View File

@ -0,0 +1,168 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.transformation;
import java.util.HashSet;
import java.util.Set;
import org.teavm.dependency.DependencyInfo;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.Instruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.Program;
import org.teavm.model.instructions.AbstractInstructionVisitor;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.util.DominatorWalker;
import org.teavm.model.util.DominatorWalkerCallback;
import org.teavm.model.util.DominatorWalkerContext;
public class ClassInitInsertion {
private static final MethodDescriptor CLINIT = new MethodDescriptor("<clinit>", void.class);
private DependencyInfo dependencyInfo;
public ClassInitInsertion(DependencyInfo dependencyInfo) {
this.dependencyInfo = dependencyInfo;
}
public void apply(Program program, MethodReader method) {
if (program.basicBlockCount() == 0) {
return;
}
String currentClass = null;
if (method.hasModifier(ElementModifier.STATIC)) {
currentClass = method.getOwnerName();
}
Visitor visitor = new Visitor(currentClass);
new DominatorWalker(program).walk(visitor);
}
class Visitor extends AbstractInstructionVisitor implements DominatorWalkerCallback<State> {
private String currentClass;
private DominatorWalkerContext context;
Set<String> initializedClasses = new HashSet<>();
private State state;
Visitor(String currentClass) {
this.currentClass = currentClass;
if (currentClass != null) {
initializedClasses.add(currentClass);
}
}
@Override
public void setContext(DominatorWalkerContext context) {
this.context = context;
}
@Override
public State visit(BasicBlock block) {
state = new State();
if (context.isExceptionHandler(block.getIndex())) {
markAllClassesAsNotInitialized();
if (currentClass != null) {
markClassAsInitialized(currentClass);
}
}
for (Instruction instruction : block) {
instruction.acceptVisitor(this);
}
return state;
}
@Override
public void endVisit(BasicBlock block, State state) {
if (state.oldInitializedClasses != null) {
initializedClasses.clear();
initializedClasses.addAll(state.oldInitializedClasses);
} else {
initializedClasses.removeAll(state.newInitializedClasses);
}
}
@Override
public void visit(GetFieldInstruction insn) {
if (insn.getInstance() == null) {
initializeClass(insn.getField().getClassName(), insn);
}
}
@Override
public void visit(PutFieldInstruction insn) {
if (insn.getInstance() == null) {
initializeClass(insn.getField().getClassName(), insn);
}
}
@Override
public void visit(InvokeInstruction insn) {
if (insn.getInstance() == null) {
markClassAsInitialized(insn.getMethod().getClassName());
}
}
@Override
public void visit(InitClassInstruction insn) {
markClassAsInitialized(insn.getClassName());
}
private void initializeClass(String className, Instruction instruction) {
if (markClassAsInitialized(className)) {
ClassReader cls = dependencyInfo.getClassSource().get(className);
if (cls == null || cls.getMethod(CLINIT) != null) {
InitClassInstruction initInsn = new InitClassInstruction();
initInsn.setClassName(className);
initInsn.setLocation(instruction.getLocation());
instruction.insertPrevious(initInsn);
}
}
}
boolean markClassAsInitialized(String className) {
if (initializedClasses.add(className)) {
if (state.newInitializedClasses != null) {
state.newInitializedClasses.add(className);
}
return true;
}
return false;
}
private void markAllClassesAsNotInitialized() {
if (state.newInitializedClasses != null) {
state.oldInitializedClasses = new HashSet<>();
for (String className : initializedClasses) {
if (!state.newInitializedClasses.contains(className)) {
state.oldInitializedClasses.add(className);
}
}
state.newInitializedClasses = null;
}
initializedClasses.clear();
}
}
static class State {
Set<String> newInitializedClasses = new HashSet<>();
Set<String> oldInitializedClasses;
}
}

View File

@ -15,29 +15,70 @@
*/
package org.teavm.model.util;
import com.carrotsearch.hppc.IntStack;
import org.teavm.common.DominatorTree;
import org.teavm.common.Graph;
import org.teavm.common.GraphUtils;
import org.teavm.model.BasicBlock;
import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
public class DominatorWalker {
private Program program;
private DominatorTree dom;
private Graph cfg;
private Graph domGraph;
private int[] order;
public DominatorWalker(Program program) {
this.program = program;
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
cfg = ProgramUtils.buildControlFlowGraph(program);
dom = GraphUtils.buildDominatorTree(cfg);
domGraph = GraphUtils.buildDominatorGraph(dom, cfg.size());
order = dfs(cfg);
}
private int[] dfs(Graph graph) {
if (graph.size() == 0) {
return new int[0];
}
int index = 0;
int[] result = new int[graph.size()];
IntStack stack = new IntStack(graph.size());
byte[] state = new byte[graph.size()];
stack.push(0);
while (!stack.isEmpty()) {
int node = stack.pop();
switch (state[node]) {
case 0: {
state[node] = 1;
stack.push(node);
for (int succ : graph.outgoingEdges(node)) {
if (state[succ] == 0) {
stack.push(succ);
}
}
break;
}
case 1: {
state[node] = 2;
result[node] = index++;
break;
}
}
}
return result;
}
public <T> void walk(DominatorWalkerCallback<T> callback) {
int[] stack = new int[program.basicBlockCount() * 2];
Object[] stateStack = new Object[stack.length];
boolean[] backward = new boolean[stack.length];
callback.setDomTree(dom);
ContextImpl context = new ContextImpl(dom, cfg, findExceptionHandlers());
callback.setContext(context);
int head = 1;
while (head > 0) {
@ -47,6 +88,7 @@ public class DominatorWalker {
@SuppressWarnings("unchecked")
T state = (T) stateStack[head];
callback.endVisit(block, state);
context.visited[block.getIndex()] = true;
} else if (callback.filter(block)) {
stack[head] = node;
backward[head] = true;
@ -54,6 +96,7 @@ public class DominatorWalker {
head++;
int[] successors = domGraph.outgoingEdges(node);
sort(successors);
for (int i = successors.length - 1; i >= 0; --i) {
stack[head] = successors[i];
backward[head] = false;
@ -62,4 +105,69 @@ public class DominatorWalker {
}
}
}
private boolean[] findExceptionHandlers() {
boolean[] exceptionHandlers = new boolean[program.basicBlockCount()];
for (BasicBlock block : program.getBasicBlocks()) {
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
exceptionHandlers[tryCatch.getHandler().getIndex()] = true;
}
}
return exceptionHandlers;
}
private void sort(int[] nodes) {
for (int i = 0; i < nodes.length - 1; ++i) {
int a = nodes[i];
int bestIndex = i;
int best = order[a];
for (int j = i + 1; j < nodes.length; ++j) {
int b = nodes[j];
int score = order[b];
if (score < best) {
best = score;
a = b;
bestIndex = j;
}
}
if (i != bestIndex) {
nodes[bestIndex] = nodes[i];
nodes[i] = a;
}
}
}
static class ContextImpl implements DominatorWalkerContext {
private boolean[] visited;
private boolean[] isExceptionHandler;
private DominatorTree dom;
private Graph cfg;
ContextImpl(DominatorTree dom, Graph cfg, boolean[] isExceptionHandler) {
this.dom = dom;
this.cfg = cfg;
this.isExceptionHandler = isExceptionHandler;
visited = new boolean[cfg.size()];
}
@Override
public DominatorTree getDominatorTree() {
return dom;
}
@Override
public Graph getControlFlowGraph() {
return cfg;
}
@Override
public boolean isVisited(int blockIndex) {
return visited[blockIndex];
}
@Override
public boolean isExceptionHandler(int blockIndex) {
return isExceptionHandler[blockIndex];
}
}
}

View File

@ -15,7 +15,6 @@
*/
package org.teavm.model.util;
import org.teavm.common.DominatorTree;
import org.teavm.model.BasicBlock;
/**
@ -24,7 +23,7 @@ import org.teavm.model.BasicBlock;
* @param <T> type of state that can be saved for each visited node.
*/
public interface DominatorWalkerCallback<T> {
default void setDomTree(DominatorTree domTree) {
default void setContext(DominatorWalkerContext context) {
}
/**

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.util;
import org.teavm.common.DominatorTree;
import org.teavm.common.Graph;
public interface DominatorWalkerContext {
DominatorTree getDominatorTree();
Graph getControlFlowGraph();
boolean isVisited(int blockIndex);
boolean isExceptionHandler(int blockIndex);
default boolean allPredecessorsVisited(int blockIndex) {
for (int predecessor : getControlFlowGraph().incomingEdges(blockIndex)) {
if (!isVisited(predecessor)) {
return false;
}
}
return true;
}
}

View File

@ -21,6 +21,7 @@ import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@StaticInit
@Unmanaged
public final class Allocator {
private Allocator() {
}
@ -62,12 +63,11 @@ public final class Allocator {
return array;
}
@Unmanaged
public static native void fillZero(Address address, int count);
@Unmanaged
public static native void fill(Address address, byte value, int count);
public static native void moveMemoryBlock(Address source, Address target, int count);
@Unmanaged
public static native boolean isInitialized(Class<?> cls);
}

File diff suppressed because it is too large Load Diff

View File

@ -29,17 +29,27 @@ public class MemoryTrace {
public static native void checkIsFree(Address address, int size);
public static native void initMark();
public static native void markStarted();
public static native void mark(Address address);
public static native void reportDirtyRegion(Address address);
public static native void markCompleted();
public static native void move(Address from, Address to, int size);
public static native void gcStarted();
public static native void gcStarted(boolean full);
public static native void sweepStarted();
public static native void sweepCompleted();
public static native void defragStarted();
public static native void defragCompleted();
public static native void writeHeapDump();
public static native void gcCompleted();
}

View File

@ -21,6 +21,7 @@ import org.teavm.interop.Structure;
@StaticInit
public class RuntimeObject extends Structure {
public static final int GC_MARKED = 0x80000000;
public static final int GC_OLD_GENERATION = 0x40000000;
public static int nextId;

View File

@ -961,7 +961,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (program == null) {
program = ProgramUtils.copy(classReader.getMethod(method.getDescriptor()).getProgram());
missingItemsProcessor.processMethod(method.getReference(), program);
linker.link(method.getReference(), program);
linker.link(method, program);
clinitInsertion.apply(method, program);
program = optimizeMethodCacheMiss(method, program);
Program finalProgram = program;

View File

@ -179,4 +179,10 @@ extern TeaVM_Class* teavm_objectClass;
extern TeaVM_Class* teavm_stringClass;
extern TeaVM_Class* teavm_charArrayClass;
extern int32_t teavm_classReferencesCount;
extern void teavm_initClasses();
extern void teavm_initClasses();
inline static void teavm_gc_writeBarrier(void* object) {
intptr_t offset = (intptr_t) ((char*) object - (char*) teavm_gc_heapAddress) / teavm_gc_regionSize;
((char*) teavm_gc_cardTable)[offset] = 0;
}

View File

@ -59,6 +59,10 @@
#define TEAVM_GC_LOG 0
#endif
#ifndef TEAVM_GC_STATS
#define TEAVM_GC_STATS 0
#endif
#ifndef TEAVM_OBFUSCATED
#define TEAVM_OBFUSCATED 0
#endif

View File

@ -2,6 +2,7 @@
#include "core.h"
#include "definitions.h"
#include "memory.h"
#include "time.h"
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
@ -9,6 +10,7 @@
#include <stdlib.h>
#include <wchar.h>
#include <wctype.h>
#include <stdbool.h>
#if TEAVM_WINDOWS
#include <Windows.h>
@ -33,6 +35,27 @@ void teavm_outOfMemory() {
static wchar_t* teavm_gc_dumpDirectory = NULL;
#ifdef TEAVM_GC_STATS
static int32_t teavm_gc_allocationCount = 0;
static int32_t teavm_gc_freeCount = 0;
static int32_t teavm_gc_freeByteCount = 0;
static int32_t teavm_gc_markCount = 0;
static int32_t teavm_gc_dirtyRegionCount = 0;
static int32_t teavm_gc_relocatedBlocks = 0;
static int32_t teavm_gc_relocatedBytes = 0;
static int64_t teavm_gc_startTimeMillis;
static int64_t teavm_gc_startTime;
static int64_t teavm_gc_endTime;
static int64_t teavm_gc_markStartTime;
static int64_t teavm_gc_markEndTime;
static int64_t teavm_gc_sweepStartTime;
static int64_t teavm_gc_sweepEndTime;
static int64_t teavm_gc_defragStartTime;
static int64_t teavm_gc_defragEndTime;
static bool teavm_gc_full;
#endif
#if TEAVM_MEMORY_TRACE
void teavm_gc_assertSize(int32_t size) {
if (size % sizeof(void*) != 0) {
@ -65,6 +88,10 @@ void teavm_gc_allocate(void* address, int32_t size) {
*map++ = 2;
}
#endif
#if TEAVM_GC_STATS
teavm_gc_allocationCount++;
#endif
}
void teavm_gc_free(void* address, int32_t size) {
@ -86,6 +113,11 @@ void teavm_gc_free(void* address, int32_t size) {
uint8_t* map = teavm_gc_heapMap + offset;
memset(map, 0, size);
#endif
#if TEAVM_GC_STATS
teavm_gc_freeCount++;
teavm_gc_freeByteCount += size;
#endif
}
void teavm_gc_assertFree(void* address, int32_t size) {
@ -106,10 +138,20 @@ void teavm_gc_assertFree(void* address, int32_t size) {
#endif
}
void teavm_gc_initMark() {
void teavm_gc_markStarted() {
#if TEAVM_MEMORY_TRACE
memset(teavm_gc_markMap, 0, teavm_gc_availableBytes / sizeof(void*));
#endif
#if TEAVM_GC_STATS
teavm_gc_markStartTime = teavm_currentTimeNano();
#endif
}
void teavm_gc_markCompleted() {
#if TEAVM_GC_STATS
teavm_gc_markEndTime = teavm_currentTimeNano();
#endif
}
int32_t teavm_gc_objectSize(void* address) {
@ -144,7 +186,8 @@ void teavm_gc_mark(void* address) {
size /= sizeof(void*);
if (*map++ != 1 || *markMap != 0) {
fprintf(stderr, "[GC] assertion failed marking object at: %d\n", (int) ((char*) address - (char*) teavm_gc_heapAddress));
fprintf(stderr, "[GC] assertion failed marking object at: %d\n",
(int) ((char*) address - (char*) teavm_gc_heapAddress));
abort();
}
*markMap++ = 1;
@ -156,6 +199,10 @@ void teavm_gc_mark(void* address) {
*markMap++ = 1;
}
#endif
#if TEAVM_GC_STATS
teavm_gc_markCount++;
#endif
}
void teavm_gc_move(void* from, void* to, int32_t size) {
@ -188,6 +235,11 @@ void teavm_gc_move(void* from, void* to, int32_t size) {
}
}
#endif
#if TEAVM_GC_STATS
teavm_gc_relocatedBlocks++;
teavm_gc_relocatedBytes += size;
#endif
}
static FILE* teavm_gc_traceFile = NULL;
@ -251,8 +303,10 @@ FILE* teavm_gc_openDumpFile(wchar_t* name) {
#endif
}
void teavm_gc_checkHeapConsistency() {
void teavm_gc_checkHeapConsistency(bool oldGen, bool offsets) {
int32_t lastCheckedRegion = -1;
TeaVM_Object* obj = teavm_gc_heapAddress;
uint16_t* regions = (uint16_t*) teavm_gc_regionsAddress;
while ((char*) obj < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
int32_t size;
if (obj->header == 0) {
@ -260,14 +314,32 @@ FILE* teavm_gc_openDumpFile(wchar_t* name) {
teavm_gc_assertFree(obj, size);
} else {
teavm_verify(obj);
if (offsets) {
int64_t offset = (int64_t) ((char*) obj - (char*) teavm_gc_heapAddress);
int32_t objRegion = (int32_t) (offset / teavm_gc_regionSize);
if (objRegion != lastCheckedRegion) {
while (++lastCheckedRegion < objRegion) {
if (regions[lastCheckedRegion] != 0) {
abort();
}
}
int32_t offsetInRegion = (int32_t) (offset % teavm_gc_regionSize);
if (regions[objRegion] != offsetInRegion + 1) {
abort();
}
}
}
if (oldGen && !(obj->header & 0x40000000)) {
abort();
}
TeaVM_Class* cls = TEAVM_CLASS_OF(obj);
if (cls->itemType != NULL) {
if (!(cls->itemType->flags & 2)) {
char* offset = NULL;
offset += sizeof(TeaVM_Array);
offset = TEAVM_ALIGN(offset, sizeof(void*));
void** data = (void**)((char*)obj + (uintptr_t)offset);
int32_t size = ((TeaVM_Array*)obj)->size;
void** data = (void**) ((char*) obj + (uintptr_t) offset);
int32_t size = ((TeaVM_Array*) obj)->size;
for (int32_t i = 0; i < size; ++i) {
teavm_verify(data[i]);
}
@ -297,26 +369,143 @@ FILE* teavm_gc_openDumpFile(wchar_t* name) {
}
obj = (TeaVM_Object*) ((char*) obj + size);
}
if (offsets) {
int32_t lastRegion = (int32_t) (teavm_gc_availableBytes / teavm_gc_regionSize);
while (++lastCheckedRegion <= lastRegion) {
if (regions[lastCheckedRegion] != 0) {
abort();
}
}
}
}
#endif
void teavm_gc_gcStarted() {
void teavm_gc_gcStarted(int32_t full) {
#if TEAVM_MEMORY_TRACE
teavm_writeHeapMemory("start");
teavm_gc_checkHeapConsistency();
teavm_gc_checkHeapConsistency(false, false);
#endif
#if TEAVM_GC_STATS
teavm_gc_startTime = teavm_currentTimeNano();
teavm_gc_startTimeMillis = teavm_currentTimeMillis();
teavm_gc_full = full;
#endif
}
void teavm_gc_sweepStarted() {
#if TEAVM_GC_STATS
teavm_gc_sweepStartTime = teavm_currentTimeNano();
#endif
}
void teavm_gc_sweepCompleted() {
#if TEAVM_MEMORY_TRACE
teavm_writeHeapMemory("sweep");
teavm_gc_checkHeapConsistency();
teavm_gc_checkHeapConsistency(false, true);
#endif
#if TEAVM_GC_STATS
teavm_gc_sweepEndTime = teavm_currentTimeNano();
#endif
}
void teavm_gc_defragStarted() {
#if TEAVM_GC_STATS
teavm_gc_defragStartTime = teavm_currentTimeNano();
#endif
}
void teavm_gc_defragCompleted() {
#if TEAVM_MEMORY_TRACE
teavm_writeHeapMemory("defrag");
teavm_gc_checkHeapConsistency(true, true);
#endif
#if TEAVM_GC_STATS
teavm_gc_defragEndTime = teavm_currentTimeNano();
#endif
}
#define TEAVM_GC_LOG_BUFFER_SIZE 512
#if TEAVM_GC_STATS
static void teavm_gc_print(wchar_t* s) {
#if TEAVM_WINDOWS_LOG
OutputDebugStringW(s);
#else
fprintf(stderr, "%ls", s);
#endif
}
static void teavm_gc_printStats() {
wchar_t buffer[TEAVM_GC_LOG_BUFFER_SIZE];
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Garbage collection (%ls) performed at %" PRIu64 ", took %"
PRIu64 " ns\n", teavm_gc_full ? L"full" : L"young", teavm_gc_startTimeMillis,
teavm_gc_endTime - teavm_gc_startTime);
teavm_gc_print(buffer);
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Allocations performed before GC: %" PRIu32 "\n",
teavm_gc_allocationCount);
teavm_gc_print(buffer);
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Mark phase took %" PRIu64 " ns, %" PRIu32
" objects reached\n", teavm_gc_markEndTime - teavm_gc_markStartTime, teavm_gc_markCount);
teavm_gc_print(buffer);
if (!teavm_gc_full) {
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Regions scanned from remembered set: %" PRIu32 "\n",
teavm_gc_dirtyRegionCount);
teavm_gc_print(buffer);
}
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Sweep phase took %" PRIu64 " ns, %" PRIu32 " regions of %"
PRIu32 " bytes freed\n", teavm_gc_sweepEndTime - teavm_gc_sweepStartTime, teavm_gc_freeCount,
teavm_gc_freeByteCount);
teavm_gc_print(buffer);
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Defrag phase took %" PRIu64 " ns\n",
teavm_gc_defragEndTime - teavm_gc_defragStartTime);
teavm_gc_print(buffer);
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Blocks relocated %" PRId32 " of total %" PRId32 " bytes\n",
teavm_gc_relocatedBlocks, teavm_gc_relocatedBytes);
teavm_gc_print(buffer);
}
static void teavm_gc_resetStats() {
teavm_gc_allocationCount = 0;
teavm_gc_markCount = 0;
teavm_gc_dirtyRegionCount = 0;
teavm_gc_freeCount = 0;
teavm_gc_freeByteCount = 0;
teavm_gc_relocatedBlocks = 0;
teavm_gc_relocatedBytes = 0;
}
#endif
void teavm_gc_gcCompleted() {
#if TEAVM_GC_STATS
teavm_gc_endTime = teavm_currentTimeNano();
teavm_gc_printStats();
teavm_gc_resetStats();
#endif
}
void teavm_gc_heapResized(int64_t newSize) {
#if TEAVM_GC_STATS
wchar_t buffer[TEAVM_GC_LOG_BUFFER_SIZE];
swprintf(buffer, TEAVM_GC_LOG_BUFFER_SIZE, L"[GC] Heap resized to %" PRIu64 " bytes\n", newSize);
teavm_gc_print(buffer);
#endif
}
void teavm_gc_reportDirtyRegion(void* address) {
#if TEAVM_GC_STATS
teavm_gc_dirtyRegionCount++;
#endif
}
@ -329,4 +518,3 @@ void teavm_gc_setDumpDirectory(const wchar_t* path) {
teavm_gc_dumpDirectory = malloc(bytesLen);
memcpy(teavm_gc_dumpDirectory, path, bytesLen);
}

View File

@ -12,12 +12,18 @@
extern void teavm_gc_allocate(void* address, int32_t size);
extern void teavm_gc_free(void* address, int32_t size);
extern void teavm_gc_assertFree(void* address, int32_t size);
extern void teavm_gc_initMark();
extern void teavm_gc_mark(void* address);
extern void teavm_gc_move(void* from, void* to, int32_t size);
extern void teavm_gc_gcStarted();
extern void teavm_gc_gcStarted(int32_t full);
extern void teavm_gc_markStarted();
extern void teavm_gc_markCompleted();
extern void teavm_gc_sweepStarted();
extern void teavm_gc_sweepCompleted();
extern void teavm_gc_defragStarted();
extern void teavm_gc_defragCompleted();
extern void teavm_gc_gcCompleted();
extern void teavm_gc_heapResized(int64_t newSize);
extern void teavm_gc_reportDirtyRegion(void* address);
extern void teavm_gc_setDumpDirectory(const wchar_t* path);
extern void teavm_gc_fixHeap();
extern void teavm_gc_writeHeapDump();

View File

@ -1,4 +1,5 @@
#include "memory.h"
#include "heaptrace.h"
#include "definitions.h"
#include <stdlib.h>
#include <string.h>
@ -20,7 +21,7 @@ void* teavm_gc_heapAddress = NULL;
void* teavm_gc_gcStorageAddress = NULL;
int32_t teavm_gc_gcStorageSize = INT32_C(0);
void* teavm_gc_regionsAddress = NULL;
int32_t teavm_gc_regionSize = INT32_C(32768);
void* teavm_gc_cardTable = NULL;
int32_t teavm_gc_regionMaxCount;
int64_t teavm_gc_availableBytes;
int64_t teavm_gc_minAvailableBytes;
@ -110,6 +111,8 @@ void teavm_gc_resizeHeap(int64_t newSize) {
return;
}
teavm_gc_heapResized(newSize);
int32_t workSize = teavm_gc_calculateWorkSize(newSize);
int32_t regionsSize = teavm_gc_calculateRegionsSize(newSize);
@ -119,6 +122,8 @@ void teavm_gc_resizeHeap(int64_t newSize) {
int64_t oldWorkSizeAligned = teavm_pageCount(teavm_gc_gcStorageSize);
int64_t newRegionsSizeAligned = teavm_pageCount(regionsSize * 2);
int64_t oldRegionsSizeAligned = teavm_pageCount(teavm_gc_regionMaxCount * 2);
int64_t newCardTableSizeAligned = teavm_pageCount(regionsSize);
int64_t oldCardTableSizeAligned = teavm_pageCount(teavm_gc_regionMaxCount);
if (newSize > teavm_gc_availableBytes) {
if (newSizeAligned > oldSizeAligned) {
@ -132,6 +137,10 @@ void teavm_gc_resizeHeap(int64_t newSize) {
teavm_virtualCommit((char*) teavm_gc_regionsAddress + oldRegionsSizeAligned,
newRegionsSizeAligned - oldRegionsSizeAligned);
}
if (newCardTableSizeAligned > oldCardTableSizeAligned) {
teavm_virtualCommit((char*) teavm_gc_cardTable + oldCardTableSizeAligned,
newCardTableSizeAligned - oldCardTableSizeAligned);
}
} else {
if (newSizeAligned < oldSizeAligned) {
teavm_virtualUncommit((char*) teavm_gc_heapAddress + newSizeAligned, oldSizeAligned - newSizeAligned);
@ -144,6 +153,10 @@ void teavm_gc_resizeHeap(int64_t newSize) {
teavm_virtualUncommit((char*) teavm_gc_regionsAddress + newRegionsSizeAligned,
oldRegionsSizeAligned - newRegionsSizeAligned);
}
if (newCardTableSizeAligned < oldCardTableSizeAligned) {
teavm_virtualUncommit((char*) teavm_gc_cardTable + newCardTableSizeAligned,
oldCardTableSizeAligned - newCardTableSizeAligned);
}
}
teavm_gc_gcStorageSize = workSize;
@ -159,6 +172,7 @@ void teavm_initHeap(int64_t minHeap, int64_t maxHeap) {
teavm_gc_heapAddress = teavm_virtualAlloc(teavm_pageCount(maxHeap));
teavm_gc_gcStorageAddress = teavm_virtualAlloc(teavm_pageCount(workSize));
teavm_gc_regionsAddress = teavm_virtualAlloc(teavm_pageCount(regionsSize * 2));
teavm_gc_cardTable = teavm_virtualAlloc(teavm_pageCount(regionsSize));
#if TEAVM_MEMORY_TRACE
int64_t heapMapSize = maxHeap / sizeof(void*);

View File

@ -5,7 +5,8 @@ extern void* teavm_gc_heapAddress;
extern void* teavm_gc_gcStorageAddress;
extern int32_t teavm_gc_gcStorageSize;
extern void* teavm_gc_regionsAddress;
extern int32_t teavm_gc_regionSize;
extern void* teavm_gc_cardTable;
#define teavm_gc_regionSize INT32_C(8192)
extern int32_t teavm_gc_regionMaxCount;
extern int64_t teavm_gc_availableBytes;
extern int64_t teavm_gc_minAvailableBytes;

View File

@ -10,9 +10,11 @@ int32_t teavm_reference_enqueue(TeaVM_Reference* reference) {
if (queue->last == NULL) {
queue->first = reference;
} else {
teavm_gc_writeBarrier(queue->last);
queue->last->next = reference;
}
queue->last = reference;
teavm_gc_writeBarrier(queue);
return INT32_C(1);
}
@ -36,6 +38,7 @@ TeaVM_Reference* teavm_reference_poll(TeaVM_ReferenceQueue* queue) {
TeaVM_Reference* reference = queue->first;
queue->first = reference->next;
teavm_gc_writeBarrier(queue);
if (queue->first == NULL) {
queue->last = NULL;
}

View File

@ -66,8 +66,8 @@ static void teavm_rehashStrings() {
}
TeaVM_String* teavm_registerString(TeaVM_String* str) {
str->parent.header = TEAVM_PACK_CLASS(teavm_stringClass);
str->characters->parent.header = TEAVM_PACK_CLASS(teavm_charArrayClass);
str->parent.header = TEAVM_PACK_CLASS(teavm_stringClass) | (int32_t) INT32_C(0x80000000);
str->characters->parent.header = TEAVM_PACK_CLASS(teavm_charArrayClass) | (int32_t) INT32_C(0x80000000);
if (teavm_stringHashtable == NULL) {
teavm_stringHashtableSize = 256;