Add transformation passes that maintain GC roots on shadow stack. Add support of shadow stack intrinsics

This commit is contained in:
Alexey Andreev 2016-09-08 13:56:58 +03:00
parent bfc37b10b5
commit 476086af47
15 changed files with 516 additions and 42 deletions

View File

@ -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()) {

View File

@ -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;
}
} }

View File

@ -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<>();

View File

@ -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);

View File

@ -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) {

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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();
} }

View File

@ -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);

View File

@ -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;
} }

View File

@ -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);

View 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 {
}

View File

@ -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();