C: add setting to generate code with lots of assertions

This commit is contained in:
Alexey Andreev 2019-07-31 17:38:13 +03:00
parent 0fdf58cbd8
commit c5334e344d
16 changed files with 838 additions and 184 deletions

View File

@ -17,8 +17,8 @@ package org.teavm.classlib.java.lang;
import java.io.PrintStream; import java.io.PrintStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.java.util.TArrays; import org.teavm.classlib.java.util.TArrays;
import org.teavm.interop.DelegateTo;
import org.teavm.interop.Remove; import org.teavm.interop.Remove;
import org.teavm.interop.Rename; import org.teavm.interop.Rename;
import org.teavm.interop.Superclass; import org.teavm.interop.Superclass;
@ -101,15 +101,12 @@ public class TThrowable extends RuntimeException {
} }
@Override @Override
@DelegateTo("fillInStackTraceLowLevel")
public Throwable fillInStackTrace() { public Throwable fillInStackTrace() {
return this; if (PlatformDetector.isLowLevel()) {
} int stackSize = ExceptionHandling.callStackSize() - 1;
stackTrace = new TStackTraceElement[stackSize];
private TThrowable fillInStackTraceLowLevel() { ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
int stackSize = ExceptionHandling.callStackSize() - 1; }
stackTrace = new TStackTraceElement[stackSize];
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
return this; return this;
} }

View File

@ -64,6 +64,7 @@ import org.teavm.backend.c.intrinsic.Intrinsic;
import org.teavm.backend.c.intrinsic.IntrinsicContext; import org.teavm.backend.c.intrinsic.IntrinsicContext;
import org.teavm.backend.c.intrinsic.IntrinsicFactory; import org.teavm.backend.c.intrinsic.IntrinsicFactory;
import org.teavm.backend.c.intrinsic.LongIntrinsic; import org.teavm.backend.c.intrinsic.LongIntrinsic;
import org.teavm.backend.c.intrinsic.MemoryTraceIntrinsic;
import org.teavm.backend.c.intrinsic.MutatorIntrinsic; import org.teavm.backend.c.intrinsic.MutatorIntrinsic;
import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic;
import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic;
@ -340,6 +341,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
intrinsics.add(new PlatformClassIntrinsic()); intrinsics.add(new PlatformClassIntrinsic());
intrinsics.add(new PlatformClassMetadataIntrinsic()); intrinsics.add(new PlatformClassMetadataIntrinsic());
intrinsics.add(new GCIntrinsic()); intrinsics.add(new GCIntrinsic());
intrinsics.add(new MemoryTraceIntrinsic());
intrinsics.add(new MutatorIntrinsic()); intrinsics.add(new MutatorIntrinsic());
intrinsics.add(new ExceptionHandlingIntrinsic()); intrinsics.add(new ExceptionHandlingIntrinsic());
intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods())); intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods()));
@ -356,9 +358,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
generators.add(new ReferenceQueueGenerator()); generators.add(new ReferenceQueueGenerator());
stringPool = new SimpleStringPool(); stringPool = new SimpleStringPool();
boolean vmAssertions = Boolean.parseBoolean(System.getProperty("teavm.c.vmAssertions", "false"));
GenerationContext context = new GenerationContext(vtableProvider, characteristics, GenerationContext context = new GenerationContext(vtableProvider, characteristics,
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes, controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
intrinsics, generators, asyncMethods::contains, buildTarget, incremental, longjmpUsed); intrinsics, generators, asyncMethods::contains, buildTarget, incremental, longjmpUsed,
vmAssertions);
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(false); BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(false);
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false); BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false);
@ -371,6 +375,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
if (longjmpUsed) { if (longjmpUsed) {
runtimeHeaderWriter.println("#define TEAVM_USE_SETJMP 1"); runtimeHeaderWriter.println("#define TEAVM_USE_SETJMP 1");
} }
if (vmAssertions) {
runtimeHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1");
}
emitResource(runtimeHeaderWriter, "runtime.h"); emitResource(runtimeHeaderWriter, "runtime.h");
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler, ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,

View File

@ -319,6 +319,16 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} }
} }
private void visitReference(Expr expr) {
if (context.isVmAssertions()) {
writer.print("TEAVM_VERIFY(");
}
expr.acceptVisitor(this);
if (context.isVmAssertions()) {
writer.print(")");
}
}
@Override @Override
public void visit(UnaryExpr expr) { public void visit(UnaryExpr expr) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
@ -337,7 +347,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
break; break;
case LENGTH: case LENGTH:
writer.print("TEAVM_ARRAY_LENGTH("); writer.print("TEAVM_ARRAY_LENGTH(");
expr.getOperand().acceptVisitor(this); visitReference(expr.getOperand());
writer.print(")"); writer.print(")");
break; break;
case NULL_CHECK: { case NULL_CHECK: {
@ -347,7 +357,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
withCallSite(); withCallSite();
} }
writer.print("teavm_nullCheck("); writer.print("teavm_nullCheck(");
expr.getOperand().acceptVisitor(this); visitReference(expr.getOperand());
writer.print(")"); writer.print(")");
if (needParenthesis) { if (needParenthesis) {
writer.print(")"); writer.print(")");
@ -412,7 +422,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
public void visit(SubscriptExpr expr) { public void visit(SubscriptExpr expr) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
writer.print("TEAVM_ARRAY_AT("); writer.print("TEAVM_ARRAY_AT(");
expr.getArray().acceptVisitor(this); visitReference(expr.getArray());
writer.print(", ").print(getArrayType(expr.getType())).print(", "); writer.print(", ").print(getArrayType(expr.getType())).print(", ");
expr.getIndex().acceptVisitor(this); expr.getIndex().acceptVisitor(this);
writer.print(")"); writer.print(")");
@ -604,7 +614,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} else { } else {
receiver = allocTemporaryVariable(CVariableType.PTR); receiver = allocTemporaryVariable(CVariableType.PTR);
writer.print("((").print(receiver).print(" = "); writer.print("((").print(receiver).print(" = ");
arguments.get(0).acceptVisitor(this); visitReference(arguments.get(0));
writer.print("), "); writer.print("), ");
closingParenthesis = true; closingParenthesis = true;
} }
@ -811,8 +821,18 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
if (qualified != null) { if (qualified != null) {
ClassReader cls = context.getClassSource().get(field.getClassName()); ClassReader cls = context.getClassSource().get(field.getClassName());
writer.print("TEAVM_FIELD("); writer.print("TEAVM_FIELD(");
boolean shouldVerify = context.isVmAssertions()
&& context.getCharacteristics().isManaged(field.getClassName());
if (shouldVerify) {
writer.print("TEAVM_VERIFY(");
}
qualified.acceptVisitor(this); qualified.acceptVisitor(this);
if (shouldVerify) {
writer.print(")");
}
writer.print(", "); writer.print(", ");
if (cls != null && isNative(cls)) { if (cls != null && isNative(cls)) {
InteropUtil.processInclude(cls.getAnnotations(), includes); InteropUtil.processInclude(cls.getAnnotations(), includes);
InteropUtil.printNativeReference(writer, cls); InteropUtil.printNativeReference(writer, cls);
@ -919,7 +939,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
public void visit(InstanceOfExpr expr) { public void visit(InstanceOfExpr expr) {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
writer.print("teavm_instanceof("); writer.print("teavm_instanceof(");
expr.getExpr().acceptVisitor(this); visitReference(expr.getExpr());
includes.includeType(expr.getType()); includes.includeType(expr.getType());
writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")"); writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")");
popLocation(expr.getLocation()); popLocation(expr.getLocation());
@ -945,7 +965,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
} }
writer.print("teavm_checkcast("); writer.print("teavm_checkcast(");
expr.getValue().acceptVisitor(this); visitReference(expr.getValue());
includes.includeType(expr.getTarget()); includes.includeType(expr.getTarget());
writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")"); writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")");

View File

@ -108,6 +108,15 @@ public class CodeGenerator {
private void generateLocals(MethodNode methodNode, int[] temporaryCount, IntContainer spilledVariables) { private void generateLocals(MethodNode methodNode, int[] temporaryCount, IntContainer spilledVariables) {
int start = methodNode.getReference().parameterCount() + 1; int start = methodNode.getReference().parameterCount() + 1;
for (int i = 0; i < start; ++i) {
if (spilledVariables.contains(i)) {
VariableNode variableNode = methodNode.getVariables().get(i);
localsWriter.print("volatile ").printType(variableNode.getType()).print(" teavm_spill_")
.print(String.valueOf(i)).println(";");
}
}
for (int i = start; i < methodNode.getVariables().size(); ++i) { for (int i = start; i < methodNode.getVariables().size(); ++i) {
VariableNode variableNode = methodNode.getVariables().get(i); VariableNode variableNode = methodNode.getVariables().get(i);
if (variableNode.getType() == null) { if (variableNode.getType() == null) {

View File

@ -45,12 +45,13 @@ public class GenerationContext {
private BuildTarget buildTarget; private BuildTarget buildTarget;
private boolean incremental; private boolean incremental;
private boolean longjmp; private boolean longjmp;
private boolean vmAssertions;
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics, public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics, DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators, ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental, Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental,
boolean longjmp) { boolean longjmp, boolean vmAssertions) {
this.virtualTableProvider = virtualTableProvider; this.virtualTableProvider = virtualTableProvider;
this.characteristics = characteristics; this.characteristics = characteristics;
this.dependencies = dependencies; this.dependencies = dependencies;
@ -64,6 +65,7 @@ public class GenerationContext {
this.buildTarget = buildTarget; this.buildTarget = buildTarget;
this.incremental = incremental; this.incremental = incremental;
this.longjmp = longjmp; this.longjmp = longjmp;
this.vmAssertions = vmAssertions;
} }
public void addIntrinsic(Intrinsic intrinsic) { public void addIntrinsic(Intrinsic intrinsic) {
@ -131,4 +133,8 @@ public class GenerationContext {
public boolean isLongjmp() { public boolean isLongjmp() {
return longjmp; return longjmp;
} }
public boolean isVmAssertions() {
return vmAssertions;
}
} }

View File

@ -0,0 +1,40 @@
/*
* 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.backend.c.intrinsic;
import org.teavm.ast.InvocationExpr;
import org.teavm.model.MethodReference;
import org.teavm.runtime.MemoryTrace;
public class MemoryTraceIntrinsic implements Intrinsic {
@Override
public boolean canHandle(MethodReference method) {
return method.getClassName().equals(MemoryTrace.class.getName());
}
@Override
public void apply(IntrinsicContext context, InvocationExpr invocation) {
context.writer().print("teavm_gc_").print(invocation.getMethod().getName()).print("(");
if (!invocation.getArguments().isEmpty()) {
context.emit(invocation.getArguments().get(0));
for (int i = 1; i < invocation.getArguments().size(); ++i) {
context.writer().print(", ");
context.emit(invocation.getArguments().get(i));
}
}
context.writer().print(")");
}
}

View File

@ -0,0 +1,185 @@
/*
* 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.backend.c.util;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
public final class GCVisualizer {
private static final int WIDTH = 1024;
private static final int LINE_HEIGHT = 4;
private static final int GAP_SIZE = 1;
private static final int GC_GAP_SIZE = 3;
private GCVisualizer() {
}
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.err.println("Two arguments (input, ouput) expected");
System.exit(1);
}
List<Line> lines = readData(args[0]);
BufferedImage bitmap = createBitmap(lines);
writeBitmap(bitmap, args[1]);
}
private static List<Line> readData(String fileName) throws IOException {
List<Line> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),
StandardCharsets.UTF_8))) {
while (true) {
String fileLine = reader.readLine();
if (fileLine == null) {
break;
}
int charIndex = fileLine.indexOf(':');
if (charIndex < 0) {
continue;
}
String kind = fileLine.substring(0, charIndex);
if (kind.equals("start") || lines.isEmpty()) {
lines.add(new Line());
}
Line line = lines.get(lines.size() - 1);
int[] array;
switch (kind) {
case "start":
array = line.start;
break;
case "sweep":
array = line.sweep;
break;
case "defrag":
array = line.defrag;
break;
default:
continue;
}
charIndex = fileLine.indexOf(' ', charIndex);
if (charIndex < 0) {
continue;
}
charIndex++;
for (int i = 0; i < array.length; ++i) {
int next = fileLine.indexOf(' ', charIndex);
if (next < 0) {
next = fileLine.length();
}
try {
array[i] = Integer.parseInt(fileLine.substring(charIndex, next));
} catch (NumberFormatException e) {
// do nothing
}
if (next == fileLine.length()) {
break;
}
charIndex = next + 1;
}
}
}
return lines;
}
private static BufferedImage createBitmap(List<Line> lines) {
int height = lines.size() * (3 * (GAP_SIZE + LINE_HEIGHT) + GC_GAP_SIZE);
int[] data = new int[WIDTH * height];
int offset = 0;
for (Line line : lines) {
offset = renderComponent(data, line.start, offset, 0, 0, 255);
offset = renderComponent(data, line.sweep, offset, 0, 255, 0);
offset = renderComponent(data, line.defrag, offset, 255, 0, 0);
renderLine(data, offset, GC_GAP_SIZE, 0, 0, 0);
offset += WIDTH * GC_GAP_SIZE;
}
BufferedImage image = new BufferedImage(WIDTH, height, BufferedImage.TYPE_INT_ARGB);
image.setRGB(0, 0, WIDTH, height, data, 0, WIDTH);
return image;
}
private static int renderComponent(int[] data, int[] array, int offset, int r, int g, int b) {
renderArray(data, array, offset, r, g, b);
offset += LINE_HEIGHT * WIDTH;
renderLine(data, offset, GAP_SIZE, 0, 0, 0);
offset += GAP_SIZE * WIDTH;
return offset;
}
private static void renderArray(int[] data, int[] array, int offset, int r, int g, int b) {
for (int i = 0; i < WIDTH; ++i) {
int start = array.length * i / WIDTH;
int end = array.length * (i + 1) / WIDTH;
int total = 0;
for (int j = start; j < end; ++j) {
total += array[j];
}
double rate = total / (4096.0 * (end - start));
int pixelR = makeColorComponent(r, rate);
int pixelG = makeColorComponent(g, rate);
int pixelB = makeColorComponent(b, rate);
int pixelOffset = offset;
int pixel = (255 << 24) | ((pixelR & 255) << 16) | ((pixelG & 255) << 8) | (pixelB & 255);
for (int j = 0; j < LINE_HEIGHT; ++j) {
data[pixelOffset] = pixel;
pixelOffset += WIDTH;
}
offset++;
}
}
private static void renderLine(int[] data, int offset, int height, int r, int g, int b) {
int count = height * WIDTH;
int pixel = (255 << 24) | ((r & 255) << 16) | ((g & 255) << 8) | (b & 255);
for (int i = 0; i < count; ++i) {
data[offset++] = pixel;
}
}
private static int makeColorComponent(int c, double rate) {
int r = (int) (c * rate + 255 * (1 - rate));
return Math.min(Math.max(r, 0), 255);
}
private static void writeBitmap(BufferedImage image, String fileName) throws IOException {
ImageIO.write(image, "png", new File(fileName));
}
static class Line {
int[] start = new int[4096];
int[] sweep = new int[4096];
int[] defrag = new int[4096];
}
}

View File

@ -52,6 +52,7 @@ import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
import org.teavm.backend.wasm.intrinsics.GCIntrinsic; import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
import org.teavm.backend.wasm.intrinsics.IntegerIntrinsic; import org.teavm.backend.wasm.intrinsics.IntegerIntrinsic;
import org.teavm.backend.wasm.intrinsics.LongIntrinsic; import org.teavm.backend.wasm.intrinsics.LongIntrinsic;
import org.teavm.backend.wasm.intrinsics.MemoryTraceIntrinsic;
import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic; import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic;
import org.teavm.backend.wasm.intrinsics.ObjectIntrinsic; import org.teavm.backend.wasm.intrinsics.ObjectIntrinsic;
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
@ -355,6 +356,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
context.addIntrinsic(new ObjectIntrinsic()); context.addIntrinsic(new ObjectIntrinsic());
context.addIntrinsic(new ConsoleIntrinsic()); context.addIntrinsic(new ConsoleIntrinsic());
context.addGenerator(new ArrayGenerator()); context.addGenerator(new ArrayGenerator());
context.addIntrinsic(new MemoryTraceIntrinsic());
IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext();
for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) { for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) {

View File

@ -0,0 +1,35 @@
/*
* 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.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.model.MethodReference;
import org.teavm.runtime.MemoryTrace;
public class MemoryTraceIntrinsic implements WasmIntrinsic {
@Override
public boolean isApplicable(MethodReference methodReference) {
return methodReference.getClassName().equals(MemoryTrace.class.getName());
}
@Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
return new WasmDrop(new WasmInt32Constant(0));
}
}

View File

@ -17,6 +17,7 @@ package org.teavm.model.lowlevel;
import com.carrotsearch.hppc.ObjectByteHashMap; import com.carrotsearch.hppc.ObjectByteHashMap;
import com.carrotsearch.hppc.ObjectByteMap; import com.carrotsearch.hppc.ObjectByteMap;
import org.teavm.interop.Address;
import org.teavm.interop.Function; import org.teavm.interop.Function;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
@ -83,6 +84,10 @@ public class Characteristics {
return result != 0; return result != 0;
} }
public boolean isManaged(String className) {
return !isStructure(className) && !isFunction(className) && !className.equals(Address.class.getName());
}
public boolean isManaged(MethodReference methodReference) { public boolean isManaged(MethodReference methodReference) {
byte result = isManaged.getOrDefault(methodReference, (byte) -1); byte result = isManaged.getOrDefault(methodReference, (byte) -1);
if (result < 0) { if (result < 0) {

View File

@ -132,7 +132,6 @@ public final class ExceptionHandling {
return size + 1; return size + 1;
} }
@Unmanaged
public static void fillStackTrace(StackTraceElement[] target) { public static void fillStackTrace(StackTraceElement[] target) {
Address stackFrame = ShadowStack.getStackTop(); Address stackFrame = ShadowStack.getStackTop();
int index = 0; int index = 0;

View File

@ -34,7 +34,7 @@ public final class GC {
static int freeMemory = (int) availableBytes(); static int freeMemory = (int) availableBytes();
static RuntimeReference firstWeakReference; static RuntimeReference firstWeakReference;
static RelocationBlock lastRelocationTarget; static RelocationBlock lastRelocationBlock;
static native Address gcStorageAddress(); static native Address gcStorageAddress();
@ -65,76 +65,72 @@ public final class GC {
currentChunkPointer = gcStorageAddress().toStructure(); currentChunkPointer = gcStorageAddress().toStructure();
currentChunkPointer.value = currentChunk; currentChunkPointer.value = currentChunk;
freeChunks = 1; freeChunks = 1;
getAvailableChunkIfPossible(0);
} }
public static RuntimeObject alloc(int size) { public static RuntimeObject alloc(int size) {
FreeChunk current = currentChunk; FreeChunk current = currentChunk;
Address next = current.toAddress().add(size); Address next = current.toAddress().add(size);
if (!next.add(Structure.sizeOf(FreeChunk.class)).isLessThan(currentChunkLimit)) { if (!next.add(Structure.sizeOf(FreeChunk.class)).isLessThan(currentChunkLimit)) {
getAvailableChunk(size); getNextChunk(size);
current = currentChunk; current = currentChunk;
next = currentChunk.toAddress().add(size); next = current.toAddress().add(size);
} }
int freeSize = current.size; currentChunk = next.toStructure();
freeSize -= size;
if (freeSize > 0) {
currentChunk = next.toStructure();
currentChunk.size = freeSize;
} else {
freeMemory -= size;
getAvailableChunkIfPossible(currentChunk.size + 1);
}
currentChunk.classReference = 0;
freeMemory -= size; freeMemory -= size;
MemoryTrace.allocate(current.toAddress(), size);
return current.toAddress().toStructure(); return current.toAddress().toStructure();
} }
private static void getAvailableChunk(int size) { private static void getNextChunk(int size) {
if (getAvailableChunkIfPossible(size)) { if (getNextChunkIfPossible(size)) {
return; return;
} }
collectGarbage(); collectGarbage();
if (!getAvailableChunkIfPossible(size)) { if (currentChunk.size < size && !getNextChunkIfPossible(size)) {
ExceptionHandling.printStack(); ExceptionHandling.printStack();
outOfMemory(); outOfMemory();
} }
} }
private static boolean getAvailableChunkIfPossible(int size) { private static boolean getNextChunkIfPossible(int size) {
if (freeChunks == 0) {
return false;
}
while (true) { while (true) {
if (currentChunk.toAddress().add(size) == currentChunkLimit) { if (currentChunk.toAddress().isLessThan(currentChunkLimit)) {
break; currentChunk.classReference = 0;
} currentChunk.size = (int) (currentChunkLimit.toLong() - currentChunk.toAddress().toLong());
if (currentChunk.toAddress().add(size + Structure.sizeOf(FreeChunk.class)).isLessThan(currentChunkLimit)) {
break;
} }
if (--freeChunks == 0) { if (--freeChunks == 0) {
return false; return false;
} }
freeMemory -= currentChunk.size;
currentChunkPointer = Structure.add(FreeChunkHolder.class, currentChunkPointer, 1); currentChunkPointer = Structure.add(FreeChunkHolder.class, currentChunkPointer, 1);
currentChunk = currentChunkPointer.value; currentChunk = currentChunkPointer.value;
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size); if (currentChunk.size >= size) {
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
break;
}
freeMemory -= currentChunk.size;
} }
return true; return true;
} }
public static void collectGarbage() { public static void collectGarbage() {
MemoryTrace.gcStarted();
mark(); mark();
processReferences(); processReferences();
sweep(); sweep();
MemoryTrace.sweepCompleted();
defragment(); defragment();
sortFreeChunks(); MemoryTrace.defragCompleted();
//sortFreeChunks();
updateFreeMemory(); updateFreeMemory();
currentChunk = currentChunkPointer.value;
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
} }
private static void mark() { private static void mark() {
MemoryTrace.initMark();
firstWeakReference = null; firstWeakReference = null;
Allocator.fillZero(regionsAddress().toAddress(), regionMaxCount() * Structure.sizeOf(Region.class)); int regionsCount = (int) ((availableBytes() - 1) / regionSize()) + 1;
Allocator.fillZero(regionsAddress().toAddress(), regionsCount * Structure.sizeOf(Region.class));
Address staticRoots = Mutator.getStaticGCRoots(); Address staticRoots = Mutator.getStaticGCRoots();
int staticCount = staticRoots.getInt(); int staticCount = staticRoots.getInt();
@ -172,6 +168,7 @@ public final class GC {
continue; continue;
} }
object.classReference |= RuntimeObject.GC_MARKED; object.classReference |= RuntimeObject.GC_MARKED;
MemoryTrace.mark(object.toAddress());
long offset = object.toAddress().toLong() - heapAddress().toLong(); long offset = object.toAddress().toLong() - heapAddress().toLong();
Region region = Structure.add(Region.class, regionsAddress(), (int) (offset / regionSize())); Region region = Structure.add(Region.class, regionsAddress(), (int) (offset / regionSize()));
@ -211,9 +208,9 @@ public final class GC {
private static void markWeakReference(RuntimeReference object) { private static void markWeakReference(RuntimeReference object) {
if (object.queue != null) { if (object.queue != null) {
mark(object.queue); enqueueMark(object.queue);
if (object.next != null && object.object != null) { if (object.next != null && object.object != null) {
mark(object.object); enqueueMark(object);
} }
} }
if (object.next == null && object.object != null) { if (object.next == null && object.object != null) {
@ -225,7 +222,7 @@ public final class GC {
private static void markReferenceQueue(RuntimeReferenceQueue object) { private static void markReferenceQueue(RuntimeReferenceQueue object) {
RuntimeReference reference = object.first; RuntimeReference reference = object.first;
while (reference != null) { while (reference != null) {
mark(reference); enqueueMark(object);
reference = reference.next; reference = reference.next;
} }
} }
@ -238,9 +235,7 @@ public final class GC {
layout = layout.add(2); layout = layout.add(2);
int fieldOffset = layout.getShort(); int fieldOffset = layout.getShort();
RuntimeObject reference = object.toAddress().add(fieldOffset).getAddress().toStructure(); RuntimeObject reference = object.toAddress().add(fieldOffset).getAddress().toStructure();
if (reference != null && !isMarked(reference)) { enqueueMark(reference);
MarkQueue.enqueue(reference);
}
} }
} }
} }
@ -252,13 +247,17 @@ public final class GC {
Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), Address.sizeOf()); Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), Address.sizeOf());
for (int i = 0; i < array.size; ++i) { for (int i = 0; i < array.size; ++i) {
RuntimeObject reference = base.getAddress().toStructure(); RuntimeObject reference = base.getAddress().toStructure();
if (reference != null && !isMarked(reference)) { enqueueMark(reference);
MarkQueue.enqueue(reference);
}
base = base.add(Address.sizeOf()); base = base.add(Address.sizeOf());
} }
} }
private static void enqueueMark(RuntimeObject object) {
if (object != null && !isMarked(object)) {
MarkQueue.enqueue(object);
}
}
private static void processReferences() { private static void processReferences() {
RuntimeReference reference = firstWeakReference; RuntimeReference reference = firstWeakReference;
while (reference != null) { while (reference != null) {
@ -297,16 +296,23 @@ public final class GC {
if (!object.toAddress().isLessThan(currentRegionEnd)) { if (!object.toAddress().isLessThan(currentRegionEnd)) {
int currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize()); int currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize());
Region currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex); Region currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
while (currentRegion.start == 0) { if (currentRegion.start == 0) {
if (++currentRegionIndex == regionsCount) { if (lastFreeSpace == null) {
object = limit.toStructure(); lastFreeSpace = object;
break loop;
} }
currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
do {
if (++currentRegionIndex == regionsCount) {
object = limit.toStructure();
break loop;
}
currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
} while (currentRegion.start == 0);
Address newRegionStart = heapAddress().add(currentRegionIndex * regionSize());
object = newRegionStart.add(currentRegion.start - 1).toStructure();
currentRegionEnd = newRegionStart.add(regionSize());
} }
Address newRegionStart = heapAddress().add(currentRegionIndex * regionSize());
object = newRegionStart.add(currentRegion.start - 1).toStructure();
currentRegionEnd = newRegionStart.add(regionSize());
} }
int tag = object.classReference; int tag = object.classReference;
@ -329,6 +335,7 @@ public final class GC {
if (lastFreeSpace != null) { if (lastFreeSpace != null) {
lastFreeSpace.classReference = 0; lastFreeSpace.classReference = 0;
lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size);
freeChunkPtr.value = lastFreeSpace; freeChunkPtr.value = lastFreeSpace;
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
freeChunks++; freeChunks++;
@ -348,6 +355,7 @@ public final class GC {
int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
lastFreeSpace.classReference = 0; lastFreeSpace.classReference = 0;
lastFreeSpace.size = freeSize; lastFreeSpace.size = freeSize;
MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size);
freeChunkPtr.value = lastFreeSpace; freeChunkPtr.value = lastFreeSpace;
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
freeChunks++; freeChunks++;
@ -381,8 +389,8 @@ public final class GC {
RuntimeObject obj = stackRootsPtr.getAddress().toStructure(); RuntimeObject obj = stackRootsPtr.getAddress().toStructure();
if (!obj.toAddress().isLessThan(relocationThreshold)) { if (!obj.toAddress().isLessThan(relocationThreshold)) {
obj.classReference |= RuntimeObject.GC_MARKED; obj.classReference |= RuntimeObject.GC_MARKED;
stackRootsPtr = stackRootsPtr.add(Address.sizeOf());
} }
stackRootsPtr = stackRootsPtr.add(Address.sizeOf());
} }
} }
} }
@ -397,12 +405,14 @@ public final class GC {
FreeChunk freeChunk = currentChunkPointer.value; FreeChunk freeChunk = currentChunkPointer.value;
FreeChunk object = freeChunk.toAddress().add(freeChunk.size).toStructure(); FreeChunk object = freeChunk.toAddress().add(freeChunk.size).toStructure();
RelocationBlock relocationTarget = Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks) RelocationBlock relocationBlock = Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks)
.toAddress().toStructure(); .toAddress().toStructure();
relocationTarget.start = freeChunk.toAddress(); Address relocationTarget = freeChunk.toAddress();
relocationTarget.current = relocationTarget.start; relocationBlock.start = relocationTarget;
relocationTarget.end = limit; relocationBlock.end = limit;
RelocationBlock lastRelocationTarget = relocationTarget; relocationBlock.count = 0;
RelocationBlock lastRelocationBlock = relocationBlock;
int countInCurrentRelocationBlock = 0;
Address relocations = Structure.add(FreeChunk.class, freeChunk, 1).toAddress(); Address relocations = Structure.add(FreeChunk.class, freeChunk, 1).toAddress();
Address relocationsLimit = freeChunk.toAddress().add(freeChunk.size); Address relocationsLimit = freeChunk.toAddress().add(freeChunk.size);
@ -411,36 +421,43 @@ public final class GC {
objects: while (object.toAddress().isLessThan(limit)) { objects: while (object.toAddress().isLessThan(limit)) {
int size = objectSize(object); int size = objectSize(object);
if (object.classReference != 0) { if (object.classReference != 0) {
if ((object.classReference & RuntimeObject.GC_MARKED) != 0 Address nextRelocationTarget = null;
|| !relocationTarget.start.isLessThan(object.toAddress())) { boolean shouldRelocateObject = (object.classReference & RuntimeObject.GC_MARKED) == 0;
if (!lastWasLocked) { if (shouldRelocateObject) {
lastRelocationTarget.end = object.toAddress();
lastRelocationTarget = Structure.add(RelocationBlock.class, lastRelocationTarget, 1);
lastRelocationTarget.end = limit;
lastWasLocked = true;
}
lastRelocationTarget.start = object.toAddress().add(size);
lastRelocationTarget.current = lastRelocationTarget.start;
object.classReference &= ~RuntimeObject.GC_MARKED;
} else {
lastRelocationTarget.end = object.toAddress().add(size);
lastWasLocked = false;
Address nextRelocationTarget;
while (true) { while (true) {
nextRelocationTarget = relocationTarget.current.add(size); nextRelocationTarget = relocationTarget.add(size);
if (nextRelocationTarget.isLessThan(relocationTarget.end)) { if (!relocationBlock.end.isLessThan(nextRelocationTarget)) {
break; break;
} }
relocationTarget = Structure.add(RelocationBlock.class, relocationTarget, 1); RelocationBlock nextRelocationBlock = Structure.add(RelocationBlock.class, relocationBlock, 1);
if (lastRelocationTarget.toAddress().isLessThan(relocationTarget.toAddress())) { if (nextRelocationBlock.start == object.toAddress()) {
break objects; shouldRelocateObject = false;
break;
} }
relocationBlock.count = countInCurrentRelocationBlock;
countInCurrentRelocationBlock = 0;
relocationBlock = nextRelocationBlock;
relocationTarget = relocationBlock.start;
} }
}
if (!shouldRelocateObject) {
if (!lastWasLocked) {
lastRelocationBlock.end = object.toAddress();
lastRelocationBlock = Structure.add(RelocationBlock.class, lastRelocationBlock, 1);
lastRelocationBlock.end = limit;
lastWasLocked = true;
}
lastRelocationBlock.start = object.toAddress().add(size);
lastRelocationBlock.count = 0;
object.classReference &= ~RuntimeObject.GC_MARKED;
} else {
lastWasLocked = false;
while (!relocations.add(Structure.sizeOf(Relocation.class)).isLessThan(relocationsLimit)) { while (!relocations.add(Structure.sizeOf(Relocation.class)).isLessThan(relocationsLimit)) {
if (--freeChunks == 0) { if (--freeChunks == 0) {
lastRelocationBlock.end = object.toAddress();
break objects; break objects;
} }
freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1); freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1);
@ -452,35 +469,37 @@ public final class GC {
Relocation relocation = relocations.toStructure(); Relocation relocation = relocations.toStructure();
relocation.classBackup = object.classReference; relocation.classBackup = object.classReference;
relocation.sizeBackup = object.size; relocation.sizeBackup = object.size;
relocation.newAddress = relocationTarget.current; relocation.newAddress = relocationTarget;
countInCurrentRelocationBlock++;
relocations = relocations.add(Structure.sizeOf(Relocation.class)); relocations = relocations.add(Structure.sizeOf(Relocation.class));
long targetAddress = relocation.toAddress().toLong(); long targetAddress = relocation.toAddress().toLong();
object.classReference = (int) (targetAddress >>> 33) | RuntimeObject.GC_MARKED; object.classReference = (int) (targetAddress >>> 33) | RuntimeObject.GC_MARKED;
object.size = (int) (targetAddress >> 1); object.size = (int) (targetAddress >> 1);
relocationTarget.current = nextRelocationTarget; relocationTarget = nextRelocationTarget;
} }
} else { } else {
lastWasLocked = false; lastWasLocked = false;
lastRelocationTarget.end = object.toAddress().add(object.size);
} }
object = object.toAddress().add(size).toStructure(); object = object.toAddress().add(size).toStructure();
} }
relocationBlock.count = countInCurrentRelocationBlock;
while (object.toAddress().isLessThan(limit)) { while (object.toAddress().isLessThan(limit)) {
int size = objectSize(object); int size = objectSize(object);
if (object.classReference != 0) { if (object.classReference != 0) {
object.classReference &= ~RuntimeObject.GC_MARKED; object.classReference &= ~RuntimeObject.GC_MARKED;
} else { } else {
lastRelocationTarget = Structure.add(RelocationBlock.class, lastRelocationTarget, 1); lastRelocationBlock = Structure.add(RelocationBlock.class, lastRelocationBlock, 1);
lastRelocationTarget.start = object.toAddress(); lastRelocationBlock.start = object.toAddress();
lastRelocationTarget.current = lastRelocationTarget.start; lastRelocationBlock.count = 0;
lastRelocationTarget.end = lastRelocationTarget.start.add(size); lastRelocationBlock.end = lastRelocationBlock.start.add(size);
} }
object = object.toAddress().add(size).toStructure(); object = object.toAddress().add(size).toStructure();
} }
GC.lastRelocationTarget = lastRelocationTarget; GC.lastRelocationBlock = lastRelocationBlock;
} }
private static void updatePointersFromStaticRoots() { private static void updatePointersFromStaticRoots() {
@ -633,47 +652,44 @@ public final class GC {
FreeChunk freeChunk = currentChunkPointer.value; FreeChunk freeChunk = currentChunkPointer.value;
FreeChunk object = freeChunk.toAddress().add(freeChunk.size).toStructure(); FreeChunk object = freeChunk.toAddress().add(freeChunk.size).toStructure();
RelocationBlock relocationTarget = Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks) RelocationBlock relocationBlock = Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks)
.toAddress().toStructure(); .toAddress().toStructure();
Address currentTarget = relocationTarget.start; int countInRelocationBlock = relocationBlock.count;
Address relocationTarget = relocationBlock.start;
Address blockTarget = null; Address blockTarget = null;
Address blockSource = null; Address blockSource = null;
int blockSize = 0; int blockSize = 0;
objects: while (object.toAddress().isLessThan(limit)) { while (object.toAddress().isLessThan(limit)) {
int size = objectSize(object); int size = objectSize(object);
if ((object.classReference & RuntimeObject.GC_MARKED) != 0) { if ((object.classReference & RuntimeObject.GC_MARKED) != 0) {
object.classReference &= ~RuntimeObject.GC_MARKED; object.classReference &= ~RuntimeObject.GC_MARKED;
Address nextRelocationTarget; while (countInRelocationBlock == 0) {
while (true) {
nextRelocationTarget = currentTarget.add(size);
if (nextRelocationTarget.isLessThan(relocationTarget.end)) {
break;
}
if (blockSize != 0) { if (blockSize != 0) {
Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize); Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize);
MemoryTrace.move(blockSource, blockTarget, blockSize);
blockSource = null; blockSource = null;
blockSize = 0; blockSize = 0;
} }
relocationTarget = Structure.add(RelocationBlock.class, relocationTarget, 1); relocationBlock.start = relocationTarget;
if (lastRelocationTarget.toAddress().isLessThan(relocationTarget.toAddress())) { relocationBlock = Structure.add(RelocationBlock.class, relocationBlock, 1);
break objects; countInRelocationBlock = relocationBlock.count;
} relocationTarget = relocationBlock.start;
currentTarget = relocationTarget.start;
} }
if (blockSource == null) { if (blockSource == null) {
blockSource = object.toAddress(); blockSource = object.toAddress();
blockTarget = currentTarget; blockTarget = relocationTarget;
} }
currentTarget = nextRelocationTarget; relocationTarget = relocationTarget.add(size);
blockSize += size; blockSize += size;
--countInRelocationBlock;
} else if (blockSource != null) { } else if (blockSource != null) {
Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize); Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize);
MemoryTrace.move(blockSource, blockTarget, blockSize);
blockSource = null; blockSource = null;
blockSize = 0; blockSize = 0;
} }
@ -681,8 +697,10 @@ public final class GC {
object = object.toAddress().add(size).toStructure(); object = object.toAddress().add(size).toStructure();
} }
relocationBlock.start = relocationTarget;
if (blockSource != null) { if (blockSource != null) {
Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize); Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize);
MemoryTrace.move(blockSource, blockTarget, blockSize);
} }
} }
@ -691,16 +709,16 @@ public final class GC {
RelocationBlock relocationBlock = Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks) RelocationBlock relocationBlock = Structure.add(FreeChunkHolder.class, currentChunkPointer, freeChunks)
.toAddress().toStructure(); .toAddress().toStructure();
freeChunks = 0; freeChunks = 0;
while (!lastRelocationTarget.toAddress().isLessThan(relocationBlock.toAddress())) { while (!lastRelocationBlock.toAddress().isLessThan(relocationBlock.toAddress())) {
if (!relocationBlock.current.isLessThan(relocationBlock.end)) { if (relocationBlock.start.isLessThan(relocationBlock.end)) {
continue; FreeChunk freeChunk = relocationBlock.start.toStructure();
freeChunk.size = (int) (relocationBlock.end.toLong() - relocationBlock.start.toLong());
freeChunk.classReference = 0;
MemoryTrace.assertFree(freeChunk.toAddress(), freeChunk.size);
freeChunkPointer.value = freeChunk;
freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1);
freeChunks++;
} }
FreeChunk freeChunk = relocationBlock.current.toStructure();
freeChunk.size = (int) (relocationBlock.end.toLong() - relocationBlock.current.toLong());
freeChunk.classReference = 0;
freeChunkPointer.value = freeChunk;
freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1);
freeChunks++;
relocationBlock = Structure.add(RelocationBlock.class, relocationBlock, 1); relocationBlock = Structure.add(RelocationBlock.class, relocationBlock, 1);
} }
} }
@ -708,8 +726,6 @@ public final class GC {
private static void sortFreeChunks() { private static void sortFreeChunks() {
currentChunkPointer = gcStorageAddress().toStructure(); currentChunkPointer = gcStorageAddress().toStructure();
sortFreeChunks(0, freeChunks - 1); sortFreeChunks(0, freeChunks - 1);
currentChunk = currentChunkPointer.value;
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
} }
private static void updateFreeMemory() { private static void updateFreeMemory() {

View File

@ -0,0 +1,43 @@
/*
* 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.runtime;
import org.teavm.interop.Address;
public class MemoryTrace {
private MemoryTrace() {
}
public static native void allocate(Address address, int size);
public static native void free(Address address, int size);
public static native void assertFree(Address address, int size);
public static native void checkIsFree(Address address, int size);
public static native void initMark();
public static native void mark(Address address);
public static native void move(Address from, Address to, int size);
public static native void gcStarted();
public static native void sweepCompleted();
public static native void defragCompleted();
}

View File

@ -19,7 +19,7 @@ import org.teavm.interop.Address;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
class RelocationBlock extends Structure { class RelocationBlock extends Structure {
int count;
Address start; Address start;
Address current;
Address end; Address end;
} }

View File

@ -23,6 +23,10 @@
#endif #endif
void* teavm_gc_heapAddress = NULL; void* teavm_gc_heapAddress = NULL;
#ifdef TEAVM_MEMORY_TRACE
uint8_t* teavm_gc_heapMap = NULL;
uint8_t* teavm_gc_markMap = NULL;
#endif
TeaVM_StackFrame* teavm_stackTop = NULL; TeaVM_StackFrame* teavm_stackTop = NULL;
@ -107,37 +111,13 @@ void teavm_beforeInit() {
} }
#ifdef __GNUC__ #ifdef __GNUC__
void teavm_initHeap(int64_t heapSize) {
long workSize = (long) (heapSize / 16);
long regionsSize = (long) (heapSize / teavm_gc_regionSize);
long pageSize = sysconf(_SC_PAGE_SIZE); static void* teavm_virtualAlloc(int size) {
int heapPages = (int) ((heapSize + pageSize + 1) / pageSize * pageSize); return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
int workPages = (int) ((workSize + pageSize + 1) / pageSize * pageSize); }
int regionsPages = (int) ((regionsSize * 2 + pageSize + 1) / pageSize * pageSize);
teavm_gc_heapAddress = mmap( static long teavm_pageSize() {
NULL, return sysconf(_SC_PAGE_SIZE);
heapPages,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0);
teavm_gc_gcStorageAddress = mmap(
NULL,
workPages,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0);
teavm_gc_regionsAddress = mmap(
NULL,
regionsPages,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0);
teavm_gc_gcStorageSize = (int) workSize;
teavm_gc_regionMaxCount = regionsSize;
teavm_gc_availableBytes = heapSize;
} }
int64_t teavm_currentTimeMillis() { int64_t teavm_currentTimeMillis() {
@ -173,24 +153,10 @@ static void* teavm_virtualAlloc(int size) {
#endif #endif
} }
void teavm_initHeap(int64_t heapSize) { static long teavm_pageSize() {
long workSize = (long) (heapSize / 16);
long regionsSize = (long) (heapSize / teavm_gc_regionSize);
SYSTEM_INFO systemInfo; SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo); GetSystemInfo(&systemInfo);
long pageSize = systemInfo.dwPageSize; return systemInfo.dwPageSize;
int heapPages = (int) ((heapSize + pageSize + 1) / pageSize * pageSize);
int workPages = (int) ((workSize + pageSize + 1) / pageSize * pageSize);
int regionsPages = (int) ((regionsSize * 2 + pageSize + 1) / pageSize * pageSize);
teavm_gc_heapAddress = teavm_virtualAlloc(heapPages);
teavm_gc_gcStorageAddress = teavm_virtualAlloc(workPages);
teavm_gc_regionsAddress = teavm_virtualAlloc(regionsPages);
teavm_gc_gcStorageSize = (int) workSize;
teavm_gc_regionMaxCount = regionsSize;
teavm_gc_availableBytes = heapSize;
} }
int64_t teavm_currentTimeMillis() { int64_t teavm_currentTimeMillis() {
@ -210,6 +176,31 @@ int64_t teavm_currentTimeNano() {
} }
#endif #endif
static int teavm_pageCount(int64_t size, int64_t pageSize) {
return (int) ((size + pageSize + 1) / pageSize * pageSize);
}
void teavm_initHeap(int64_t heapSize) {
long workSize = (long) (heapSize / 16);
long regionsSize = (long) (heapSize / teavm_gc_regionSize) + 1;
long pageSize = teavm_pageSize();
teavm_gc_heapAddress = teavm_virtualAlloc(teavm_pageCount(heapSize, pageSize));
teavm_gc_gcStorageAddress = teavm_virtualAlloc(teavm_pageCount(workSize, pageSize));
teavm_gc_regionsAddress = teavm_virtualAlloc(teavm_pageCount(regionsSize * 2, pageSize));
#ifdef TEAVM_MEMORY_TRACE
int64_t heapMapSize = heapSize / sizeof(void*);
teavm_gc_heapMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize, pageSize));
memset(teavm_gc_heapMap, 0, heapMapSize);
teavm_gc_markMap = teavm_virtualAlloc(teavm_pageCount(heapMapSize, pageSize));
#endif
teavm_gc_gcStorageSize = (int) workSize;
teavm_gc_regionMaxCount = regionsSize;
teavm_gc_availableBytes = heapSize;
}
#ifdef _MSC_VER #ifdef _MSC_VER
#undef gmtime_r #undef gmtime_r
#undef localtime_r #undef localtime_r
@ -606,3 +597,263 @@ void teavm_logchar(int32_t c) {
OutputDebugStringW(buffer); OutputDebugStringW(buffer);
} }
#endif #endif
#ifdef TEAVM_MEMORY_TRACE
void teavm_gc_assertSize(int32_t size) {
if (size % sizeof(void*) != 0) {
abort();
}
}
#endif
void teavm_gc_allocate(void* address, int32_t size) {
#ifdef TEAVM_MEMORY_TRACE
teavm_gc_assertAddress(address);
teavm_gc_assertSize(size);
size /= sizeof(void*);
uint8_t* map = teavm_gc_heapMap + (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
if (*map != 0) {
fprintf(stderr, "[GC] trying allocate at memory in use at: %d\n",
(int) ((char*) address - (char*) teavm_gc_heapAddress));
abort();
}
*map++ = 1;
for (int32_t i = 1; i < size; ++i) {
if (*map != 0) {
fprintf(stderr, "[GC] trying allocate at memory in use at: %d\n",
(int) ((char*) address - (char*) teavm_gc_heapAddress));
abort();
}
*map++ = 2;
}
#endif
}
void teavm_gc_free(void* address, int32_t size) {
#ifdef TEAVM_MEMORY_TRACE
teavm_gc_assertAddress(address);
teavm_gc_assertSize(size);
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
uint8_t* markMap = teavm_gc_markMap + offset;
size /= sizeof(void*);
for (int32_t i = 0; i < size; ++i) {
if (markMap[i] != 0) {
fprintf(stderr, "[GC] trying to release reachable object at: %d\n",
(int) ((char*) address - (char*) teavm_gc_heapAddress));
abort();
}
}
uint8_t* map = teavm_gc_heapMap + offset;
memset(map, 0, size);
#endif
}
void teavm_gc_assertFree(void* address, int32_t size) {
#ifdef TEAVM_MEMORY_TRACE
teavm_gc_assertAddress(address);
teavm_gc_assertSize(size);
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
uint8_t* map = teavm_gc_heapMap + offset;
size /= sizeof(void*);
for (int32_t i = 0; i < size; ++i) {
if (map[i] != 0) {
fprintf(stderr, "[GC] memory supposed to be free at: %d\n",
(int) ((char*) address - (char*) teavm_gc_heapAddress));
abort();
}
}
#endif
}
void teavm_gc_initMark() {
#ifdef TEAVM_MEMORY_TRACE
memset(teavm_gc_markMap, 0, teavm_gc_availableBytes / sizeof(void*));
#endif
}
#ifdef TEAVM_MEMORY_TRACE
int32_t teavm_gc_objectSize(void* address) {
TeaVM_Class* cls = TEAVM_CLASS_OF(address);
if (cls->itemType == NULL) {
return cls->size;
}
int32_t itemSize = cls->itemType->flags & 2 ? cls->itemType->size : sizeof(void*);
TeaVM_Array* array = (TeaVM_Array*) address;
char* size = TEAVM_ALIGN((void*) sizeof(TeaVM_Array), itemSize);
size += array->size * itemSize;
size = TEAVM_ALIGN(size, sizeof(void*));
return (int32_t) (intptr_t) size;
}
#endif
void teavm_gc_mark(void* address) {
#ifdef TEAVM_MEMORY_TRACE
if (address < teavm_gc_heapAddress
|| (char*) address >= (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
return;
}
teavm_gc_assertAddress(address);
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
uint8_t* map = teavm_gc_heapMap + offset;
uint8_t* markMap = teavm_gc_markMap + offset;
int32_t size = teavm_gc_objectSize(address);
teavm_gc_assertSize(size);
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));
abort();
}
*markMap++ = 1;
for (int32_t i = 1; i < size; ++i) {
if (*map++ != 2 || *markMap != 0) {
abort();
}
*markMap++ = 1;
}
#endif
}
void teavm_gc_move(void* from, void* to, int32_t size) {
#ifdef TEAVM_MEMORY_TRACE
teavm_gc_assertAddress(from);
teavm_gc_assertAddress(to);
teavm_gc_assertSize(size);
uint8_t* mapFrom = teavm_gc_heapMap + (((char*) from - (char*) teavm_gc_heapAddress) / sizeof(void*));
uint8_t* mapTo = teavm_gc_heapMap + (((char*) to - (char*) teavm_gc_heapAddress) / sizeof(void*));
size /= sizeof(void*);
if (mapFrom > mapTo) {
for (int32_t i = 0; i < size; ++i) {
if (mapFrom[i] == 0 || mapTo[i] != 0) {
fprintf(stderr, "[GC] assertion failed moving object from: %d to %d\n",
(int) ((char*) from - (char*) teavm_gc_heapAddress), (int) ((char*) to - (char*) teavm_gc_heapAddress));
abort();
}
mapTo[i] = mapFrom[i];
mapFrom[i] = 0;
}
} else {
for (int32_t i = size - 1; i >= 0; --i) {
if (mapFrom[i] == 0 || mapTo[i] != 0) {
abort();
}
mapTo[i] = mapFrom[i];
mapFrom[i] = 0;
}
}
#endif
}
#ifdef TEAVM_MEMORY_TRACE
static FILE* teavm_gc_traceFile = NULL;
static void teavm_writeHeapMemory(char* name) {
#ifdef TEAVM_GC_LOG
if (teavm_gc_traceFile == NULL) {
teavm_gc_traceFile = fopen("teavm-gc-trace.txt", "w");
}
FILE* file = teavm_gc_traceFile;
fprintf(file, "%s:", name);
int32_t numbers = 4096;
int64_t mapSize = teavm_gc_availableBytes / sizeof(void*);
for (int i = 0; i < numbers; ++i) {
int64_t start = mapSize * i / numbers;
int64_t end = mapSize * (i + 1) / numbers;
int count = 0;
for (int j = start; j < end; ++j) {
if (teavm_gc_heapMap[j] != 0) {
count++;
}
}
int rate = count * 4096 / (end - start);
fprintf(file, " %d", rate);
}
fprintf(file, "\n");
fflush(file);
#endif
}
void teavm_gc_checkHeapConsistency() {
TeaVM_Object* obj = teavm_gc_heapAddress;
while ((char*) obj < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
int32_t size;
if (obj->header == 0) {
size = obj->hash;
teavm_gc_assertFree(obj, size);
} else {
teavm_verify(obj);
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;
for (int32_t i = 0; i < size; ++i) {
teavm_verify(data[i]);
}
}
} else {
while (cls != NULL) {
int32_t kind = (cls->flags >> 7) & 7;
if (kind == 1) {
} else if (kind == 2) {
} else {
int16_t* layout = cls->layout;
if (layout != NULL) {
int16_t size = *layout++;
for (int32_t i = 0; i < size; ++i) {
void** ptr = (void**) ((char*) obj + *layout++);
teavm_verify(*ptr);
}
}
}
cls = cls->superclass;
}
}
size = teavm_gc_objectSize(obj);
}
obj = (TeaVM_Object*) ((char*) obj + size);
}
}
#endif
void teavm_gc_gcStarted() {
#ifdef TEAVM_MEMORY_TRACE
teavm_writeHeapMemory("start");
teavm_gc_checkHeapConsistency();
#endif
}
void teavm_gc_sweepCompleted() {
#ifdef TEAVM_MEMORY_TRACE
teavm_writeHeapMemory("sweep");
teavm_gc_checkHeapConsistency();
#endif
}
void teavm_gc_defragCompleted() {
#ifdef TEAVM_MEMORY_TRACE
teavm_writeHeapMemory("defrag");
#endif
}

View File

@ -75,13 +75,50 @@ typedef struct TeaVM_StackFrame {
} TeaVM_StackFrame; } TeaVM_StackFrame;
extern void* teavm_gc_heapAddress; extern void* teavm_gc_heapAddress;
extern char *teavm_beforeClasses; extern void* teavm_gc_gcStorageAddress;
extern int32_t teavm_gc_gcStorageSize;
extern void* teavm_gc_regionsAddress;
extern int32_t teavm_gc_regionSize;
extern int32_t teavm_gc_regionMaxCount;
extern int64_t teavm_gc_availableBytes;
extern void*** teavm_gc_staticRoots;
extern char* teavm_beforeClasses;
#ifdef TEAVM_MEMORY_TRACE
extern uint8_t* teavm_gc_heapMap;
extern uint8_t* teavm_gc_markMap;
#endif
#define TEAVM_PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - teavm_beforeClasses) >> 3)) #define TEAVM_PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - teavm_beforeClasses) >> 3))
#define TEAVM_UNPACK_CLASS(cls) ((TeaVM_Class*) (teavm_beforeClasses + ((cls) << 3))) #define TEAVM_UNPACK_CLASS(cls) ((TeaVM_Class*) (teavm_beforeClasses + ((cls) << 3)))
#define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header)) #define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header))
#define TEAVM_AS(ptr, type) ((type*) (ptr)) #define TEAVM_AS(ptr, type) ((type*) (ptr))
#ifdef TEAVM_MEMORY_TRACE
static inline void teavm_gc_assertAddress(void* address) {
if ((unsigned int) (uintptr_t) address % sizeof(void*) != 0) {
abort();
}
}
static inline void* teavm_verify(void* address) {
if (address >= teavm_gc_heapAddress
&& (char*) address < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
teavm_gc_assertAddress(address);
uint8_t* map = teavm_gc_heapMap + (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
if (*map != 1) {
abort();
}
}
return address;
}
#define TEAVM_VERIFY(ptr) teavm_verify(ptr)
#else
#define TEAVM_VERIFY(ptr) ptr
#endif
#define TEAVM_VTABLE(obj, type) (TEAVM_AS(TEAVM_CLASS_OF(obj), type)) #define TEAVM_VTABLE(obj, type) (TEAVM_AS(TEAVM_CLASS_OF(obj), type))
#define TEAVM_METHOD(obj, type, method) (TEAVM_VTABLE(obj, type)->method) #define TEAVM_METHOD(obj, type, method) (TEAVM_VTABLE(obj, type)->method)
#define TEAVM_FIELD(ptr, type, name) (TEAVM_AS(ptr, type)->name) #define TEAVM_FIELD(ptr, type, name) (TEAVM_AS(ptr, type)->name)
@ -181,14 +218,6 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
extern TeaVM_StackFrame* teavm_stackTop; extern TeaVM_StackFrame* teavm_stackTop;
extern void* teavm_gc_gcStorageAddress;
extern int32_t teavm_gc_gcStorageSize;
extern void* teavm_gc_regionsAddress;
extern int32_t teavm_gc_regionSize;
extern int32_t teavm_gc_regionMaxCount;
extern int64_t teavm_gc_availableBytes;
extern void*** teavm_gc_staticRoots;
extern double teavm_rand(); extern double teavm_rand();
static inline float teavm_getNaN() { static inline float teavm_getNaN() {
@ -434,4 +463,14 @@ inline static void* teavm_nullCheck(void* o) {
#define TEAVM_JUMP_TO_FRAME(frame, id) #define TEAVM_JUMP_TO_FRAME(frame, id)
#endif #endif
extern void* teavm_catchException(); extern void* teavm_catchException();
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_sweepCompleted();
extern void teavm_gc_defragCompleted();