From 824cc799017a825e5b6c32793075f38b6cb5782e Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 9 Mar 2015 18:37:04 +0300 Subject: [PATCH] Speed up TeaVM compiler --- .../main/java/org/teavm/cache/ProgramIO.java | 2 - .../java/org/teavm/common/GraphUtils.java | 8 + .../dependency/DataFlowGraphBuilder.java | 290 ++++++++++++++++++ .../teavm/dependency/DependencyChecker.java | 20 +- .../dependency/DependencyGraphBuilder.java | 10 +- .../org/teavm/dependency/DependencyNode.java | 22 +- .../teavm/dependency/MethodDependency.java | 4 + .../java/org/teavm/model/MethodReference.java | 56 +++- 8 files changed, 386 insertions(+), 26 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java diff --git a/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java b/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java index b4689db64..7321f496b 100644 --- a/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java +++ b/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java @@ -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); } diff --git a/teavm-core/src/main/java/org/teavm/common/GraphUtils.java b/teavm-core/src/main/java/org/teavm/common/GraphUtils.java index 692f4190d..4d30f6d25 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphUtils.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphUtils.java @@ -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 */ diff --git a/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java new file mode 100644 index 000000000..064b300c5 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java @@ -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 methodNodes = new ObjectIntOpenHashMap<>(); + private ObjectIntMap 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 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 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 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) { + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 96018d66e..3174539ca 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -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 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 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 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()) { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index afb0b066c..93820754c 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java index 4daaaf5b7..d5d4f8dbc 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -24,7 +24,7 @@ import java.util.*; public class DependencyNode implements ValueDependencyInfo { private DependencyChecker dependencyChecker; private Set followers = new HashSet<>(); - private BitSet types = new BitSet(); + private BitSet types; private Map 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 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 result = new ArrayList<>(); for (int index = types.nextSetBit(0); index >= 0; index = types.nextSetBit(index + 1)) { result.add(dependencyChecker.types.get(index).getName()); diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java index 778338210..cb1cce5b7 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -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; diff --git a/teavm-core/src/main/java/org/teavm/model/MethodReference.java b/teavm-core/src/main/java/org/teavm/model/MethodReference.java index 38979ac56..66ab8a600 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodReference.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodReference.java @@ -15,6 +15,8 @@ */ package org.teavm.model; +import java.util.Arrays; + /** *

Specifies a fully qualified name of a method, including its name, class name, parameter types * and return value type. This class overloads equals and hashCode @@ -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(); } }