From e77997c93ff78ea9f4faf8b2635f2d9e7b510853 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 17 Apr 2018 00:05:46 +0300 Subject: [PATCH] Implement null check in C backend Although initial purpose of this comment was null check, it took much time to complete it and it caused many unrelated changes. Besides just implementing null check in quite naive fashion (I could not use the trick with memory protection, since I have to maintain shadow stack, and support WebAssembly), I had to optimize things. I relied on my existing nullness analysis to eliminate as much null checks as possible. However, the whole nullness analysis was wrong. After some thoughts I came up with solution very close to range analysis, which required me to introduce extension to IR sometimes called e-SSA form with so called sigma nodes. Also, I found some bugs in few different places (by the time write this message I could only remember escape analysis/scalar replacement and after-inlining devirtualization) and fixed them. --- .../classlib/impl/PlatformMarkerSupport.java | 6 +- .../ast/decompilation/StatementGenerator.java | 4 +- .../ast/optimization/BreakEliminator.java | 12 +- .../java/org/teavm/backend/c/CTarget.java | 22 +- .../backend/c/generate/ClassGenerator.java | 2 +- .../c/generate/CodeGenerationVisitor.java | 2 - .../backend/c/generate/CodeGenerator.java | 3 + .../backend/c/generate/GenerationContext.java | 2 +- .../backend/c/intrinsic/AddressIntrinsic.java | 4 +- .../c/intrinsic/FunctionIntrinsic.java | 2 +- .../c/intrinsic/StructureIntrinsic.java | 4 +- .../backend/javascript/JavaScriptTarget.java | 4 + .../java/org/teavm/backend/wasm/Example.java | 10 + .../org/teavm/backend/wasm/WasmTarget.java | 9 +- .../org/teavm/callgraph/DefaultCallGraph.java | 4 +- .../teavm/callgraph/DefaultCallGraphNode.java | 6 +- .../java/org/teavm/common/GraphUtils.java | 37 ++ .../dependency/DependencyGraphBuilder.java | 4 +- .../org/teavm/model/AnnotationContainer.java | 4 +- .../org/teavm/model/AnnotationHolder.java | 4 +- .../main/java/org/teavm/model/BasicBlock.java | 37 +- .../main/java/org/teavm/model/Outgoing.java | 51 +++ core/src/main/java/org/teavm/model/Sigma.java | 99 +++++ .../teavm/model/analysis/ClassInference.java | 2 +- .../teavm/model/analysis/EscapeAnalysis.java | 47 ++- .../model/analysis/NullnessInformation.java | 22 +- .../analysis/NullnessInformationBuilder.java | 379 ++++++++++-------- .../org/teavm/model/classes/TagRegistry.java | 10 +- .../model/classes/VirtualTableProvider.java | 3 +- .../org/teavm/model/emit/ProgramEmitter.java | 4 +- .../lowlevel}/Characteristics.java | 28 +- ...ceptionHandlingShadowStackContributor.java | 42 +- .../lowlevel/GCShadowStackContributor.java | 15 +- .../lowlevel/ManagedMethodRepository.java | 50 --- .../model/lowlevel/NullCheckInsertion.java | 126 ++++++ .../lowlevel/NullCheckTransformation.java | 174 ++++++++ .../lowlevel/ShadowStackTransformer.java | 7 +- .../ConstantConditionElimination.java | 33 +- .../teavm/model/optimization/Inlining.java | 4 +- .../RedundantJumpElimination.java | 8 +- .../RedundantNullCheckElimination.java | 71 ++++ .../model/optimization/ScalarReplacement.java | 54 +-- .../UnreachableBasicBlockEliminator.java | 4 +- .../model/util/AsyncProgramSplitter.java | 2 +- .../model/util/InterferenceGraphBuilder.java | 2 +- .../model/util/MissingItemsProcessor.java | 2 +- .../java/org/teavm/model/util/PhiUpdater.java | 142 ++++++- .../org/teavm/model/util/ProgramUtils.java | 27 +- ...xtractor.java => TransitionExtractor.java} | 2 +- .../org/teavm/model/util/TypeInferer.java | 47 +-- .../main/java/org/teavm/parsing/Parser.java | 6 +- .../java/org/teavm/parsing/ProgramParser.java | 92 ++++- .../org/teavm/runtime/ExceptionHandling.java | 5 + core/src/main/java/org/teavm/vm/TeaVM.java | 4 + .../main/java/org/teavm/vm/TeaVMTarget.java | 2 + .../analysis/test/NullnessAnalysisTest.java | 5 + .../analysis/nullness/branch.extended.txt | 8 +- .../nullness/nonDominatedBranch.extended.txt | 12 +- .../nullness/nullAndNull.extended.txt | 25 ++ .../nullness/nullAndNull.original.txt | 17 + .../analysis/nullness/phiJoin.extended.txt | 2 - .../impl/MetaprogrammingImpl.java | 4 +- 62 files changed, 1407 insertions(+), 414 deletions(-) create mode 100644 core/src/main/java/org/teavm/model/Outgoing.java create mode 100644 core/src/main/java/org/teavm/model/Sigma.java rename core/src/main/java/org/teavm/{backend/c/analyze => model/lowlevel}/Characteristics.java (74%) delete mode 100644 core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java create mode 100644 core/src/main/java/org/teavm/model/optimization/RedundantNullCheckElimination.java rename core/src/main/java/org/teavm/model/util/{InstructionTransitionExtractor.java => TransitionExtractor.java} (98%) create mode 100644 core/src/test/resources/model/analysis/nullness/nullAndNull.extended.txt create mode 100644 core/src/test/resources/model/analysis/nullness/nullAndNull.original.txt diff --git a/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java index 8029ec213..84a53333b 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java @@ -40,7 +40,7 @@ import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.optimization.ConstantConditionElimination; import org.teavm.model.optimization.GlobalValueNumbering; -import org.teavm.model.optimization.UnreachableBasicBlockElimination; +import org.teavm.model.optimization.UnreachableBasicBlockEliminator; public class PlatformMarkerSupport implements ClassHolderTransformer { private String[] tags; @@ -124,8 +124,8 @@ public class PlatformMarkerSupport implements ClassHolderTransformer { boolean changed; do { changed = new GlobalValueNumbering(true).optimize(program) - | new ConstantConditionElimination().optimize(null, program) - | new UnreachableBasicBlockElimination().optimize(null, program); + | new ConstantConditionElimination().optimize(containingMethod.getDescriptor(), program); + new UnreachableBasicBlockEliminator().optimize(program); } while (changed); } } diff --git a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java index c714e814a..37a11d9c5 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java +++ b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java @@ -18,7 +18,7 @@ package org.teavm.ast.decompilation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.teavm.ast.ArrayType; @@ -325,7 +325,7 @@ class StatementGenerator implements InstructionVisitor { SwitchStatement stmt = new SwitchStatement(); stmt.setId("sblock" + (lastSwitchId++)); stmt.setValue(Expr.var(insn.getCondition().getIndex())); - Map> switchMap = new HashMap<>(); + Map> switchMap = new LinkedHashMap<>(); for (int i = 0; i < insn.getEntries().size(); ++i) { SwitchTableEntry entry = insn.getEntries().get(i); List conditions = switchMap.computeIfAbsent(entry.getTarget().getIndex(), k -> new ArrayList<>()); diff --git a/core/src/main/java/org/teavm/ast/optimization/BreakEliminator.java b/core/src/main/java/org/teavm/ast/optimization/BreakEliminator.java index 05d1eccb9..da9acbdd6 100644 --- a/core/src/main/java/org/teavm/ast/optimization/BreakEliminator.java +++ b/core/src/main/java/org/teavm/ast/optimization/BreakEliminator.java @@ -15,8 +15,8 @@ */ package org.teavm.ast.optimization; -import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -41,8 +41,8 @@ import org.teavm.ast.TryCatchStatement; import org.teavm.ast.WhileStatement; class BreakEliminator implements StatementVisitor { - private Map> blockSuccessors = new HashMap<>(); - private Set outerStatements = new HashSet<>(); + private Map> blockSuccessors = new LinkedHashMap<>(); + private Set outerStatements = new LinkedHashSet<>(); private List currentSequence; private int currentIndex; private AllBlocksCountVisitor usageCounter; @@ -153,8 +153,8 @@ class BreakEliminator implements StatementVisitor { public void visit(TryCatchStatement statement) { Map> oldBlockSuccessors = blockSuccessors; Set oldOuterStatements = outerStatements; - outerStatements = new HashSet<>(); - blockSuccessors = new HashMap<>(); + outerStatements = new LinkedHashSet<>(); + blockSuccessors = new LinkedHashMap<>(); processSequence(statement.getProtectedBody()); outerStatements = oldOuterStatements; blockSuccessors = oldBlockSuccessors; diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 712f413f2..fce405d98 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -28,11 +28,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.c.analyze.CDependencyListener; -import org.teavm.backend.c.analyze.Characteristics; import org.teavm.backend.c.generate.BufferedCodeWriter; import org.teavm.backend.c.generate.ClassGenerator; import org.teavm.backend.c.generate.CodeWriter; @@ -79,8 +79,11 @@ import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; +import org.teavm.model.lowlevel.NullCheckInsertion; +import org.teavm.model.lowlevel.NullCheckTransformation; import org.teavm.model.lowlevel.ShadowStackTransformer; import org.teavm.model.transformation.ClassInitializerInsertionTransformer; import org.teavm.model.transformation.ClassPatch; @@ -101,6 +104,8 @@ public class CTarget implements TeaVMTarget { private ClassInitializerEliminator classInitializerEliminator; private ClassInitializerTransformer classInitializerTransformer; private ShadowStackTransformer shadowStackTransformer; + private NullCheckInsertion nullCheckInsertion; + private NullCheckTransformation nullCheckTransformation; private int minHeapSize = 32 * 1024 * 1024; public void setMinHeapSize(int minHeapSize) { @@ -123,10 +128,13 @@ public class CTarget implements TeaVMTarget { @Override public void setController(TeaVMTargetController controller) { this.controller = controller; + Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); - shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource()); + shadowStackTransformer = new ShadowStackTransformer(characteristics); clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource()); + nullCheckInsertion = new NullCheckInsertion(characteristics); + nullCheckTransformation = new NullCheckTransformation(); } @Override @@ -154,6 +162,8 @@ public class CTarget implements TeaVMTarget { Throwable.class, void.class), null).use(); dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException", void.class), null).use(); + dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException", + void.class), null).use(); dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException", Throwable.class), null).use(); @@ -171,11 +181,17 @@ public class CTarget implements TeaVMTarget { } } + @Override + public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { + nullCheckInsertion.transformProgram(program, method.getReference()); + } + @Override public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { clinitInsertionTransformer.apply(method, program); classInitializerEliminator.apply(program); classInitializerTransformer.transform(program); + nullCheckTransformation.apply(program, method.getResultType()); shadowStackTransformer.apply(program, method); } @@ -289,7 +305,7 @@ public class CTarget implements TeaVMTarget { } private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { - Set virtualMethods = new HashSet<>(); + Set virtualMethods = new LinkedHashSet<>(); for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index b96a09248..45f9bbc98 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Set; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.decompilation.Decompiler; -import org.teavm.backend.c.analyze.Characteristics; import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.generators.GeneratorContext; import org.teavm.diagnostics.Diagnostics; @@ -45,6 +44,7 @@ import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTable; import org.teavm.model.classes.VirtualTableEntry; +import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ShadowStackTransformer; import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeObject; diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index b985e2631..cd3d1eb65 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -225,9 +225,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { writer.print(")"); break; case NULL_CHECK: - writer.print("("); expr.getOperand().acceptVisitor(this); - writer.print(" == NULL)"); break; case INT_TO_BYTE: writer.print("TO_BYTE("); diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java index 7a9139345..49b1a2ff1 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerator.java @@ -92,6 +92,9 @@ public class CodeGenerator { int start = methodNode.getReference().parameterCount() + 1; for (int i = start; i < methodNode.getVariables().size(); ++i) { VariableNode variableNode = methodNode.getVariables().get(i); + if (variableNode.getType() == null) { + continue; + } localsWriter.printType(variableNode.getType()).print(" local_").print(String.valueOf(i)).println(";"); } diff --git a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java index 8449ad938..161868010 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java @@ -19,13 +19,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.teavm.backend.c.analyze.Characteristics; import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; import org.teavm.model.classes.VirtualTableProvider; +import org.teavm.model.lowlevel.Characteristics; public class GenerationContext { private VirtualTableProvider virtualTableProvider; diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java index 5ab58eaae..248c0ae40 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java @@ -168,7 +168,9 @@ public class AddressIntrinsic implements Intrinsic { String className = StructureIntrinsic.getClassLiteral(context, invocation, invocation.getArguments().get(1)); context.emit(invocation.getArguments().get(2)); - context.writer().print(" * sizeof(").print(context.names().forClass(className)).print(")"); + context.writer().print(" * sizeof(") + .print(className != null ? context.names().forClass(className) : "**") + .print(")"); context.writer().print(")"); } break; diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java index 2a7bb9732..6aa9258ec 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java @@ -16,8 +16,8 @@ package org.teavm.backend.c.intrinsic; import org.teavm.ast.InvocationExpr; -import org.teavm.backend.c.analyze.Characteristics; import org.teavm.model.MethodReference; +import org.teavm.model.lowlevel.Characteristics; public class FunctionIntrinsic implements Intrinsic { private Characteristics characteristics; diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java index 61ff2b78c..1a4462bd6 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java @@ -18,11 +18,11 @@ package org.teavm.backend.c.intrinsic; import org.teavm.ast.ConstantExpr; import org.teavm.ast.Expr; import org.teavm.ast.InvocationExpr; -import org.teavm.backend.c.analyze.Characteristics; import org.teavm.interop.Structure; import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; +import org.teavm.model.lowlevel.Characteristics; public class StructureIntrinsic implements Intrinsic { private Characteristics characteristics; @@ -92,6 +92,6 @@ public class StructureIntrinsic implements Intrinsic { context.getDiagnotics().error( new CallLocation(context.getCallingMethod(), invocation.getLocation()), "This method should take class literal"); - return null; + return ""; } } diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index b4ce6a548..cc72b066c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -230,6 +230,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } } + @Override + public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { + } + @Override public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { clinitInsertionTransformer.apply(method, program); diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java index 4c7caeaa8..7cb485ee9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ b/core/src/main/java/org/teavm/backend/wasm/Example.java @@ -214,6 +214,16 @@ public final class Example { } catch (IllegalStateException e) { System.out.println("Caught 3: " + e.getMessage()); } + + Object[] objects = { "a", null }; + for (Object o : objects) { + try { + System.out.println(o.toString()); + } catch (RuntimeException e) { + System.out.println("Caught NPE"); + e.printStackTrace(); + } + } } private static void testArrayReflection() { 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 897fddbff..02e1c009c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -110,6 +110,7 @@ import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ShadowStackTransformer; @@ -142,9 +143,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { @Override public void setController(TeaVMTargetController controller) { this.controller = controller; + Characteristics managedMethodRepository = new Characteristics( + controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); - shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource()); + shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository); clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource()); } @@ -273,6 +276,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } } + @Override + public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { + } + @Override public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classes) { clinitInsertionTransformer.apply(method, program); diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java b/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java index a495fac27..09622f134 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java +++ b/core/src/main/java/org/teavm/callgraph/DefaultCallGraph.java @@ -24,8 +24,8 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -48,7 +48,7 @@ public class DefaultCallGraph implements CallGraph, Serializable { } void addFieldAccess(DefaultFieldAccessSite accessSite) { - fieldAccessSites.computeIfAbsent(accessSite.getField(), k -> new HashSet<>()).add(accessSite); + fieldAccessSites.computeIfAbsent(accessSite.getField(), k -> new LinkedHashSet<>()).add(accessSite); } private void writeObject(ObjectOutputStream out) throws IOException { diff --git a/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java b/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java index 24efc7b54..2cc32c775 100644 --- a/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java +++ b/core/src/main/java/org/teavm/callgraph/DefaultCallGraphNode.java @@ -18,7 +18,7 @@ package org.teavm.callgraph; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.teavm.model.FieldReference; @@ -28,11 +28,11 @@ import org.teavm.model.TextLocation; public class DefaultCallGraphNode implements CallGraphNode { private DefaultCallGraph graph; private MethodReference method; - private Set callSites = new HashSet<>(); + private Set callSites = new LinkedHashSet<>(); private Set safeCallSites; private List callerCallSites = new ArrayList<>(); private List safeCallersCallSites; - private Set fieldAccessSites = new HashSet<>(); + private Set fieldAccessSites = new LinkedHashSet<>(); private Set safeFieldAccessSites; DefaultCallGraphNode(DefaultCallGraph graph, MethodReference method) { diff --git a/core/src/main/java/org/teavm/common/GraphUtils.java b/core/src/main/java/org/teavm/common/GraphUtils.java index 6f121effb..8c88a1c17 100644 --- a/core/src/main/java/org/teavm/common/GraphUtils.java +++ b/core/src/main/java/org/teavm/common/GraphUtils.java @@ -67,6 +67,43 @@ public final class GraphUtils { return result.getAll(); } + public static Graph removeLoops(Graph graph) { + int sz = graph.size(); + int[] stack = new int[sz * 2]; + int stackSize = 0; + byte[] state = new byte[sz]; + for (int i = 0; i < sz; ++i) { + if (graph.incomingEdgesCount(i) == 0) { + stack[stackSize++] = i; + } + } + GraphBuilder builder = new GraphBuilder(graph.size()); + while (stackSize > 0) { + int node = stack[--stackSize]; + switch (state[node]) { + case NONE: + state[node] = VISITING; + stack[stackSize++] = node; + for (int next : graph.outgoingEdges(node)) { + switch (state[next]) { + case NONE: + stack[stackSize++] = next; + builder.addEdge(node, next); + break; + case VISITED: + builder.addEdge(node, next); + break; + } + } + break; + case VISITING: + state[node] = VISITED; + break; + } + } + return builder.build(); + } + public static boolean isIrreducible(Graph graph) { DominatorTree dom = buildDominatorTree(graph); int[] backEdges = findBackEdges(graph); diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index ad16d503a..47c149f4b 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -686,7 +686,9 @@ class DependencyGraphBuilder { public void nullCheck(VariableReader receiver, VariableReader value) { DependencyNode valueNode = nodes[value.getIndex()]; DependencyNode receiverNode = nodes[receiver.getIndex()]; - valueNode.connect(receiverNode); + if (valueNode != null) { + valueNode.connect(receiverNode); + } dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "", void.class), new CallLocation(caller.getMethod(), currentLocation)).use(); currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException")); diff --git a/core/src/main/java/org/teavm/model/AnnotationContainer.java b/core/src/main/java/org/teavm/model/AnnotationContainer.java index efeff7826..d8a296cc3 100644 --- a/core/src/main/java/org/teavm/model/AnnotationContainer.java +++ b/core/src/main/java/org/teavm/model/AnnotationContainer.java @@ -15,11 +15,11 @@ */ package org.teavm.model; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; public class AnnotationContainer implements AnnotationContainerReader { - private Map annotations = new HashMap<>(); + private Map annotations = new LinkedHashMap<>(); public void add(AnnotationHolder annotation) { if (annotations.containsKey(annotation.getType())) { diff --git a/core/src/main/java/org/teavm/model/AnnotationHolder.java b/core/src/main/java/org/teavm/model/AnnotationHolder.java index 78e37c05e..e027f8c45 100644 --- a/core/src/main/java/org/teavm/model/AnnotationHolder.java +++ b/core/src/main/java/org/teavm/model/AnnotationHolder.java @@ -15,7 +15,7 @@ */ package org.teavm.model; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -24,7 +24,7 @@ import java.util.Map; */ public class AnnotationHolder implements AnnotationReader { private String type; - private Map values = new HashMap<>(); + private Map values = new LinkedHashMap<>(); public AnnotationHolder(String type) { this.type = type; diff --git a/core/src/main/java/org/teavm/model/BasicBlock.java b/core/src/main/java/org/teavm/model/BasicBlock.java index c35235701..67dfa5fc7 100644 --- a/core/src/main/java/org/teavm/model/BasicBlock.java +++ b/core/src/main/java/org/teavm/model/BasicBlock.java @@ -15,8 +15,15 @@ */ package org.teavm.model; -import java.util.*; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; import org.teavm.model.instructions.InstructionReader; +import org.teavm.model.util.TransitionExtractor; public class BasicBlock implements BasicBlockReader, Iterable { private Program program; @@ -359,4 +366,32 @@ public class BasicBlock implements BasicBlockReader, Iterable { public void setLabel(String label) { this.label = label; } + + public void detachSuccessors() { + Instruction lastInstruction = getLastInstruction(); + if (lastInstruction == null) { + return; + } + + TransitionExtractor transitionExtractor = new TransitionExtractor(); + lastInstruction.acceptVisitor(transitionExtractor); + if (transitionExtractor.getTargets() == null) { + return; + } + + for (BasicBlock successor : transitionExtractor.getTargets()) { + List phis = successor.getPhis(); + for (int i = 0; i < phis.size(); i++) { + Phi phi = phis.get(i); + for (int j = 0; j < phi.getIncomings().size(); ++j) { + if (phi.getIncomings().get(j).getSource() == this) { + phi.getIncomings().remove(j--); + } + } + if (phi.getIncomings().isEmpty()) { + phis.remove(i--); + } + } + } + } } diff --git a/core/src/main/java/org/teavm/model/Outgoing.java b/core/src/main/java/org/teavm/model/Outgoing.java new file mode 100644 index 000000000..6db23cf8b --- /dev/null +++ b/core/src/main/java/org/teavm/model/Outgoing.java @@ -0,0 +1,51 @@ +/* + * Copyright 2018 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; + +public class Outgoing { + private Sigma sigma; + private Variable value; + private BasicBlock target; + + public Outgoing(Variable value, BasicBlock target) { + this.value = value; + this.target = target; + } + + public Sigma getSigma() { + return sigma; + } + + void setSigma(Sigma sigma) { + this.sigma = sigma; + } + + public Variable getValue() { + return value; + } + + public void setValue(Variable value) { + this.value = value; + } + + public BasicBlock getTarget() { + return target; + } + + public void setTarget(BasicBlock target) { + this.target = target; + } +} diff --git a/core/src/main/java/org/teavm/model/Sigma.java b/core/src/main/java/org/teavm/model/Sigma.java new file mode 100644 index 000000000..d82a22e7d --- /dev/null +++ b/core/src/main/java/org/teavm/model/Sigma.java @@ -0,0 +1,99 @@ +/* + * Copyright 2018 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; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; + +public class Sigma { + private BasicBlock basicBlock; + private Variable value; + private List outgoings = new ArrayList<>(); + + public Sigma(BasicBlock basicBlock, Variable value) { + this.basicBlock = basicBlock; + this.value = value; + } + + public BasicBlock getBasicBlock() { + return basicBlock; + } + + public void setBasicBlock(BasicBlock basicBlock) { + this.basicBlock = basicBlock; + } + + public Variable getValue() { + return value; + } + + public void setValue(Variable value) { + this.value = value; + } + + public List getOutgoings() { + return safeOutgoings; + } + + private List safeOutgoings = new AbstractList() { + @Override + public Outgoing get(int index) { + return outgoings.get(index); + } + + @Override + public Outgoing set(int index, Outgoing element) { + if (element.getSigma() != null) { + throw new IllegalArgumentException("This outgoing is already in some sigma"); + } + element.setSigma(Sigma.this); + Outgoing old = outgoings.get(index); + old.setSigma(null); + outgoings.set(index, element); + return old; + } + + @Override + public void add(int index, Outgoing element) { + if (element.getSigma() != null) { + throw new IllegalArgumentException("This outgoing is already in some sigma"); + } + element.setSigma(Sigma.this); + outgoings.add(index, element); + } + + @Override + public Outgoing remove(int index) { + Outgoing outgoing = outgoings.remove(index); + outgoing.setSigma(null); + return outgoing; + } + + @Override + public void clear() { + for (Outgoing outgoing : outgoings) { + outgoing.setSigma(null); + } + outgoings.clear(); + } + + @Override + public int size() { + return outgoings.size(); + } + }; +} diff --git a/core/src/main/java/org/teavm/model/analysis/ClassInference.java b/core/src/main/java/org/teavm/model/analysis/ClassInference.java index 4c1ef45cf..cb6761cd9 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassInference.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassInference.java @@ -680,7 +680,7 @@ public class ClassInference { virtualCallSites.add(callSite); if (insn.getReceiver() != null) { - for (int j = 1; j <= MAX_DEGREE; ++j) { + for (int j = 0; j <= MAX_DEGREE; ++j) { getNodeTypes(packNodeAndDegree(callSite.receiver, j)); } } diff --git a/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java b/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java index 26ce49361..d64c5bb48 100644 --- a/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java +++ b/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java @@ -64,8 +64,8 @@ import org.teavm.model.instructions.RaiseInstruction; import org.teavm.model.instructions.StringConstantInstruction; import org.teavm.model.instructions.UnwrapArrayInstruction; import org.teavm.model.util.DefinitionExtractor; -import org.teavm.model.util.InstructionTransitionExtractor; import org.teavm.model.util.LivenessAnalyzer; +import org.teavm.model.util.TransitionExtractor; import org.teavm.model.util.UsageExtractor; public class EscapeAnalysis { @@ -134,6 +134,16 @@ public class EscapeAnalysis { if (usedVars.get(assign.getAssignee().getIndex())) { queue.addLast(definitionClasses[assign.getAssignee().getIndex()]); } + } else if (insn instanceof NullCheckInstruction) { + NullCheckInstruction nullCheck = (NullCheckInstruction) insn; + if (usedVars.get(nullCheck.getValue().getIndex())) { + queue.addLast(definitionClasses[nullCheck.getValue().getIndex()]); + } + } else if (insn instanceof UnwrapArrayInstruction) { + UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) insn; + if (usedVars.get(unwrapArray.getArray().getIndex())) { + queue.addLast(definitionClasses[unwrapArray.getArray().getIndex()]); + } } insn.acceptVisitor(useExtractor); @@ -180,7 +190,7 @@ public class EscapeAnalysis { private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) { BitSet usedVars = new BitSet(); - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); block.getLastInstruction().acceptVisitor(transitionExtractor); for (BasicBlock successor : transitionExtractor.getTargets()) { usedVars.or(liveness.liveIn(successor.getIndex())); @@ -205,21 +215,36 @@ public class EscapeAnalysis { GraphBuilder graphBuilder = new GraphBuilder(program.variableCount()); for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction insn : block) { + if (insn instanceof AssignInstruction) { + AssignInstruction assign = (AssignInstruction) insn; + graphBuilder.addEdge(assign.getReceiver().getIndex(), assign.getAssignee().getIndex()); + } else if (insn instanceof NullCheckInstruction) { + NullCheckInstruction nullCheck = (NullCheckInstruction) insn; + graphBuilder.addEdge(nullCheck.getReceiver().getIndex(), nullCheck.getValue().getIndex()); + } else if (insn instanceof UnwrapArrayInstruction) { + UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) insn; + graphBuilder.addEdge(unwrapArray.getReceiver().getIndex(), unwrapArray.getArray().getIndex()); + } + } for (Phi phi : block.getPhis()) { for (Incoming incoming : phi.getIncomings()) { graphBuilder.addEdge(phi.getReceiver().getIndex(), incoming.getValue().getIndex()); - Set receiverFields = fields.get(phi.getReceiver().getIndex()); - if (receiverFields != null) { - for (FieldReference field : receiverFields) { - queue.add(new Task(phi.getReceiver().getIndex(), field)); - } - receiverFields.clear(); - } } } } Graph graph = graphBuilder.build(); + for (int i = 0; i < program.variableCount(); ++i) { + Set receiverFields = fields.get(i); + if (receiverFields != null) { + for (FieldReference field : receiverFields) { + queue.add(new Task(i, field)); + } + receiverFields.clear(); + } + } + while (!queue.isEmpty()) { Task task = queue.remove(); @@ -263,13 +288,13 @@ public class EscapeAnalysis { return packedFields; } - private static class InstructionEscapeVisitor extends AbstractInstructionVisitor { + static class InstructionEscapeVisitor extends AbstractInstructionVisitor { DisjointSet definitionClasses; boolean[] escapingVars; List> fields; Map fieldTypes = new HashMap<>(); - public InstructionEscapeVisitor(int variableCount) { + InstructionEscapeVisitor(int variableCount) { fields = new ArrayList<>(Collections.nCopies(variableCount, null)); definitionClasses = new DisjointSet(); for (int i = 0; i < variableCount; ++i) { diff --git a/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java b/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java index d82be9666..b34744a6b 100644 --- a/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java +++ b/core/src/main/java/org/teavm/model/analysis/NullnessInformation.java @@ -25,6 +25,7 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.Phi; import org.teavm.model.Program; import org.teavm.model.Variable; +import org.teavm.model.analysis.NullnessInformationBuilder.Nullness; import org.teavm.model.util.DefinitionExtractor; import org.teavm.model.util.InstructionVariableMapper; import org.teavm.model.util.PhiUpdater; @@ -33,24 +34,26 @@ public class NullnessInformation { private Program program; private BitSet synthesizedVariables; private PhiUpdater phiUpdater; - private BitSet notNullVariables; - private BitSet nullVariables; + private Nullness[] nullnessArray; - NullnessInformation(Program program, BitSet synthesizedVariables, PhiUpdater phiUpdater, BitSet notNullVariables, - BitSet nullVariables) { + NullnessInformation(Program program, BitSet synthesizedVariables, PhiUpdater phiUpdater, + Nullness[] nullnessArray) { this.program = program; this.synthesizedVariables = synthesizedVariables; this.phiUpdater = phiUpdater; - this.notNullVariables = notNullVariables; - this.nullVariables = nullVariables; + this.nullnessArray = nullnessArray; } public boolean isNotNull(Variable variable) { - return notNullVariables.get(variable.getIndex()); + return nullnessArray[variable.getIndex()] == Nullness.NOT_NULL; } public boolean isNull(Variable variable) { - return nullVariables.get(variable.getIndex()); + return nullnessArray[variable.getIndex()] == Nullness.NULL; + } + + public boolean isSynthesized(Variable variable) { + return synthesizedVariables.get(variable.getIndex()); } public void dispose() { @@ -89,7 +92,6 @@ public class NullnessInformation { public static NullnessInformation build(Program program, MethodDescriptor methodDescriptor) { NullnessInformationBuilder builder = new NullnessInformationBuilder(program, methodDescriptor); builder.build(); - return new NullnessInformation(program, builder.synthesizedVariables, builder.phiUpdater, - builder.notNullVariables, builder.nullVariables); + return new NullnessInformation(program, builder.synthesizedVariables, builder.phiUpdater, builder.statuses); } } diff --git a/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java b/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java index 5258867f0..6423299f3 100644 --- a/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java +++ b/core/src/main/java/org/teavm/model/analysis/NullnessInformationBuilder.java @@ -18,11 +18,10 @@ package org.teavm.model.analysis; import com.carrotsearch.hppc.IntArrayDeque; import com.carrotsearch.hppc.IntDeque; import com.carrotsearch.hppc.IntHashSet; -import com.carrotsearch.hppc.IntIntHashMap; -import com.carrotsearch.hppc.IntIntMap; import com.carrotsearch.hppc.IntSet; import java.util.ArrayList; import java.util.BitSet; +import java.util.Collections; import java.util.List; import org.teavm.common.Graph; import org.teavm.common.GraphBuilder; @@ -33,6 +32,7 @@ import org.teavm.model.Instruction; import org.teavm.model.MethodDescriptor; import org.teavm.model.Phi; import org.teavm.model.Program; +import org.teavm.model.Sigma; import org.teavm.model.Variable; import org.teavm.model.instructions.AbstractInstructionVisitor; import org.teavm.model.instructions.ArrayLengthInstruction; @@ -60,15 +60,13 @@ import org.teavm.model.util.PhiUpdater; class NullnessInformationBuilder { private Program program; private MethodDescriptor methodDescriptor; - BitSet notNullVariables = new BitSet(); - BitSet nullVariables = new BitSet(); BitSet synthesizedVariables = new BitSet(); PhiUpdater phiUpdater; - private List nullInstructions = new ArrayList<>(); private List notNullInstructions = new ArrayList<>(); private Graph assignmentGraph; private int[] notNullPredecessorsLeft; - private int[] sccIndexes; + Nullness[] statuses; + private int[][] variablePairs; NullnessInformationBuilder(Program program, MethodDescriptor methodDescriptor) { this.program = program; @@ -77,20 +75,65 @@ class NullnessInformationBuilder { void build() { extendProgram(); + buildVariablePairs(); buildAssignmentGraph(); propagateNullness(); } + private void buildVariablePairs() { + List pairsBuilder = new ArrayList<>( + Collections.nCopies(program.variableCount(), null)); + + for (BasicBlock block : program.getBasicBlocks()) { + Instruction lastInstruction = block.getLastInstruction(); + if (lastInstruction instanceof BinaryBranchingInstruction) { + BinaryBranchingInstruction branching = (BinaryBranchingInstruction) lastInstruction; + addVariablePair(pairsBuilder, branching.getFirstOperand(), branching.getSecondOperand()); + addVariablePair(pairsBuilder, branching.getSecondOperand(), branching.getFirstOperand()); + } + } + + variablePairs = new int[pairsBuilder.size()][]; + for (int i = 0; i < variablePairs.length; ++i) { + IntSet itemBuilder = pairsBuilder.get(i); + variablePairs[i] = itemBuilder != null ? itemBuilder.toArray() : null; + } + } + + private void addVariablePair(List target, Variable first, Variable second) { + IntSet pairs = target.get(first.getIndex()); + if (pairs == null) { + pairs = new IntHashSet(); + target.set(first.getIndex(), pairs); + } + pairs.add(second.getIndex()); + } + private void extendProgram() { - notNullVariables.set(0); insertAdditionalVariables(); - Variable[] parameters = new Variable[methodDescriptor.parameterCount() + 1]; - for (int i = 0; i < parameters.length; ++i) { - parameters[i] = program.variableAt(i); - } phiUpdater = new PhiUpdater(); - phiUpdater.updatePhis(program, parameters); + phiUpdater.setSigmaPredicate(instruction -> { + if (instruction instanceof BinaryBranchingInstruction) { + switch (((BinaryBranchingInstruction) instruction).getCondition()) { + case REFERENCE_EQUAL: + case REFERENCE_NOT_EQUAL: + return true; + default: + break; + } + } else if (instruction instanceof BranchingInstruction) { + switch (((BranchingInstruction) instruction).getCondition()) { + case NULL: + case NOT_NULL: + return true; + default: + break; + } + } + return false; + }); + phiUpdater.updatePhis(program, methodDescriptor.parameterCount() + 1); collectAdditionalVariables(); } @@ -102,16 +145,10 @@ class NullnessInformationBuilder { } private void collectAdditionalVariables() { - for (NullConstantInstruction nullInstruction : nullInstructions) { - nullVariables.set(nullInstruction.getReceiver().getIndex()); - synthesizedVariables.set(nullInstruction.getReceiver().getIndex()); - } for (NullCheckInstruction notNullInstruction : notNullInstructions) { - notNullVariables.set(notNullInstruction.getReceiver().getIndex()); synthesizedVariables.set(notNullInstruction.getReceiver().getIndex()); } - nullInstructions.clear(); notNullInstructions.clear(); } @@ -132,55 +169,110 @@ class NullnessInformationBuilder { } } } - assignmentGraph = builder.build(); - - sccIndexes = new int[program.variableCount()]; - if (assignmentGraph.size() > 0) { - int[][] sccs = GraphUtils.findStronglyConnectedComponents(assignmentGraph); - for (int i = 0; i < sccs.length; ++i) { - for (int sccNode : sccs[i]) { - sccIndexes[sccNode] = i + 1; - } - } - } + assignmentGraph = GraphUtils.removeLoops(builder.build()); notNullPredecessorsLeft = new int[assignmentGraph.size()]; for (int i = 0; i < assignmentGraph.size(); ++i) { notNullPredecessorsLeft[i] = assignmentGraph.incomingEdgesCount(i); - if (sccIndexes[i] > 0) { - for (int predecessor : assignmentGraph.outgoingEdges(i)) { - if (sccIndexes[predecessor] == sccIndexes[i]) { - notNullPredecessorsLeft[i]--; - } - } - } } } - private void propagateNullness() { - if (assignmentGraph.size() == 0) { - return; + private void initNullness(IntDeque queue) { + NullnessInitVisitor visitor = new NullnessInitVisitor(queue); + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + instruction.acceptVisitor(visitor); + } + + Instruction last = block.getLastInstruction(); + if (!(last instanceof BranchingInstruction)) { + continue; + } + BranchingInstruction branching = (BranchingInstruction) last; + Sigma[] sigmas = phiUpdater.getSigmasAt(block.getIndex()); + if (sigmas == null) { + continue; + } + + Sigma sigma = null; + for (int i = 0; i < sigmas.length; ++i) { + if (sigmas[i].getValue() == branching.getOperand()) { + sigma = sigmas[i]; + break; + } + } + if (sigma == null) { + continue; + } + + Variable trueVar; + Variable falseVar; + if (sigma.getOutgoings().get(0).getTarget() == branching.getConsequent()) { + trueVar = sigma.getOutgoings().get(0).getValue(); + falseVar = sigma.getOutgoings().get(1).getValue(); + } else { + trueVar = sigma.getOutgoings().get(1).getValue(); + falseVar = sigma.getOutgoings().get(0).getValue(); + } + + switch (branching.getCondition()) { + case NULL: + queue.addLast(trueVar.getIndex()); + queue.addLast(0); + queue.addLast(falseVar.getIndex()); + queue.addLast(1); + break; + case NOT_NULL: + queue.addLast(trueVar.getIndex()); + queue.addLast(1); + queue.addLast(falseVar.getIndex()); + queue.addLast(0); + break; + default: + break; + } } + queue.addLast(0); + queue.addLast(1); + } + + private void propagateNullness() { + statuses = new Nullness[program.variableCount()]; + IntDeque deque = new IntArrayDeque(); - for (int i = notNullVariables.nextSetBit(0); i >= 0; i = notNullVariables.nextSetBit(i + 1)) { - deque.addLast(i); - } - boolean[] visited = new boolean[program.variableCount()]; + initNullness(deque); while (!deque.isEmpty()) { int node = deque.removeFirst(); - if (visited[node]) { + if (statuses[node] != null) { + deque.removeFirst(); continue; } - visited[node] = true; - notNullVariables.set(node); - for (int successor : assignmentGraph.outgoingEdges(node)) { - if (sccIndexes[successor] == 0 || sccIndexes[successor] != sccIndexes[node]) { - if (--notNullPredecessorsLeft[successor] == 0) { - deque.addLast(successor); + Nullness status = deque.removeFirst() == 1 ? Nullness.NOT_NULL : Nullness.NULL; + statuses[node] = status; + + int[] pairs = variablePairs[node]; + if (pairs != null) { + int pairStatus = status == Nullness.NULL ? 1 : 0; + for (int pair : pairs) { + deque.addLast(pair); + deque.addLast(pairStatus); + } + } + + successors: for (int successor : assignmentGraph.outgoingEdges(node)) { + if (--notNullPredecessorsLeft[successor] != 0) { + continue; + } + + for (int sibling : assignmentGraph.incomingEdges(successor)) { + if (statuses[sibling] != statuses[node]) { + continue successors; } } + deque.addLast(successor); + deque.addLast(statuses[node] == Nullness.NULL ? 0 : 1); } } } @@ -188,8 +280,11 @@ class NullnessInformationBuilder { class NullExtensionVisitor extends AbstractInstructionVisitor implements DominatorWalkerCallback { State currentState; BasicBlock currentBlock; - IntIntMap nullSuccessors = new IntIntHashMap(); - IntIntMap notNullSuccessors = new IntIntHashMap(); + BitSet notNullVariables = new BitSet(); + + NullExtensionVisitor() { + notNullVariables.set(0); + } @Override public State visit(BasicBlock block) { @@ -200,15 +295,6 @@ class NullnessInformationBuilder { } currentBlock = block; - if (nullSuccessors.containsKey(block.getIndex())) { - int varIndex = nullSuccessors.remove(block.getIndex()); - insertNullInstruction(program.variableAt(varIndex)); - } - if (notNullSuccessors.containsKey(block.getIndex())) { - int varIndex = notNullSuccessors.remove(block.getIndex()); - insertNotNullInstruction(null, program.variableAt(varIndex)); - } - for (Instruction insn : block) { insn.acceptVisitor(this); } @@ -221,9 +307,6 @@ class NullnessInformationBuilder { for (int rollbackToNull : state.newlyNonNull.toArray()) { notNullVariables.clear(rollbackToNull); } - for (int rollbackToNotNull : state.newlyNull.toArray()) { - nullVariables.clear(rollbackToNotNull); - } } @Override @@ -274,104 +357,32 @@ class NullnessInformationBuilder { @Override public void visit(StringConstantInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex()); + markAsNotNull(insn.getReceiver()); } @Override public void visit(ClassConstantInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex()); - } - - @Override - public void visit(NullConstantInstruction insn) { - nullVariables.set(insn.getReceiver().getIndex()); - } - - @Override - public void visit(AssignInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex(), notNullVariables.get(insn.getAssignee().getIndex())); - nullVariables.set(insn.getReceiver().getIndex(), nullVariables.get(insn.getAssignee().getIndex())); + markAsNotNull(insn.getReceiver()); } @Override public void visit(ConstructArrayInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex()); + markAsNotNull(insn.getReceiver()); } @Override public void visit(ConstructInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex()); + markAsNotNull(insn.getReceiver()); } @Override public void visit(ConstructMultiArrayInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex()); + markAsNotNull(insn.getReceiver()); } @Override public void visit(NullCheckInstruction insn) { - notNullVariables.set(insn.getReceiver().getIndex()); - } - - @Override - public void visit(BranchingInstruction insn) { - switch (insn.getCondition()) { - case NOT_NULL: - setNotNullSuccessor(insn.getConsequent(), insn.getOperand()); - setNullSuccessor(insn.getAlternative(), insn.getOperand()); - break; - case NULL: - setNullSuccessor(insn.getConsequent(), insn.getOperand()); - setNotNullSuccessor(insn.getAlternative(), insn.getOperand()); - break; - default: - break; - } - } - - @Override - public void visit(BinaryBranchingInstruction insn) { - Variable first = insn.getFirstOperand(); - Variable second = insn.getSecondOperand(); - if (nullVariables.get(first.getIndex())) { - first = second; - } else if (!nullVariables.get(second.getIndex())) { - return; - } - - switch (insn.getCondition()) { - case REFERENCE_EQUAL: - setNotNullSuccessor(insn.getConsequent(), first); - setNullSuccessor(insn.getAlternative(), first); - break; - case REFERENCE_NOT_EQUAL: - setNullSuccessor(insn.getConsequent(), first); - setNotNullSuccessor(insn.getAlternative(), first); - break; - default: - break; - } - } - - private void setNullSuccessor(BasicBlock successor, Variable value) { - if (shouldSetSuccessor(successor, value)) { - nullSuccessors.put(successor.getIndex(), value.getIndex()); - } - } - - private void setNotNullSuccessor(BasicBlock successor, Variable value) { - if (shouldSetSuccessor(successor, value)) { - notNullSuccessors.put(successor.getIndex(), value.getIndex()); - } - } - - private boolean shouldSetSuccessor(BasicBlock successor, Variable value) { - for (Phi phi : successor.getPhis()) { - if (phi.getIncomings().stream().anyMatch(incoming -> incoming.getValue() == value)) { - return false; - } - } - return true; + markAsNotNull(insn.getReceiver()); } private void insertNotNullInstruction(Instruction currentInstruction, Variable var) { @@ -387,39 +398,71 @@ class NullnessInformationBuilder { } else { currentBlock.addFirst(insn); } - markAsNonNull(var); - } - - private void insertNullInstruction(Variable var) { - if (nullVariables.get(var.getIndex())) { - return; - } - NullConstantInstruction insn = new NullConstantInstruction(); - insn.setReceiver(var); - nullInstructions.add(insn); - currentBlock.addFirst(insn); - markAsNull(var); - } - - private void markAsNonNull(Variable var) { - if (notNullVariables.get(var.getIndex())) { - return; - } - notNullVariables.set(var.getIndex()); + markAsNotNull(var); currentState.newlyNonNull.add(var.getIndex()); } - private void markAsNull(Variable var) { - if (nullVariables.get(var.getIndex())) { - return; - } - nullVariables.set(var.getIndex()); - currentState.newlyNull.add(var.getIndex()); + private void markAsNotNull(Variable var) { + notNullVariables.set(var.getIndex()); } } static class State { IntSet newlyNonNull = new IntHashSet(); - IntSet newlyNull = new IntHashSet(); + } + + class NullnessInitVisitor extends AbstractInstructionVisitor { + private IntDeque queue; + + NullnessInitVisitor(IntDeque queue) { + this.queue = queue; + } + + @Override + public void visit(NullCheckInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(1); + } + + @Override + public void visit(NullConstantInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(0); + } + + @Override + public void visit(StringConstantInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(1); + } + + @Override + public void visit(ClassConstantInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(1); + } + + @Override + public void visit(ConstructArrayInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(1); + } + + @Override + public void visit(ConstructInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(1); + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + queue.addLast(insn.getReceiver().getIndex()); + queue.addLast(1); + } + } + + enum Nullness { + NULL, + NOT_NULL } } diff --git a/core/src/main/java/org/teavm/model/classes/TagRegistry.java b/core/src/main/java/org/teavm/model/classes/TagRegistry.java index 38b711e9a..29991f77b 100644 --- a/core/src/main/java/org/teavm/model/classes/TagRegistry.java +++ b/core/src/main/java/org/teavm/model/classes/TagRegistry.java @@ -20,7 +20,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -106,7 +106,7 @@ public class TagRegistry { private void markImplementor(ClassReaderSource classSource, String className, String ifaceName, Map> implementedBy) { - if (!implementedBy.computeIfAbsent(ifaceName, key -> new HashSet<>()).add(className)) { + if (!implementedBy.computeIfAbsent(ifaceName, key -> new LinkedHashSet<>()).add(className)) { return; } @@ -120,11 +120,11 @@ public class TagRegistry { } } - private int assignRange(int start, Map> hierarhy, String className, + private int assignRange(int start, Map> hierarchy, String className, Map ranges) { int end = start + 1; - for (String childClass : hierarhy.getOrDefault(className, Collections.emptyList())) { - end = assignRange(end, hierarhy, childClass, ranges); + for (String childClass : hierarchy.getOrDefault(className, Collections.emptyList())) { + end = assignRange(end, hierarchy, childClass, ranges); } ++end; ranges.put(className, new Range(start, end)); diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java index 9c4a9ef87..de462e8bc 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java @@ -18,6 +18,7 @@ package org.teavm.model.classes; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.teavm.model.ClassReader; @@ -45,7 +46,7 @@ public class VirtualTableProvider { cls = virtualMethod.getClassName(); } classNames.add(cls); - virtualMethodMap.computeIfAbsent(cls, c -> new HashSet<>()).add(virtualMethod.getDescriptor()); + virtualMethodMap.computeIfAbsent(cls, c -> new LinkedHashSet<>()).add(virtualMethod.getDescriptor()); } for (String className : classNames) { diff --git a/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java b/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java index 4b98cd893..5cfddb6ca 100644 --- a/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java +++ b/core/src/main/java/org/teavm/model/emit/ProgramEmitter.java @@ -46,7 +46,7 @@ import org.teavm.model.instructions.NullConstantInstruction; import org.teavm.model.instructions.PutFieldInstruction; import org.teavm.model.instructions.StringConstantInstruction; import org.teavm.model.instructions.SwitchInstruction; -import org.teavm.model.util.InstructionTransitionExtractor; +import org.teavm.model.util.TransitionExtractor; public final class ProgramEmitter { private Program program; @@ -467,7 +467,7 @@ public final class ProgramEmitter { if (insn == null) { return false; } - InstructionTransitionExtractor extractor = new InstructionTransitionExtractor(); + TransitionExtractor extractor = new TransitionExtractor(); insn.acceptVisitor(extractor); return extractor.getTargets() != null; } diff --git a/core/src/main/java/org/teavm/backend/c/analyze/Characteristics.java b/core/src/main/java/org/teavm/model/lowlevel/Characteristics.java similarity index 74% rename from core/src/main/java/org/teavm/backend/c/analyze/Characteristics.java rename to core/src/main/java/org/teavm/model/lowlevel/Characteristics.java index d3a66a9b7..2a1615eb9 100644 --- a/core/src/main/java/org/teavm/backend/c/analyze/Characteristics.java +++ b/core/src/main/java/org/teavm/model/lowlevel/Characteristics.java @@ -13,21 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.c.analyze; +package org.teavm.model.lowlevel; import com.carrotsearch.hppc.ObjectByteHashMap; import com.carrotsearch.hppc.ObjectByteMap; import org.teavm.interop.Function; import org.teavm.interop.StaticInit; import org.teavm.interop.Structure; +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 Characteristics { private ClassReaderSource classSource; private ObjectByteMap isStructure = new ObjectByteHashMap<>(); private ObjectByteMap isStaticInit = new ObjectByteHashMap<>(); private ObjectByteMap isFunction = new ObjectByteHashMap<>(); + private ObjectByteMap isManaged = new ObjectByteHashMap<>(); public Characteristics(ClassReaderSource classSource) { this.classSource = classSource; @@ -78,4 +82,26 @@ public class Characteristics { } return result != 0; } + + public boolean isManaged(MethodReference methodReference) { + byte result = isManaged.getOrDefault(methodReference, (byte) -1); + if (result < 0) { + result = computeIsManaged(methodReference) ? (byte) 1 : 0; + isManaged.put(methodReference, result); + } + return result != 0; + } + + private boolean computeIsManaged(MethodReference methodReference) { + MethodReader method = classSource.resolve(methodReference); + if (method == null) { + return true; + } + + ClassReader cls = classSource.get(method.getOwnerName()); + if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) { + return false; + } + return method == null || method.getAnnotations().get(Unmanaged.class.getName()) == null; + } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java index c6d340eb4..f34397491 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java @@ -59,7 +59,7 @@ import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ShadowStack; public class ExceptionHandlingShadowStackContributor { - private ManagedMethodRepository managedMethodRepository; + private Characteristics characteristics; private List callSites; private BasicBlock defaultExceptionHandler; private MethodReference method; @@ -67,10 +67,11 @@ public class ExceptionHandlingShadowStackContributor { private DominatorTree dom; private BasicBlock[] variableDefinitionPlaces; private boolean hasExceptionHandlers; + private int parameterCount; - public ExceptionHandlingShadowStackContributor(ManagedMethodRepository managedMethodRepository, + public ExceptionHandlingShadowStackContributor(Characteristics characteristics, List callSites, MethodReference method, Program program) { - this.managedMethodRepository = managedMethodRepository; + this.characteristics = characteristics; this.callSites = callSites; this.method = method; this.program = program; @@ -78,6 +79,7 @@ public class ExceptionHandlingShadowStackContributor { Graph cfg = ProgramUtils.buildControlFlowGraph(program); dom = GraphUtils.buildDominatorTree(cfg); variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program); + parameterCount = method.parameterCount() + 1; } public boolean contribute() { @@ -145,7 +147,8 @@ public class ExceptionHandlingShadowStackContributor { for (Variable sourceVar : sourceVariables) { BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()]; - if (dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())) { + if (sourceVar.getIndex() < parameterCount + || dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())) { currentJointSources[phi.getReceiver().getIndex()] = sourceVar.getIndex(); break; } @@ -164,7 +167,14 @@ public class ExceptionHandlingShadowStackContributor { if (isCallInstruction(insn)) { BasicBlock next; boolean last = false; - if (insn instanceof RaiseInstruction) { + if (isSpecialCallInstruction(insn)) { + next = null; + while (insn.getNext() != null) { + Instruction nextInsn = insn.getNext(); + nextInsn.delete(); + } + last = true; + } else if (insn instanceof RaiseInstruction) { InvokeInstruction raise = new InvokeInstruction(); raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class, void.class)); @@ -229,11 +239,24 @@ public class ExceptionHandlingShadowStackContributor { || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) { return true; } else if (insn instanceof InvokeInstruction) { - return managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod()); + MethodReference method = ((InvokeInstruction) insn).getMethod(); + if (characteristics.isManaged(method)) { + return true; + } + return method.getClassName().equals(ExceptionHandling.class.getName()) + && method.getName().startsWith("throw"); } return false; } + private boolean isSpecialCallInstruction(Instruction insn) { + if (!(insn instanceof InvokeInstruction)) { + return false; + } + MethodReference method = ((InvokeInstruction) insn).getMethod(); + return method.getClassName().equals(ExceptionHandling.class.getName()) && method.getName().startsWith("throw"); + } + private List setLocation(List instructions, TextLocation location) { if (location != null) { for (Instruction instruction : instructions) { @@ -318,7 +341,12 @@ public class ExceptionHandlingShadowStackContributor { switchInsn.setDefaultTarget(getDefaultExceptionHandler()); } - if (switchInsn.getEntries().size() == 1) { + if (switchInsn.getEntries().isEmpty()) { + instructions.clear(); + JumpInstruction jump = new JumpInstruction(); + jump.setTarget(switchInsn.getDefaultTarget()); + instructions.add(jump); + } else if (switchInsn.getEntries().size() == 1) { SwitchTableEntry entry = switchInsn.getEntries().get(0); IntegerConstantInstruction singleTestConstant = new IntegerConstantInstruction(); diff --git a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java index 160863488..c0ca218db 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java @@ -25,7 +25,8 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -60,10 +61,10 @@ import org.teavm.model.util.VariableType; import org.teavm.runtime.ShadowStack; public class GCShadowStackContributor { - private ManagedMethodRepository managedMethodRepository; + private Characteristics characteristics; - public GCShadowStackContributor(ManagedMethodRepository managedMethodRepository) { - this.managedMethodRepository = managedMethodRepository; + public GCShadowStackContributor(Characteristics characteristics) { + this.characteristics = characteristics; } public int contribute(Program program, MethodReader method) { @@ -156,7 +157,7 @@ public class GCShadowStackContributor { || insn instanceof ConstructMultiArrayInstruction || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) { if (insn instanceof InvokeInstruction - && !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) { + && !characteristics.isManaged(((InvokeInstruction) insn).getMethod())) { continue; } @@ -220,7 +221,7 @@ public class GCShadowStackContributor { for (Incoming incoming : phi.getIncomings()) { Set phis = destinationPhis.get(incoming.getValue().getIndex()); if (phis == null) { - phis = new HashSet<>(); + phis = new LinkedHashSet<>(); destinationPhis.set(incoming.getValue().getIndex(), phis); } phis.add(phi); @@ -256,7 +257,7 @@ public class GCShadowStackContributor { List> slotsToUpdate = new ArrayList<>(); for (int i = 0; i < program.basicBlockCount(); ++i) { - slotsToUpdate.add(new HashMap<>()); + slotsToUpdate.add(new LinkedHashMap<>()); } Graph cfg = ProgramUtils.buildControlFlowGraph(program); diff --git a/core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java b/core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java deleted file mode 100644 index 59af9ab65..000000000 --- a/core/src/main/java/org/teavm/model/lowlevel/ManagedMethodRepository.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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) { - MethodReader method = classSource.resolve(methodReference); - if (method == null) { - return true; - } - - ClassReader cls = classSource.get(method.getOwnerName()); - if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) { - return false; - } - return method == null || method.getAnnotations().get(Unmanaged.class.getName()) == null; - } -} diff --git a/core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java b/core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java new file mode 100644 index 000000000..920247f62 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java @@ -0,0 +1,126 @@ +/* + * Copyright 2018 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.function.BiConsumer; +import java.util.function.Function; +import org.teavm.model.BasicBlock; +import org.teavm.model.Instruction; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.Variable; +import org.teavm.model.instructions.AbstractInstructionVisitor; +import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.MonitorEnterInstruction; +import org.teavm.model.instructions.MonitorExitInstruction; +import org.teavm.model.instructions.NullCheckInstruction; +import org.teavm.model.instructions.PutFieldInstruction; +import org.teavm.model.instructions.RaiseInstruction; +import org.teavm.model.instructions.UnwrapArrayInstruction; +import org.teavm.model.util.PhiUpdater; + +public class NullCheckInsertion { + private Characteristics characteristics; + + public NullCheckInsertion(Characteristics characteristics) { + this.characteristics = characteristics; + } + + public void transformProgram(Program program, MethodReference methodReference) { + if (!characteristics.isManaged(methodReference)) { + return; + } + + InsertionVisitor visitor = new InsertionVisitor(); + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + instruction.acceptVisitor(visitor); + } + } + if (visitor.changed) { + new PhiUpdater().updatePhis(program, methodReference.parameterCount() + 1); + } + } + + class InsertionVisitor extends AbstractInstructionVisitor { + boolean changed; + + @Override + public void visit(RaiseInstruction insn) { + addGuard(insn, RaiseInstruction::getException, RaiseInstruction::setException); + } + + @Override + public void visit(GetFieldInstruction insn) { + if (!characteristics.isStructure(insn.getField().getClassName())) { + addGuard(insn, GetFieldInstruction::getInstance, GetFieldInstruction::setInstance); + } + } + + @Override + public void visit(PutFieldInstruction insn) { + if (!characteristics.isStructure(insn.getField().getClassName())) { + addGuard(insn, PutFieldInstruction::getInstance, PutFieldInstruction::setInstance); + } + } + + @Override + public void visit(CloneArrayInstruction insn) { + addGuard(insn, CloneArrayInstruction::getArray, CloneArrayInstruction::setArray); + } + + @Override + public void visit(UnwrapArrayInstruction insn) { + addGuard(insn, UnwrapArrayInstruction::getArray, UnwrapArrayInstruction::setArray); + } + + @Override + public void visit(InvokeInstruction insn) { + if (!characteristics.isStructure(insn.getMethod().getClassName()) + && characteristics.isManaged(insn.getMethod())) { + addGuard(insn, InvokeInstruction::getInstance, InvokeInstruction::setInstance); + } + } + + @Override + public void visit(MonitorEnterInstruction insn) { + addGuard(insn, MonitorEnterInstruction::getObjectRef, MonitorEnterInstruction::setObjectRef); + } + + @Override + public void visit(MonitorExitInstruction insn) { + addGuard(insn, MonitorExitInstruction::getObjectRef, MonitorExitInstruction::setObjectRef); + } + + private void addGuard(T instruction, Function get, + BiConsumer set) { + Variable value = get.apply(instruction); + if (value == null) { + return; + } + + NullCheckInstruction nullCheck = new NullCheckInstruction(); + nullCheck.setValue(value); + nullCheck.setReceiver(value); + nullCheck.setLocation(instruction.getLocation()); + set.accept(instruction, nullCheck.getReceiver()); + instruction.insertPrevious(nullCheck); + changed = true; + } + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java b/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java new file mode 100644 index 000000000..e564f0011 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/NullCheckTransformation.java @@ -0,0 +1,174 @@ +/* + * Copyright 2018 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 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.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.BranchingCondition; +import org.teavm.model.instructions.BranchingInstruction; +import org.teavm.model.instructions.DoubleConstantInstruction; +import org.teavm.model.instructions.ExitInstruction; +import org.teavm.model.instructions.FloatConstantInstruction; +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.LongConstantInstruction; +import org.teavm.model.instructions.NullCheckInstruction; +import org.teavm.model.instructions.NullConstantInstruction; +import org.teavm.model.optimization.RedundantJumpElimination; +import org.teavm.model.util.ProgramUtils; +import org.teavm.runtime.ExceptionHandling; + +public class NullCheckTransformation { + public void apply(Program program, ValueType returnType) { + int[] mappings = new int[program.basicBlockCount()]; + for (int i = 0; i < mappings.length; ++i) { + mappings[i] = i; + } + + BasicBlock returnBlock = null; + + int count = program.basicBlockCount(); + for (int i = 0; i < count; ++i) { + BasicBlock next = program.basicBlockAt(i); + BasicBlock block = null; + int newIndex = i; + while (next != null) { + block = next; + newIndex = block.getIndex(); + next = null; + for (Instruction instruction : block) { + if (!(instruction instanceof NullCheckInstruction)) { + continue; + } + NullCheckInstruction nullCheck = (NullCheckInstruction) instruction; + + BasicBlock continueBlock = program.createBasicBlock(); + + continueBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); + while (nullCheck.getNext() != null) { + Instruction nextInstruction = nullCheck.getNext(); + nextInstruction.delete(); + continueBlock.add(nextInstruction); + } + + BasicBlock throwBlock = program.createBasicBlock(); + + throwBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); + InvokeInstruction throwNPE = new InvokeInstruction(); + throwNPE.setType(InvocationType.SPECIAL); + throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException", + void.class)); + throwNPE.setLocation(nullCheck.getLocation()); + throwBlock.add(throwNPE); + + if (returnBlock == null) { + returnBlock = program.createBasicBlock(); + } + + JumpInstruction jumpToFakeReturn = new JumpInstruction(); + jumpToFakeReturn.setTarget(returnBlock); + jumpToFakeReturn.setLocation(nullCheck.getLocation()); + throwBlock.add(jumpToFakeReturn); + + BranchingInstruction jumpIfNull = new BranchingInstruction(BranchingCondition.NULL); + jumpIfNull.setOperand(nullCheck.getValue()); + jumpIfNull.setConsequent(throwBlock); + jumpIfNull.setAlternative(continueBlock); + jumpIfNull.setLocation(nullCheck.getLocation()); + nullCheck.replace(jumpIfNull); + + AssignInstruction assign = new AssignInstruction(); + assign.setAssignee(nullCheck.getValue()); + assign.setReceiver(nullCheck.getReceiver()); + assign.setLocation(nullCheck.getLocation()); + continueBlock.addFirst(assign); + + next = continueBlock; + break; + } + } + + mappings[i] = newIndex; + } + + if (returnBlock != null) { + ExitInstruction fakeExit = new ExitInstruction(); + if (returnType != ValueType.VOID) { + Variable fakeReturnVar = program.createVariable(); + createFakeReturnValue(returnBlock, fakeReturnVar, returnType); + fakeExit.setValueToReturn(fakeReturnVar); + } + returnBlock.add(fakeExit); + } + + for (BasicBlock block : program.getBasicBlocks()) { + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + int source = incoming.getSource().getIndex(); + if (source < mappings.length && mappings[source] != source) { + incoming.setSource(program.basicBlockAt(mappings[source])); + } + } + } + } + + RedundantJumpElimination.optimize(program); + } + + private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) { + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + case BYTE: + case SHORT: + case INTEGER: + case CHARACTER: + IntegerConstantInstruction intZero = new IntegerConstantInstruction(); + intZero.setReceiver(variable); + block.add(intZero); + return; + case LONG: + LongConstantInstruction longZero = new LongConstantInstruction(); + longZero.setReceiver(variable); + block.add(longZero); + return; + case FLOAT: + FloatConstantInstruction floatZero = new FloatConstantInstruction(); + floatZero.setReceiver(variable); + block.add(floatZero); + return; + case DOUBLE: + DoubleConstantInstruction doubleZero = new DoubleConstantInstruction(); + doubleZero.setReceiver(variable); + block.add(doubleZero); + return; + } + } + + NullConstantInstruction nullConstant = new NullConstantInstruction(); + nullConstant.setReceiver(variable); + block.add(nullConstant); + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java index 9393bb12b..b06ff7d9c 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java @@ -18,7 +18,6 @@ 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; @@ -34,13 +33,13 @@ import org.teavm.model.instructions.JumpInstruction; import org.teavm.runtime.ShadowStack; public class ShadowStackTransformer { - private ManagedMethodRepository managedMethodRepository; + private Characteristics managedMethodRepository; private GCShadowStackContributor gcContributor; private List callSites = new ArrayList<>(); - public ShadowStackTransformer(ClassReaderSource classSource) { - managedMethodRepository = new ManagedMethodRepository(classSource); + public ShadowStackTransformer(Characteristics managedMethodRepository) { gcContributor = new GCShadowStackContributor(managedMethodRepository); + this.managedMethodRepository = managedMethodRepository; } public List getCallSites() { diff --git a/core/src/main/java/org/teavm/model/optimization/ConstantConditionElimination.java b/core/src/main/java/org/teavm/model/optimization/ConstantConditionElimination.java index 06af5173a..03d662b5b 100644 --- a/core/src/main/java/org/teavm/model/optimization/ConstantConditionElimination.java +++ b/core/src/main/java/org/teavm/model/optimization/ConstantConditionElimination.java @@ -17,27 +17,33 @@ package org.teavm.model.optimization; import org.teavm.model.BasicBlock; import org.teavm.model.Instruction; +import org.teavm.model.MethodDescriptor; import org.teavm.model.Phi; import org.teavm.model.Program; +import org.teavm.model.analysis.NullnessInformation; import org.teavm.model.instructions.BinaryBranchingCondition; import org.teavm.model.instructions.BinaryBranchingInstruction; import org.teavm.model.instructions.BranchingCondition; import org.teavm.model.instructions.BranchingInstruction; import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.JumpInstruction; -import org.teavm.model.instructions.NullConstantInstruction; -import org.teavm.model.util.InstructionTransitionExtractor; +import org.teavm.model.util.TransitionExtractor; public class ConstantConditionElimination implements MethodOptimization { private int[] constants; private boolean[] constantDefined; - private boolean[] nullConstants; + private NullnessInformation nullness; @Override public boolean optimize(MethodOptimizationContext context, Program program) { + return optimize(context.getMethod().getDescriptor(), program); + } + + public boolean optimize(MethodDescriptor descriptor, Program program) { constants = new int[program.variableCount()]; constantDefined = new boolean[program.variableCount()]; - nullConstants = new boolean[program.variableCount()]; + nullness = NullnessInformation.build(program, descriptor); + for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); for (Instruction insn : block) { @@ -46,16 +52,12 @@ public class ConstantConditionElimination implements MethodOptimization { int receiver = constInsn.getReceiver().getIndex(); constants[receiver] = constInsn.getConstant(); constantDefined[receiver] = true; - } else if (insn instanceof NullConstantInstruction) { - NullConstantInstruction constInsn = (NullConstantInstruction) insn; - int receiver = constInsn.getReceiver().getIndex(); - nullConstants[receiver] = true; } } } boolean changed = false; - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); Instruction insn = block.getLastInstruction(); @@ -84,6 +86,11 @@ public class ConstantConditionElimination implements MethodOptimization { } } + nullness.dispose(); + nullness = null; + constantDefined = null; + constants = null; + if (changed) { new UnreachableBasicBlockEliminator().optimize(program); } @@ -96,13 +103,17 @@ public class ConstantConditionElimination implements MethodOptimization { BranchingInstruction branching = (BranchingInstruction) instruction; switch (branching.getCondition()) { case NULL: - if (nullConstants[branching.getOperand().getIndex()]) { + if (nullness.isNull(branching.getOperand())) { return branching.getConsequent(); + } else if (nullness.isNotNull(branching.getOperand())) { + return branching.getAlternative(); } break; case NOT_NULL: - if (nullConstants[branching.getOperand().getIndex()]) { + if (nullness.isNull(branching.getOperand())) { return branching.getAlternative(); + } else if (nullness.isNotNull(branching.getOperand())) { + return branching.getConsequent(); } break; default: { 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 afa8e6a0e..30c12f6ca 100644 --- a/core/src/main/java/org/teavm/model/optimization/Inlining.java +++ b/core/src/main/java/org/teavm/model/optimization/Inlining.java @@ -46,9 +46,9 @@ 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.InstructionTransitionExtractor; import org.teavm.model.util.InstructionVariableMapper; import org.teavm.model.util.ProgramUtils; +import org.teavm.model.util.TransitionExtractor; public class Inlining { private static final int DEFAULT_THRESHOLD = 17; @@ -191,7 +191,7 @@ public class Inlining { } } - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); Instruction splitLastInsn = splitBlock.getLastInstruction(); if (splitLastInsn != null) { splitLastInsn.acceptVisitor(transitionExtractor); diff --git a/core/src/main/java/org/teavm/model/optimization/RedundantJumpElimination.java b/core/src/main/java/org/teavm/model/optimization/RedundantJumpElimination.java index cc16a09d0..3ec80b959 100644 --- a/core/src/main/java/org/teavm/model/optimization/RedundantJumpElimination.java +++ b/core/src/main/java/org/teavm/model/optimization/RedundantJumpElimination.java @@ -25,18 +25,22 @@ import org.teavm.model.Program; import org.teavm.model.TryCatchBlock; import org.teavm.model.instructions.AssignInstruction; import org.teavm.model.instructions.JumpInstruction; -import org.teavm.model.util.InstructionTransitionExtractor; import org.teavm.model.util.ProgramUtils; +import org.teavm.model.util.TransitionExtractor; public class RedundantJumpElimination implements MethodOptimization { @Override public boolean optimize(MethodOptimizationContext context, Program program) { + return optimize(program); + } + + public static boolean optimize(Program program) { Graph cfg = ProgramUtils.buildControlFlowGraph(program); int[] incomingCount = new int[cfg.size()]; Arrays.setAll(incomingCount, cfg::incomingEdgesCount); boolean changed = false; - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); for (int i = 1; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); if (block == null) { diff --git a/core/src/main/java/org/teavm/model/optimization/RedundantNullCheckElimination.java b/core/src/main/java/org/teavm/model/optimization/RedundantNullCheckElimination.java new file mode 100644 index 000000000..e740c842c --- /dev/null +++ b/core/src/main/java/org/teavm/model/optimization/RedundantNullCheckElimination.java @@ -0,0 +1,71 @@ +/* + * Copyright 2018 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.optimization; + +import org.teavm.model.BasicBlock; +import org.teavm.model.Instruction; +import org.teavm.model.Program; +import org.teavm.model.analysis.NullnessInformation; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.NullCheckInstruction; +import org.teavm.model.util.ProgramUtils; + +public class RedundantNullCheckElimination implements MethodOptimization { + @Override + public boolean optimize(MethodOptimizationContext context, Program program) { + NullnessInformation nullness = NullnessInformation.build(program, context.getMethod().getDescriptor()); + + boolean hasChanges = false; + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + if (!(instruction instanceof NullCheckInstruction)) { + continue; + } + + NullCheckInstruction nullCheck = (NullCheckInstruction) instruction; + if (nullness.isSynthesized(nullCheck.getReceiver())) { + continue; + } + + if (nullness.isNotNull(nullCheck.getValue())) { + AssignInstruction assign = new AssignInstruction(); + assign.setAssignee(nullCheck.getValue()); + assign.setReceiver(nullCheck.getReceiver()); + assign.setLocation(nullCheck.getLocation()); + nullCheck.replace(assign); + hasChanges = true; + } else if (nullness.isNull(nullCheck.getValue())) { + block.detachSuccessors(); + while (nullCheck.getNext() != null) { + nullCheck.delete(); + } + + nullCheck.insertPreviousAll(ProgramUtils.createThrowNPEInstructions( + program, nullCheck.getLocation())); + nullCheck.delete(); + hasChanges = true; + } + } + } + + if (hasChanges) { + new UnreachableBasicBlockEliminator().optimize(program); + } + + nullness.dispose(); + return hasChanges; + } +} diff --git a/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java b/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java index be87f579d..ab813854f 100644 --- a/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java +++ b/core/src/main/java/org/teavm/model/optimization/ScalarReplacement.java @@ -38,6 +38,7 @@ import org.teavm.model.instructions.FloatConstantInstruction; import org.teavm.model.instructions.GetFieldInstruction; import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.LongConstantInstruction; +import org.teavm.model.instructions.NullCheckInstruction; import org.teavm.model.instructions.NullConstantInstruction; import org.teavm.model.instructions.PutFieldInstruction; import org.teavm.model.util.PhiUpdater; @@ -96,32 +97,26 @@ public class ScalarReplacement implements MethodOptimization { } FieldReference[] fields = escapeAnalysis.getFields(phi.getReceiver().getIndex()); - if (fields == null) { - continue; - } + if (fields != null) { + for (FieldReference field : fields) { + Phi phiReplacement = new Phi(); + phiReplacement.setReceiver(fieldMappings.get(phi.getReceiver().getIndex()).get(field)); - for (FieldReference field : fields) { - boolean allIncomingsInitialized = true; - for (Incoming incoming : phi.getIncomings()) { - if (fieldMappings.get(incoming.getValue().getIndex()).get(field) == null) { - allIncomingsInitialized = false; + for (Incoming incoming : phi.getIncomings()) { + Incoming incomingReplacement = new Incoming(); + incomingReplacement.setSource(incoming.getSource()); + incomingReplacement.setValue(fieldMappings.get(incoming.getValue().getIndex()).get(field)); + phiReplacement.getIncomings().add(incomingReplacement); } - } - if (!allIncomingsInitialized) { + additionalPhis.add(phiReplacement); + } + } else { + boolean isClass = phi.getIncomings().stream() + .anyMatch(incoming -> escapeAnalysis.getFields(incoming.getValue().getIndex()) != null); + if (!isClass) { continue; } - Phi phiReplacement = new Phi(); - phiReplacement.setReceiver(fieldMappings.get(phi.getReceiver().getIndex()).get(field)); - - for (Incoming incoming : phi.getIncomings()) { - Incoming incomingReplacement = new Incoming(); - incomingReplacement.setSource(incoming.getSource()); - incomingReplacement.setValue(fieldMappings.get(incoming.getValue().getIndex()).get(field)); - phiReplacement.getIncomings().add(incomingReplacement); - } - - additionalPhis.add(phiReplacement); } block.getPhis().remove(i--); } @@ -162,6 +157,11 @@ public class ScalarReplacement implements MethodOptimization { } } + @Override + public void visit(NullCheckInstruction insn) { + transfer(insn, insn.getValue(), insn.getReceiver()); + } + @Override public void visit(GetFieldInstruction insn) { if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) { @@ -188,20 +188,24 @@ public class ScalarReplacement implements MethodOptimization { @Override public void visit(AssignInstruction insn) { - if (escapeAnalysis.escapes(insn.getAssignee().getIndex())) { + transfer(insn, insn.getAssignee(), insn.getReceiver()); + } + + private void transfer(Instruction insn, Variable from, Variable to) { + if (escapeAnalysis.escapes(from.getIndex())) { return; } - FieldReference[] fields = escapeAnalysis.getFields(insn.getReceiver().getIndex()); + FieldReference[] fields = escapeAnalysis.getFields(to.getIndex()); if (fields == null) { return; } for (FieldReference field : fields) { - Variable assignee = fieldMappings.get(insn.getAssignee().getIndex()).get(field); + Variable assignee = fieldMappings.get(from.getIndex()).get(field); if (assignee == null) { continue; } - Variable receiver = fieldMappings.get(insn.getReceiver().getIndex()).get(field); + Variable receiver = fieldMappings.get(to.getIndex()).get(field); AssignInstruction assignment = new AssignInstruction(); assignment.setReceiver(receiver); assignment.setAssignee(assignee); diff --git a/core/src/main/java/org/teavm/model/optimization/UnreachableBasicBlockEliminator.java b/core/src/main/java/org/teavm/model/optimization/UnreachableBasicBlockEliminator.java index fbd6c6002..8470819d1 100644 --- a/core/src/main/java/org/teavm/model/optimization/UnreachableBasicBlockEliminator.java +++ b/core/src/main/java/org/teavm/model/optimization/UnreachableBasicBlockEliminator.java @@ -22,14 +22,14 @@ import org.teavm.model.Incoming; import org.teavm.model.Phi; import org.teavm.model.Program; import org.teavm.model.TryCatchBlock; -import org.teavm.model.util.InstructionTransitionExtractor; +import org.teavm.model.util.TransitionExtractor; public class UnreachableBasicBlockEliminator { public void optimize(Program program) { if (program.basicBlockCount() == 0) { return; } - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); boolean[] reachable = new boolean[program.basicBlockCount()]; IntegerStack stack = new IntegerStack(program.basicBlockCount()); stack.push(0); diff --git a/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java b/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java index c295d57c1..0b3f00402 100644 --- a/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java +++ b/core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java @@ -162,7 +162,7 @@ public class AsyncProgramSplitter { queue.add(next); } } - InstructionTransitionExtractor successorExtractor = new InstructionTransitionExtractor(); + TransitionExtractor successorExtractor = new TransitionExtractor(); sourceBlock.getLastInstruction().acceptVisitor(successorExtractor); for (BasicBlock successor : successorExtractor.getTargets()) { BasicBlock targetSuccessor = targetBlock.getProgram().basicBlockAt(successor.getIndex()); diff --git a/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java b/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java index 9a17fa68f..b63374aaf 100644 --- a/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java +++ b/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java @@ -27,7 +27,7 @@ class InterferenceGraphBuilder { } UsageExtractor useExtractor = new UsageExtractor(); DefinitionExtractor defExtractor = new DefinitionExtractor(); - InstructionTransitionExtractor succExtractor = new InstructionTransitionExtractor(); + TransitionExtractor succExtractor = new TransitionExtractor(); List> outgoings = ProgramUtils.getPhiOutputs(program); BitSet live = new BitSet(); for (int i = 0; i < program.basicBlockCount(); ++i) { diff --git a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java index a4899aed0..4fb15b049 100644 --- a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java +++ b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java @@ -80,7 +80,7 @@ public class MissingItemsProcessor { } private void truncateBlock(Instruction instruction) { - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); BasicBlock block = instruction.getBasicBlock(); if (block.getLastInstruction() != null) { block.getLastInstruction().acceptVisitor(transitionExtractor); diff --git a/core/src/main/java/org/teavm/model/util/PhiUpdater.java b/core/src/main/java/org/teavm/model/util/PhiUpdater.java index c7e9f0ad9..530271027 100644 --- a/core/src/main/java/org/teavm/model/util/PhiUpdater.java +++ b/core/src/main/java/org/teavm/model/util/PhiUpdater.java @@ -23,11 +23,11 @@ import com.carrotsearch.hppc.IntObjectMap; import com.carrotsearch.hppc.IntSet; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.function.Predicate; import org.teavm.common.DominatorTree; import org.teavm.common.Graph; import org.teavm.common.GraphUtils; @@ -36,8 +36,10 @@ import org.teavm.model.BasicBlock; import org.teavm.model.Incoming; import org.teavm.model.Instruction; import org.teavm.model.InvokeDynamicInstruction; +import org.teavm.model.Outgoing; import org.teavm.model.Phi; import org.teavm.model.Program; +import org.teavm.model.Sigma; import org.teavm.model.TryCatchBlock; import org.teavm.model.Variable; import org.teavm.model.instructions.ArrayLengthInstruction; @@ -93,10 +95,11 @@ public class PhiUpdater { private List> synthesizedPhisByBlock = new ArrayList<>(); private IntObjectMap phisByReceiver = new IntObjectHashMap<>(); private BitSet usedPhis = new BitSet(); - private Variable[] originalExceptionVariables; private boolean[] usedDefinitions; private IntegerArray variableToSourceMap = new IntegerArray(10); private List synthesizedPhis = new ArrayList<>(); + private Sigma[][] sigmas; + private Predicate sigmaPredicate = instruction -> false; public int getSourceVariable(int var) { if (var >= variableToSourceMap.size()) { @@ -109,7 +112,24 @@ public class PhiUpdater { return synthesizedPhis; } - public void updatePhis(Program program, Variable[] arguments) { + public void updatePhis(Program program, int parameterCount) { + Variable[] parameters = new Variable[parameterCount]; + for (int i = 0; i < parameters.length; ++i) { + parameters[i] = program.variableAt(i); + } + updatePhis(program, parameters); + } + + public Sigma[] getSigmasAt(int blockIndex) { + Sigma[] result = sigmas[blockIndex]; + return result != null ? result.clone() : null; + } + + public void setSigmaPredicate(Predicate sigmaPredicate) { + this.sigmaPredicate = sigmaPredicate; + } + + public void updatePhis(Program program, Variable[] parameters) { if (program.basicBlockCount() == 0) { return; } @@ -122,8 +142,8 @@ public class PhiUpdater { variableMap = new Variable[program.variableCount()]; usedDefinitions = new boolean[program.variableCount()]; - for (int i = 0; i < arguments.length; ++i) { - variableMap[i] = arguments[i]; + for (int i = 0; i < parameters.length; ++i) { + variableMap[i] = parameters[i]; usedDefinitions[i] = true; } @@ -146,15 +166,55 @@ public class PhiUpdater { synthesizedPhisByBlock.add(new ArrayList<>()); } - originalExceptionVariables = new Variable[program.basicBlockCount()]; - Arrays.setAll(originalExceptionVariables, i -> program.basicBlockAt(i).getExceptionVariable()); - + estimateSigmas(); estimatePhis(); renameVariables(); propagatePhiUsageInformation(); addSynthesizedPhis(); } + private void estimateSigmas() { + TransitionExtractor transitionExtractor = new TransitionExtractor(); + UsageExtractor usageExtractor = new UsageExtractor(); + + sigmas = new Sigma[program.basicBlockCount()][]; + for (int i = 0; i < sigmas.length; ++i) { + BasicBlock block = program.basicBlockAt(i); + Instruction instruction = block.getLastInstruction(); + if (instruction == null) { + continue; + } + + instruction.acceptVisitor(transitionExtractor); + BasicBlock[] targets = transitionExtractor.getTargets(); + if (targets == null || targets.length < 2) { + continue; + } + + if (!sigmaPredicate.test(instruction)) { + continue; + } + + instruction.acceptVisitor(usageExtractor); + Variable[] variables = usageExtractor.getUsedVariables(); + + Sigma[] sigmasInBlock = new Sigma[variables.length]; + for (int j = 0; j < sigmasInBlock.length; ++j) { + Sigma sigma = new Sigma(block, variables[j]); + sigmasInBlock[j] = sigma; + for (BasicBlock target : targets) { + Variable outgoingVar = program.createVariable(); + variableToSourceMap.add(sigma.getValue().getIndex()); + outgoingVar.setDebugName(sigma.getValue().getDebugName()); + outgoingVar.setLabel(sigma.getValue().getLabel()); + Outgoing outgoing = new Outgoing(outgoingVar, target); + sigma.getOutgoings().add(outgoing); + } + } + sigmas[i] = sigmasInBlock; + } + } + private void estimatePhis() { DefinitionExtractor definitionExtractor = new DefinitionExtractor(); variableDefined = new boolean[program.variableCount()]; @@ -165,6 +225,23 @@ public class PhiUpdater { int i = stack.removeLast(); currentBlock = program.basicBlockAt(i); + for (int predecessor : cfg.incomingEdges(i)) { + if (sigmas[predecessor] == null) { + continue; + } + if (domTree.immediateDominatorOf(i) != predecessor) { + continue; + } + for (Sigma sigma : sigmas[predecessor]) { + for (Outgoing outgoing : sigma.getOutgoings()) { + if (outgoing.getTarget() == currentBlock) { + markAssignment(sigma.getValue()); + break; + } + } + } + } + if (currentBlock.getExceptionVariable() != null) { markAssignment(currentBlock.getExceptionVariable()); } @@ -181,6 +258,12 @@ public class PhiUpdater { } } + if (sigmas[i] != null) { + for (Sigma sigma : sigmas[i]) { + markAssignment(sigma.getValue()); + } + } + for (int successor : domGraph.outgoingEdges(i)) { stack.addLast(successor); } @@ -237,14 +320,27 @@ public class PhiUpdater { for (Incoming output : phiOutputs.get(index)) { Variable var = output.getValue(); - output.setValue(use(var)); + Variable sigmaVar = applySigmaRename(output.getPhi().getBasicBlock(), var); + var = sigmaVar != var ? sigmaVar : use(var); + output.setValue(var); } + Sigma[] nextSigmas = sigmas[index]; for (int j = successors.length - 1; j >= 0; --j) { int successor = successors[j]; Task next = new Task(); next.variables = variableMap.clone(); next.block = program.basicBlockAt(successor); + if (nextSigmas != null) { + for (Sigma sigma : nextSigmas) { + for (Outgoing outgoing : sigma.getOutgoings()) { + if (outgoing.getTarget().getIndex() == successor) { + next.variables[sigma.getValue().getIndex()] = outgoing.getValue(); + break; + } + } + } + } stack.push(next); } @@ -256,6 +352,12 @@ public class PhiUpdater { for (int successor : cfg.outgoingEdges(index)) { renameOutgoingPhis(successor, exceptionHandlingSuccessors.contains(successor)); } + + if (sigmas[index] != null) { + for (Sigma sigma : sigmas[index]) { + sigma.setValue(use(sigma.getValue())); + } + } } } @@ -306,6 +408,7 @@ public class PhiUpdater { for (int j = 0; j < phis.size(); ++j) { Phi phi = phis.get(j); + Variable originalVar = program.variableAt(phiIndexes[j]); Variable var = variableMap[phiIndexes[j]]; if (var != null) { List versions = definedVersions.get(phiIndexes[j]); @@ -318,15 +421,34 @@ public class PhiUpdater { } } + Variable sigmaVar = applySigmaRename(program.basicBlockAt(successor), originalVar); Incoming incoming = new Incoming(); incoming.setSource(currentBlock); - incoming.setValue(var); + incoming.setValue(sigmaVar != originalVar ? sigmaVar : var); phi.getIncomings().add(incoming); phi.getReceiver().setDebugName(var.getDebugName()); } } } + private Variable applySigmaRename(BasicBlock target, Variable var) { + Sigma[] blockSigmas = sigmas[currentBlock.getIndex()]; + if (blockSigmas == null) { + return var; + } + for (Sigma sigma : blockSigmas) { + if (sigma.getValue() != var) { + continue; + } + for (Outgoing outgoing : sigma.getOutgoings()) { + if (outgoing.getTarget() == target) { + return outgoing.getValue(); + } + } + } + return var; + } + private void markAssignment(Variable var) { Deque worklist = new ArrayDeque<>(); worklist.push(currentBlock); 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 d7bc95c30..ffac84ec2 100644 --- a/core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -16,6 +16,7 @@ package org.teavm.model.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -29,6 +30,7 @@ import org.teavm.model.IncomingReader; import org.teavm.model.Instruction; import org.teavm.model.InstructionIterator; import org.teavm.model.InstructionReadVisitor; +import org.teavm.model.MethodReference; import org.teavm.model.Phi; import org.teavm.model.PhiReader; import org.teavm.model.Program; @@ -37,6 +39,10 @@ import org.teavm.model.TextLocation; import org.teavm.model.TryCatchBlock; import org.teavm.model.TryCatchBlockReader; import org.teavm.model.Variable; +import org.teavm.model.instructions.ConstructInstruction; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.RaiseInstruction; public final class ProgramUtils { private ProgramUtils() { @@ -44,7 +50,7 @@ public final class ProgramUtils { public static Graph buildControlFlowGraph(Program program) { GraphBuilder graphBuilder = new GraphBuilder(program.basicBlockCount()); - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); Instruction insn = block.getLastInstruction(); @@ -203,4 +209,23 @@ public final class ProgramUtils { var.setLabel(suggestedName); } } + + public static List createThrowNPEInstructions(Program program, TextLocation location) { + ConstructInstruction newNPE = new ConstructInstruction(); + newNPE.setType(NullPointerException.class.getName()); + newNPE.setReceiver(program.createVariable()); + newNPE.setLocation(location); + + InvokeInstruction initNPE = new InvokeInstruction(); + initNPE.setType(InvocationType.SPECIAL); + initNPE.setInstance(newNPE.getReceiver()); + initNPE.setMethod(new MethodReference(NullPointerException.class, "", void.class)); + initNPE.setLocation(location); + + RaiseInstruction raise = new RaiseInstruction(); + raise.setException(newNPE.getReceiver()); + raise.setLocation(location); + + return Arrays.asList(newNPE, initNPE, raise); + } } diff --git a/core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java b/core/src/main/java/org/teavm/model/util/TransitionExtractor.java similarity index 98% rename from core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java rename to core/src/main/java/org/teavm/model/util/TransitionExtractor.java index 92cb04b0d..42a49e336 100644 --- a/core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java +++ b/core/src/main/java/org/teavm/model/util/TransitionExtractor.java @@ -20,7 +20,7 @@ import org.teavm.model.BasicBlock; import org.teavm.model.InvokeDynamicInstruction; import org.teavm.model.instructions.*; -public class InstructionTransitionExtractor implements InstructionVisitor { +public class TransitionExtractor implements InstructionVisitor { private BasicBlock[] targets; public BasicBlock[] getTargets() { diff --git a/core/src/main/java/org/teavm/model/util/TypeInferer.java b/core/src/main/java/org/teavm/model/util/TypeInferer.java index 0c97a6803..afe8ba713 100644 --- a/core/src/main/java/org/teavm/model/util/TypeInferer.java +++ b/core/src/main/java/org/teavm/model/util/TypeInferer.java @@ -23,6 +23,7 @@ import org.teavm.model.*; import org.teavm.model.instructions.*; public class TypeInferer { + private static VariableType[] typesByOrdinal = VariableType.values(); VariableType[] types; GraphBuilder builder; GraphBuilder arrayElemBuilder; @@ -54,47 +55,35 @@ public class TypeInferer { } } - IntegerStack stack = new IntegerStack(sz); + IntegerStack stack = new IntegerStack(sz * 2); Graph graph = builder.build(); Graph arrayElemGraph = arrayElemBuilder.build(); - for (int i = 0; i < sz; ++i) { - if ((i >= graph.size() || graph.incomingEdgesCount(i) == 0) - && (i >= arrayElemGraph.size() || arrayElemGraph.incomingEdgesCount(i) == 0)) { + for (int i = 0; i < graph.size(); ++i) { + if (types[i] != null && graph.outgoingEdgesCount(i) > 0) { + stack.push(types[i].ordinal()); stack.push(i); + types[i] = null; } } - boolean[] visited = new boolean[sz]; while (!stack.isEmpty()) { int node = stack.pop(); - if (visited[node]) { + VariableType type = typesByOrdinal[stack.pop()]; + if (types[node] != null) { continue; } - visited[node] = true; - if (types[node] == null) { - for (int pred : graph.incomingEdges(node)) { - if (types[pred] != null) { - types[node] = types[pred]; - break; - } + types[node] = type; + + for (int successor : graph.outgoingEdges(node)) { + if (types[successor] == null) { + stack.push(type.ordinal()); + stack.push(successor); } } - if (types[node] == null) { - for (int pred : arrayElemGraph.incomingEdges(node)) { - if (types[pred] != null) { - types[node] = convertFromArray(types[pred]); - break; - } - } - } - for (int succ : graph.outgoingEdges(node)) { - if (!visited[succ]) { - stack.push(succ); - } - } - for (int succ : arrayElemGraph.outgoingEdges(node)) { - if (!visited[succ]) { - stack.push(succ); + for (int successor : arrayElemGraph.outgoingEdges(node)) { + if (types[successor] == null) { + stack.push(convertFromArray(type).ordinal()); + stack.push(successor); } } } diff --git a/core/src/main/java/org/teavm/parsing/Parser.java b/core/src/main/java/org/teavm/parsing/Parser.java index 4eb159337..fb29f3440 100644 --- a/core/src/main/java/org/teavm/parsing/Parser.java +++ b/core/src/main/java/org/teavm/parsing/Parser.java @@ -63,7 +63,7 @@ public class Parser { this.referenceCache = referenceCache; } - public MethodHolder parseMethod(MethodNode node, String className, String fileName) { + public MethodHolder parseMethod(MethodNode node, String fileName) { MethodNode nodeWithoutJsr = new MethodNode(Opcodes.ASM5, node.access, node.name, node.desc, node.signature, node.exceptions.toArray(new String[0])); JSRInlinerAdapter adapter = new JSRInlinerAdapter(nodeWithoutJsr, node.access, node.name, node.desc, @@ -76,7 +76,7 @@ public class Parser { ProgramParser programParser = new ProgramParser(referenceCache); programParser.setFileName(fileName); - Program program = programParser.parse(node, className); + Program program = programParser.parse(node); new UnreachableBasicBlockEliminator().optimize(program); PhiUpdater phiUpdater = new PhiUpdater(); Variable[] argumentMapping = applySignature(program, method.getParameterTypes()); @@ -245,7 +245,7 @@ public class Parser { } String fullFileName = node.name.substring(0, node.name.lastIndexOf('/') + 1) + node.sourceFile; for (MethodNode methodNode : node.methods) { - cls.addMethod(parseMethod(methodNode, node.name, fullFileName)); + cls.addMethod(parseMethod(methodNode, fullFileName)); } if (node.outerClass != null) { cls.setOwnerName(node.outerClass.replace('/', '.')); diff --git a/core/src/main/java/org/teavm/parsing/ProgramParser.java b/core/src/main/java/org/teavm/parsing/ProgramParser.java index d6ec8a988..2057871ec 100644 --- a/core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -15,13 +15,87 @@ */ package org.teavm.parsing; -import java.util.*; -import org.objectweb.asm.*; -import org.objectweb.asm.tree.*; -import org.teavm.model.*; -import org.teavm.model.instructions.*; -import org.teavm.model.util.InstructionTransitionExtractor; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LineNumberNode; +import org.objectweb.asm.tree.LocalVariableNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TryCatchBlockNode; +import org.teavm.model.BasicBlock; +import org.teavm.model.FieldReference; +import org.teavm.model.Instruction; +import org.teavm.model.InvokeDynamicInstruction; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodHandle; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.ReferenceCache; +import org.teavm.model.RuntimeConstant; +import org.teavm.model.TextLocation; +import org.teavm.model.TryCatchBlock; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.instructions.ArrayElementType; +import org.teavm.model.instructions.ArrayLengthInstruction; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.BinaryBranchingCondition; +import org.teavm.model.instructions.BinaryBranchingInstruction; +import org.teavm.model.instructions.BinaryInstruction; +import org.teavm.model.instructions.BinaryOperation; +import org.teavm.model.instructions.BranchingCondition; +import org.teavm.model.instructions.BranchingInstruction; +import org.teavm.model.instructions.CastInstruction; +import org.teavm.model.instructions.CastIntegerDirection; +import org.teavm.model.instructions.CastIntegerInstruction; +import org.teavm.model.instructions.CastNumberInstruction; +import org.teavm.model.instructions.ClassConstantInstruction; +import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.ConstructArrayInstruction; +import org.teavm.model.instructions.ConstructInstruction; +import org.teavm.model.instructions.ConstructMultiArrayInstruction; +import org.teavm.model.instructions.DoubleConstantInstruction; +import org.teavm.model.instructions.EmptyInstruction; +import org.teavm.model.instructions.ExitInstruction; +import org.teavm.model.instructions.FloatConstantInstruction; +import org.teavm.model.instructions.GetElementInstruction; +import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.IntegerConstantInstruction; +import org.teavm.model.instructions.IntegerSubtype; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.IsInstanceInstruction; +import org.teavm.model.instructions.JumpInstruction; +import org.teavm.model.instructions.LongConstantInstruction; +import org.teavm.model.instructions.MonitorEnterInstruction; +import org.teavm.model.instructions.MonitorExitInstruction; +import org.teavm.model.instructions.NegateInstruction; +import org.teavm.model.instructions.NullConstantInstruction; +import org.teavm.model.instructions.NumericOperandType; +import org.teavm.model.instructions.PutElementInstruction; +import org.teavm.model.instructions.PutFieldInstruction; +import org.teavm.model.instructions.RaiseInstruction; +import org.teavm.model.instructions.StringConstantInstruction; +import org.teavm.model.instructions.SwitchInstruction; +import org.teavm.model.instructions.SwitchTableEntry; +import org.teavm.model.instructions.UnwrapArrayInstruction; import org.teavm.model.util.ProgramUtils; +import org.teavm.model.util.TransitionExtractor; public class ProgramParser { private ReferenceCache referenceCache; @@ -42,7 +116,6 @@ public class ProgramParser { private List basicBlocks = new ArrayList<>(); private int minLocal; private Program program; - private String currentClassName; private Map> localVariableMap = new HashMap<>(); private Map> variableDebugNames = new HashMap<>(); @@ -86,9 +159,8 @@ public class ProgramParser { this.fileName = fileName; } - public Program parse(MethodNode method, String className) { + public Program parse(MethodNode method) { program = new Program(); - this.currentClassName = className; InsnList instructions = method.instructions; if (instructions.size() == 0) { return program; @@ -315,7 +387,7 @@ public class ProgramParser { if (lastInsn == null) { return false; } - InstructionTransitionExtractor extractor = new InstructionTransitionExtractor(); + TransitionExtractor extractor = new TransitionExtractor(); lastInsn.acceptVisitor(extractor); return extractor.getTargets() != null; } diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index 98b54f7a1..88bef8522 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -70,6 +70,11 @@ public final class ExceptionHandling { throw new ClassCastException(); } + @Unmanaged + public static void throwNullPointerException() { + throw new NullPointerException(); + } + @Unmanaged public static int callStackSize() { Address stackFrame = ShadowStack.getStackTop(); diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 15839d29e..aa9ae0365 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -63,6 +63,7 @@ import org.teavm.model.optimization.LoopInvariantMotion; import org.teavm.model.optimization.MethodOptimization; import org.teavm.model.optimization.MethodOptimizationContext; import org.teavm.model.optimization.RedundantJumpElimination; +import org.teavm.model.optimization.RedundantNullCheckElimination; import org.teavm.model.optimization.ScalarReplacement; import org.teavm.model.optimization.UnreachableBasicBlockElimination; import org.teavm.model.optimization.UnusedVariableElimination; @@ -512,6 +513,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository { MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classSource); if (optimizedProgram == null) { optimizedProgram = ProgramUtils.copy(method.getProgram()); + target.beforeOptimizations(optimizedProgram, method, classSource); + if (optimizedProgram.basicBlockCount() > 0) { boolean changed; do { @@ -578,6 +581,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } optimizations.add(new GlobalValueNumbering(optimizationLevel == TeaVMOptimizationLevel.SIMPLE)); if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) { + optimizations.add(new RedundantNullCheckElimination()); optimizations.add(new ConstantConditionElimination()); optimizations.add(new RedundantJumpElimination()); optimizations.add(new UnusedVariableElimination()); diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index 94ddddedc..b1fc1f726 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -39,6 +39,8 @@ public interface TeaVMTarget { void contributeDependencies(DependencyAnalyzer dependencyAnalyzer); + void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource); + void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource); void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException; diff --git a/core/src/test/java/org/teavm/model/analysis/test/NullnessAnalysisTest.java b/core/src/test/java/org/teavm/model/analysis/test/NullnessAnalysisTest.java index e5fb67d2e..2d7134953 100644 --- a/core/src/test/java/org/teavm/model/analysis/test/NullnessAnalysisTest.java +++ b/core/src/test/java/org/teavm/model/analysis/test/NullnessAnalysisTest.java @@ -85,6 +85,11 @@ public class NullnessAnalysisTest { test(); } + @Test + public void nullAndNull() { + test(); + } + private void test() { String baseName = "model/analysis/nullness/" + name.getMethodName(); String originalResourceName = baseName + ".original.txt"; diff --git a/core/src/test/resources/model/analysis/nullness/branch.extended.txt b/core/src/test/resources/model/analysis/nullness/branch.extended.txt index a6f08bbc3..37a6ab062 100644 --- a/core/src/test/resources/model/analysis/nullness/branch.extended.txt +++ b/core/src/test/resources/model/analysis/nullness/branch.extended.txt @@ -4,18 +4,16 @@ $start @v := invokeStatic `Foo.get()LFoo;` if @v === null then goto $ifNull else goto $ifNotNull $ifNull - @v_1 := null invokeVirtual `Foo.bar()V` @v_1 - @v_2 := nullCheck @v_1 + @v_3 := nullCheck @v_1 goto $join $ifNotNull - @v_3 := nullCheck @v - invokeVirtual `Foo.baz()V` @v_3 + invokeVirtual `Foo.baz()V` @v_2 + @v_4 := nullCheck @v_2 goto $join $join return // NULLABLE v // NULLABLE v_1 -// NOT_NULL v_2 // NOT_NULL v_3 \ No newline at end of file diff --git a/core/src/test/resources/model/analysis/nullness/nonDominatedBranch.extended.txt b/core/src/test/resources/model/analysis/nullness/nonDominatedBranch.extended.txt index 8e39be6f0..4789246c5 100644 --- a/core/src/test/resources/model/analysis/nullness/nonDominatedBranch.extended.txt +++ b/core/src/test/resources/model/analysis/nullness/nonDominatedBranch.extended.txt @@ -4,12 +4,14 @@ $start @a := invokeStatic `Foo.f()LBar;` if @a === null then goto $joint else goto $ifNonNull $ifNonNull - @a_1 := nullCheck @a @b := invokeStatic `Foo.g()LBar;` if @b !== null then goto $joint else goto $ifNull $ifNull - @b_1 := null - return @a_1 + return @a_2 $joint - @c := phi @a from $start, @b from $ifNonNull - return @c \ No newline at end of file + @c := phi @a_1 from $start, @b_1 from $ifNonNull + return @c + +// NULLABLE c +// NULLABLE a_1 +// NOT_NULL a_2 \ No newline at end of file diff --git a/core/src/test/resources/model/analysis/nullness/nullAndNull.extended.txt b/core/src/test/resources/model/analysis/nullness/nullAndNull.extended.txt new file mode 100644 index 000000000..995c55d9d --- /dev/null +++ b/core/src/test/resources/model/analysis/nullness/nullAndNull.extended.txt @@ -0,0 +1,25 @@ +var @this as this + +$start + @a := invokeStatic `Foo.get()LFoo;` + @b := invokeStatic `Foo.get2()LFoo;` + if @a === null then goto $ifANull else goto $join +$ifANull + @tmp := @a_1 + if @b === null then goto $ifBNull else goto $join +$ifBNull + @p := @a_1 + @q := @b_1 + return +$join + @a_3 := phi @a_2 from $start, @a_1 from $ifANull + @b_3 := phi @b from $start, @b_2 from $ifANull + @u := @a_3 + @v := @b_3 + return + +// NULLABLE tmp +// NULLABLE p +// NULLABLE q +// NULLABLE u +// NULLABLE v \ No newline at end of file diff --git a/core/src/test/resources/model/analysis/nullness/nullAndNull.original.txt b/core/src/test/resources/model/analysis/nullness/nullAndNull.original.txt new file mode 100644 index 000000000..2019bd060 --- /dev/null +++ b/core/src/test/resources/model/analysis/nullness/nullAndNull.original.txt @@ -0,0 +1,17 @@ +var @this as this + +$start + @a := invokeStatic `Foo.get()LFoo;` + @b := invokeStatic `Foo.get2()LFoo;` + if @a === null then goto $ifANull else goto $join +$ifANull + @tmp := @a + if @b === null then goto $ifBNull else goto $join +$ifBNull + @p := @a + @q := @b + return +$join + @u := @a + @v := @b + return diff --git a/core/src/test/resources/model/analysis/nullness/phiJoin.extended.txt b/core/src/test/resources/model/analysis/nullness/phiJoin.extended.txt index dff4d52b8..3a6c6044f 100644 --- a/core/src/test/resources/model/analysis/nullness/phiJoin.extended.txt +++ b/core/src/test/resources/model/analysis/nullness/phiJoin.extended.txt @@ -4,11 +4,9 @@ $start @cond := invokeStatic `Cond.get()Ljava/lang/Object;` if @cond === null then goto $ifNull else goto $ifNotNull $ifNull - @cond_1 := null @a := 'qwe' goto $join $ifNotNull - @cond_2 := nullCheck @cond @b := invokeStatic `org.test.Foo.bar()Ljava/lang/String;` @b_1 := nullCheck @b goto $join diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java index 595cc0168..e18c825a9 100644 --- a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java @@ -55,7 +55,7 @@ import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.LongConstantInstruction; import org.teavm.model.instructions.NullConstantInstruction; -import org.teavm.model.util.InstructionTransitionExtractor; +import org.teavm.model.util.TransitionExtractor; public final class MetaprogrammingImpl { static Map proxySuffixGenerators = new HashMap<>(); @@ -340,7 +340,7 @@ public final class MetaprogrammingImpl { } public static void close() { - InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor(); + TransitionExtractor transitionExtractor = new TransitionExtractor(); BasicBlock block = generator.currentBlock(); Instruction lastInstruction = block.getLastInstruction(); if (lastInstruction != null) {