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