From c5334e344d3c57c2b29215e63092b8bdcf9903ed Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 31 Jul 2019 17:38:13 +0300 Subject: [PATCH] C: add setting to generate code with lots of assertions --- .../teavm/classlib/java/lang/TThrowable.java | 15 +- .../java/org/teavm/backend/c/CTarget.java | 9 +- .../c/generate/CodeGenerationVisitor.java | 32 +- .../backend/c/generate/CodeGenerator.java | 9 + .../backend/c/generate/GenerationContext.java | 8 +- .../c/intrinsic/MemoryTraceIntrinsic.java | 40 ++ .../teavm/backend/c/util/GCVisualizer.java | 185 ++++++++++ .../org/teavm/backend/wasm/WasmTarget.java | 2 + .../wasm/intrinsics/MemoryTraceIntrinsic.java | 35 ++ .../teavm/model/lowlevel/Characteristics.java | 5 + .../org/teavm/runtime/ExceptionHandling.java | 1 - core/src/main/java/org/teavm/runtime/GC.java | 236 ++++++------ .../java/org/teavm/runtime/MemoryTrace.java | 43 +++ .../org/teavm/runtime/RelocationBlock.java | 2 +- .../resources/org/teavm/backend/c/runtime.c | 341 +++++++++++++++--- .../resources/org/teavm/backend/c/runtime.h | 59 ++- 16 files changed, 838 insertions(+), 184 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/c/intrinsic/MemoryTraceIntrinsic.java create mode 100644 core/src/main/java/org/teavm/backend/c/util/GCVisualizer.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/MemoryTraceIntrinsic.java create mode 100644 core/src/main/java/org/teavm/runtime/MemoryTrace.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java index 7a28fad4b..b862f40ee 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java @@ -17,8 +17,8 @@ package org.teavm.classlib.java.lang; import java.io.PrintStream; import java.io.PrintWriter; +import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.java.util.TArrays; -import org.teavm.interop.DelegateTo; import org.teavm.interop.Remove; import org.teavm.interop.Rename; import org.teavm.interop.Superclass; @@ -101,15 +101,12 @@ public class TThrowable extends RuntimeException { } @Override - @DelegateTo("fillInStackTraceLowLevel") public Throwable fillInStackTrace() { - return this; - } - - private TThrowable fillInStackTraceLowLevel() { - int stackSize = ExceptionHandling.callStackSize() - 1; - stackTrace = new TStackTraceElement[stackSize]; - ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace); + if (PlatformDetector.isLowLevel()) { + int stackSize = ExceptionHandling.callStackSize() - 1; + stackTrace = new TStackTraceElement[stackSize]; + ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace); + } return this; } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 69fd9d822..6de45d62a 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -64,6 +64,7 @@ import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.backend.c.intrinsic.IntrinsicContext; import org.teavm.backend.c.intrinsic.IntrinsicFactory; 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.PlatformClassIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic; @@ -340,6 +341,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { intrinsics.add(new PlatformClassIntrinsic()); intrinsics.add(new PlatformClassMetadataIntrinsic()); intrinsics.add(new GCIntrinsic()); + intrinsics.add(new MemoryTraceIntrinsic()); intrinsics.add(new MutatorIntrinsic()); intrinsics.add(new ExceptionHandlingIntrinsic()); intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods())); @@ -356,9 +358,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { generators.add(new ReferenceQueueGenerator()); stringPool = new SimpleStringPool(); + boolean vmAssertions = Boolean.parseBoolean(System.getProperty("teavm.c.vmAssertions", "false")); GenerationContext context = new GenerationContext(vtableProvider, characteristics, 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 runtimeHeaderWriter = new BufferedCodeWriter(false); @@ -371,6 +375,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { if (longjmpUsed) { runtimeHeaderWriter.println("#define TEAVM_USE_SETJMP 1"); } + if (vmAssertions) { + runtimeHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1"); + } emitResource(runtimeHeaderWriter, "runtime.h"); ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler, diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index 34fd40db9..4254a7939 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -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 public void visit(UnaryExpr expr) { pushLocation(expr.getLocation()); @@ -337,7 +347,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { break; case LENGTH: writer.print("TEAVM_ARRAY_LENGTH("); - expr.getOperand().acceptVisitor(this); + visitReference(expr.getOperand()); writer.print(")"); break; case NULL_CHECK: { @@ -347,7 +357,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { withCallSite(); } writer.print("teavm_nullCheck("); - expr.getOperand().acceptVisitor(this); + visitReference(expr.getOperand()); writer.print(")"); if (needParenthesis) { writer.print(")"); @@ -412,7 +422,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { public void visit(SubscriptExpr expr) { pushLocation(expr.getLocation()); writer.print("TEAVM_ARRAY_AT("); - expr.getArray().acceptVisitor(this); + visitReference(expr.getArray()); writer.print(", ").print(getArrayType(expr.getType())).print(", "); expr.getIndex().acceptVisitor(this); writer.print(")"); @@ -604,7 +614,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } else { receiver = allocTemporaryVariable(CVariableType.PTR); writer.print("((").print(receiver).print(" = "); - arguments.get(0).acceptVisitor(this); + visitReference(arguments.get(0)); writer.print("), "); closingParenthesis = true; } @@ -811,8 +821,18 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { if (qualified != null) { ClassReader cls = context.getClassSource().get(field.getClassName()); writer.print("TEAVM_FIELD("); + + boolean shouldVerify = context.isVmAssertions() + && context.getCharacteristics().isManaged(field.getClassName()); + if (shouldVerify) { + writer.print("TEAVM_VERIFY("); + } qualified.acceptVisitor(this); + if (shouldVerify) { + writer.print(")"); + } writer.print(", "); + if (cls != null && isNative(cls)) { InteropUtil.processInclude(cls.getAnnotations(), includes); InteropUtil.printNativeReference(writer, cls); @@ -919,7 +939,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { public void visit(InstanceOfExpr expr) { pushLocation(expr.getLocation()); writer.print("teavm_instanceof("); - expr.getExpr().acceptVisitor(this); + visitReference(expr.getExpr()); includes.includeType(expr.getType()); writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")"); popLocation(expr.getLocation()); @@ -945,7 +965,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } writer.print("teavm_checkcast("); - expr.getValue().acceptVisitor(this); + visitReference(expr.getValue()); includes.includeType(expr.getTarget()); writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")"); diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java index dd6be74e7..88a20a4c0 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java @@ -108,6 +108,15 @@ public class CodeGenerator { private void generateLocals(MethodNode methodNode, int[] temporaryCount, IntContainer spilledVariables) { 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) { VariableNode variableNode = methodNode.getVariables().get(i); if (variableNode.getType() == null) { diff --git a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java index 4c2a2b9d2..3ed45c85a 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java @@ -45,12 +45,13 @@ public class GenerationContext { private BuildTarget buildTarget; private boolean incremental; private boolean longjmp; + private boolean vmAssertions; public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics, DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics, ClassReaderSource classSource, List intrinsics, List generators, Predicate asyncMethods, BuildTarget buildTarget, boolean incremental, - boolean longjmp) { + boolean longjmp, boolean vmAssertions) { this.virtualTableProvider = virtualTableProvider; this.characteristics = characteristics; this.dependencies = dependencies; @@ -64,6 +65,7 @@ public class GenerationContext { this.buildTarget = buildTarget; this.incremental = incremental; this.longjmp = longjmp; + this.vmAssertions = vmAssertions; } public void addIntrinsic(Intrinsic intrinsic) { @@ -131,4 +133,8 @@ public class GenerationContext { public boolean isLongjmp() { return longjmp; } + + public boolean isVmAssertions() { + return vmAssertions; + } } diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/MemoryTraceIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/MemoryTraceIntrinsic.java new file mode 100644 index 000000000..0f2fa2bb1 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/MemoryTraceIntrinsic.java @@ -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(")"); + } +} diff --git a/core/src/main/java/org/teavm/backend/c/util/GCVisualizer.java b/core/src/main/java/org/teavm/backend/c/util/GCVisualizer.java new file mode 100644 index 000000000..cd92fed5c --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/util/GCVisualizer.java @@ -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 lines = readData(args[0]); + BufferedImage bitmap = createBitmap(lines); + writeBitmap(bitmap, args[1]); + } + + private static List readData(String fileName) throws IOException { + List 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 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]; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 75ac0f72f..2e75f59de 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -52,6 +52,7 @@ import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic; import org.teavm.backend.wasm.intrinsics.GCIntrinsic; import org.teavm.backend.wasm.intrinsics.IntegerIntrinsic; 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.ObjectIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; @@ -355,6 +356,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { context.addIntrinsic(new ObjectIntrinsic()); context.addIntrinsic(new ConsoleIntrinsic()); context.addGenerator(new ArrayGenerator()); + context.addIntrinsic(new MemoryTraceIntrinsic()); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/MemoryTraceIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/MemoryTraceIntrinsic.java new file mode 100644 index 000000000..1ec0b42e3 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/MemoryTraceIntrinsic.java @@ -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)); + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/Characteristics.java b/core/src/main/java/org/teavm/model/lowlevel/Characteristics.java index 2a1615eb9..21fd39844 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/Characteristics.java +++ b/core/src/main/java/org/teavm/model/lowlevel/Characteristics.java @@ -17,6 +17,7 @@ package org.teavm.model.lowlevel; import com.carrotsearch.hppc.ObjectByteHashMap; import com.carrotsearch.hppc.ObjectByteMap; +import org.teavm.interop.Address; import org.teavm.interop.Function; import org.teavm.interop.StaticInit; import org.teavm.interop.Structure; @@ -83,6 +84,10 @@ public class Characteristics { return result != 0; } + public boolean isManaged(String className) { + return !isStructure(className) && !isFunction(className) && !className.equals(Address.class.getName()); + } + public boolean isManaged(MethodReference methodReference) { byte result = isManaged.getOrDefault(methodReference, (byte) -1); if (result < 0) { diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index 9695164de..d4b29ebcf 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -132,7 +132,6 @@ public final class ExceptionHandling { return size + 1; } - @Unmanaged public static void fillStackTrace(StackTraceElement[] target) { Address stackFrame = ShadowStack.getStackTop(); int index = 0; diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index 80622d3b4..4f257c014 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -34,7 +34,7 @@ public final class GC { static int freeMemory = (int) availableBytes(); static RuntimeReference firstWeakReference; - static RelocationBlock lastRelocationTarget; + static RelocationBlock lastRelocationBlock; static native Address gcStorageAddress(); @@ -65,76 +65,72 @@ public final class GC { currentChunkPointer = gcStorageAddress().toStructure(); currentChunkPointer.value = currentChunk; freeChunks = 1; - getAvailableChunkIfPossible(0); } public static RuntimeObject alloc(int size) { FreeChunk current = currentChunk; Address next = current.toAddress().add(size); if (!next.add(Structure.sizeOf(FreeChunk.class)).isLessThan(currentChunkLimit)) { - getAvailableChunk(size); + getNextChunk(size); current = currentChunk; - next = currentChunk.toAddress().add(size); + next = current.toAddress().add(size); } - int freeSize = current.size; - freeSize -= size; - if (freeSize > 0) { - currentChunk = next.toStructure(); - currentChunk.size = freeSize; - } else { - freeMemory -= size; - getAvailableChunkIfPossible(currentChunk.size + 1); - } - currentChunk.classReference = 0; + currentChunk = next.toStructure(); freeMemory -= size; + MemoryTrace.allocate(current.toAddress(), size); return current.toAddress().toStructure(); } - private static void getAvailableChunk(int size) { - if (getAvailableChunkIfPossible(size)) { + private static void getNextChunk(int size) { + if (getNextChunkIfPossible(size)) { return; } collectGarbage(); - if (!getAvailableChunkIfPossible(size)) { + if (currentChunk.size < size && !getNextChunkIfPossible(size)) { ExceptionHandling.printStack(); outOfMemory(); } } - private static boolean getAvailableChunkIfPossible(int size) { - if (freeChunks == 0) { - return false; - } + private static boolean getNextChunkIfPossible(int size) { while (true) { - if (currentChunk.toAddress().add(size) == currentChunkLimit) { - break; - } - if (currentChunk.toAddress().add(size + Structure.sizeOf(FreeChunk.class)).isLessThan(currentChunkLimit)) { - break; + if (currentChunk.toAddress().isLessThan(currentChunkLimit)) { + currentChunk.classReference = 0; + currentChunk.size = (int) (currentChunkLimit.toLong() - currentChunk.toAddress().toLong()); } if (--freeChunks == 0) { return false; } - freeMemory -= currentChunk.size; currentChunkPointer = Structure.add(FreeChunkHolder.class, currentChunkPointer, 1); 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; } public static void collectGarbage() { + MemoryTrace.gcStarted(); mark(); processReferences(); sweep(); + MemoryTrace.sweepCompleted(); defragment(); - sortFreeChunks(); + MemoryTrace.defragCompleted(); + //sortFreeChunks(); updateFreeMemory(); + currentChunk = currentChunkPointer.value; + currentChunkLimit = currentChunk.toAddress().add(currentChunk.size); } private static void mark() { + MemoryTrace.initMark(); 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(); int staticCount = staticRoots.getInt(); @@ -172,6 +168,7 @@ public final class GC { continue; } object.classReference |= RuntimeObject.GC_MARKED; + MemoryTrace.mark(object.toAddress()); long offset = object.toAddress().toLong() - heapAddress().toLong(); Region region = Structure.add(Region.class, regionsAddress(), (int) (offset / regionSize())); @@ -211,9 +208,9 @@ public final class GC { private static void markWeakReference(RuntimeReference object) { if (object.queue != null) { - mark(object.queue); + enqueueMark(object.queue); if (object.next != null && object.object != null) { - mark(object.object); + enqueueMark(object); } } if (object.next == null && object.object != null) { @@ -225,7 +222,7 @@ public final class GC { private static void markReferenceQueue(RuntimeReferenceQueue object) { RuntimeReference reference = object.first; while (reference != null) { - mark(reference); + enqueueMark(object); reference = reference.next; } } @@ -238,9 +235,7 @@ public final class GC { layout = layout.add(2); int fieldOffset = layout.getShort(); RuntimeObject reference = object.toAddress().add(fieldOffset).getAddress().toStructure(); - if (reference != null && !isMarked(reference)) { - MarkQueue.enqueue(reference); - } + enqueueMark(reference); } } } @@ -252,13 +247,17 @@ public final class GC { Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), Address.sizeOf()); for (int i = 0; i < array.size; ++i) { RuntimeObject reference = base.getAddress().toStructure(); - if (reference != null && !isMarked(reference)) { - MarkQueue.enqueue(reference); - } + enqueueMark(reference); base = base.add(Address.sizeOf()); } } + private static void enqueueMark(RuntimeObject object) { + if (object != null && !isMarked(object)) { + MarkQueue.enqueue(object); + } + } + private static void processReferences() { RuntimeReference reference = firstWeakReference; while (reference != null) { @@ -297,16 +296,23 @@ public final class GC { if (!object.toAddress().isLessThan(currentRegionEnd)) { int currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize()); Region currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex); - while (currentRegion.start == 0) { - if (++currentRegionIndex == regionsCount) { - object = limit.toStructure(); - break loop; + if (currentRegion.start == 0) { + if (lastFreeSpace == null) { + lastFreeSpace = object; } - 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; @@ -329,6 +335,7 @@ public final class GC { if (lastFreeSpace != null) { lastFreeSpace.classReference = 0; lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); + MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size); freeChunkPtr.value = lastFreeSpace; freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunks++; @@ -348,6 +355,7 @@ public final class GC { int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); lastFreeSpace.classReference = 0; lastFreeSpace.size = freeSize; + MemoryTrace.free(lastFreeSpace.toAddress(), lastFreeSpace.size); freeChunkPtr.value = lastFreeSpace; freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunks++; @@ -381,8 +389,8 @@ public final class GC { RuntimeObject obj = stackRootsPtr.getAddress().toStructure(); if (!obj.toAddress().isLessThan(relocationThreshold)) { 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 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(); - relocationTarget.start = freeChunk.toAddress(); - relocationTarget.current = relocationTarget.start; - relocationTarget.end = limit; - RelocationBlock lastRelocationTarget = relocationTarget; + Address relocationTarget = freeChunk.toAddress(); + relocationBlock.start = relocationTarget; + relocationBlock.end = limit; + relocationBlock.count = 0; + RelocationBlock lastRelocationBlock = relocationBlock; + int countInCurrentRelocationBlock = 0; Address relocations = Structure.add(FreeChunk.class, freeChunk, 1).toAddress(); Address relocationsLimit = freeChunk.toAddress().add(freeChunk.size); @@ -411,36 +421,43 @@ public final class GC { objects: while (object.toAddress().isLessThan(limit)) { int size = objectSize(object); if (object.classReference != 0) { - if ((object.classReference & RuntimeObject.GC_MARKED) != 0 - || !relocationTarget.start.isLessThan(object.toAddress())) { - if (!lastWasLocked) { - 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; + Address nextRelocationTarget = null; + boolean shouldRelocateObject = (object.classReference & RuntimeObject.GC_MARKED) == 0; + if (shouldRelocateObject) { while (true) { - nextRelocationTarget = relocationTarget.current.add(size); - if (nextRelocationTarget.isLessThan(relocationTarget.end)) { + nextRelocationTarget = relocationTarget.add(size); + if (!relocationBlock.end.isLessThan(nextRelocationTarget)) { break; } - relocationTarget = Structure.add(RelocationBlock.class, relocationTarget, 1); - if (lastRelocationTarget.toAddress().isLessThan(relocationTarget.toAddress())) { - break objects; + RelocationBlock nextRelocationBlock = Structure.add(RelocationBlock.class, relocationBlock, 1); + if (nextRelocationBlock.start == object.toAddress()) { + 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)) { if (--freeChunks == 0) { + lastRelocationBlock.end = object.toAddress(); break objects; } freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1); @@ -452,35 +469,37 @@ public final class GC { Relocation relocation = relocations.toStructure(); relocation.classBackup = object.classReference; relocation.sizeBackup = object.size; - relocation.newAddress = relocationTarget.current; + relocation.newAddress = relocationTarget; + countInCurrentRelocationBlock++; relocations = relocations.add(Structure.sizeOf(Relocation.class)); long targetAddress = relocation.toAddress().toLong(); object.classReference = (int) (targetAddress >>> 33) | RuntimeObject.GC_MARKED; object.size = (int) (targetAddress >> 1); - relocationTarget.current = nextRelocationTarget; + relocationTarget = nextRelocationTarget; } } else { lastWasLocked = false; - lastRelocationTarget.end = object.toAddress().add(object.size); } object = object.toAddress().add(size).toStructure(); } + relocationBlock.count = countInCurrentRelocationBlock; + while (object.toAddress().isLessThan(limit)) { int size = objectSize(object); if (object.classReference != 0) { object.classReference &= ~RuntimeObject.GC_MARKED; } else { - lastRelocationTarget = Structure.add(RelocationBlock.class, lastRelocationTarget, 1); - lastRelocationTarget.start = object.toAddress(); - lastRelocationTarget.current = lastRelocationTarget.start; - lastRelocationTarget.end = lastRelocationTarget.start.add(size); + lastRelocationBlock = Structure.add(RelocationBlock.class, lastRelocationBlock, 1); + lastRelocationBlock.start = object.toAddress(); + lastRelocationBlock.count = 0; + lastRelocationBlock.end = lastRelocationBlock.start.add(size); } object = object.toAddress().add(size).toStructure(); } - GC.lastRelocationTarget = lastRelocationTarget; + GC.lastRelocationBlock = lastRelocationBlock; } private static void updatePointersFromStaticRoots() { @@ -633,47 +652,44 @@ public final class GC { FreeChunk freeChunk = currentChunkPointer.value; 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(); - Address currentTarget = relocationTarget.start; + int countInRelocationBlock = relocationBlock.count; + Address relocationTarget = relocationBlock.start; Address blockTarget = null; Address blockSource = null; int blockSize = 0; - objects: while (object.toAddress().isLessThan(limit)) { + while (object.toAddress().isLessThan(limit)) { int size = objectSize(object); if ((object.classReference & RuntimeObject.GC_MARKED) != 0) { object.classReference &= ~RuntimeObject.GC_MARKED; - Address nextRelocationTarget; - while (true) { - nextRelocationTarget = currentTarget.add(size); - if (nextRelocationTarget.isLessThan(relocationTarget.end)) { - break; - } - + while (countInRelocationBlock == 0) { if (blockSize != 0) { Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize); + MemoryTrace.move(blockSource, blockTarget, blockSize); blockSource = null; blockSize = 0; } - relocationTarget = Structure.add(RelocationBlock.class, relocationTarget, 1); - if (lastRelocationTarget.toAddress().isLessThan(relocationTarget.toAddress())) { - break objects; - } - currentTarget = relocationTarget.start; + relocationBlock.start = relocationTarget; + relocationBlock = Structure.add(RelocationBlock.class, relocationBlock, 1); + countInRelocationBlock = relocationBlock.count; + relocationTarget = relocationBlock.start; } if (blockSource == null) { blockSource = object.toAddress(); - blockTarget = currentTarget; + blockTarget = relocationTarget; } - currentTarget = nextRelocationTarget; + relocationTarget = relocationTarget.add(size); blockSize += size; + --countInRelocationBlock; } else if (blockSource != null) { Allocator.moveMemoryBlock(blockSource, blockTarget, blockSize); + MemoryTrace.move(blockSource, blockTarget, blockSize); blockSource = null; blockSize = 0; } @@ -681,8 +697,10 @@ public final class GC { object = object.toAddress().add(size).toStructure(); } + relocationBlock.start = relocationTarget; if (blockSource != null) { 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) .toAddress().toStructure(); freeChunks = 0; - while (!lastRelocationTarget.toAddress().isLessThan(relocationBlock.toAddress())) { - if (!relocationBlock.current.isLessThan(relocationBlock.end)) { - continue; + while (!lastRelocationBlock.toAddress().isLessThan(relocationBlock.toAddress())) { + if (relocationBlock.start.isLessThan(relocationBlock.end)) { + 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); } } @@ -708,8 +726,6 @@ public final class GC { private static void sortFreeChunks() { currentChunkPointer = gcStorageAddress().toStructure(); sortFreeChunks(0, freeChunks - 1); - currentChunk = currentChunkPointer.value; - currentChunkLimit = currentChunk.toAddress().add(currentChunk.size); } private static void updateFreeMemory() { diff --git a/core/src/main/java/org/teavm/runtime/MemoryTrace.java b/core/src/main/java/org/teavm/runtime/MemoryTrace.java new file mode 100644 index 000000000..86b6a86ff --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/MemoryTrace.java @@ -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(); +} diff --git a/core/src/main/java/org/teavm/runtime/RelocationBlock.java b/core/src/main/java/org/teavm/runtime/RelocationBlock.java index 85c8610bc..3f2225bb4 100644 --- a/core/src/main/java/org/teavm/runtime/RelocationBlock.java +++ b/core/src/main/java/org/teavm/runtime/RelocationBlock.java @@ -19,7 +19,7 @@ import org.teavm.interop.Address; import org.teavm.interop.Structure; class RelocationBlock extends Structure { + int count; Address start; - Address current; Address end; } diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.c b/core/src/main/resources/org/teavm/backend/c/runtime.c index 2578abf2a..1a7ea2e98 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -23,6 +23,10 @@ #endif 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; @@ -107,37 +111,13 @@ void teavm_beforeInit() { } #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); - 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); +static void* teavm_virtualAlloc(int size) { + return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); +} - teavm_gc_heapAddress = mmap( - NULL, - 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; +static long teavm_pageSize() { + return sysconf(_SC_PAGE_SIZE); } int64_t teavm_currentTimeMillis() { @@ -173,24 +153,10 @@ static void* teavm_virtualAlloc(int size) { #endif } -void teavm_initHeap(int64_t heapSize) { - long workSize = (long) (heapSize / 16); - long regionsSize = (long) (heapSize / teavm_gc_regionSize); - +static long teavm_pageSize() { SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); - long pageSize = 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; + return systemInfo.dwPageSize; } int64_t teavm_currentTimeMillis() { @@ -210,6 +176,31 @@ int64_t teavm_currentTimeNano() { } #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 #undef gmtime_r #undef localtime_r @@ -606,3 +597,263 @@ void teavm_logchar(int32_t c) { OutputDebugStringW(buffer); } #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 +} \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.h b/core/src/main/resources/org/teavm/backend/c/runtime.h index 232e37373..08432d414 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.h +++ b/core/src/main/resources/org/teavm/backend/c/runtime.h @@ -75,13 +75,50 @@ typedef struct TeaVM_StackFrame { } TeaVM_StackFrame; 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_UNPACK_CLASS(cls) ((TeaVM_Class*) (teavm_beforeClasses + ((cls) << 3))) #define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header)) #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_METHOD(obj, type, method) (TEAVM_VTABLE(obj, type)->method) #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 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(); static inline float teavm_getNaN() { @@ -434,4 +463,14 @@ inline static void* teavm_nullCheck(void* o) { #define TEAVM_JUMP_TO_FRAME(frame, id) #endif -extern void* teavm_catchException(); \ No newline at end of file +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(); \ No newline at end of file