mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Support generations in GC
This commit is contained in:
parent
0ff17fc2fb
commit
16f4d2571b
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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(");
|
||||
|
|
|
@ -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")) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "resizeHeap":
|
||||
context.writer().print("teavm_gc_resizeHeap(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -180,3 +180,9 @@ extern TeaVM_Class* teavm_stringClass;
|
|||
extern TeaVM_Class* teavm_charArrayClass;
|
||||
extern int32_t teavm_classReferencesCount;
|
||||
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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user