mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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.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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(")");
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<Intrinsic> intrinsics, List<Generator> generators,
|
||||
Predicate<MethodReference> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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) {
|
||||
|
|
|
@ -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.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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
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;
|
||||
|
||||
class RelocationBlock extends Structure {
|
||||
int count;
|
||||
Address start;
|
||||
Address current;
|
||||
Address end;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
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