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:
Alexey Andreev 2018-04-17 00:05:46 +03:00
parent 744eb39e6d
commit e77997c93f
62 changed files with 1407 additions and 414 deletions

View File

@ -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);
}
}

View File

@ -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<>());

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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(");

View File

@ -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(";");
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 "";
}
}

View File

@ -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);

View File

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

View File

@ -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);

View File

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

View File

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

View File

@ -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);

View File

@ -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"));

View File

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

View File

@ -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;

View File

@ -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--);
}
}
}
}
}

View 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;
}
}

View 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();
}
};
}

View File

@ -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));
}
}

View File

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

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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));

View File

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

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

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

View File

@ -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: {

View File

@ -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);

View File

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

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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());

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

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

View File

@ -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);
}
}
}

View File

@ -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('/', '.'));

View File

@ -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;
}

View File

@ -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();

View File

@ -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());

View File

@ -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;

View File

@ -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";

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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