diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index c9c183601..6e14df993 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -52,7 +52,9 @@ import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ElementModifier; import org.teavm.model.ListableClassHolderSource; +import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.Program; 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) { List clsNodes = modelToAst(classes); if (controller.wasCancelled()) { diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index 5ac7ae698..adca7bc6f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -17,11 +17,19 @@ package org.teavm.backend.wasm; import org.teavm.interop.Address; import org.teavm.interop.Import; +import org.teavm.interop.NoGC; +import org.teavm.interop.StaticInit; +@StaticInit +@NoGC public final class WasmRuntime { + public static Address stack = initStack(); + private WasmRuntime() { } + private static native Address initStack(); + public static int compare(int a, int b) { 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; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 88e5e7bca..ebf929013 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -98,8 +98,12 @@ import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.InitClassInstruction; import org.teavm.model.instructions.InvocationType; 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.RuntimeArray; import org.teavm.runtime.RuntimeClass; @@ -116,10 +120,17 @@ public class WasmTarget implements TeaVMTarget { private boolean debugging; private boolean wastEmitted; private boolean cEmitted; + private ClassInitializerEliminator classInitializerEliminator; + private ClassInitializerTransformer classInitializerTransformer; + private GcRootMaintainingTransformer gcRootMaintainingTransformer; + private MethodDescriptor clinitDescriptor = new MethodDescriptor("", void.class); @Override public void setController(TeaVMTargetController controller) { this.controller = controller; + classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); + classInitializerTransformer = new ClassInitializerTransformer(); + gcRootMaintainingTransformer = new GcRootMaintainingTransformer(controller.getUnprocessedClassSource()); } @Override @@ -188,6 +199,8 @@ public class WasmTarget implements TeaVMTarget { void.class), null).use(); dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "moveMemoryBlock", Address.class, 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", 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("")) { + return false; + } + if (method.getName().equals("")) { + return true; + } + return method.hasModifier(ElementModifier.STATIC); + } + @Override public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException { @@ -238,7 +277,8 @@ public class WasmTarget implements TeaVMTarget { context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator)); context.addIntrinsic(new ClassIntrinsic()); - WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator); + WasmGenerator generator = new WasmGenerator(decompiler, classes, + context, classGenerator); module.setMemorySize(64); generateMethods(classes, context, generator, module); @@ -250,7 +290,8 @@ public class WasmTarget implements TeaVMTarget { dataSegment.setOffset(256); module.getSegments().add(dataSegment); - renderAllocatorInit(module, binaryWriter.getAddress()); + int address = renderStackInit(module, binaryWriter.getAddress()); + renderAllocatorInit(module, address); renderClinit(classes, classGenerator, module); if (controller.wasCancelled()) { return; @@ -333,6 +374,10 @@ public class WasmTarget implements TeaVMTarget { && method.getName().equals("initialize")) { continue; } + if (method.getOwnerName().equals(WasmRuntime.class.getName()) + && method.getName().equals("initStack")) { + continue; + } if (context.getIntrinsic(method.getReference()) != null) { continue; } @@ -555,8 +600,6 @@ public class WasmTarget implements TeaVMTarget { } private void renderAllocatorInit(WasmModule module, int address) { - address = (((address - 1) / 4096) + 1) * 4096; - WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference( Allocator.class, "initialize", Address.class))); function.setResult(WasmType.INT32); @@ -564,6 +607,18 @@ public class WasmTarget implements TeaVMTarget { 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) { Set virtualMethods = new HashSet<>(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 8dcbab07e..9a7898349 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -129,6 +129,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private Map continueTargets = new HashMap<>(); private Set usedBlocks = new HashSet<>(); private List> temporaryVariablesByType = new ArrayList<>(); + private WasmLocal stackVariable; WasmExpression result; WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, @@ -780,6 +781,23 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override 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()); if (intrinsic != null) { 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 public void visit(BlockStatement statement) { WasmBlock block = new WasmBlock(false); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index 1de814e8d..0aac90820 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -21,17 +21,12 @@ import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; -import org.teavm.model.BasicBlock; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderSource; import org.teavm.model.ElementModifier; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; -import org.teavm.model.Program; 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 { private Decompiler decompiler; @@ -39,8 +34,8 @@ public class WasmGenerator { private WasmGenerationContext context; private WasmClassGenerator classGenerator; - public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, WasmGenerationContext context, - WasmClassGenerator classGenerator) { + public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, + WasmGenerationContext context, WasmClassGenerator classGenerator) { this.decompiler = decompiler; this.classSource = classSource; this.context = context; @@ -50,17 +45,6 @@ public class WasmGenerator { public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) { ClassHolder cls = classSource.get(methodReference.getClassName()); 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); WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); @@ -88,16 +72,6 @@ public class WasmGenerator { return function; } - private static boolean needsClinitCall(MethodHolder method) { - if (method.getName().equals("")) { - return false; - } - if (method.getName().equals("")) { - return true; - } - return method.hasModifier(ElementModifier.STATIC); - } - public WasmFunction generateNative(MethodReference methodReference) { WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); for (int i = 0; i < methodReference.parameterCount(); ++i) { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java index 16fadfa39..ced624728 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java @@ -16,7 +16,6 @@ package org.teavm.backend.wasm.intrinsics; import java.util.stream.Collectors; -import org.teavm.ast.ConstantExpr; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.WasmRuntime; 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.model.FieldReference; import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; import org.teavm.runtime.Allocator; import org.teavm.runtime.RuntimeClass; public class AllocatorIntrinsic implements WasmIntrinsic { private static final FieldReference flagsField = new FieldReference(RuntimeClass.class.getName(), "flags"); - private WasmClassGenerator classGenerator; private int flagsFieldOffset; public AllocatorIntrinsic(WasmClassGenerator classGenerator) { - this.classGenerator = classGenerator; flagsFieldOffset = classGenerator.getFieldOffset(flagsField); } @@ -74,10 +70,14 @@ public class AllocatorIntrinsic implements WasmIntrinsic { return call; } case "isInitialized": { - ConstantExpr argument = (ConstantExpr) invocation.getArguments().get(0); - ValueType type = (ValueType) argument.getValue(); - int pointer = classGenerator.getClassPointer(type) + flagsFieldOffset; - WasmExpression flags = new WasmLoadInt32(4, new WasmInt32Constant(pointer), WasmInt32Subtype.INT32); + WasmExpression pointer = manager.generate(invocation.getArguments().get(0)); + if (pointer instanceof WasmInt32Constant) { + pointer = new WasmInt32Constant(((WasmInt32Constant) pointer).getValue() + flagsFieldOffset); + } 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, new WasmInt32Constant(RuntimeClass.INITIALIZED)); return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, flag, new WasmInt32Constant(0)); diff --git a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java new file mode 100644 index 000000000..a2f67eb4b --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java @@ -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> liveInInformation = findCallSiteLiveIns(program, method); + int maxDepth = putLiveInGcRoots(program, liveInInformation); + if (maxDepth > 0) { + addStackAllocation(program, maxDepth); + addStackRelease(program, maxDepth); + } + } + + private List> findCallSiteLiveIns(Program program, MethodReader method) { + Graph cfg = ProgramUtils.buildControlFlowGraph(program); + TypeInferer typeInferer = new TypeInferer(); + typeInferer.inferTypes(program, method.getReference()); + List> 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 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> liveInInformation) { + int maxDepth = 0; + for (IntObjectMap liveInsMap : liveInInformation) { + for (ObjectCursor liveIns : liveInsMap.values()) { + maxDepth = Math.max(maxDepth, liveIns.value.cardinality()); + } + } + + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + List instructions = block.getInstructions(); + IntObjectMap 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 instructions = block.getInstructions(); + Instruction callInstruction = instructions.get(index); + List 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 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 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 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; + } +} diff --git a/core/src/main/java/org/teavm/runtime/Allocator.java b/core/src/main/java/org/teavm/runtime/Allocator.java index 251045878..19d485328 100644 --- a/core/src/main/java/org/teavm/runtime/Allocator.java +++ b/core/src/main/java/org/teavm/runtime/Allocator.java @@ -40,6 +40,7 @@ public final class Allocator { public static Address allocateArray(RuntimeClass tag, int size) { Address result = address; int sizeInBytes = tag.itemType.size * size + Structure.sizeOf(RuntimeArray.class); + sizeInBytes = Address.align(Address.fromInt(sizeInBytes), 4).toInt(); address = result.add(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 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); } diff --git a/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java b/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java index 748f286c8..0981ffe37 100644 --- a/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java +++ b/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java @@ -16,7 +16,9 @@ package org.teavm.runtime; import org.teavm.interop.Function; +import org.teavm.interop.NoGC; public abstract class IsSupertypeFunction extends Function { + @NoGC public abstract boolean apply(RuntimeClass superType); } diff --git a/core/src/main/java/org/teavm/runtime/RuntimeClass.java b/core/src/main/java/org/teavm/runtime/RuntimeClass.java index 7c638324d..d31b086ae 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeClass.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeClass.java @@ -16,6 +16,7 @@ package org.teavm.runtime; import org.teavm.interop.Address; +import org.teavm.interop.NoGC; public class RuntimeClass extends RuntimeJavaObject { public static final int INITIALIZED = 1; @@ -29,14 +30,17 @@ public class RuntimeClass extends RuntimeJavaObject { public RuntimeClass arrayType; public IsSupertypeFunction isSupertypeOf; + @NoGC public static int computeCanary(int size, int tag) { return size ^ (tag << 8) ^ (tag >>> 24) ^ 0xAAAAAAAA; } + @NoGC public int computeCanary() { return computeCanary(size, tag); } + @NoGC public static RuntimeClass getClass(RuntimeObject object) { return Address.fromInt(object.classReference << 3).toStructure(); } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 15bc01776..900d09b29 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -455,7 +455,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { for (String className : classSource.getClassNames()) { ClassHolder cls = classSource.get(className); for (MethodHolder method : cls.getMethods()) { - processMethod(method); + processMethod(method, classSource); } if (wasCancelled()) { 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) { return; } @@ -482,6 +482,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } } while (changed); + target.afterOptimizations(optimizedProgram, method, classSource); if (target.requiresRegisterAllocation()) { RegisterAllocator allocator = new RegisterAllocator(); allocator.allocateRegisters(method, optimizedProgram); diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index 04df7b096..0a3188eab 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -21,6 +21,9 @@ import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyListener; import org.teavm.model.ClassHolderTransformer; 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; public interface TeaVMTarget { @@ -36,5 +39,7 @@ public interface TeaVMTarget { void contributeDependencies(DependencyChecker dependencyChecker); + void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource); + void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException; } diff --git a/interop/core/src/main/java/org/teavm/interop/Address.java b/interop/core/src/main/java/org/teavm/interop/Address.java index af8cfede5..04347b1af 100644 --- a/interop/core/src/main/java/org/teavm/interop/Address.java +++ b/interop/core/src/main/java/org/teavm/interop/Address.java @@ -16,6 +16,7 @@ package org.teavm.interop; @StaticInit +@NoGC public final class Address { public native Address add(int offset); diff --git a/interop/core/src/main/java/org/teavm/interop/NoGC.java b/interop/core/src/main/java/org/teavm/interop/NoGC.java new file mode 100644 index 000000000..2dbd4ea69 --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/NoGC.java @@ -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 { +} diff --git a/interop/core/src/main/java/org/teavm/interop/Structure.java b/interop/core/src/main/java/org/teavm/interop/Structure.java index 5ec2b71bb..41c527077 100644 --- a/interop/core/src/main/java/org/teavm/interop/Structure.java +++ b/interop/core/src/main/java/org/teavm/interop/Structure.java @@ -15,6 +15,7 @@ */ package org.teavm.interop; +@NoGC public class Structure { public final native T cast();