mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
C: add setting to generate code with lots of assertions
This commit is contained in:
parent
0fdf58cbd8
commit
c5334e344d
|
@ -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()) {
|
||||||
}
|
|
||||||
|
|
||||||
private TThrowable fillInStackTraceLowLevel() {
|
|
||||||
int stackSize = ExceptionHandling.callStackSize() - 1;
|
int stackSize = ExceptionHandling.callStackSize() - 1;
|
||||||
stackTrace = new TStackTraceElement[stackSize];
|
stackTrace = new TStackTraceElement[stackSize];
|
||||||
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
|
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(")");
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(")");
|
||||||
|
}
|
||||||
|
}
|
185
core/src/main/java/org/teavm/backend/c/util/GCVisualizer.java
Normal file
185
core/src/main/java/org/teavm/backend/c/util/GCVisualizer.java
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
freeSize -= size;
|
|
||||||
if (freeSize > 0) {
|
|
||||||
currentChunk = next.toStructure();
|
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;
|
||||||
|
if (currentChunk.size >= size) {
|
||||||
currentChunkLimit = currentChunk.toAddress().add(currentChunk.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,17 +296,24 @@ 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 (lastFreeSpace == null) {
|
||||||
|
lastFreeSpace = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
if (++currentRegionIndex == regionsCount) {
|
if (++currentRegionIndex == regionsCount) {
|
||||||
object = limit.toStructure();
|
object = limit.toStructure();
|
||||||
break loop;
|
break loop;
|
||||||
}
|
}
|
||||||
currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
|
currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
|
||||||
}
|
} while (currentRegion.start == 0);
|
||||||
|
|
||||||
Address newRegionStart = heapAddress().add(currentRegionIndex * regionSize());
|
Address newRegionStart = heapAddress().add(currentRegionIndex * regionSize());
|
||||||
object = newRegionStart.add(currentRegion.start - 1).toStructure();
|
object = newRegionStart.add(currentRegion.start - 1).toStructure();
|
||||||
currentRegionEnd = newRegionStart.add(regionSize());
|
currentRegionEnd = newRegionStart.add(regionSize());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int tag = object.classReference;
|
int tag = object.classReference;
|
||||||
boolean free;
|
boolean free;
|
||||||
|
@ -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 freeChunk = relocationBlock.current.toStructure();
|
|
||||||
freeChunk.size = (int) (relocationBlock.end.toLong() - relocationBlock.current.toLong());
|
|
||||||
freeChunk.classReference = 0;
|
freeChunk.classReference = 0;
|
||||||
|
MemoryTrace.assertFree(freeChunk.toAddress(), freeChunk.size);
|
||||||
freeChunkPointer.value = freeChunk;
|
freeChunkPointer.value = freeChunk;
|
||||||
freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1);
|
freeChunkPointer = Structure.add(FreeChunkHolder.class, freeChunkPointer, 1);
|
||||||
freeChunks++;
|
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() {
|
||||||
|
|
43
core/src/main/java/org/teavm/runtime/MemoryTrace.java
Normal file
43
core/src/main/java/org/teavm/runtime/MemoryTrace.java
Normal 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();
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -75,13 +75,50 @@ typedef struct TeaVM_StackFrame {
|
||||||
} TeaVM_StackFrame;
|
} TeaVM_StackFrame;
|
||||||
|
|
||||||
extern void* teavm_gc_heapAddress;
|
extern void* teavm_gc_heapAddress;
|
||||||
|
extern void* teavm_gc_gcStorageAddress;
|
||||||
|
extern int32_t teavm_gc_gcStorageSize;
|
||||||
|
extern void* teavm_gc_regionsAddress;
|
||||||
|
extern int32_t teavm_gc_regionSize;
|
||||||
|
extern int32_t teavm_gc_regionMaxCount;
|
||||||
|
extern int64_t teavm_gc_availableBytes;
|
||||||
|
extern void*** teavm_gc_staticRoots;
|
||||||
extern char* teavm_beforeClasses;
|
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() {
|
||||||
|
@ -435,3 +464,13 @@ inline static void* teavm_nullCheck(void* o) {
|
||||||
#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();
|
Loading…
Reference in New Issue
Block a user