mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-03 05:44:10 -08:00
Implement null check in C backend
Although initial purpose of this comment was null check, it took much time to complete it and it caused many unrelated changes. Besides just implementing null check in quite naive fashion (I could not use the trick with memory protection, since I have to maintain shadow stack, and support WebAssembly), I had to optimize things. I relied on my existing nullness analysis to eliminate as much null checks as possible. However, the whole nullness analysis was wrong. After some thoughts I came up with solution very close to range analysis, which required me to introduce extension to IR sometimes called e-SSA form with so called sigma nodes. Also, I found some bugs in few different places (by the time write this message I could only remember escape analysis/scalar replacement and after-inlining devirtualization) and fixed them.
This commit is contained in:
parent
744eb39e6d
commit
e77997c93f
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Integer, List<Integer>> switchMap = new HashMap<>();
|
||||
Map<Integer, List<Integer>> switchMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < insn.getEntries().size(); ++i) {
|
||||
SwitchTableEntry entry = insn.getEntries().get(i);
|
||||
List<Integer> conditions = switchMap.computeIfAbsent(entry.getTarget().getIndex(), k -> new ArrayList<>());
|
||||
|
|
|
@ -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<BlockStatement, List<Statement>> blockSuccessors = new HashMap<>();
|
||||
private Set<IdentifiedStatement> outerStatements = new HashSet<>();
|
||||
private Map<BlockStatement, List<Statement>> blockSuccessors = new LinkedHashMap<>();
|
||||
private Set<IdentifiedStatement> outerStatements = new LinkedHashSet<>();
|
||||
private List<Statement> currentSequence;
|
||||
private int currentIndex;
|
||||
private AllBlocksCountVisitor usageCounter;
|
||||
|
@ -153,8 +153,8 @@ class BreakEliminator implements StatementVisitor {
|
|||
public void visit(TryCatchStatement statement) {
|
||||
Map<BlockStatement, List<Statement>> oldBlockSuccessors = blockSuccessors;
|
||||
Set<IdentifiedStatement> oldOuterStatements = outerStatements;
|
||||
outerStatements = new HashSet<>();
|
||||
blockSuccessors = new HashMap<>();
|
||||
outerStatements = new LinkedHashSet<>();
|
||||
blockSuccessors = new LinkedHashMap<>();
|
||||
processSequence(statement.getProtectedBody());
|
||||
outerStatements = oldOuterStatements;
|
||||
blockSuccessors = oldBlockSuccessors;
|
||||
|
|
|
@ -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<MethodReference> virtualMethods = new HashSet<>();
|
||||
Set<MethodReference> virtualMethods = new LinkedHashSet<>();
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(");
|
||||
|
|
|
@ -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(";");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<DefaultCallSite> callSites = new HashSet<>();
|
||||
private Set<DefaultCallSite> callSites = new LinkedHashSet<>();
|
||||
private Set<DefaultCallSite> safeCallSites;
|
||||
private List<DefaultCallSite> callerCallSites = new ArrayList<>();
|
||||
private List<DefaultCallSite> safeCallersCallSites;
|
||||
private Set<DefaultFieldAccessSite> fieldAccessSites = new HashSet<>();
|
||||
private Set<DefaultFieldAccessSite> fieldAccessSites = new LinkedHashSet<>();
|
||||
private Set<DefaultFieldAccessSite> safeFieldAccessSites;
|
||||
|
||||
DefaultCallGraphNode(DefaultCallGraph graph, MethodReference method) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, "<init>", void.class),
|
||||
new CallLocation(caller.getMethod(), currentLocation)).use();
|
||||
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException"));
|
||||
|
|
|
@ -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<String, AnnotationHolder> annotations = new HashMap<>();
|
||||
private Map<String, AnnotationHolder> annotations = new LinkedHashMap<>();
|
||||
|
||||
public void add(AnnotationHolder annotation) {
|
||||
if (annotations.containsKey(annotation.getType())) {
|
||||
|
|
|
@ -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<String, AnnotationValue> values = new HashMap<>();
|
||||
private Map<String, AnnotationValue> values = new LinkedHashMap<>();
|
||||
|
||||
public AnnotationHolder(String type) {
|
||||
this.type = type;
|
||||
|
|
|
@ -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<Instruction> {
|
||||
private Program program;
|
||||
|
@ -359,4 +366,32 @@ public class BasicBlock implements BasicBlockReader, Iterable<Instruction> {
|
|||
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<Phi> 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--);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
51
core/src/main/java/org/teavm/model/Outgoing.java
Normal file
51
core/src/main/java/org/teavm/model/Outgoing.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
99
core/src/main/java/org/teavm/model/Sigma.java
Normal file
99
core/src/main/java/org/teavm/model/Sigma.java
Normal file
|
@ -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<Outgoing> 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<Outgoing> getOutgoings() {
|
||||
return safeOutgoings;
|
||||
}
|
||||
|
||||
private List<Outgoing> safeOutgoings = new AbstractList<Outgoing>() {
|
||||
@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();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<FieldReference> 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<FieldReference> 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<Set<FieldReference>> fields;
|
||||
Map<FieldReference, ValueType> 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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NullConstantInstruction> nullInstructions = new ArrayList<>();
|
||||
private List<NullCheckInstruction> 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<IntSet> 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<IntSet> 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> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, Set<String>> 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<String, List<String>> hierarhy, String className,
|
||||
private int assignRange(int start, Map<String, List<String>> hierarchy, String className,
|
||||
Map<String, Range> 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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<String> isStructure = new ObjectByteHashMap<>();
|
||||
private ObjectByteMap<String> isStaticInit = new ObjectByteHashMap<>();
|
||||
private ObjectByteMap<String> isFunction = new ObjectByteHashMap<>();
|
||||
private ObjectByteMap<MethodReference> 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;
|
||||
}
|
||||
}
|
|
@ -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<CallSiteDescriptor> 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<CallSiteDescriptor> 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<Instruction> setLocation(List<Instruction> 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();
|
||||
|
|
|
@ -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<Phi> 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<Map<Instruction, int[]>> slotsToUpdate = new ArrayList<>();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
slotsToUpdate.add(new HashMap<>());
|
||||
slotsToUpdate.add(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||
|
|
|
@ -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<MethodReference, Boolean> cache = new HashMap<>();
|
||||
|
||||
public ManagedMethodRepository(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
}
|
||||
|
||||
public boolean isManaged(MethodReference methodReference) {
|
||||
return cache.computeIfAbsent(methodReference, this::computeIsManaged);
|
||||
}
|
||||
|
||||
private boolean computeIsManaged(MethodReference methodReference) {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <T extends Instruction> void addGuard(T instruction, Function<T, Variable> get,
|
||||
BiConsumer<T, Variable> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
|
||||
public ShadowStackTransformer(ClassReaderSource classSource) {
|
||||
managedMethodRepository = new ManagedMethodRepository(classSource);
|
||||
public ShadowStackTransformer(Characteristics managedMethodRepository) {
|
||||
gcContributor = new GCShadowStackContributor(managedMethodRepository);
|
||||
this.managedMethodRepository = managedMethodRepository;
|
||||
}
|
||||
|
||||
public List<CallSiteDescriptor> getCallSites() {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -27,7 +27,7 @@ class InterferenceGraphBuilder {
|
|||
}
|
||||
UsageExtractor useExtractor = new UsageExtractor();
|
||||
DefinitionExtractor defExtractor = new DefinitionExtractor();
|
||||
InstructionTransitionExtractor succExtractor = new InstructionTransitionExtractor();
|
||||
TransitionExtractor succExtractor = new TransitionExtractor();
|
||||
List<List<Incoming>> outgoings = ProgramUtils.getPhiOutputs(program);
|
||||
BitSet live = new BitSet();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<List<Phi>> synthesizedPhisByBlock = new ArrayList<>();
|
||||
private IntObjectMap<Phi> phisByReceiver = new IntObjectHashMap<>();
|
||||
private BitSet usedPhis = new BitSet();
|
||||
private Variable[] originalExceptionVariables;
|
||||
private boolean[] usedDefinitions;
|
||||
private IntegerArray variableToSourceMap = new IntegerArray(10);
|
||||
private List<Phi> synthesizedPhis = new ArrayList<>();
|
||||
private Sigma[][] sigmas;
|
||||
private Predicate<Instruction> 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<Instruction> 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<Variable> 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<BasicBlock> worklist = new ArrayDeque<>();
|
||||
worklist.push(currentBlock);
|
||||
|
|
|
@ -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<Instruction> 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, "<init>", void.class));
|
||||
initNPE.setLocation(location);
|
||||
|
||||
RaiseInstruction raise = new RaiseInstruction();
|
||||
raise.setException(newNPE.getReceiver());
|
||||
raise.setLocation(location);
|
||||
|
||||
return Arrays.asList(newNPE, initNPE, raise);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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('/', '.'));
|
||||
|
|
|
@ -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<BasicBlock> basicBlocks = new ArrayList<>();
|
||||
private int minLocal;
|
||||
private Program program;
|
||||
private String currentClassName;
|
||||
private Map<Integer, List<LocalVariableNode>> localVariableMap = new HashMap<>();
|
||||
private Map<Instruction, Map<Integer, String>> 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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
@c := phi @a_1 from $start, @b_1 from $ifNonNull
|
||||
return @c
|
||||
|
||||
// NULLABLE c
|
||||
// NULLABLE a_1
|
||||
// NOT_NULL a_2
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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<String, Integer> 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) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user