From b67d243ad45146b65652b11aa8a3780fb4082857 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 22 Sep 2016 16:27:28 +0300 Subject: [PATCH] WASM: add transformation that converts exception handling to explicit guard checks --- .../org/teavm/classlib/java/lang/TSystem.java | 9 +- .../classlib/java/lang/reflect/TArray.java | 13 +- .../org/teavm/backend/wasm/WasmRuntime.java | 8 +- .../org/teavm/backend/wasm/WasmTarget.java | 13 +- .../wasm/generate/WasmGenerationVisitor.java | 37 +- .../ExceptionHandlingIntrinsic.java | 48 +++ .../wasm/intrinsics/StructureIntrinsic.java | 16 + .../model/lowlevel/CallSiteDescriptor.java | 36 ++ .../lowlevel/ExceptionHandlerDescriptor.java | 30 ++ ...ceptionHandlingShadowStackContributor.java | 335 ++++++++++++++++++ ...mer.java => GCShadowStackContributor.java} | 119 +------ .../lowlevel/ManagedMethodRepository.java | 49 +++ .../lowlevel/ShadowStackTransformer.java | 144 ++++++++ .../teavm/model/optimization/Inlining.java | 61 +--- .../org/teavm/model/util/ProgramUtils.java | 18 +- .../java/org/teavm/runtime/Allocator.java | 8 +- .../main/java/org/teavm/runtime/CallSite.java | 27 ++ .../org/teavm/runtime/ExceptionHandler.java | 27 ++ .../org/teavm/runtime/ExceptionHandling.java | 69 ++++ core/src/main/java/org/teavm/runtime/GC.java | 21 +- .../teavm/runtime/IsSupertypeFunction.java | 4 +- .../main/java/org/teavm/runtime/Mutator.java | 6 +- .../java/org/teavm/runtime/RuntimeClass.java | 8 +- .../main/java/org/teavm/interop/Address.java | 2 +- .../java/org/teavm/interop/Structure.java | 4 +- .../interop/{NoGC.java => Unmanaged.java} | 2 +- 26 files changed, 894 insertions(+), 220 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java rename core/src/main/java/org/teavm/model/lowlevel/{GcRootMaintainingTransformer.java => GCShadowStackContributor.java} (77%) create mode 100644 core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java create mode 100644 core/src/main/java/org/teavm/runtime/CallSite.java create mode 100644 core/src/main/java/org/teavm/runtime/ExceptionHandler.java create mode 100644 core/src/main/java/org/teavm/runtime/ExceptionHandling.java rename interop/core/src/main/java/org/teavm/interop/{NoGC.java => Unmanaged.java} (96%) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index a60402038..fc03d11ed 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -15,17 +15,16 @@ */ 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.TInputStream; import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.dependency.PluggableDependency; -import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.interop.Import; -import org.teavm.interop.NoGC; -import org.teavm.interop.Structure; +import org.teavm.interop.Unmanaged; import org.teavm.runtime.Allocator; import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeClass; @@ -81,7 +80,7 @@ public final class TSystem extends TObject { @DelegateTo("doArrayCopyLowLevel") 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) { RuntimeClass type = RuntimeClass.getClass(src); int itemSize = type.itemType.size; @@ -99,7 +98,7 @@ public final class TSystem extends TObject { } @GeneratedBy(SystemNativeGenerator.class) - @DelegateTo(("currentTimeMillisLowLevel")) + @DelegateTo("currentTimeMillisLowLevel") public static native long currentTimeMillis(); private static long currentTimeMillisLowLevel() { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java index 49b4565f4..23e964db6 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java @@ -15,11 +15,16 @@ */ 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.interop.DelegateTo; -import org.teavm.backend.javascript.spi.GeneratedBy; -import org.teavm.interop.NoGC; +import org.teavm.interop.Unmanaged; import org.teavm.platform.PlatformClass; import org.teavm.runtime.Allocator; import org.teavm.runtime.RuntimeArray; @@ -61,7 +66,7 @@ public final class TArray extends TObject { private static native TObject newInstanceImpl(PlatformClass componentType, int length); @SuppressWarnings("unused") - @NoGC + @Unmanaged private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) { return Allocator.allocateArray(cls.arrayType, length).toStructure(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index 23609a12b..19619c587 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -17,11 +17,11 @@ 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; +import org.teavm.interop.Unmanaged; @StaticInit -@NoGC +@Unmanaged public final class WasmRuntime { public static Address stack = initStack(); @@ -245,7 +245,7 @@ public final class WasmRuntime { public static Address allocStack(int size) { Address result = stack.add(4); - stack = result.add(size << 2); + stack = result.add((size << 2) + 4); stack.putInt(size); return result; } @@ -255,7 +255,7 @@ public final class WasmRuntime { } public static Address getNextStackRoots(Address address) { - int size = address.getInt() + 1; + int size = address.getInt() + 2; Address result = address.add(-size * 4); if (result == initStack()) { result = null; diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 5651ee50f..e99689785 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -38,6 +38,7 @@ import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic; 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.GCIntrinsic; 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.lowlevel.ClassInitializerEliminator; 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.RuntimeArray; import org.teavm.runtime.RuntimeClass; @@ -126,7 +127,7 @@ public class WasmTarget implements TeaVMTarget { private boolean cEmitted; private ClassInitializerEliminator classInitializerEliminator; private ClassInitializerTransformer classInitializerTransformer; - private GcRootMaintainingTransformer gcRootMaintainingTransformer; + private ShadowStackTransformer shadowStackTransformer; private MethodDescriptor clinitDescriptor = new MethodDescriptor("", void.class); @Override @@ -134,7 +135,7 @@ public class WasmTarget implements TeaVMTarget { this.controller = controller; classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); - gcRootMaintainingTransformer = new GcRootMaintainingTransformer(controller.getUnprocessedClassSource()); + shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource()); } @Override @@ -248,7 +249,7 @@ public class WasmTarget implements TeaVMTarget { classInitializerEliminator.apply(program); classInitializerTransformer.transform(program); - gcRootMaintainingTransformer.apply(program, method); + shadowStackTransformer.apply(program, method); } private static boolean needsClinitCall(MethodReader method) { @@ -293,6 +294,7 @@ public class WasmTarget implements TeaVMTarget { context.addIntrinsic(gcIntrinsic); MutatorIntrinsic mutatorIntrinsic = new MutatorIntrinsic(); context.addIntrinsic(mutatorIntrinsic); + context.addIntrinsic(new ExceptionHandlingIntrinsic()); WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator); @@ -326,12 +328,13 @@ public class WasmTarget implements TeaVMTarget { initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className))); } module.add(initFunction); - module.setStartFunction(initFunction); + //module.setStartFunction(initFunction); for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) { String mangledName = WasmMangling.mangleMethod(entryPoint.getReference()); WasmFunction function = module.getFunctions().get(mangledName); if (function != null) { + function.getBody().add(0, new WasmCall(initFunction.getName())); function.setExportName(entryPoint.getPublicName()); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index eb0ecc33c..b121f060a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -113,6 +113,7 @@ import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTableEntry; import org.teavm.runtime.Allocator; +import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.Mutator; import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeClass; @@ -797,6 +798,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { generateRemoveGcRoot(expr.getArguments().get(0)); 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()); @@ -912,6 +922,27 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { 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) { if (stackVariable == null) { 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) { if (slot instanceof WasmInt32Constant) { int slotConstant = ((WasmInt32Constant) slot).getValue(); - return new WasmInt32Constant(slotConstant << 2); + return new WasmInt32Constant((slotConstant << 2) + 4); } 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; } } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java new file mode 100644 index 000000000..d4cec9ec8 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java @@ -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()); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/StructureIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/StructureIntrinsic.java index 5eca87ded..962ab433e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/StructureIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/StructureIntrinsic.java @@ -20,6 +20,9 @@ import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.model.expression.WasmExpression; 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.model.MethodReference; 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(); 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: throw new IllegalArgumentException(invocation.getMethod().toString()); } diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java new file mode 100644 index 000000000..64723c0ca --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java @@ -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 handlers = new ArrayList<>(); + + public CallSiteDescriptor(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public List getHandlers() { + return handlers; + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java new file mode 100644 index 000000000..fff9b9109 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java @@ -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; + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java new file mode 100644 index 000000000..b2cc35d03 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java @@ -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 callSites; + private BasicBlock defaultExceptionHandler; + private MethodReference method; + private Program program; + private DominatorTree dom; + private BasicBlock[] variableDefinitionPlaces; + private Phi[] jointPhis; + + public ExceptionHandlingShadowStackContributor(ManagedMethodRepository managedMethodRepository, + List 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 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 remainingInstructions = instructions.subList(i + 1, instructions.size()); + List instructionsToMove = new ArrayList<>(remainingInstructions); + remainingInstructions.clear(); + next.getInstructions().addAll(instructionsToMove); + + CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size()); + callSites.add(callSite); + List pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); + List 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 getInstructionsBeforeCallSite(CallSiteDescriptor callSite) { + List 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 setLocation(List instructions, TextLocation location) { + if (location != null) { + for (Instruction instruction : instructions) { + instruction.setLocation(location); + } + } + return instructions; + } + + private List getInstructionsAfterCallSite(BasicBlock block, BasicBlock next, + CallSiteDescriptor callSite, int[] currentJointSources) { + Program program = block.getProgram(); + List 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 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; + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java similarity index 77% rename from core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java rename to core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java index 29df5627a..74468e8ac 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java @@ -30,10 +30,7 @@ import org.teavm.common.DominatorTree; import org.teavm.common.Graph; import org.teavm.common.GraphBuilder; import org.teavm.common.GraphUtils; -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; @@ -44,12 +41,10 @@ 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.GraphColorer; @@ -60,17 +55,14 @@ import org.teavm.model.util.UsageExtractor; import org.teavm.model.util.VariableType; import org.teavm.runtime.Mutator; -public class GcRootMaintainingTransformer { - private ClassReaderSource classSource; +public class GCShadowStackContributor { + private ManagedMethodRepository managedMethodRepository; - public GcRootMaintainingTransformer(ClassReaderSource classSource) { - this.classSource = classSource; + public GCShadowStackContributor(ManagedMethodRepository managedMethodRepository) { + this.managedMethodRepository = managedMethodRepository; } - public void apply(Program program, MethodReader method) { - if (!requiresGC(method.getReference())) { - return; - } + public int contribute(Program program, MethodReader method) { List> liveInInformation = findCallSiteLiveIns(program, method); Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program); @@ -87,7 +79,7 @@ public class GcRootMaintainingTransformer { } } if (usedColors == 0) { - return; + return 0; } // 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> liveInStores = reduceGcRootStores(program, usedColors, liveInInformation, colors, autoSpilled); putLiveInGcRoots(program, liveInStores); - addStackAllocation(program, usedColors); - addStackRelease(program, usedColors); + + return usedColors; } private void findAutoSpilledPhis(boolean[] spilled, List> destinationPhis, int[] inputCount, @@ -158,7 +150,8 @@ public class GcRootMaintainingTransformer { 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())) { + if (insn instanceof InvokeInstruction + && !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) { continue; } @@ -379,86 +372,6 @@ public class GcRootMaintainingTransformer { instructions.addAll(index, instructionsToAdd); } - private void addStackAllocation(Program program, int maxDepth) { - BasicBlock block = program.basicBlockAt(0); - List instructionsToAdd = new ArrayList<>(); - Variable sizeVariable = program.createVariable(); - - IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction(); - sizeConstant.setReceiver(sizeVariable); - sizeConstant.setConstant(maxDepth); - instructionsToAdd.add(sizeConstant); - - InvokeInstruction invocation = new InvokeInstruction(); - invocation.setType(InvocationType.SPECIAL); - invocation.setMethod(new MethodReference(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 blocks = new ArrayList<>(); - boolean hasResult = false; - for (int i = 0; i < program.basicBlockCount(); ++i) { - BasicBlock block = program.basicBlockAt(i); - Instruction instruction = block.getLastInstruction(); - if (instruction instanceof ExitInstruction) { - blocks.add(block); - if (((ExitInstruction) instruction).getValueToReturn() != null) { - hasResult = true; - } - } - } - - BasicBlock exitBlock; - if (blocks.size() == 1) { - exitBlock = blocks.get(0); - } else { - exitBlock = program.createBasicBlock(); - ExitInstruction exit = new ExitInstruction(); - exitBlock.getInstructions().add(exit); - - if (hasResult) { - Phi phi = new Phi(); - phi.setReceiver(program.createVariable()); - exitBlock.getPhis().add(phi); - exit.setValueToReturn(phi.getReceiver()); - - for (BasicBlock block : blocks) { - ExitInstruction oldExit = (ExitInstruction) block.getLastInstruction(); - Incoming incoming = new Incoming(); - incoming.setSource(block); - incoming.setValue(oldExit.getValueToReturn()); - phi.getIncomings().add(incoming); - - JumpInstruction jumpToExit = new JumpInstruction(); - jumpToExit.setTarget(exitBlock); - jumpToExit.setLocation(oldExit.getLocation()); - - block.getInstructions().set(block.getInstructions().size() - 1, jumpToExit); - } - } - } - - List instructionsToAdd = new ArrayList<>(); - Variable sizeVariable = program.createVariable(); - - IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction(); - sizeConstant.setReceiver(sizeVariable); - sizeConstant.setConstant(maxDepth); - instructionsToAdd.add(sizeConstant); - - InvokeInstruction invocation = new InvokeInstruction(); - invocation.setType(InvocationType.SPECIAL); - invocation.setMethod(new MethodReference(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) { VariableType liveType = typeInferer.typeOf(var); switch (liveType) { @@ -476,16 +389,4 @@ public class GcRootMaintainingTransformer { return false; } } - - private boolean requiresGC(MethodReference methodReference) { - ClassReader cls = classSource.get(methodReference.getClassName()); - if (cls == null) { - return true; - } - if (cls.getAnnotations().get(NoGC.class.getName()) != null) { - return false; - } - MethodReader method = cls.getMethod(methodReference.getDescriptor()); - return method.getAnnotations().get(NoGC.class.getName()) == null; - } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java b/core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java new file mode 100644 index 000000000..af9826f1c --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java @@ -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 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; + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java new file mode 100644 index 000000000..e607f3808 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java @@ -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 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 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 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 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); + } +} diff --git a/core/src/main/java/org/teavm/model/optimization/Inlining.java b/core/src/main/java/org/teavm/model/optimization/Inlining.java index c6ed3a58c..1d040e81a 100644 --- a/core/src/main/java/org/teavm/model/optimization/Inlining.java +++ b/core/src/main/java/org/teavm/model/optimization/Inlining.java @@ -29,8 +29,6 @@ import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.Phi; 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.BinaryBranchingInstruction; 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.SwitchInstruction; import org.teavm.model.util.BasicBlockMapper; -import org.teavm.model.util.InstructionCopyReader; import org.teavm.model.util.InstructionTransitionExtractor; import org.teavm.model.util.InstructionVariableMapper; import org.teavm.model.util.ProgramUtils; @@ -82,7 +79,7 @@ public class Inlining { List instructionsToMove = new ArrayList<>(movedInstructions); movedInstructions.clear(); splitBlock.getInstructions().addAll(instructionsToMove); - copyTryCatchBlocks(block, splitBlock); + splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); block.getInstructions().remove(block.getInstructions().size() - 1); if (invoke.getInstance() == null || invoke.getMethod().getName().equals("")) { @@ -97,7 +94,7 @@ public class Inlining { for (int i = 0; i < inlineProgram.basicBlockCount(); ++i) { BasicBlock blockToInline = inlineProgram.basicBlockAt(i); BasicBlock inlineBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i); - copyInlinedBlock(blockToInline, inlineBlock); + ProgramUtils.copyBasicBlock(blockToInline, inlineBlock); } BasicBlockMapper blockMapper = new BasicBlockMapper(index -> index + firstInlineBlock.getIndex()); @@ -116,7 +113,7 @@ public class Inlining { BasicBlock mappedBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i); blockMapper.transform(mappedBlock); variableMapper.apply(mappedBlock); - copyTryCatchBlocks(block, mappedBlock); + mappedBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); Instruction lastInsn = mappedBlock.getLastInstruction(); if (lastInsn instanceof ExitInstruction) { ExitInstruction exit = (ExitInstruction) lastInsn; @@ -165,58 +162,6 @@ public class Inlining { 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 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 buildPlan(Program program, ClassReaderSource classSource, int depth) { if (depth >= MAX_DEPTH) { return Collections.emptyList(); diff --git a/core/src/main/java/org/teavm/model/util/ProgramUtils.java b/core/src/main/java/org/teavm/model/util/ProgramUtils.java index f9bf394b4..c7cfb237e 100644 --- a/core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -78,16 +78,22 @@ public final class ProgramUtils { for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlockReader block = program.basicBlockAt(i); BasicBlock blockCopy = copy.basicBlockAt(i); - if (block.getExceptionVariable() != null) { - 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)); + copyBasicBlock(block, blockCopy); } 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 copyInstructions(BasicBlockReader block, int from, int to, Program target) { List result = new ArrayList<>(); InstructionCopyReader copyReader = new InstructionCopyReader(target); diff --git a/core/src/main/java/org/teavm/runtime/Allocator.java b/core/src/main/java/org/teavm/runtime/Allocator.java index b1ad8862c..3f53b8b9a 100644 --- a/core/src/main/java/org/teavm/runtime/Allocator.java +++ b/core/src/main/java/org/teavm/runtime/Allocator.java @@ -16,9 +16,9 @@ package org.teavm.runtime; import org.teavm.interop.Address; -import org.teavm.interop.NoGC; import org.teavm.interop.StaticInit; import org.teavm.interop.Structure; +import org.teavm.interop.Unmanaged; @StaticInit public final class Allocator { @@ -47,12 +47,12 @@ public final class Allocator { return result; } - @NoGC + @Unmanaged public static native void fillZero(Address address, int count); - @NoGC + @Unmanaged public static native void moveMemoryBlock(Address source, Address target, int count); - @NoGC + @Unmanaged public static native boolean isInitialized(Class cls); } diff --git a/core/src/main/java/org/teavm/runtime/CallSite.java b/core/src/main/java/org/teavm/runtime/CallSite.java new file mode 100644 index 000000000..915f7a18d --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/CallSite.java @@ -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; +} diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandler.java b/core/src/main/java/org/teavm/runtime/ExceptionHandler.java new file mode 100644 index 000000000..1b3c0f7c2 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandler.java @@ -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; +} diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java new file mode 100644 index 000000000..c3e0ae3d3 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -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); + } + } +} diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index 2e671c449..2a3737f9c 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -16,11 +16,11 @@ package org.teavm.runtime; import org.teavm.interop.Address; -import org.teavm.interop.NoGC; import org.teavm.interop.StaticInit; import org.teavm.interop.Structure; +import org.teavm.interop.Unmanaged; -@NoGC +@Unmanaged @StaticInit public final class GC { private GC() { @@ -93,7 +93,7 @@ public final class GC { if (--freeChunks == 0) { return false; } - currentChunkPointer = currentChunkPointer.toAddress().add(FreeChunkHolder.class, 1).toStructure(); + currentChunkPointer = Structure.add(FreeChunkHolder.class, currentChunkPointer, 1); currentChunk = currentChunkPointer.value; currentChunkLimit = currentChunk.toAddress().add(currentChunk.size); } @@ -147,8 +147,7 @@ public final class GC { object.classReference |= RuntimeObject.GC_MARKED; long offset = object.toAddress().toLong() - heapAddress().toLong(); - Region region = regionsAddress().toAddress().add(Region.class, (int) (offset / regionSize())) - .toStructure(); + Region region = Structure.add(Region.class, regionsAddress(), (int) (offset / regionSize())); short relativeOffset = (short) (offset % regionSize() + 1); if (region.start == 0 || region.start > relativeOffset) { region.start = relativeOffset; @@ -221,16 +220,14 @@ public final class GC { if (!object.toAddress().isLessThan(currentRegionEnd)) { currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize()); - Region currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex) - .toStructure(); + Region currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex); if (currentRegion.start == 0) { do { if (++currentRegionIndex == regionsCount) { object = limit.toStructure(); break loop; } - currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex) - .toStructure(); + currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex); } while (currentRegion.start == 0); } currentRegionEnd = currentRegion.toAddress().add(regionSize()); @@ -239,7 +236,7 @@ public final class GC { if (lastFreeSpace != null) { lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); freeChunkPtr.value = lastFreeSpace; - freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure(); + freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunks++; reclaimedSpace += lastFreeSpace.size; if (maxFreeChunk < lastFreeSpace.size) { @@ -257,7 +254,7 @@ public final class GC { int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong()); lastFreeSpace.size = freeSize; freeChunkPtr.value = lastFreeSpace; - freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure(); + freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1); freeChunks++; reclaimedSpace += freeSize; if (maxFreeChunk < freeSize) { @@ -310,7 +307,7 @@ public final class GC { } 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) { diff --git a/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java b/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java index 0981ffe37..ed37b4c03 100644 --- a/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java +++ b/core/src/main/java/org/teavm/runtime/IsSupertypeFunction.java @@ -16,9 +16,9 @@ package org.teavm.runtime; import org.teavm.interop.Function; -import org.teavm.interop.NoGC; +import org.teavm.interop.Unmanaged; public abstract class IsSupertypeFunction extends Function { - @NoGC + @Unmanaged public abstract boolean apply(RuntimeClass superType); } diff --git a/core/src/main/java/org/teavm/runtime/Mutator.java b/core/src/main/java/org/teavm/runtime/Mutator.java index 769f44dea..79cd560f9 100644 --- a/core/src/main/java/org/teavm/runtime/Mutator.java +++ b/core/src/main/java/org/teavm/runtime/Mutator.java @@ -16,9 +16,11 @@ package org.teavm.runtime; 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 { private Mutator() { } diff --git a/core/src/main/java/org/teavm/runtime/RuntimeClass.java b/core/src/main/java/org/teavm/runtime/RuntimeClass.java index aa41fe815..f4b04ee1e 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeClass.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeClass.java @@ -16,7 +16,7 @@ package org.teavm.runtime; import org.teavm.interop.Address; -import org.teavm.interop.NoGC; +import org.teavm.interop.Unmanaged; public class RuntimeClass extends RuntimeJavaObject { public static final int INITIALIZED = 1; @@ -32,17 +32,17 @@ public class RuntimeClass extends RuntimeJavaObject { public RuntimeClass parent; public Address layout; - @NoGC + @Unmanaged public static int computeCanary(int size, int tag) { return size ^ (tag << 8) ^ (tag >>> 24) ^ 0xAAAAAAAA; } - @NoGC + @Unmanaged public int computeCanary() { return computeCanary(size, tag); } - @NoGC + @Unmanaged public static RuntimeClass getClass(RuntimeObject object) { return Address.fromInt(object.classReference << 3).toStructure(); } diff --git a/interop/core/src/main/java/org/teavm/interop/Address.java b/interop/core/src/main/java/org/teavm/interop/Address.java index 87298d245..d61bde530 100644 --- a/interop/core/src/main/java/org/teavm/interop/Address.java +++ b/interop/core/src/main/java/org/teavm/interop/Address.java @@ -16,7 +16,7 @@ package org.teavm.interop; @StaticInit -@NoGC +@Unmanaged public final class Address { public native Address add(int offset); diff --git a/interop/core/src/main/java/org/teavm/interop/Structure.java b/interop/core/src/main/java/org/teavm/interop/Structure.java index 41c527077..c09d1ed23 100644 --- a/interop/core/src/main/java/org/teavm/interop/Structure.java +++ b/interop/core/src/main/java/org/teavm/interop/Structure.java @@ -15,11 +15,13 @@ */ package org.teavm.interop; -@NoGC +@Unmanaged public class Structure { public final native T cast(); public final native Address toAddress(); public static native int sizeOf(Class type); + + public static native T add(Class type, T base, int offset); } diff --git a/interop/core/src/main/java/org/teavm/interop/NoGC.java b/interop/core/src/main/java/org/teavm/interop/Unmanaged.java similarity index 96% rename from interop/core/src/main/java/org/teavm/interop/NoGC.java rename to interop/core/src/main/java/org/teavm/interop/Unmanaged.java index 2dbd4ea69..1b3bdf9e7 100644 --- a/interop/core/src/main/java/org/teavm/interop/NoGC.java +++ b/interop/core/src/main/java/org/teavm/interop/Unmanaged.java @@ -22,5 +22,5 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) -public @interface NoGC { +public @interface Unmanaged { }