mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Add transformation passes that maintain GC roots on shadow stack. Add support of shadow stack intrinsics
This commit is contained in:
parent
bfc37b10b5
commit
476086af47
|
@ -52,7 +52,9 @@ import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.TextLocation;
|
import org.teavm.model.TextLocation;
|
||||||
|
@ -207,6 +209,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||||
|
}
|
||||||
|
|
||||||
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
|
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {
|
||||||
List<ClassNode> clsNodes = modelToAst(classes);
|
List<ClassNode> clsNodes = modelToAst(classes);
|
||||||
if (controller.wasCancelled()) {
|
if (controller.wasCancelled()) {
|
||||||
|
|
|
@ -17,11 +17,19 @@ package org.teavm.backend.wasm;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
|
import org.teavm.interop.NoGC;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
|
||||||
|
@StaticInit
|
||||||
|
@NoGC
|
||||||
public final class WasmRuntime {
|
public final class WasmRuntime {
|
||||||
|
public static Address stack = initStack();
|
||||||
|
|
||||||
private WasmRuntime() {
|
private WasmRuntime() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static native Address initStack();
|
||||||
|
|
||||||
public static int compare(int a, int b) {
|
public static int compare(int a, int b) {
|
||||||
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
@ -234,4 +242,11 @@ public final class WasmRuntime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Address allocStack(int size) {
|
||||||
|
Address result = stack;
|
||||||
|
stack = stack.add((size + 1) << 2);
|
||||||
|
stack.add(-4).putInt(size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,8 +98,12 @@ import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
import org.teavm.model.classes.VirtualTableProvider;
|
import org.teavm.model.classes.VirtualTableProvider;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
|
import org.teavm.model.lowlevel.GcRootMaintainingTransformer;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
@ -116,10 +120,17 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
private boolean debugging;
|
private boolean debugging;
|
||||||
private boolean wastEmitted;
|
private boolean wastEmitted;
|
||||||
private boolean cEmitted;
|
private boolean cEmitted;
|
||||||
|
private ClassInitializerEliminator classInitializerEliminator;
|
||||||
|
private ClassInitializerTransformer classInitializerTransformer;
|
||||||
|
private GcRootMaintainingTransformer gcRootMaintainingTransformer;
|
||||||
|
private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setController(TeaVMTargetController controller) {
|
public void setController(TeaVMTargetController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
|
gcRootMaintainingTransformer = new GcRootMaintainingTransformer(controller.getUnprocessedClassSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -188,6 +199,8 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
void.class), null).use();
|
void.class), null).use();
|
||||||
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "moveMemoryBlock", Address.class,
|
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "moveMemoryBlock", Address.class,
|
||||||
Address.class, int.class, void.class), null).use();
|
Address.class, int.class, void.class), null).use();
|
||||||
|
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "allocStack",
|
||||||
|
int.class, Address.class), null).use();
|
||||||
|
|
||||||
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocate",
|
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||||
RuntimeClass.class, Address.class), null).use();
|
RuntimeClass.class, Address.class), null).use();
|
||||||
|
@ -210,6 +223,32 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classes) {
|
||||||
|
ClassReader cls = classes.get(method.getOwnerName());
|
||||||
|
boolean hasClinit = cls.getMethod(clinitDescriptor) != null;
|
||||||
|
if (needsClinitCall(method) && hasClinit) {
|
||||||
|
BasicBlock entryBlock = program.basicBlockAt(0);
|
||||||
|
InitClassInstruction initInsn = new InitClassInstruction();
|
||||||
|
initInsn.setClassName(method.getOwnerName());
|
||||||
|
entryBlock.getInstructions().add(0, initInsn);
|
||||||
|
}
|
||||||
|
|
||||||
|
classInitializerEliminator.apply(program);
|
||||||
|
classInitializerTransformer.transform(program);
|
||||||
|
gcRootMaintainingTransformer.apply(program, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean needsClinitCall(MethodReader method) {
|
||||||
|
if (method.getName().equals("<clinit>")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.getName().equals("<init>")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return method.hasModifier(ElementModifier.STATIC);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName)
|
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@ -238,7 +277,8 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator));
|
context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator));
|
||||||
context.addIntrinsic(new ClassIntrinsic());
|
context.addIntrinsic(new ClassIntrinsic());
|
||||||
|
|
||||||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
|
WasmGenerator generator = new WasmGenerator(decompiler, classes,
|
||||||
|
context, classGenerator);
|
||||||
|
|
||||||
module.setMemorySize(64);
|
module.setMemorySize(64);
|
||||||
generateMethods(classes, context, generator, module);
|
generateMethods(classes, context, generator, module);
|
||||||
|
@ -250,7 +290,8 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
dataSegment.setOffset(256);
|
dataSegment.setOffset(256);
|
||||||
module.getSegments().add(dataSegment);
|
module.getSegments().add(dataSegment);
|
||||||
|
|
||||||
renderAllocatorInit(module, binaryWriter.getAddress());
|
int address = renderStackInit(module, binaryWriter.getAddress());
|
||||||
|
renderAllocatorInit(module, address);
|
||||||
renderClinit(classes, classGenerator, module);
|
renderClinit(classes, classGenerator, module);
|
||||||
if (controller.wasCancelled()) {
|
if (controller.wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
|
@ -333,6 +374,10 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
&& method.getName().equals("initialize")) {
|
&& method.getName().equals("initialize")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (method.getOwnerName().equals(WasmRuntime.class.getName())
|
||||||
|
&& method.getName().equals("initStack")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (context.getIntrinsic(method.getReference()) != null) {
|
if (context.getIntrinsic(method.getReference()) != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -555,8 +600,6 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderAllocatorInit(WasmModule module, int address) {
|
private void renderAllocatorInit(WasmModule module, int address) {
|
||||||
address = (((address - 1) / 4096) + 1) * 4096;
|
|
||||||
|
|
||||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference(
|
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference(
|
||||||
Allocator.class, "initialize", Address.class)));
|
Allocator.class, "initialize", Address.class)));
|
||||||
function.setResult(WasmType.INT32);
|
function.setResult(WasmType.INT32);
|
||||||
|
@ -564,6 +607,18 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
module.add(function);
|
module.add(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int renderStackInit(WasmModule module, int address) {
|
||||||
|
address = (((address - 1) / 256) + 1) * 256;
|
||||||
|
|
||||||
|
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference(
|
||||||
|
WasmRuntime.class, "initStack", Address.class)));
|
||||||
|
function.setResult(WasmType.INT32);
|
||||||
|
function.getBody().add(new WasmReturn(new WasmInt32Constant(address)));
|
||||||
|
module.add(function);
|
||||||
|
|
||||||
|
return address + 65536;
|
||||||
|
}
|
||||||
|
|
||||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||||
Set<MethodReference> virtualMethods = new HashSet<>();
|
Set<MethodReference> virtualMethods = new HashSet<>();
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
|
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
|
||||||
private Set<WasmBlock> usedBlocks = new HashSet<>();
|
private Set<WasmBlock> usedBlocks = new HashSet<>();
|
||||||
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>();
|
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>();
|
||||||
|
private WasmLocal stackVariable;
|
||||||
WasmExpression result;
|
WasmExpression result;
|
||||||
|
|
||||||
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
|
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
|
||||||
|
@ -780,6 +781,23 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvocationExpr expr) {
|
public void visit(InvocationExpr expr) {
|
||||||
|
if (expr.getMethod().getClassName().equals(Allocator.class.getName())) {
|
||||||
|
switch (expr.getMethod().getName()) {
|
||||||
|
case "allocStack":
|
||||||
|
generateAllocStack(expr.getArguments().get(0));
|
||||||
|
return;
|
||||||
|
case "releaseStack":
|
||||||
|
generateReleaseStack();
|
||||||
|
return;
|
||||||
|
case "registerGcRoot":
|
||||||
|
generateRegisterGcRoot(expr.getArguments().get(0), expr.getArguments().get(1));
|
||||||
|
return;
|
||||||
|
case "removeGcRoot":
|
||||||
|
generateRemoveGcRoot(expr.getArguments().get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
||||||
if (intrinsic != null) {
|
if (intrinsic != null) {
|
||||||
result = intrinsic.apply(expr, intrinsicManager);
|
result = intrinsic.apply(expr, intrinsicManager);
|
||||||
|
@ -865,6 +883,63 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateAllocStack(Expr sizeExpr) {
|
||||||
|
if (stackVariable != null) {
|
||||||
|
throw new IllegalStateException("Call to Allocator.allocStack must be done only once");
|
||||||
|
}
|
||||||
|
stackVariable = getTemporary(WasmType.INT32);
|
||||||
|
stackVariable.setName("__stack__");
|
||||||
|
InvocationExpr expr = new InvocationExpr();
|
||||||
|
expr.setType(InvocationType.SPECIAL);
|
||||||
|
expr.setMethod(new MethodReference(WasmRuntime.class, "allocStack", int.class, Address.class));
|
||||||
|
expr.getArguments().add(sizeExpr);
|
||||||
|
expr.acceptVisitor(this);
|
||||||
|
|
||||||
|
result = new WasmSetLocal(stackVariable, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateReleaseStack() {
|
||||||
|
if (stackVariable == null) {
|
||||||
|
throw new IllegalStateException("Call to Allocator.releaseStack must be dominated by "
|
||||||
|
+ "Allocator.allocStack");
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = classGenerator.getFieldOffset(new FieldReference(WasmRuntime.class.getName(), "stack"));
|
||||||
|
result = new WasmStoreInt32(4, new WasmInt32Constant(offset), new WasmGetLocal(stackVariable),
|
||||||
|
WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) {
|
||||||
|
if (stackVariable == null) {
|
||||||
|
throw new IllegalStateException("Call to Allocator.registerGcRoot must be dominated by "
|
||||||
|
+ "Allocator.allocStack");
|
||||||
|
}
|
||||||
|
|
||||||
|
slotExpr.acceptVisitor(this);
|
||||||
|
WasmExpression slot = result;
|
||||||
|
WasmExpression address = new WasmGetLocal(stackVariable);
|
||||||
|
address = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, address, slot);
|
||||||
|
|
||||||
|
gcRootExpr.acceptVisitor(this);
|
||||||
|
WasmExpression gcRoot = result;
|
||||||
|
|
||||||
|
result = new WasmStoreInt32(4, address, gcRoot, WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateRemoveGcRoot(Expr slotExpr) {
|
||||||
|
if (stackVariable == null) {
|
||||||
|
throw new IllegalStateException("Call to Allocator.removeGcRoot must be dominated by "
|
||||||
|
+ "Allocator.allocStack");
|
||||||
|
}
|
||||||
|
|
||||||
|
slotExpr.acceptVisitor(this);
|
||||||
|
WasmExpression slot = result;
|
||||||
|
WasmExpression address = new WasmGetLocal(stackVariable);
|
||||||
|
address = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, address, slot);
|
||||||
|
|
||||||
|
result = new WasmStoreInt32(4, address, new WasmInt32Constant(0), WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BlockStatement statement) {
|
public void visit(BlockStatement statement) {
|
||||||
WasmBlock block = new WasmBlock(false);
|
WasmBlock block = new WasmBlock(false);
|
||||||
|
|
|
@ -21,17 +21,12 @@ import org.teavm.ast.decompilation.Decompiler;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
import org.teavm.backend.wasm.model.WasmLocal;
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
import org.teavm.backend.wasm.model.WasmType;
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.instructions.InitClassInstruction;
|
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
|
||||||
|
|
||||||
public class WasmGenerator {
|
public class WasmGenerator {
|
||||||
private Decompiler decompiler;
|
private Decompiler decompiler;
|
||||||
|
@ -39,8 +34,8 @@ public class WasmGenerator {
|
||||||
private WasmGenerationContext context;
|
private WasmGenerationContext context;
|
||||||
private WasmClassGenerator classGenerator;
|
private WasmClassGenerator classGenerator;
|
||||||
|
|
||||||
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, WasmGenerationContext context,
|
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource,
|
||||||
WasmClassGenerator classGenerator) {
|
WasmGenerationContext context, WasmClassGenerator classGenerator) {
|
||||||
this.decompiler = decompiler;
|
this.decompiler = decompiler;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
@ -50,17 +45,6 @@ public class WasmGenerator {
|
||||||
public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) {
|
public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) {
|
||||||
ClassHolder cls = classSource.get(methodReference.getClassName());
|
ClassHolder cls = classSource.get(methodReference.getClassName());
|
||||||
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
|
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
|
||||||
Program program = bodyMethod.getProgram();
|
|
||||||
|
|
||||||
if (needsClinitCall(method) && classGenerator.hasClinit(method.getOwnerName())) {
|
|
||||||
BasicBlock entryBlock = program.basicBlockAt(0);
|
|
||||||
InitClassInstruction initInsn = new InitClassInstruction();
|
|
||||||
initInsn.setClassName(bodyMethod.getOwnerName());
|
|
||||||
entryBlock.getInstructions().add(0, initInsn);
|
|
||||||
}
|
|
||||||
|
|
||||||
new ClassInitializerEliminator(classSource).apply(program);
|
|
||||||
new ClassInitializerTransformer().transform(program);
|
|
||||||
|
|
||||||
RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod);
|
RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod);
|
||||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference));
|
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference));
|
||||||
|
@ -88,16 +72,6 @@ public class WasmGenerator {
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean needsClinitCall(MethodHolder method) {
|
|
||||||
if (method.getName().equals("<clinit>")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (method.getName().equals("<init>")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return method.hasModifier(ElementModifier.STATIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WasmFunction generateNative(MethodReference methodReference) {
|
public WasmFunction generateNative(MethodReference methodReference) {
|
||||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference));
|
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference));
|
||||||
for (int i = 0; i < methodReference.parameterCount(); ++i) {
|
for (int i = 0; i < methodReference.parameterCount(); ++i) {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package org.teavm.backend.wasm.intrinsics;
|
package org.teavm.backend.wasm.intrinsics;
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.teavm.ast.ConstantExpr;
|
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.backend.wasm.WasmRuntime;
|
import org.teavm.backend.wasm.WasmRuntime;
|
||||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||||
|
@ -31,17 +30,14 @@ import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
|
||||||
public class AllocatorIntrinsic implements WasmIntrinsic {
|
public class AllocatorIntrinsic implements WasmIntrinsic {
|
||||||
private static final FieldReference flagsField = new FieldReference(RuntimeClass.class.getName(), "flags");
|
private static final FieldReference flagsField = new FieldReference(RuntimeClass.class.getName(), "flags");
|
||||||
private WasmClassGenerator classGenerator;
|
|
||||||
private int flagsFieldOffset;
|
private int flagsFieldOffset;
|
||||||
|
|
||||||
public AllocatorIntrinsic(WasmClassGenerator classGenerator) {
|
public AllocatorIntrinsic(WasmClassGenerator classGenerator) {
|
||||||
this.classGenerator = classGenerator;
|
|
||||||
flagsFieldOffset = classGenerator.getFieldOffset(flagsField);
|
flagsFieldOffset = classGenerator.getFieldOffset(flagsField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +70,14 @@ public class AllocatorIntrinsic implements WasmIntrinsic {
|
||||||
return call;
|
return call;
|
||||||
}
|
}
|
||||||
case "isInitialized": {
|
case "isInitialized": {
|
||||||
ConstantExpr argument = (ConstantExpr) invocation.getArguments().get(0);
|
WasmExpression pointer = manager.generate(invocation.getArguments().get(0));
|
||||||
ValueType type = (ValueType) argument.getValue();
|
if (pointer instanceof WasmInt32Constant) {
|
||||||
int pointer = classGenerator.getClassPointer(type) + flagsFieldOffset;
|
pointer = new WasmInt32Constant(((WasmInt32Constant) pointer).getValue() + flagsFieldOffset);
|
||||||
WasmExpression flags = new WasmLoadInt32(4, new WasmInt32Constant(pointer), WasmInt32Subtype.INT32);
|
} else {
|
||||||
|
pointer = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, pointer,
|
||||||
|
new WasmInt32Constant(flagsFieldOffset));
|
||||||
|
}
|
||||||
|
WasmExpression flags = new WasmLoadInt32(4, pointer, WasmInt32Subtype.INT32);
|
||||||
WasmExpression flag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, flags,
|
WasmExpression flag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, flags,
|
||||||
new WasmInt32Constant(RuntimeClass.INITIALIZED));
|
new WasmInt32Constant(RuntimeClass.INITIALIZED));
|
||||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, flag, new WasmInt32Constant(0));
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, flag, new WasmInt32Constant(0));
|
||||||
|
|
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntObjectMap;
|
||||||
|
import com.carrotsearch.hppc.IntObjectOpenHashMap;
|
||||||
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.interop.NoGC;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
|
import org.teavm.model.util.LivenessAnalyzer;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.model.util.TypeInferer;
|
||||||
|
import org.teavm.model.util.VariableType;
|
||||||
|
import org.teavm.runtime.Allocator;
|
||||||
|
|
||||||
|
public class GcRootMaintainingTransformer {
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
|
||||||
|
public GcRootMaintainingTransformer(ClassReaderSource classSource) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Program program, MethodReader method) {
|
||||||
|
if (!requiresGc(method.getReference())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<IntObjectMap<BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
|
||||||
|
int maxDepth = putLiveInGcRoots(program, liveInInformation);
|
||||||
|
if (maxDepth > 0) {
|
||||||
|
addStackAllocation(program, maxDepth);
|
||||||
|
addStackRelease(program, maxDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IntObjectMap<BitSet>> findCallSiteLiveIns(Program program, MethodReader method) {
|
||||||
|
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
|
TypeInferer typeInferer = new TypeInferer();
|
||||||
|
typeInferer.inferTypes(program, method.getReference());
|
||||||
|
List<IntObjectMap<BitSet>> liveInInformation = new ArrayList<>();
|
||||||
|
|
||||||
|
LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
|
||||||
|
livenessAnalyzer.analyze(program);
|
||||||
|
DefinitionExtractor defExtractor = new DefinitionExtractor();
|
||||||
|
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
IntObjectMap<BitSet> blockLiveIn = new IntObjectOpenHashMap<>();
|
||||||
|
liveInInformation.add(blockLiveIn);
|
||||||
|
BitSet currentLiveOut = new BitSet();
|
||||||
|
for (int successor : cfg.outgoingEdges(i)) {
|
||||||
|
currentLiveOut.or(livenessAnalyzer.liveIn(successor));
|
||||||
|
}
|
||||||
|
for (int j = block.getInstructions().size() - 1; j >= 0; --j) {
|
||||||
|
Instruction insn = block.getInstructions().get(j);
|
||||||
|
insn.acceptVisitor(defExtractor);
|
||||||
|
for (Variable definedVar : defExtractor.getDefinedVariables()) {
|
||||||
|
currentLiveOut.clear(definedVar.getIndex());
|
||||||
|
}
|
||||||
|
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|
||||||
|
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|
||||||
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
||||||
|
if (insn instanceof InvokeInstruction && !requiresGc(((InvokeInstruction) insn).getMethod())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitSet csLiveIn = (BitSet) currentLiveOut.clone();
|
||||||
|
for (int v = csLiveIn.nextSetBit(0); v >= 0; v = csLiveIn.nextSetBit(v + 1)) {
|
||||||
|
if (!isReference(typeInferer, v)) {
|
||||||
|
csLiveIn.clear(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
csLiveIn.clear(0, method.parameterCount() + 1);
|
||||||
|
blockLiveIn.put(j, csLiveIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block.getExceptionVariable() != null) {
|
||||||
|
currentLiveOut.clear(block.getExceptionVariable().getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return liveInInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int putLiveInGcRoots(Program program, List<IntObjectMap<BitSet>> liveInInformation) {
|
||||||
|
int maxDepth = 0;
|
||||||
|
for (IntObjectMap<BitSet> liveInsMap : liveInInformation) {
|
||||||
|
for (ObjectCursor<BitSet> liveIns : liveInsMap.values()) {
|
||||||
|
maxDepth = Math.max(maxDepth, liveIns.value.cardinality());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
List<Instruction> instructions = block.getInstructions();
|
||||||
|
IntObjectMap<BitSet> liveInsByIndex = liveInInformation.get(i);
|
||||||
|
for (int j = instructions.size() - 1; j >= 0; --j) {
|
||||||
|
BitSet liveIns = liveInsByIndex.get(j);
|
||||||
|
if (liveIns == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
storeLiveIns(block, j, liveIns, maxDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeLiveIns(BasicBlock block, int index, BitSet liveIns, int maxDepth) {
|
||||||
|
Program program = block.getProgram();
|
||||||
|
List<Instruction> instructions = block.getInstructions();
|
||||||
|
Instruction callInstruction = instructions.get(index);
|
||||||
|
List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
|
|
||||||
|
int slot = 0;
|
||||||
|
for (int liveVar = liveIns.nextSetBit(0); liveVar >= 0; liveVar = liveIns.nextSetBit(liveVar + 1)) {
|
||||||
|
Variable slotVar = program.createVariable();
|
||||||
|
IntegerConstantInstruction slotConstant = new IntegerConstantInstruction();
|
||||||
|
slotConstant.setReceiver(slotVar);
|
||||||
|
slotConstant.setConstant(slot++);
|
||||||
|
slotConstant.setLocation(callInstruction.getLocation());
|
||||||
|
instructionsToAdd.add(slotConstant);
|
||||||
|
|
||||||
|
InvokeInstruction registerInvocation = new InvokeInstruction();
|
||||||
|
registerInvocation.setType(InvocationType.SPECIAL);
|
||||||
|
registerInvocation.setMethod(new MethodReference(Allocator.class, "registerGcRoot", int.class,
|
||||||
|
Object.class, void.class));
|
||||||
|
registerInvocation.getArguments().add(slotVar);
|
||||||
|
registerInvocation.getArguments().add(program.variableAt(liveVar));
|
||||||
|
instructionsToAdd.add(registerInvocation);
|
||||||
|
|
||||||
|
++slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (slot < maxDepth) {
|
||||||
|
Variable slotVar = program.createVariable();
|
||||||
|
IntegerConstantInstruction slotConstant = new IntegerConstantInstruction();
|
||||||
|
slotConstant.setReceiver(slotVar);
|
||||||
|
slotConstant.setConstant(slot++);
|
||||||
|
slotConstant.setLocation(callInstruction.getLocation());
|
||||||
|
instructionsToAdd.add(slotConstant);
|
||||||
|
|
||||||
|
InvokeInstruction clearInvocation = new InvokeInstruction();
|
||||||
|
clearInvocation.setType(InvocationType.SPECIAL);
|
||||||
|
clearInvocation.setMethod(new MethodReference(Allocator.class, "removeGcRoot", int.class, void.class));
|
||||||
|
clearInvocation.getArguments().add(slotVar);
|
||||||
|
clearInvocation.setLocation(callInstruction.getLocation());
|
||||||
|
instructionsToAdd.add(clearInvocation);
|
||||||
|
|
||||||
|
++slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.addAll(index, instructionsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStackAllocation(Program program, int maxDepth) {
|
||||||
|
BasicBlock block = program.basicBlockAt(0);
|
||||||
|
List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
|
Variable sizeVariable = program.createVariable();
|
||||||
|
|
||||||
|
IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction();
|
||||||
|
sizeConstant.setReceiver(sizeVariable);
|
||||||
|
sizeConstant.setConstant(maxDepth);
|
||||||
|
instructionsToAdd.add(sizeConstant);
|
||||||
|
|
||||||
|
InvokeInstruction invocation = new InvokeInstruction();
|
||||||
|
invocation.setType(InvocationType.SPECIAL);
|
||||||
|
invocation.setMethod(new MethodReference(Allocator.class, "allocStack", int.class, void.class));
|
||||||
|
invocation.getArguments().add(sizeVariable);
|
||||||
|
instructionsToAdd.add(invocation);
|
||||||
|
|
||||||
|
block.getInstructions().addAll(0, instructionsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStackRelease(Program program, int maxDepth) {
|
||||||
|
List<BasicBlock> blocks = new ArrayList<>();
|
||||||
|
boolean hasResult = false;
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
Instruction instruction = block.getLastInstruction();
|
||||||
|
if (instruction instanceof ExitInstruction) {
|
||||||
|
blocks.add(block);
|
||||||
|
if (((ExitInstruction) instruction).getValueToReturn() != null) {
|
||||||
|
hasResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicBlock exitBlock;
|
||||||
|
if (blocks.size() == 1) {
|
||||||
|
exitBlock = blocks.get(0);
|
||||||
|
} else {
|
||||||
|
exitBlock = program.createBasicBlock();
|
||||||
|
ExitInstruction exit = new ExitInstruction();
|
||||||
|
exitBlock.getInstructions().add(exit);
|
||||||
|
|
||||||
|
if (hasResult) {
|
||||||
|
Phi phi = new Phi();
|
||||||
|
phi.setReceiver(program.createVariable());
|
||||||
|
exitBlock.getPhis().add(phi);
|
||||||
|
exit.setValueToReturn(phi.getReceiver());
|
||||||
|
|
||||||
|
for (BasicBlock block : blocks) {
|
||||||
|
ExitInstruction oldExit = (ExitInstruction) block.getLastInstruction();
|
||||||
|
Incoming incoming = new Incoming();
|
||||||
|
incoming.setSource(block);
|
||||||
|
incoming.setValue(oldExit.getValueToReturn());
|
||||||
|
phi.getIncomings().add(incoming);
|
||||||
|
|
||||||
|
JumpInstruction jumpToExit = new JumpInstruction();
|
||||||
|
jumpToExit.setTarget(exitBlock);
|
||||||
|
jumpToExit.setLocation(oldExit.getLocation());
|
||||||
|
|
||||||
|
block.getInstructions().set(block.getInstructions().size() - 1, jumpToExit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
|
Variable sizeVariable = program.createVariable();
|
||||||
|
|
||||||
|
IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction();
|
||||||
|
sizeConstant.setReceiver(sizeVariable);
|
||||||
|
sizeConstant.setConstant(maxDepth);
|
||||||
|
instructionsToAdd.add(sizeConstant);
|
||||||
|
|
||||||
|
InvokeInstruction invocation = new InvokeInstruction();
|
||||||
|
invocation.setType(InvocationType.SPECIAL);
|
||||||
|
invocation.setMethod(new MethodReference(Allocator.class, "releaseStack", int.class, void.class));
|
||||||
|
invocation.getArguments().add(sizeVariable);
|
||||||
|
instructionsToAdd.add(invocation);
|
||||||
|
|
||||||
|
exitBlock.getInstructions().addAll(exitBlock.getInstructions().size() - 1, instructionsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isReference(TypeInferer typeInferer, int var) {
|
||||||
|
VariableType liveType = typeInferer.typeOf(var);
|
||||||
|
switch (liveType) {
|
||||||
|
case BYTE_ARRAY:
|
||||||
|
case CHAR_ARRAY:
|
||||||
|
case SHORT_ARRAY:
|
||||||
|
case INT_ARRAY:
|
||||||
|
case FLOAT_ARRAY:
|
||||||
|
case LONG_ARRAY:
|
||||||
|
case DOUBLE_ARRAY:
|
||||||
|
case OBJECT_ARRAY:
|
||||||
|
case OBJECT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean requiresGc(MethodReference methodReference) {
|
||||||
|
ClassReader cls = classSource.get(methodReference.getClassName());
|
||||||
|
if (cls == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (cls.getAnnotations().get(NoGC.class.getName()) != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MethodReader method = cls.getMethod(methodReference.getDescriptor());
|
||||||
|
return method.getAnnotations().get(NoGC.class.getName()) == null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ public final class Allocator {
|
||||||
public static Address allocateArray(RuntimeClass tag, int size) {
|
public static Address allocateArray(RuntimeClass tag, int size) {
|
||||||
Address result = address;
|
Address result = address;
|
||||||
int sizeInBytes = tag.itemType.size * size + Structure.sizeOf(RuntimeArray.class);
|
int sizeInBytes = tag.itemType.size * size + Structure.sizeOf(RuntimeArray.class);
|
||||||
|
sizeInBytes = Address.align(Address.fromInt(sizeInBytes), 4).toInt();
|
||||||
address = result.add(sizeInBytes);
|
address = result.add(sizeInBytes);
|
||||||
fillZero(result, sizeInBytes);
|
fillZero(result, sizeInBytes);
|
||||||
|
|
||||||
|
@ -55,4 +56,12 @@ public final class Allocator {
|
||||||
public static native void moveMemoryBlock(Address source, Address target, int count);
|
public static native void moveMemoryBlock(Address source, Address target, int count);
|
||||||
|
|
||||||
public static native boolean isInitialized(Class<?> cls);
|
public static native boolean isInitialized(Class<?> cls);
|
||||||
|
|
||||||
|
public static native void allocStack(int size);
|
||||||
|
|
||||||
|
public static native void registerGcRoot(int index, Object object);
|
||||||
|
|
||||||
|
public static native void removeGcRoot(int index);
|
||||||
|
|
||||||
|
public static native void releaseStack(int size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Function;
|
import org.teavm.interop.Function;
|
||||||
|
import org.teavm.interop.NoGC;
|
||||||
|
|
||||||
public abstract class IsSupertypeFunction extends Function {
|
public abstract class IsSupertypeFunction extends Function {
|
||||||
|
@NoGC
|
||||||
public abstract boolean apply(RuntimeClass superType);
|
public abstract boolean apply(RuntimeClass superType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.interop.NoGC;
|
||||||
|
|
||||||
public class RuntimeClass extends RuntimeJavaObject {
|
public class RuntimeClass extends RuntimeJavaObject {
|
||||||
public static final int INITIALIZED = 1;
|
public static final int INITIALIZED = 1;
|
||||||
|
@ -29,14 +30,17 @@ public class RuntimeClass extends RuntimeJavaObject {
|
||||||
public RuntimeClass arrayType;
|
public RuntimeClass arrayType;
|
||||||
public IsSupertypeFunction isSupertypeOf;
|
public IsSupertypeFunction isSupertypeOf;
|
||||||
|
|
||||||
|
@NoGC
|
||||||
public static int computeCanary(int size, int tag) {
|
public static int computeCanary(int size, int tag) {
|
||||||
return size ^ (tag << 8) ^ (tag >>> 24) ^ 0xAAAAAAAA;
|
return size ^ (tag << 8) ^ (tag >>> 24) ^ 0xAAAAAAAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NoGC
|
||||||
public int computeCanary() {
|
public int computeCanary() {
|
||||||
return computeCanary(size, tag);
|
return computeCanary(size, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NoGC
|
||||||
public static RuntimeClass getClass(RuntimeObject object) {
|
public static RuntimeClass getClass(RuntimeObject object) {
|
||||||
return Address.fromInt(object.classReference << 3).toStructure();
|
return Address.fromInt(object.classReference << 3).toStructure();
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,7 +455,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
for (String className : classSource.getClassNames()) {
|
for (String className : classSource.getClassNames()) {
|
||||||
ClassHolder cls = classSource.get(className);
|
ClassHolder cls = classSource.get(className);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
processMethod(method);
|
processMethod(method, classSource);
|
||||||
}
|
}
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
|
@ -463,7 +463,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMethod(MethodHolder method) {
|
private void processMethod(MethodHolder method, ListableClassReaderSource classSource) {
|
||||||
if (method.getProgram() == null) {
|
if (method.getProgram() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -482,6 +482,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
} while (changed);
|
} while (changed);
|
||||||
|
|
||||||
|
target.afterOptimizations(optimizedProgram, method, classSource);
|
||||||
if (target.requiresRegisterAllocation()) {
|
if (target.requiresRegisterAllocation()) {
|
||||||
RegisterAllocator allocator = new RegisterAllocator();
|
RegisterAllocator allocator = new RegisterAllocator();
|
||||||
allocator.allocateRegisters(method, optimizedProgram);
|
allocator.allocateRegisters(method, optimizedProgram);
|
||||||
|
|
|
@ -21,6 +21,9 @@ import org.teavm.dependency.DependencyChecker;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.Program;
|
||||||
import org.teavm.vm.spi.TeaVMHostExtension;
|
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||||
|
|
||||||
public interface TeaVMTarget {
|
public interface TeaVMTarget {
|
||||||
|
@ -36,5 +39,7 @@ public interface TeaVMTarget {
|
||||||
|
|
||||||
void contributeDependencies(DependencyChecker dependencyChecker);
|
void contributeDependencies(DependencyChecker dependencyChecker);
|
||||||
|
|
||||||
|
void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource);
|
||||||
|
|
||||||
void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;
|
void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.interop;
|
package org.teavm.interop;
|
||||||
|
|
||||||
@StaticInit
|
@StaticInit
|
||||||
|
@NoGC
|
||||||
public final class Address {
|
public final class Address {
|
||||||
public native Address add(int offset);
|
public native Address add(int offset);
|
||||||
|
|
||||||
|
|
26
interop/core/src/main/java/org/teavm/interop/NoGC.java
Normal file
26
interop/core/src/main/java/org/teavm/interop/NoGC.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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.interop;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
|
public @interface NoGC {
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.interop;
|
package org.teavm.interop;
|
||||||
|
|
||||||
|
@NoGC
|
||||||
public class Structure {
|
public class Structure {
|
||||||
public final native <T extends Structure> T cast();
|
public final native <T extends Structure> T cast();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user