WASM: add transformation that converts exception handling to explicit guard checks

This commit is contained in:
Alexey Andreev 2016-09-22 16:27:28 +03:00
parent 8af1e3e66d
commit b67d243ad4
26 changed files with 894 additions and 220 deletions

View File

@ -15,17 +15,16 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.java.io.TConsole; import org.teavm.classlib.java.io.TConsole;
import org.teavm.classlib.java.io.TInputStream; import org.teavm.classlib.java.io.TInputStream;
import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.io.TPrintStream;
import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.classlib.java.lang.reflect.TArray;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoGC; import org.teavm.interop.Unmanaged;
import org.teavm.interop.Structure;
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;
@ -81,7 +80,7 @@ public final class TSystem extends TObject {
@DelegateTo("doArrayCopyLowLevel") @DelegateTo("doArrayCopyLowLevel")
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length); private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
@NoGC @Unmanaged
static void doArrayCopyLowLevel(RuntimeArray src, int srcPos, RuntimeArray dest, int destPos, int length) { static void doArrayCopyLowLevel(RuntimeArray src, int srcPos, RuntimeArray dest, int destPos, int length) {
RuntimeClass type = RuntimeClass.getClass(src); RuntimeClass type = RuntimeClass.getClass(src);
int itemSize = type.itemType.size; int itemSize = type.itemType.size;
@ -99,7 +98,7 @@ public final class TSystem extends TObject {
} }
@GeneratedBy(SystemNativeGenerator.class) @GeneratedBy(SystemNativeGenerator.class)
@DelegateTo(("currentTimeMillisLowLevel")) @DelegateTo("currentTimeMillisLowLevel")
public static native long currentTimeMillis(); public static native long currentTimeMillis();
private static long currentTimeMillisLowLevel() { private static long currentTimeMillisLowLevel() {

View File

@ -15,11 +15,16 @@
*/ */
package org.teavm.classlib.java.lang.reflect; package org.teavm.classlib.java.lang.reflect;
import org.teavm.classlib.java.lang.*; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.java.lang.TArrayIndexOutOfBoundsException;
import org.teavm.classlib.java.lang.TClass;
import org.teavm.classlib.java.lang.TIllegalArgumentException;
import org.teavm.classlib.java.lang.TNegativeArraySizeException;
import org.teavm.classlib.java.lang.TNullPointerException;
import org.teavm.classlib.java.lang.TObject;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.DelegateTo; import org.teavm.interop.DelegateTo;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.interop.Unmanaged;
import org.teavm.interop.NoGC;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeArray;
@ -61,7 +66,7 @@ public final class TArray extends TObject {
private static native TObject newInstanceImpl(PlatformClass componentType, int length); private static native TObject newInstanceImpl(PlatformClass componentType, int length);
@SuppressWarnings("unused") @SuppressWarnings("unused")
@NoGC @Unmanaged
private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) { private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) {
return Allocator.allocateArray(cls.arrayType, length).toStructure(); return Allocator.allocateArray(cls.arrayType, length).toStructure();
} }

View File

@ -17,11 +17,11 @@ 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; import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged;
@StaticInit @StaticInit
@NoGC @Unmanaged
public final class WasmRuntime { public final class WasmRuntime {
public static Address stack = initStack(); public static Address stack = initStack();
@ -245,7 +245,7 @@ public final class WasmRuntime {
public static Address allocStack(int size) { public static Address allocStack(int size) {
Address result = stack.add(4); Address result = stack.add(4);
stack = result.add(size << 2); stack = result.add((size << 2) + 4);
stack.putInt(size); stack.putInt(size);
return result; return result;
} }
@ -255,7 +255,7 @@ public final class WasmRuntime {
} }
public static Address getNextStackRoots(Address address) { public static Address getNextStackRoots(Address address) {
int size = address.getInt() + 1; int size = address.getInt() + 2;
Address result = address.add(-size * 4); Address result = address.add(-size * 4);
if (result == initStack()) { if (result == initStack()) {
result = null; result = null;

View File

@ -38,6 +38,7 @@ import org.teavm.backend.wasm.generate.WasmStringPool;
import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic; import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic;
import org.teavm.backend.wasm.intrinsics.ClassIntrinsic; import org.teavm.backend.wasm.intrinsics.ClassIntrinsic;
import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic;
import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic; import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
import org.teavm.backend.wasm.intrinsics.GCIntrinsic; import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
@ -107,7 +108,7 @@ import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.MutatorIntrinsic; import org.teavm.model.instructions.MutatorIntrinsic;
import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ClassInitializerTransformer;
import org.teavm.model.lowlevel.GcRootMaintainingTransformer; import org.teavm.model.lowlevel.ShadowStackTransformer;
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;
@ -126,7 +127,7 @@ public class WasmTarget implements TeaVMTarget {
private boolean cEmitted; private boolean cEmitted;
private ClassInitializerEliminator classInitializerEliminator; private ClassInitializerEliminator classInitializerEliminator;
private ClassInitializerTransformer classInitializerTransformer; private ClassInitializerTransformer classInitializerTransformer;
private GcRootMaintainingTransformer gcRootMaintainingTransformer; private ShadowStackTransformer shadowStackTransformer;
private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class); private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
@Override @Override
@ -134,7 +135,7 @@ public class WasmTarget implements TeaVMTarget {
this.controller = controller; this.controller = controller;
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
classInitializerTransformer = new ClassInitializerTransformer(); classInitializerTransformer = new ClassInitializerTransformer();
gcRootMaintainingTransformer = new GcRootMaintainingTransformer(controller.getUnprocessedClassSource()); shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource());
} }
@Override @Override
@ -248,7 +249,7 @@ public class WasmTarget implements TeaVMTarget {
classInitializerEliminator.apply(program); classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program); classInitializerTransformer.transform(program);
gcRootMaintainingTransformer.apply(program, method); shadowStackTransformer.apply(program, method);
} }
private static boolean needsClinitCall(MethodReader method) { private static boolean needsClinitCall(MethodReader method) {
@ -293,6 +294,7 @@ public class WasmTarget implements TeaVMTarget {
context.addIntrinsic(gcIntrinsic); context.addIntrinsic(gcIntrinsic);
MutatorIntrinsic mutatorIntrinsic = new MutatorIntrinsic(); MutatorIntrinsic mutatorIntrinsic = new MutatorIntrinsic();
context.addIntrinsic(mutatorIntrinsic); context.addIntrinsic(mutatorIntrinsic);
context.addIntrinsic(new ExceptionHandlingIntrinsic());
WasmGenerator generator = new WasmGenerator(decompiler, classes, WasmGenerator generator = new WasmGenerator(decompiler, classes,
context, classGenerator); context, classGenerator);
@ -326,12 +328,13 @@ public class WasmTarget implements TeaVMTarget {
initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className))); initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className)));
} }
module.add(initFunction); module.add(initFunction);
module.setStartFunction(initFunction); //module.setStartFunction(initFunction);
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) { for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
String mangledName = WasmMangling.mangleMethod(entryPoint.getReference()); String mangledName = WasmMangling.mangleMethod(entryPoint.getReference());
WasmFunction function = module.getFunctions().get(mangledName); WasmFunction function = module.getFunctions().get(mangledName);
if (function != null) { if (function != null) {
function.getBody().add(0, new WasmCall(initFunction.getName()));
function.setExportName(entryPoint.getPublicName()); function.setExportName(entryPoint.getPublicName());
} }
} }

View File

@ -113,6 +113,7 @@ import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableEntry; import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.ExceptionHandling;
import org.teavm.runtime.Mutator; import org.teavm.runtime.Mutator;
import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
@ -797,6 +798,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
generateRemoveGcRoot(expr.getArguments().get(0)); generateRemoveGcRoot(expr.getArguments().get(0));
return; return;
} }
} else if (expr.getMethod().getClassName().equals(ExceptionHandling.class.getName())) {
switch (expr.getMethod().getName()) {
case "registerCallSite":
generateRegisterCallSite(expr.getArguments().get(0));
return;
case "getHandlerId":
generateGetHandlerId();
return;
}
} }
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod()); WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
@ -912,6 +922,27 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
result = new WasmStoreInt32(4, new WasmInt32Constant(offset), oldValue, WasmInt32Subtype.INT32); result = new WasmStoreInt32(4, new WasmInt32Constant(offset), oldValue, WasmInt32Subtype.INT32);
} }
private void generateRegisterCallSite(Expr callSiteExpr) {
if (stackVariable == null) {
throw new IllegalStateException("Call to ExceptionHandling.registerCallSite must be dominated by "
+ "Mutator.allocStack");
}
callSiteExpr.acceptVisitor(this);
WasmExpression callSite = result;
result = new WasmStoreInt32(4, new WasmGetLocal(stackVariable), callSite, WasmInt32Subtype.INT32);
}
private void generateGetHandlerId() {
if (stackVariable == null) {
throw new IllegalStateException("Call to ExceptionHandling.getHandlerId must be dominated by "
+ "Mutator.allocStack");
}
result = new WasmLoadInt32(4, new WasmGetLocal(stackVariable), WasmInt32Subtype.INT32);
}
private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) { private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) {
if (stackVariable == null) { if (stackVariable == null) {
throw new IllegalStateException("Call to Mutator.registerGcRoot must be dominated by " throw new IllegalStateException("Call to Mutator.registerGcRoot must be dominated by "
@ -946,9 +977,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmExpression getSlotOffset(WasmExpression slot) { private WasmExpression getSlotOffset(WasmExpression slot) {
if (slot instanceof WasmInt32Constant) { if (slot instanceof WasmInt32Constant) {
int slotConstant = ((WasmInt32Constant) slot).getValue(); int slotConstant = ((WasmInt32Constant) slot).getValue();
return new WasmInt32Constant(slotConstant << 2); return new WasmInt32Constant((slotConstant << 2) + 4);
} else { } else {
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, slot, new WasmInt32Constant(2)); slot = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, slot, new WasmInt32Constant(2));
slot = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, slot, new WasmInt32Constant(4));
return slot;
} }
} }

View File

@ -0,0 +1,48 @@
/*
* 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.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.model.MethodReference;
import org.teavm.runtime.ExceptionHandling;
public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
@Override
public boolean isApplicable(MethodReference methodReference) {
if (!methodReference.getClassName().equals(ExceptionHandling.class.getName())) {
return false;
}
switch (methodReference.getName()) {
case "registerCallSite":
case "getHandlerId":
return true;
}
return false;
}
@Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) {
case "registerCallSite":
break;
}
throw new IllegalArgumentException(invocation.getMethod().toString());
}
}

View File

@ -20,6 +20,9 @@ import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
@ -46,6 +49,19 @@ public class StructureIntrinsic implements WasmIntrinsic {
ValueType.Object type = (ValueType.Object) ((ConstantExpr) invocation.getArguments().get(0)).getValue(); ValueType.Object type = (ValueType.Object) ((ConstantExpr) invocation.getArguments().get(0)).getValue();
return new WasmInt32Constant(classGenerator.getClassSize(type.getClassName())); return new WasmInt32Constant(classGenerator.getClassSize(type.getClassName()));
} }
case "add": {
WasmExpression base = manager.generate(invocation.getArguments().get(1));
WasmExpression offset = manager.generate(invocation.getArguments().get(2));
Object type = ((ConstantExpr) invocation.getArguments().get(0)).getValue();
String className = ((ValueType.Object) type).getClassName();
int size = classGenerator.getClassSize(className);
int alignment = classGenerator.getClassAlignment(className);
size = WasmClassGenerator.align(size, alignment);
offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL, offset,
new WasmInt32Constant(size));
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset);
}
default: default:
throw new IllegalArgumentException(invocation.getMethod().toString()); throw new IllegalArgumentException(invocation.getMethod().toString());
} }

View File

@ -0,0 +1,36 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
public class CallSiteDescriptor {
private int id;
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>();
public CallSiteDescriptor(int id) {
this.id = id;
}
public int getId() {
return id;
}
public List<ExceptionHandlerDescriptor> getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,30 @@
/*
* 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;
public class ExceptionHandlerDescriptor {
private int id;
private String className;
public ExceptionHandlerDescriptor(int id, String className) {
this.id = id;
this.className = className;
}
public int getId() {
return id;
}
}

View File

@ -0,0 +1,335 @@
/*
* 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.common.DominatorTree;
import org.teavm.common.Graph;
import org.teavm.common.GraphUtils;
import org.teavm.model.BasicBlock;
import org.teavm.model.Incoming;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.TextLocation;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.TryCatchJoint;
import org.teavm.model.ValueType;
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.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
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.LongConstantInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.instructions.SwitchTableEntry;
import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.ProgramUtils;
import org.teavm.runtime.ExceptionHandling;
public class ExceptionHandlingShadowStackContributor {
private ManagedMethodRepository managedMethodRepository;
private List<CallSiteDescriptor> callSites;
private BasicBlock defaultExceptionHandler;
private MethodReference method;
private Program program;
private DominatorTree dom;
private BasicBlock[] variableDefinitionPlaces;
private Phi[] jointPhis;
public ExceptionHandlingShadowStackContributor(ManagedMethodRepository managedMethodRepository,
List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
this.managedMethodRepository = managedMethodRepository;
this.callSites = callSites;
this.method = method;
this.program = program;
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
dom = GraphUtils.buildDominatorTree(cfg);
variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program);
jointPhis = new Phi[program.variableCount()];
}
public boolean contribute() {
boolean hasExceptionHandlers = false;
int[] blockMapping = new int[program.basicBlockCount()];
for (int i = 0; i < blockMapping.length; ++i) {
blockMapping[i] = i;
}
int blockCount = program.basicBlockCount();
for (int i = 0; i < blockCount; ++i) {
BasicBlock block = program.basicBlockAt(i);
int newIndex = contributeToBasicBlock(block);
if (newIndex != i) {
blockMapping[i] = newIndex;
hasExceptionHandlers = true;
}
}
for (int i = 0; i < blockCount; ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
int mappedSource = blockMapping[incoming.getSource().getIndex()];
incoming.setSource(program.basicBlockAt(mappedSource));
}
}
}
return hasExceptionHandlers;
}
private int contributeToBasicBlock(BasicBlock block) {
List<Instruction> instructions = block.getInstructions();
int[] currentJointSources = new int[program.variableCount()];
int[] jointReceiverMap = new int[program.variableCount()];
Arrays.fill(currentJointSources, -1);
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
for (TryCatchJoint joint : tryCatch.getJoints()) {
for (Variable sourceVar : joint.getSourceVariables()) {
BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()];
if (dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())) {
currentJointSources[joint.getReceiver().getIndex()] = sourceVar.getIndex();
break;
}
}
for (Variable sourceVar : joint.getSourceVariables()) {
jointReceiverMap[sourceVar.getIndex()] = joint.getReceiver().getIndex();
}
}
}
DefinitionExtractor defExtractor = new DefinitionExtractor();
for (int i = 0; i < instructions.size(); ++i) {
Instruction insn = instructions.get(i);
if (isCallInstruction(insn)) {
BasicBlock next = program.createBasicBlock();
next.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
block.getTryCatchBlocks().clear();
List<Instruction> remainingInstructions = instructions.subList(i + 1, instructions.size());
List<Instruction> instructionsToMove = new ArrayList<>(remainingInstructions);
remainingInstructions.clear();
next.getInstructions().addAll(instructionsToMove);
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size());
callSites.add(callSite);
List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
List<Instruction> post = setLocation(
getInstructionsAfterCallSite(block, next, callSite, currentJointSources),
insn.getLocation());
instructions.addAll(instructions.size() - 1, pre);
instructions.addAll(post);
block = next;
instructions = block.getInstructions();
i = 0;
} else if (insn instanceof RaiseInstruction) {
InvokeInstruction raise = new InvokeInstruction();
raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class,
void.class));
raise.setType(InvocationType.SPECIAL);
raise.getArguments().add(((RaiseInstruction) insn).getException());
raise.setLocation(insn.getLocation());
instructions.add(i++, raise);
}
insn.acceptVisitor(defExtractor);
for (Variable definedVar : defExtractor.getDefinedVariables()) {
int jointReceiver = jointReceiverMap[definedVar.getIndex()];
currentJointSources[jointReceiver] = definedVar.getIndex();
}
}
block.getTryCatchBlocks().clear();
return block.getIndex();
}
private boolean isCallInstruction(Instruction insn) {
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|| insn instanceof ConstructArrayInstruction || insn instanceof CloneArrayInstruction) {
return true;
} else if (insn instanceof InvokeInstruction) {
return managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod());
}
return false;
}
private List<Instruction> getInstructionsBeforeCallSite(CallSiteDescriptor callSite) {
List<Instruction> instructions = new ArrayList<>();
Variable idVariable = program.createVariable();
IntegerConstantInstruction idInsn = new IntegerConstantInstruction();
idInsn.setConstant(callSite.getId());
idInsn.setReceiver(idVariable);
instructions.add(idInsn);
InvokeInstruction registerInsn = new InvokeInstruction();
registerInsn.setMethod(new MethodReference(ExceptionHandling.class, "registerCallSite",
int.class, void.class));
registerInsn.setType(InvocationType.SPECIAL);
registerInsn.getArguments().add(idVariable);
instructions.add(registerInsn);
return instructions;
}
private List<Instruction> setLocation(List<Instruction> instructions, TextLocation location) {
if (location != null) {
for (Instruction instruction : instructions) {
instruction.setLocation(location);
}
}
return instructions;
}
private List<Instruction> getInstructionsAfterCallSite(BasicBlock block, BasicBlock next,
CallSiteDescriptor callSite, int[] currentJointSources) {
Program program = block.getProgram();
List<Instruction> instructions = new ArrayList<>();
Variable handlerIdVariable = program.createVariable();
InvokeInstruction getHandlerIdInsn = new InvokeInstruction();
getHandlerIdInsn.setMethod(new MethodReference(ExceptionHandling.class, "getHandlerId", int.class));
getHandlerIdInsn.setType(InvocationType.SPECIAL);
getHandlerIdInsn.setReceiver(handlerIdVariable);
instructions.add(getHandlerIdInsn);
SwitchInstruction switchInsn = new SwitchInstruction();
switchInsn.setCondition(handlerIdVariable);
SwitchTableEntry continueExecutionEntry = new SwitchTableEntry();
continueExecutionEntry.setCondition(callSite.getId());
continueExecutionEntry.setTarget(next);
switchInsn.getEntries().add(continueExecutionEntry);
instructions.add(switchInsn);
boolean defaultExists = false;
int nextHandlerId = callSite.getId();
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
if (tryCatch.getExceptionType() == null) {
defaultExists = true;
switchInsn.setDefaultTarget(tryCatch.getHandler());
} else {
ExceptionHandlerDescriptor handler = new ExceptionHandlerDescriptor(++nextHandlerId,
tryCatch.getExceptionType());
callSite.getHandlers().add(handler);
SwitchTableEntry catchEntry = new SwitchTableEntry();
catchEntry.setTarget(tryCatch.getHandler());
catchEntry.setCondition(handler.getId());
switchInsn.getEntries().add(catchEntry);
}
for (TryCatchJoint joint : tryCatch.getJoints()) {
Phi phi = getJointPhi(joint);
Incoming incoming = new Incoming();
incoming.setSource(block);
int value = currentJointSources[joint.getReceiver().getIndex()];
incoming.setValue(program.variableAt(value));
phi.getIncomings().add(incoming);
}
}
if (!defaultExists) {
switchInsn.setDefaultTarget(getDefaultExceptionHandler());
}
return instructions;
}
private BasicBlock getDefaultExceptionHandler() {
if (defaultExceptionHandler == null) {
defaultExceptionHandler = program.createBasicBlock();
Variable result = createReturnValueInstructions(defaultExceptionHandler.getInstructions());
ExitInstruction exit = new ExitInstruction();
exit.setValueToReturn(result);
defaultExceptionHandler.getInstructions().add(exit);
}
return defaultExceptionHandler;
}
private Phi getJointPhi(TryCatchJoint joint) {
Phi phi = jointPhis[joint.getReceiver().getIndex()];
if (phi == null) {
phi = new Phi();
phi.setReceiver(joint.getReceiver());
BasicBlock handler = program.basicBlockAt(joint.getBlock().getHandler().getIndex());
handler.getPhis().add(phi);
jointPhis[joint.getReceiver().getIndex()] = phi;
}
return phi;
}
private Variable createReturnValueInstructions(List<Instruction> instructions) {
ValueType returnType = method.getReturnType();
if (returnType == ValueType.VOID) {
return null;
}
Variable variable = program.createVariable();
if (returnType instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) returnType).getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case CHARACTER:
case INTEGER:
IntegerConstantInstruction intConstant = new IntegerConstantInstruction();
intConstant.setReceiver(variable);
instructions.add(intConstant);
return variable;
case LONG:
LongConstantInstruction longConstant = new LongConstantInstruction();
longConstant.setReceiver(variable);
instructions.add(longConstant);
return variable;
case FLOAT:
FloatConstantInstruction floatConstant = new FloatConstantInstruction();
floatConstant.setReceiver(variable);
instructions.add(floatConstant);
return variable;
case DOUBLE:
DoubleConstantInstruction doubleConstant = new DoubleConstantInstruction();
doubleConstant.setReceiver(variable);
instructions.add(doubleConstant);
return variable;
}
}
NullConstantInstruction nullConstant = new NullConstantInstruction();
nullConstant.setReceiver(variable);
instructions.add(nullConstant);
return variable;
}
}

View File

@ -30,10 +30,7 @@ import org.teavm.common.DominatorTree;
import org.teavm.common.Graph; import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder; import org.teavm.common.GraphBuilder;
import org.teavm.common.GraphUtils; import org.teavm.common.GraphUtils;
import org.teavm.interop.NoGC;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.Incoming; import org.teavm.model.Incoming;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
@ -44,12 +41,10 @@ import org.teavm.model.Variable;
import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction; import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction; import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.InitClassInstruction; import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.IntegerConstantInstruction;
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.instructions.JumpInstruction;
import org.teavm.model.instructions.RaiseInstruction; import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.util.DefinitionExtractor; import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.GraphColorer; import org.teavm.model.util.GraphColorer;
@ -60,17 +55,14 @@ import org.teavm.model.util.UsageExtractor;
import org.teavm.model.util.VariableType; import org.teavm.model.util.VariableType;
import org.teavm.runtime.Mutator; import org.teavm.runtime.Mutator;
public class GcRootMaintainingTransformer { public class GCShadowStackContributor {
private ClassReaderSource classSource; private ManagedMethodRepository managedMethodRepository;
public GcRootMaintainingTransformer(ClassReaderSource classSource) { public GCShadowStackContributor(ManagedMethodRepository managedMethodRepository) {
this.classSource = classSource; this.managedMethodRepository = managedMethodRepository;
} }
public void apply(Program program, MethodReader method) { public int contribute(Program program, MethodReader method) {
if (!requiresGC(method.getReference())) {
return;
}
List<IntObjectMap<BitSet>> liveInInformation = findCallSiteLiveIns(program, method); List<IntObjectMap<BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program); Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program);
@ -87,7 +79,7 @@ public class GcRootMaintainingTransformer {
} }
} }
if (usedColors == 0) { if (usedColors == 0) {
return; return 0;
} }
// If a variable is spilled to stack, then phi which input this variable also spilled to stack // If a variable is spilled to stack, then phi which input this variable also spilled to stack
@ -103,8 +95,8 @@ public class GcRootMaintainingTransformer {
List<IntObjectMap<int[]>> liveInStores = reduceGcRootStores(program, usedColors, liveInInformation, List<IntObjectMap<int[]>> liveInStores = reduceGcRootStores(program, usedColors, liveInInformation,
colors, autoSpilled); colors, autoSpilled);
putLiveInGcRoots(program, liveInStores); putLiveInGcRoots(program, liveInStores);
addStackAllocation(program, usedColors);
addStackRelease(program, usedColors); return usedColors;
} }
private void findAutoSpilledPhis(boolean[] spilled, List<Set<Phi>> destinationPhis, int[] inputCount, private void findAutoSpilledPhis(boolean[] spilled, List<Set<Phi>> destinationPhis, int[] inputCount,
@ -158,7 +150,8 @@ public class GcRootMaintainingTransformer {
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction || insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) { || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
if (insn instanceof InvokeInstruction && !requiresGC(((InvokeInstruction) insn).getMethod())) { if (insn instanceof InvokeInstruction
&& !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) {
continue; continue;
} }
@ -379,86 +372,6 @@ public class GcRootMaintainingTransformer {
instructions.addAll(index, instructionsToAdd); 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(Mutator.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(Mutator.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) { private boolean isReference(TypeInferer typeInferer, int var) {
VariableType liveType = typeInferer.typeOf(var); VariableType liveType = typeInferer.typeOf(var);
switch (liveType) { switch (liveType) {
@ -476,16 +389,4 @@ public class GcRootMaintainingTransformer {
return false; 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

@ -0,0 +1,49 @@
/*
* 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 java.util.HashMap;
import java.util.Map;
import org.teavm.interop.Unmanaged;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
public class ManagedMethodRepository {
private ClassReaderSource classSource;
private Map<MethodReference, Boolean> cache = new HashMap<>();
public ManagedMethodRepository(ClassReaderSource classSource) {
this.classSource = classSource;
}
public boolean isManaged(MethodReference methodReference) {
return cache.computeIfAbsent(methodReference, this::computeIsManaged);
}
private boolean computeIsManaged(MethodReference methodReference) {
ClassReader cls = classSource.get(methodReference.getClassName());
if (cls == null) {
return true;
}
if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) {
return false;
}
MethodReader method = cls.getMethod(methodReference.getDescriptor());
return method.getAnnotations().get(Unmanaged.class.getName()) == null;
}
}

View File

@ -0,0 +1,144 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import org.teavm.model.BasicBlock;
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.ExitInstruction;
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.runtime.Mutator;
public class ShadowStackTransformer {
private ManagedMethodRepository managedMethodRepository;
private GCShadowStackContributor gcContributor;
private List<CallSiteDescriptor> callSites = new ArrayList<>();
public ShadowStackTransformer(ClassReaderSource classSource) {
managedMethodRepository = new ManagedMethodRepository(classSource);
gcContributor = new GCShadowStackContributor(managedMethodRepository);
}
public void apply(Program program, MethodReader method) {
if (!managedMethodRepository.isManaged(method.getReference())) {
return;
}
int shadowStackSize = gcContributor.contribute(program, method);
boolean exceptions = new ExceptionHandlingShadowStackContributor(managedMethodRepository, callSites,
method.getReference(), program).contribute();
if (shadowStackSize > 0 || exceptions) {
addStackAllocation(program, shadowStackSize);
addStackRelease(program, shadowStackSize);
}
}
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(Mutator.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);
}
}
for (BasicBlock block : blocks) {
ExitInstruction oldExit = (ExitInstruction) block.getLastInstruction();
JumpInstruction jumpToExit = new JumpInstruction();
jumpToExit.setTarget(exitBlock);
jumpToExit.setLocation(oldExit.getLocation());
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(Mutator.class, "releaseStack", int.class, void.class));
invocation.getArguments().add(sizeVariable);
instructionsToAdd.add(invocation);
exitBlock.getInstructions().addAll(exitBlock.getInstructions().size() - 1, instructionsToAdd);
}
}

View File

@ -29,8 +29,6 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.TryCatchJoint;
import org.teavm.model.instructions.AssignInstruction; import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BinaryBranchingInstruction; import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BranchingInstruction; import org.teavm.model.instructions.BranchingInstruction;
@ -42,7 +40,6 @@ import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.SwitchInstruction; import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.util.BasicBlockMapper; import org.teavm.model.util.BasicBlockMapper;
import org.teavm.model.util.InstructionCopyReader;
import org.teavm.model.util.InstructionTransitionExtractor; import org.teavm.model.util.InstructionTransitionExtractor;
import org.teavm.model.util.InstructionVariableMapper; import org.teavm.model.util.InstructionVariableMapper;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
@ -82,7 +79,7 @@ public class Inlining {
List<Instruction> instructionsToMove = new ArrayList<>(movedInstructions); List<Instruction> instructionsToMove = new ArrayList<>(movedInstructions);
movedInstructions.clear(); movedInstructions.clear();
splitBlock.getInstructions().addAll(instructionsToMove); splitBlock.getInstructions().addAll(instructionsToMove);
copyTryCatchBlocks(block, splitBlock); splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
block.getInstructions().remove(block.getInstructions().size() - 1); block.getInstructions().remove(block.getInstructions().size() - 1);
if (invoke.getInstance() == null || invoke.getMethod().getName().equals("<init>")) { if (invoke.getInstance() == null || invoke.getMethod().getName().equals("<init>")) {
@ -97,7 +94,7 @@ public class Inlining {
for (int i = 0; i < inlineProgram.basicBlockCount(); ++i) { for (int i = 0; i < inlineProgram.basicBlockCount(); ++i) {
BasicBlock blockToInline = inlineProgram.basicBlockAt(i); BasicBlock blockToInline = inlineProgram.basicBlockAt(i);
BasicBlock inlineBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i); BasicBlock inlineBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i);
copyInlinedBlock(blockToInline, inlineBlock); ProgramUtils.copyBasicBlock(blockToInline, inlineBlock);
} }
BasicBlockMapper blockMapper = new BasicBlockMapper(index -> index + firstInlineBlock.getIndex()); BasicBlockMapper blockMapper = new BasicBlockMapper(index -> index + firstInlineBlock.getIndex());
@ -116,7 +113,7 @@ public class Inlining {
BasicBlock mappedBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i); BasicBlock mappedBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i);
blockMapper.transform(mappedBlock); blockMapper.transform(mappedBlock);
variableMapper.apply(mappedBlock); variableMapper.apply(mappedBlock);
copyTryCatchBlocks(block, mappedBlock); mappedBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
Instruction lastInsn = mappedBlock.getLastInstruction(); Instruction lastInsn = mappedBlock.getLastInstruction();
if (lastInsn instanceof ExitInstruction) { if (lastInsn instanceof ExitInstruction) {
ExitInstruction exit = (ExitInstruction) lastInsn; ExitInstruction exit = (ExitInstruction) lastInsn;
@ -165,58 +162,6 @@ public class Inlining {
execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex()); execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex());
} }
private void copyInlinedBlock(BasicBlock source, BasicBlock target) {
if (source.getExceptionVariable() != null) {
target.setExceptionVariable(source.getExceptionVariable());
}
InstructionCopyReader insnCopier = new InstructionCopyReader(target.getProgram());
for (int i = 0; i < source.instructionCount(); ++i) {
source.readInstruction(i, insnCopier);
Instruction insn = insnCopier.getCopy();
target.getInstructions().add(insn);
}
for (Phi phi : source.getPhis()) {
Phi phiCopy = new Phi();
phiCopy.setReceiver(phi.getReceiver());
for (Incoming incoming : phi.getIncomings()) {
Incoming incomingCopy = new Incoming();
int sourceIndex = incoming.getSource().getIndex();
incomingCopy.setSource(target.getProgram().basicBlockAt(sourceIndex));
incomingCopy.setValue(incoming.getValue());
phiCopy.getIncomings().add(incomingCopy);
}
target.getPhis().add(phiCopy);
}
for (TryCatchBlock tryCatch : source.getTryCatchBlocks()) {
TryCatchBlock tryCatchCopy = new TryCatchBlock();
int handler = tryCatch.getHandler().getIndex();
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
tryCatchCopy.setHandler(target.getProgram().basicBlockAt(handler));
target.getTryCatchBlocks().add(tryCatchCopy);
for (TryCatchJoint joint : tryCatch.getJoints()) {
TryCatchJoint jointCopy = new TryCatchJoint();
jointCopy.setReceiver(joint.getReceiver());
jointCopy.getSourceVariables().addAll(joint.getSourceVariables());
tryCatchCopy.getJoints().add(joint);
}
}
}
private void copyTryCatchBlocks(BasicBlock source, BasicBlock target) {
List<TryCatchBlock> copiedTryCatches = new ArrayList<>();
for (TryCatchBlock tryCatch : source.getTryCatchBlocks()) {
TryCatchBlock tryCatchCopy = new TryCatchBlock();
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
tryCatchCopy.setHandler(tryCatch.getHandler());
copiedTryCatches.add(tryCatchCopy);
}
target.getTryCatchBlocks().addAll(copiedTryCatches);
}
private List<PlanEntry> buildPlan(Program program, ClassReaderSource classSource, int depth) { private List<PlanEntry> buildPlan(Program program, ClassReaderSource classSource, int depth) {
if (depth >= MAX_DEPTH) { if (depth >= MAX_DEPTH) {
return Collections.emptyList(); return Collections.emptyList();

View File

@ -78,16 +78,22 @@ public final class ProgramUtils {
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i); BasicBlockReader block = program.basicBlockAt(i);
BasicBlock blockCopy = copy.basicBlockAt(i); BasicBlock blockCopy = copy.basicBlockAt(i);
if (block.getExceptionVariable() != null) { copyBasicBlock(block, blockCopy);
blockCopy.setExceptionVariable(copy.variableAt(block.getExceptionVariable().getIndex()));
}
blockCopy.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), copy));
blockCopy.getPhis().addAll(copyPhis(block, copy));
blockCopy.getTryCatchBlocks().addAll(copyTryCatches(block, copy));
} }
return copy; return copy;
} }
public static void copyBasicBlock(BasicBlockReader block, BasicBlock target) {
Program targetProgram = target.getProgram();
if (block.getExceptionVariable() != null) {
target.setExceptionVariable(targetProgram.variableAt(block.getExceptionVariable().getIndex()));
}
target.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), targetProgram));
target.getPhis().addAll(copyPhis(block, targetProgram));
target.getTryCatchBlocks().addAll(copyTryCatches(block, targetProgram));
}
public static List<Instruction> copyInstructions(BasicBlockReader block, int from, int to, Program target) { public static List<Instruction> copyInstructions(BasicBlockReader block, int from, int to, Program target) {
List<Instruction> result = new ArrayList<>(); List<Instruction> result = new ArrayList<>();
InstructionCopyReader copyReader = new InstructionCopyReader(target); InstructionCopyReader copyReader = new InstructionCopyReader(target);

View File

@ -16,9 +16,9 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.NoGC;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@StaticInit @StaticInit
public final class Allocator { public final class Allocator {
@ -47,12 +47,12 @@ public final class Allocator {
return result; return result;
} }
@NoGC @Unmanaged
public static native void fillZero(Address address, int count); public static native void fillZero(Address address, int count);
@NoGC @Unmanaged
public static native void moveMemoryBlock(Address source, Address target, int count); public static native void moveMemoryBlock(Address source, Address target, int count);
@NoGC @Unmanaged
public static native boolean isInitialized(Class<?> cls); public static native boolean isInitialized(Class<?> cls);
} }

View File

@ -0,0 +1,27 @@
/*
* 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.runtime;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@Unmanaged
@StaticInit
public class CallSite extends Structure {
public int handlerCount;
public ExceptionHandler firstHandler;
}

View File

@ -0,0 +1,27 @@
/*
* 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.runtime;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@Unmanaged
@StaticInit
public class ExceptionHandler extends Structure {
public int id;
public RuntimeClass exceptionClass;
}

View File

@ -0,0 +1,69 @@
/*
* 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.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@Unmanaged
@StaticInit
public final class ExceptionHandling {
private ExceptionHandling() {
}
public static native void registerCallSite(int id);
public static native int callSiteResult();
public static native Address getStackTop();
public static native Address getNextStackFrame(Address stackFrame);
public static native int getCallSiteId(Address stackFrame);
public static native void setHandlerId(Address stackFrame, int id);
public static native int getHandlerId();
public static native CallSite findCallSiteById(int id);
public static void throwException(Throwable exception) {
RuntimeObject exceptionPtr = Address.ofObject(exception).toStructure();
RuntimeClass exceptionClass = RuntimeClass.getClass(exceptionPtr);
IsSupertypeFunction isExceptionSupertype = exceptionClass.isSupertypeOf;
Address stackFrame = getStackTop();
stackLoop: while (stackFrame != null) {
int callSiteId = getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId);
ExceptionHandler handler = callSite.firstHandler;
for (int i = 0; i < callSite.handlerCount; ++i) {
if (isExceptionSupertype.apply(handler.exceptionClass)) {
setHandlerId(stackFrame, handler.id);
break stackLoop;
}
handler = Structure.add(ExceptionHandler.class, handler, 1);
}
setHandlerId(stackFrame, callSiteId - 1);
stackFrame = getNextStackFrame(stackFrame);
}
}
}

View File

@ -16,11 +16,11 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.NoGC;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@NoGC @Unmanaged
@StaticInit @StaticInit
public final class GC { public final class GC {
private GC() { private GC() {
@ -93,7 +93,7 @@ public final class GC {
if (--freeChunks == 0) { if (--freeChunks == 0) {
return false; return false;
} }
currentChunkPointer = currentChunkPointer.toAddress().add(FreeChunkHolder.class, 1).toStructure(); currentChunkPointer = Structure.add(FreeChunkHolder.class, currentChunkPointer, 1);
currentChunk = currentChunkPointer.value; currentChunk = currentChunkPointer.value;
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size); currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
} }
@ -147,8 +147,7 @@ public final class GC {
object.classReference |= RuntimeObject.GC_MARKED; object.classReference |= RuntimeObject.GC_MARKED;
long offset = object.toAddress().toLong() - heapAddress().toLong(); long offset = object.toAddress().toLong() - heapAddress().toLong();
Region region = regionsAddress().toAddress().add(Region.class, (int) (offset / regionSize())) Region region = Structure.add(Region.class, regionsAddress(), (int) (offset / regionSize()));
.toStructure();
short relativeOffset = (short) (offset % regionSize() + 1); short relativeOffset = (short) (offset % regionSize() + 1);
if (region.start == 0 || region.start > relativeOffset) { if (region.start == 0 || region.start > relativeOffset) {
region.start = relativeOffset; region.start = relativeOffset;
@ -221,16 +220,14 @@ public final class GC {
if (!object.toAddress().isLessThan(currentRegionEnd)) { if (!object.toAddress().isLessThan(currentRegionEnd)) {
currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize()); currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize());
Region currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex) Region currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
.toStructure();
if (currentRegion.start == 0) { if (currentRegion.start == 0) {
do { do {
if (++currentRegionIndex == regionsCount) { if (++currentRegionIndex == regionsCount) {
object = limit.toStructure(); object = limit.toStructure();
break loop; break loop;
} }
currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex) currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
.toStructure();
} while (currentRegion.start == 0); } while (currentRegion.start == 0);
} }
currentRegionEnd = currentRegion.toAddress().add(regionSize()); currentRegionEnd = currentRegion.toAddress().add(regionSize());
@ -239,7 +236,7 @@ public final class GC {
if (lastFreeSpace != null) { if (lastFreeSpace != null) {
lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
freeChunkPtr.value = lastFreeSpace; freeChunkPtr.value = lastFreeSpace;
freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure(); freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
freeChunks++; freeChunks++;
reclaimedSpace += lastFreeSpace.size; reclaimedSpace += lastFreeSpace.size;
if (maxFreeChunk < lastFreeSpace.size) { if (maxFreeChunk < lastFreeSpace.size) {
@ -257,7 +254,7 @@ public final class GC {
int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
lastFreeSpace.size = freeSize; lastFreeSpace.size = freeSize;
freeChunkPtr.value = lastFreeSpace; freeChunkPtr.value = lastFreeSpace;
freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure(); freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
freeChunks++; freeChunks++;
reclaimedSpace += freeSize; reclaimedSpace += freeSize;
if (maxFreeChunk < freeSize) { if (maxFreeChunk < freeSize) {
@ -310,7 +307,7 @@ public final class GC {
} }
private static FreeChunkHolder getFreeChunk(int index) { private static FreeChunkHolder getFreeChunk(int index) {
return currentChunkPointer.toAddress().add(FreeChunkHolder.class, index).toStructure(); return Structure.add(FreeChunkHolder.class, currentChunkPointer, index);
} }
private static int objectSize(RuntimeObject object) { private static int objectSize(RuntimeObject object) {

View File

@ -16,9 +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; import org.teavm.interop.Unmanaged;
public abstract class IsSupertypeFunction extends Function { public abstract class IsSupertypeFunction extends Function {
@NoGC @Unmanaged
public abstract boolean apply(RuntimeClass superType); public abstract boolean apply(RuntimeClass superType);
} }

View File

@ -16,9 +16,11 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.NoGC; import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged;
@NoGC @Unmanaged
@StaticInit
public final class Mutator { public final class Mutator {
private Mutator() { private Mutator() {
} }

View File

@ -16,7 +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; import org.teavm.interop.Unmanaged;
public class RuntimeClass extends RuntimeJavaObject { public class RuntimeClass extends RuntimeJavaObject {
public static final int INITIALIZED = 1; public static final int INITIALIZED = 1;
@ -32,17 +32,17 @@ public class RuntimeClass extends RuntimeJavaObject {
public RuntimeClass parent; public RuntimeClass parent;
public Address layout; public Address layout;
@NoGC @Unmanaged
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 @Unmanaged
public int computeCanary() { public int computeCanary() {
return computeCanary(size, tag); return computeCanary(size, tag);
} }
@NoGC @Unmanaged
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

@ -16,7 +16,7 @@
package org.teavm.interop; package org.teavm.interop;
@StaticInit @StaticInit
@NoGC @Unmanaged
public final class Address { public final class Address {
public native Address add(int offset); public native Address add(int offset);

View File

@ -15,11 +15,13 @@
*/ */
package org.teavm.interop; package org.teavm.interop;
@NoGC @Unmanaged
public class Structure { public class Structure {
public final native <T extends Structure> T cast(); public final native <T extends Structure> T cast();
public final native Address toAddress(); public final native Address toAddress();
public static native int sizeOf(Class<? extends Structure> type); public static native int sizeOf(Class<? extends Structure> type);
public static native <T extends Structure> T add(Class<T> type, T base, int offset);
} }

View File

@ -22,5 +22,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE }) @Target({ ElementType.METHOD, ElementType.TYPE })
public @interface NoGC { public @interface Unmanaged {
} }