Speed up TeaVM compiler

This commit is contained in:
konsoletyper 2015-03-09 18:37:04 +03:00
parent fe2adc4675
commit 824cc79901
8 changed files with 386 additions and 26 deletions

View File

@ -600,7 +600,6 @@ public class ProgramIO {
try {
output.writeByte(39);
output.writeShort(insn.getObjectRef().getIndex());
} catch (IOException e) {
throw new IOExceptionWrapper(e);
}
@ -611,7 +610,6 @@ public class ProgramIO {
try {
output.writeByte(40);
output.writeShort(insn.getObjectRef().getIndex());
} catch (IOException e) {
throw new IOExceptionWrapper(e);
}

View File

@ -79,6 +79,14 @@ public final class GraphUtils {
return false;
}
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start) {
return findStronglyConnectedComponents(graph, start, new GraphNodeFilter() {
@Override public boolean match(int node) {
return true;
}
});
}
/*
* Tarjan's algorithm
*/

View File

@ -0,0 +1,290 @@
/*
* Copyright 2015 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.dependency;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.ObjectIntMap;
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
import java.util.Arrays;
import java.util.List;
import org.teavm.common.*;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
/**
*
* @author Alexey Andreev
*/
public class DataFlowGraphBuilder implements InstructionReader {
private int lastIndex;
private GraphBuilder builder = new GraphBuilder();
private IntSet importantNodes = new IntOpenHashSet();
private ObjectIntMap<MethodReference> methodNodes = new ObjectIntOpenHashMap<>();
private ObjectIntMap<FieldReference> fieldNodes = new ObjectIntOpenHashMap<>();
private int[] arrayNodes;
public void important(int node) {
importantNodes.add(node);
}
public int[] buildMapping(ProgramReader program, int paramCount) {
lastIndex = program.variableCount();
arrayNodes = new int[lastIndex];
Arrays.fill(arrayNodes, -1);
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i);
for (PhiReader phi : block.readPhis()) {
for (IncomingReader incoming : phi.readIncomings()) {
builder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
}
}
block.readAllInstructions(this);
}
Graph graph = builder.build();
DisjointSet classes = new DisjointSet();
for (int i = 0; i < lastIndex; ++i) {
classes.create();
}
IntegerArray startNodes = new IntegerArray(graph.size());
for (int i = paramCount; i < graph.size(); ++i) {
if (!importantNodes.contains(i) && graph.incomingEdgesCount(i) == 1) {
classes.union(graph.incomingEdges(i)[0], i);
}
if (graph.incomingEdgesCount(i) == 0) {
startNodes.add(i);
}
}
int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, startNodes.getAll());
for (int[] scc : sccs) {
int first = scc[0];
for (int i = 1; i < scc.length; ++i) {
classes.union(first, scc[i]);
}
}
return classes.pack(program.variableCount());
}
@Override
public void location(InstructionLocation location) {
}
@Override
public void nop() {
}
@Override
public void classConstant(VariableReader receiver, ValueType cst) {
}
@Override
public void nullConstant(VariableReader receiver) {
}
@Override
public void integerConstant(VariableReader receiver, int cst) {
}
@Override
public void longConstant(VariableReader receiver, long cst) {
}
@Override
public void floatConstant(VariableReader receiver, float cst) {
}
@Override
public void doubleConstant(VariableReader receiver, double cst) {
}
@Override
public void stringConstant(VariableReader receiver, String cst) {
}
@Override
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) {
}
@Override
public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
}
@Override
public void assign(VariableReader receiver, VariableReader assignee) {
builder.addEdge(assignee.getIndex(), receiver.getIndex());
}
@Override
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
builder.addEdge(value.getIndex(), receiver.getIndex());
important(receiver.getIndex());
}
@Override
public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
NumericOperandType targetType) {
}
@Override
public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
CastIntegerDirection targetType) {
}
@Override
public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
BasicBlockReader alternative) {
}
@Override
public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
BasicBlockReader consequent, BasicBlockReader alternative) {
}
@Override
public void jump(BasicBlockReader target) {
}
@Override
public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
BasicBlockReader defaultTarget) {
}
@Override
public void exit(VariableReader valueToReturn) {
if (valueToReturn != null) {
important(valueToReturn.getIndex());
}
}
@Override
public void raise(VariableReader exception) {
important(exception.getIndex());
}
@Override
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
}
@Override
public void createArray(VariableReader receiver, ValueType itemType, List<? extends VariableReader> dimensions) {
}
@Override
public void create(VariableReader receiver, String type) {
}
private int getFieldNode(FieldReference field) {
int fieldNode = fieldNodes.getOrDefault(field, -1);
if (fieldNode < 0) {
fieldNode = lastIndex++;
fieldNodes.put(field, fieldNode);
}
important(fieldNode);
return fieldNode;
}
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field, ValueType fieldType) {
int fieldNode = getFieldNode(field);
builder.addEdge(fieldNode, receiver.getIndex());
}
@Override
public void putField(VariableReader instance, FieldReference field, VariableReader value) {
int fieldNode = getFieldNode(field);
builder.addEdge(value.getIndex(), fieldNode);
}
@Override
public void arrayLength(VariableReader receiver, VariableReader array) {
}
@Override
public void cloneArray(VariableReader receiver, VariableReader array) {
important(receiver.getIndex());
builder.addEdge(array.getIndex(), receiver.getIndex());
}
@Override
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
if (elementType == ArrayElementType.OBJECT) {
builder.addEdge(array.getIndex(), receiver.getIndex());
}
}
private int getArrayElementNode(int array) {
int node = arrayNodes[array];
if (node < 0) {
node = lastIndex++;
arrayNodes[array] = node;
}
important(node);
return node;
}
@Override
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
builder.addEdge(getArrayElementNode(array.getIndex()), receiver.getIndex());
}
@Override
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
builder.addEdge(value.getIndex(), getArrayElementNode(array.getIndex()));
}
private int getMethodNode(MethodReference method) {
int methodNode = methodNodes.getOrDefault(method, -1);
if (methodNode < 0) {
methodNode = lastIndex++;
methodNodes.put(method, methodNode);
}
important(methodNode);
return methodNode;
}
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
if (receiver != null) {
builder.addEdge(getMethodNode(method), receiver.getIndex());
}
}
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
}
@Override
public void initClass(String className) {
}
@Override
public void nullCheck(VariableReader receiver, VariableReader value) {
builder.addEdge(value.getIndex(), receiver.getIndex());
}
@Override
public void monitorEnter(VariableReader objectRef) {
}
@Override
public void monitorExit(VariableReader objectRef) {
}
}

View File

@ -15,13 +15,7 @@
*/
package org.teavm.dependency;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.*;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.DefaultCallGraph;
import org.teavm.callgraph.DefaultCallGraphNode;
@ -202,6 +196,8 @@ public class DependencyChecker implements DependencyInfo {
});
}
private Set<String> classesAddedByRoot = new HashSet<>();
public ClassDependency linkClass(String className, CallLocation callLocation) {
ClassDependency dep = classCache.map(className);
boolean added = true;
@ -210,6 +206,8 @@ public class DependencyChecker implements DependencyInfo {
if (!addClassAccess(callGraphNode, className, callLocation.getSourceLocation())) {
added = false;
}
} else {
added = classesAddedByRoot.add(className);
}
if (!dep.isMissing() && added) {
for (DependencyListener listener : listeners) {
@ -249,6 +247,8 @@ public class DependencyChecker implements DependencyInfo {
return dependency;
}
private Set<MethodReference> methodsAddedByRoot = new HashSet<>();
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
if (methodRef == null) {
throw new IllegalArgumentException();
@ -262,6 +262,8 @@ public class DependencyChecker implements DependencyInfo {
if (callLocation != null && callLocation.getMethod() != null) {
added = callGraph.getNode(callLocation.getMethod()).addCallSite(methodRef,
callLocation.getSourceLocation());
} else {
added = methodsAddedByRoot.add(methodRef);
}
MethodDependency graph = methodCache.map(methodRef);
if (!graph.isMissing() && added) {
@ -393,10 +395,14 @@ public class DependencyChecker implements DependencyInfo {
return classCache.getCachedPreimages();
}
private Set<FieldReference> fieldsAddedByRoot = new HashSet<>();
public FieldDependency linkField(final FieldReference fieldRef, final CallLocation location) {
boolean added = true;
if (location != null) {
added = callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation());
} else {
added = fieldsAddedByRoot.add(fieldRef);
}
FieldDependency dep = fieldCache.map(fieldRef);
if (!dep.isMissing()) {

View File

@ -52,7 +52,15 @@ class DependencyGraphBuilder {
System.out.println(new ListingBuilder().buildListing(program, " "));
}
resultNode = dep.getResult();
nodes = dep.getVariables();
DependencyNode[] origNodes = dep.getVariables();
int[] nodeMapping = new DataFlowGraphBuilder().buildMapping(program, dep.getParameterCount());
nodes = new DependencyNode[origNodes.length];
for (int i = 0; i < nodes.length; ++i) {
nodes[i] = origNodes[nodeMapping[i]];
}
dep.setVariables(nodes);
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i);
currentExceptionConsumer = createExceptionConsumer(dep, block);

View File

@ -24,7 +24,7 @@ import java.util.*;
public class DependencyNode implements ValueDependencyInfo {
private DependencyChecker dependencyChecker;
private Set<DependencyConsumer> followers = new HashSet<>();
private BitSet types = new BitSet();
private BitSet types;
private Map<DependencyNode, DependencyNodeToNodeTransition> transitions = new HashMap<>();
private volatile String tag;
private DependencyNode arrayItemNode;
@ -46,6 +46,9 @@ public class DependencyNode implements ValueDependencyInfo {
if (degree > 2) {
return;
}
if (types == null) {
types = new BitSet();
}
if (!types.get(type.index)) {
types.set(type.index);
if (DependencyChecker.shouldLog) {
@ -65,10 +68,13 @@ public class DependencyNode implements ValueDependencyInfo {
if (type.getDependencyChecker() != dependencyChecker) {
throw new IllegalArgumentException("The given type does not belong to the same dependency checker");
}
if (!this.types.get(type.index)) {
if (this.types == null || !this.types.get(type.index)) {
types[j++] = type;
}
}
if (this.types == null) {
this.types = new BitSet();
}
for (int i = 0; i < j; ++i) {
this.types.set(types[i].index);
if (DependencyChecker.shouldLog) {
@ -81,7 +87,7 @@ public class DependencyNode implements ValueDependencyInfo {
}
public void addConsumer(DependencyConsumer consumer) {
if (followers.add(consumer)) {
if (followers.add(consumer) && this.types != null) {
List<DependencyType> types = new ArrayList<>();
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
types.add(dependencyChecker.types.get(index));
@ -91,6 +97,9 @@ public class DependencyNode implements ValueDependencyInfo {
}
public void connect(DependencyNode node, DependencyTypeFilter filter) {
if (this == node) {
return;
}
DependencyNodeToNodeTransition transition = new DependencyNodeToNodeTransition(this, node, filter);
if (!transitions.containsKey(node)) {
transitions.put(node, transition);
@ -123,11 +132,11 @@ public class DependencyNode implements ValueDependencyInfo {
@Override
public boolean hasArrayType() {
return arrayItemNode != null && arrayItemNode.types.isEmpty();
return arrayItemNode != null && arrayItemNode.types != null && !arrayItemNode.types.isEmpty();
}
public boolean hasType(DependencyType type) {
return type.getDependencyChecker() == dependencyChecker && types.get(type.index);
return types != null && type.getDependencyChecker() == dependencyChecker && types.get(type.index);
}
@Override
@ -137,6 +146,9 @@ public class DependencyNode implements ValueDependencyInfo {
@Override
public String[] getTypes() {
if (types == null) {
return new String[0];
}
List<String> result = new ArrayList<>();
for (int index = types.nextSetBit(0); index >= 0; index = types.nextSetBit(index + 1)) {
result.add(dependencyChecker.types.get(index).getName());

View File

@ -55,6 +55,10 @@ public class MethodDependency implements MethodDependencyInfo {
return Arrays.copyOf(variableNodes, variableNodes.length);
}
void setVariables(DependencyNode[] variables) {
this.variableNodes = variables;
}
@Override
public int getVariableCount() {
return variableNodes.length;

View File

@ -15,6 +15,8 @@
*/
package org.teavm.model;
import java.util.Arrays;
/**
* <p>Specifies a fully qualified name of a method, including its name, class name, parameter types
* and return value type. This class overloads <code>equals</code> and <code>hashCode</code>
@ -28,11 +30,16 @@ package org.teavm.model;
*/
public class MethodReference {
private String className;
private MethodDescriptor descriptor;
private String name;
private ValueType[] signature;
private transient MethodDescriptor descriptor;
private transient String reprCache;
public MethodReference(String className, MethodDescriptor descriptor) {
this.className = className;
this.descriptor = descriptor;
this.name = descriptor.getName();
this.signature = descriptor.getSignature();
}
/**
@ -52,11 +59,21 @@ public class MethodReference {
* a type of a returning value, and all the remaining elements are types of arguments.
*/
public MethodReference(String className, String name, ValueType... signature) {
this(className, new MethodDescriptor(name, signature));
this.className = className;
this.name = name;
this.signature = Arrays.copyOf(signature, signature.length);
}
public MethodReference(Class<?> cls, String name, Class<?>... signature) {
this(cls.getName(), new MethodDescriptor(name, signature));
this(cls.getName(), name, convertSignature(signature));
}
private static ValueType[] convertSignature(Class<?>... signature) {
ValueType[] types = new ValueType[signature.length];
for (int i = 0; i < types.length; ++i) {
types[i] = ValueType.parse(signature[i]);
}
return types;
}
public String getClassName() {
@ -64,44 +81,61 @@ public class MethodReference {
}
public MethodDescriptor getDescriptor() {
if (descriptor == null) {
descriptor = new MethodDescriptor(name, signature);
}
return descriptor;
}
public int parameterCount() {
return descriptor.parameterCount();
return signature.length - 1;
}
public ValueType[] getParameterTypes() {
return descriptor.getParameterTypes();
return Arrays.copyOf(signature, signature.length - 1);
}
public ValueType[] getSignature() {
return descriptor.getSignature();
return Arrays.copyOf(signature, signature.length);
}
public String getName() {
return descriptor.getName();
return name;
}
@Override
public int hashCode() {
return className.hashCode() ^ descriptor.hashCode();
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return false;
return true;
}
if (!(obj instanceof MethodReference)) {
return false;
}
MethodReference other = (MethodReference)obj;
return className.equals(other.className) && descriptor.equals(other.descriptor);
return toString().equals(other.toString());
}
@Override
public String toString() {
return className + "." + descriptor;
if (reprCache == null) {
reprCache = className + "." + name + signatureToString();
}
return reprCache;
}
public String signatureToString() {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (int i = 0; i < signature.length - 1; ++i) {
sb.append(signature[i].toString());
}
sb.append(')');
sb.append(signature[signature.length - 1]);
return sb.toString();
}
}