mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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.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<ClassNode> clsNodes = modelToAst(classes);
|
||||
if (controller.wasCancelled()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("<clinit>", 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("<clinit>")) {
|
||||
return false;
|
||||
}
|
||||
if (method.getName().equals("<init>")) {
|
||||
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<MethodReference> virtualMethods = new HashSet<>();
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
|
||||
private Set<WasmBlock> usedBlocks = new HashSet<>();
|
||||
private List<Deque<WasmLocal>> 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);
|
||||
|
|
|
@ -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("<clinit>")) {
|
||||
return false;
|
||||
}
|
||||
if (method.getName().equals("<init>")) {
|
||||
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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.interop;
|
||||
|
||||
@StaticInit
|
||||
@NoGC
|
||||
public final class Address {
|
||||
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;
|
||||
|
||||
@NoGC
|
||||
public class Structure {
|
||||
public final native <T extends Structure> T cast();
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user