diff --git a/teavm-core/src/main/java/org/teavm/common/IntegerStack.java b/teavm-core/src/main/java/org/teavm/common/IntegerStack.java index 24cbc528f..ccf8eab12 100644 --- a/teavm-core/src/main/java/org/teavm/common/IntegerStack.java +++ b/teavm-core/src/main/java/org/teavm/common/IntegerStack.java @@ -31,7 +31,7 @@ public class IntegerStack { public void push(int value) { if (head == buffer.length) { - buffer = Arrays.copyOf(buffer, buffer.length * 2); + buffer = Arrays.copyOf(buffer, Math.max(buffer.length * 2, 1)); } buffer[head++] = value; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java index 8999690e4..f9c2b845c 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DataFlowGraphBuilder.java @@ -19,6 +19,7 @@ import com.carrotsearch.hppc.IntOpenHashSet; import com.carrotsearch.hppc.IntSet; import com.carrotsearch.hppc.ObjectIntMap; import com.carrotsearch.hppc.ObjectIntOpenHashMap; +import com.carrotsearch.hppc.cursors.IntCursor; import java.util.Arrays; import java.util.List; import org.teavm.common.*; @@ -32,95 +33,62 @@ import org.teavm.model.instructions.*; 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; private int returnIndex = -1; private int exceptionIndex; + private DisjointSet classes = new DisjointSet(); + private int paramCount; + private IntSet escaping = new IntOpenHashSet(); - public void important(int node) { - importantNodes.add(node); + private void join(int a, int b) { + if (a < paramCount || b < paramCount) { + return; + } + classes.union(a, b); } - public int[] buildMapping(ProgramReader program, int paramCount, boolean needsReturn) { + public int[] buildMapping(ProgramReader program, boolean[] significantParams, boolean needsReturn) { lastIndex = program.variableCount(); - arrayNodes = new int[lastIndex]; + this.paramCount = significantParams.length; if (needsReturn) { returnIndex = lastIndex++; + escaping.add(returnIndex); } exceptionIndex = lastIndex++; - Arrays.fill(arrayNodes, -1); + for (int i = 0; i < lastIndex; ++i) { + classes.create(); + } + 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()); - } - } - for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) { - if (tryCatch.getExceptionVariable() != null) { - important(tryCatch.getExceptionVariable().getIndex()); + int from = incoming.getValue().getIndex(); + int to = phi.getReceiver().getIndex(); + builder.addEdge(from, to); + join(from, to); } } 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 (graph.incomingEdgesCount(i) == 0) { - startNodes.add(i); - } - for (int pred : graph.incomingEdges(i)) { - boolean predImportant = importantNodes.contains(classes.find(pred)); - boolean nodeImportant = importantNodes.contains(classes.find(i)); - if (predImportant && nodeImportant) { - continue; - } - int newCls = classes.union(pred, i); - if (nodeImportant || predImportant) { - importantNodes.add(newCls); - } - } - for (int succ : graph.outgoingEdges(i)) { - boolean succImportant = importantNodes.contains(classes.find(succ)); - boolean nodeImportant = importantNodes.contains(classes.find(i)); - if (succImportant && nodeImportant) { - continue; - } - int newCls = classes.union(succ, i); - if (nodeImportant || succImportant) { - importantNodes.add(newCls); - } - } - } - - int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, startNodes.getAll()); - for (int[] scc : sccs) { - int last = -1; - for (int node : scc) { - if (!importantNodes.contains(classes.find(node))) { - continue; - } - last = last < 0 ? node : classes.union(node, last); + for (int i = 0; i < paramCount; ++i) { + if (significantParams[i]) { + escaping.add(i); } } + propagateEscaping(graph); int[] classMap = new int[classes.size()]; Arrays.fill(classMap, -1); int[] result = new int[program.variableCount()]; int classCount = 0; for (int i = 0; i < program.variableCount(); ++i) { - int cls = classes.find(i); - if (!importantNodes.contains(cls)) { + if (!escaping.contains(i) && i >= significantParams.length) { result[i] = -1; continue; } + int cls = classes.find(i); int packedCls = classMap[cls]; if (packedCls < 0) { packedCls = classCount++; @@ -131,6 +99,32 @@ public class DataFlowGraphBuilder implements InstructionReader { return result; } + private void propagateEscaping(Graph graph) { + IntegerStack stack = new IntegerStack(graph.size()); + for (IntCursor node : escaping) { + stack.push(node.value); + } + escaping.clear(); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (!escaping.add(node)) { + continue; + } + if (node < graph.size()) { + for (int pred : graph.incomingEdges(node)) { + if (!escaping.contains(pred)) { + stack.push(pred); + } + } + for (int succ : graph.outgoingEdges(node)) { + if (!escaping.contains(succ)) { + stack.push(succ); + } + } + } + } + } + @Override public void location(InstructionLocation location) { } @@ -176,15 +170,19 @@ public class DataFlowGraphBuilder implements InstructionReader { public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) { } + private void connect(int a, int b) { + builder.addEdge(a, b); + join(a, b); + } + @Override public void assign(VariableReader receiver, VariableReader assignee) { - builder.addEdge(assignee.getIndex(), receiver.getIndex()); + connect(assignee.getIndex(), receiver.getIndex()); } @Override public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { builder.addEdge(value.getIndex(), receiver.getIndex()); - important(receiver.getIndex()); } @Override @@ -219,15 +217,14 @@ public class DataFlowGraphBuilder implements InstructionReader { @Override public void exit(VariableReader valueToReturn) { if (valueToReturn != null && returnIndex >= 0) { - important(returnIndex); - builder.addEdge(valueToReturn.getIndex(), returnIndex); + connect(valueToReturn.getIndex(), returnIndex); } } @Override public void raise(VariableReader exception) { builder.addEdge(exception.getIndex(), exceptionIndex); - important(exceptionIndex); + escaping.add(exceptionIndex); } @Override @@ -245,10 +242,10 @@ public class DataFlowGraphBuilder implements InstructionReader { private int getFieldNode(FieldReference field) { int fieldNode = fieldNodes.getOrDefault(field, -1); if (fieldNode < 0) { - fieldNode = lastIndex++; + fieldNode = classes.create(); fieldNodes.put(field, fieldNode); } - important(fieldNode); + escaping.add(fieldNode); return fieldNode; } @@ -257,8 +254,7 @@ public class DataFlowGraphBuilder implements InstructionReader { if (fieldType instanceof ValueType.Primitive) { return; } - int fieldNode = getFieldNode(field); - builder.addEdge(fieldNode, receiver.getIndex()); + connect(getFieldNode(field), receiver.getIndex()); } @Override @@ -266,8 +262,7 @@ public class DataFlowGraphBuilder implements InstructionReader { if (fieldType instanceof ValueType.Primitive) { return; } - int fieldNode = getFieldNode(field); - builder.addEdge(value.getIndex(), fieldNode); + connect(value.getIndex(), getFieldNode(field)); } @Override @@ -276,65 +271,40 @@ public class DataFlowGraphBuilder implements InstructionReader { @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()); + connect(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) { - important(array.getIndex()); - builder.addEdge(getArrayElementNode(array.getIndex()), receiver.getIndex()); + builder.addEdge(array.getIndex(), receiver.getIndex()); } @Override public void putElement(VariableReader array, VariableReader index, VariableReader value) { - important(array.getIndex()); - 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; + builder.addEdge(value.getIndex(), array.getIndex()); } @Override public void invoke(VariableReader receiver, VariableReader instance, MethodReference method, List arguments, InvocationType type) { - if (receiver != null) { - if (!(method.getReturnType() instanceof ValueType.Primitive)) { - builder.addEdge(getMethodNode(method), receiver.getIndex()); - } - } ValueType[] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; ++i) { if (!(paramTypes[i] instanceof ValueType.Primitive)) { - important(arguments.get(i).getIndex()); + escaping.add(arguments.get(i).getIndex()); } } if (instance != null) { - important(instance.getIndex()); + escaping.add(instance.getIndex()); + } + if (receiver != null && !(method.getReturnType() instanceof ValueType.Primitive)) { + escaping.add(receiver.getIndex()); } } @@ -348,16 +318,16 @@ public class DataFlowGraphBuilder implements InstructionReader { @Override public void nullCheck(VariableReader receiver, VariableReader value) { - builder.addEdge(value.getIndex(), receiver.getIndex()); + connect(value.getIndex(), receiver.getIndex()); } @Override public void monitorEnter(VariableReader objectRef) { - important(objectRef.getIndex()); + escaping.add(objectRef.getIndex()); } @Override public void monitorExit(VariableReader objectRef) { - important(objectRef.getIndex()); + escaping.add(exceptionIndex); } } 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 f2b9e96df..d0a1c9ddd 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -504,7 +504,6 @@ public class DependencyChecker implements DependencyInfo { index = 0; } } - return; } public T getService(Class type) { 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 ed38a3f88..02ce79b18 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -51,10 +51,15 @@ class DependencyGraphBuilder { resultNode = dep.getResult(); DataFlowGraphBuilder dfgBuilder = new DataFlowGraphBuilder(); - for (int i = 0; i < dep.getParameterCount(); ++i) { - dfgBuilder.important(i); + boolean[] significantParams = new boolean[dep.getParameterCount()]; + significantParams[0] = true; + for (int i = 1; i < dep.getParameterCount(); ++i) { + ValueType arg = method.parameterType(i - 1); + if (!(arg instanceof ValueType.Primitive)) { + significantParams[i] = true; + } } - int[] nodeMapping = dfgBuilder.buildMapping(program, dep.getParameterCount(), + int[] nodeMapping = dfgBuilder.buildMapping(program, significantParams, !(method.getResultType() instanceof ValueType.Primitive) && method.getResultType() != ValueType.VOID); if (DependencyChecker.shouldLog) { @@ -93,8 +98,8 @@ class DependencyGraphBuilder { for (IncomingReader incoming : phi.readIncomings()) { DependencyNode incomingNode = nodes[incoming.getValue().getIndex()]; DependencyNode receiverNode = nodes[phi.getReceiver().getIndex()]; - if (incomingNode != null || receiverNode != null) { - nodes[incoming.getValue().getIndex()].connect(nodes[phi.getReceiver().getIndex()]); + if (incomingNode != null && receiverNode != null) { + incomingNode.connect(receiverNode); } } } @@ -139,7 +144,9 @@ class DependencyGraphBuilder { for (int i = 0; i < exceptions.length; ++i) { if (exceptions[i] == null || isAssignableFrom(checker.getClassSource(), exceptions[i], type.getName())) { - vars[i].propagate(type); + if (vars[i] != null) { + vars[i].propagate(type); + } return; } } @@ -193,7 +200,7 @@ class DependencyGraphBuilder { methodDep.use(); DependencyNode[] targetParams = methodDep.getVariables(); for (int i = 0; i < parameters.length; ++i) { - if (parameters[i] != null) { + if (parameters[i] != null && targetParams[i] != null) { parameters[i].connect(targetParams[i]); } } @@ -307,18 +314,23 @@ class DependencyGraphBuilder { String targetClsName = ((ValueType.Object)targetType).getClassName(); final ClassReader targetClass = dependencyChecker.getClassSource().get(targetClsName); if (targetClass != null) { - valueNode.connect(receiverNode, new DependencyTypeFilter() { - @Override public boolean match(DependencyType type) { - if (targetClass.getName().equals("java.lang.Object")) { - return true; + if (valueNode != null && receiverNode != null) { + valueNode.connect(receiverNode, new DependencyTypeFilter() { + @Override public boolean match(DependencyType type) { + if (targetClass.getName().equals("java.lang.Object")) { + return true; + } + return isAssignableFrom(dependencyChecker.getClassSource(), targetClass, + type.getName()); } - return isAssignableFrom(dependencyChecker.getClassSource(), targetClass, type.getName()); - } - }); + }); + } return; } } - valueNode.connect(receiverNode); + if (valueNode != null && receiverNode != null) { + valueNode.connect(receiverNode); + } } @Override @@ -481,7 +493,7 @@ class DependencyGraphBuilder { public void getElement(VariableReader receiver, VariableReader array, VariableReader index) { DependencyNode arrayNode = nodes[array.getIndex()]; DependencyNode receiverNode = nodes[receiver.getIndex()]; - if (arrayNode != null && receiverNode != null) { + if (arrayNode != null && receiverNode != null && receiverNode != arrayNode.getArrayItem()) { arrayNode.getArrayItem().connect(receiverNode); } } @@ -490,7 +502,7 @@ class DependencyGraphBuilder { public void putElement(VariableReader array, VariableReader index, VariableReader value) { DependencyNode valueNode = nodes[value.getIndex()]; DependencyNode arrayNode = nodes[array.getIndex()]; - if (valueNode != null && arrayNode != null) { + if (valueNode != null && arrayNode != null && valueNode != arrayNode.getArrayItem()) { valueNode.connect(arrayNode.getArrayItem()); } } 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 a4522c30b..374040f23 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -24,6 +24,7 @@ import java.util.*; public class DependencyNode implements ValueDependencyInfo { private DependencyChecker dependencyChecker; private List followers; + private int[] smallTypes; private BitSet types; private List transitions; private volatile String tag; @@ -41,6 +42,38 @@ public class DependencyNode implements ValueDependencyInfo { this.degree = degree; } + private boolean addType(DependencyType type) { + if (types == null) { + if (smallTypes == null) { + smallTypes = new int[] { type.index }; + return true; + } + } + if (smallTypes != null) { + for (int i = 0; i < smallTypes.length; ++i) { + if (smallTypes[i] == type.index) { + return false; + } + } + if (smallTypes.length == 5) { + types = new BitSet(); + for (int existingType : smallTypes) { + types.set(existingType); + } + smallTypes = null; + } else { + smallTypes = Arrays.copyOf(smallTypes, smallTypes.length + 1); + smallTypes[smallTypes.length - 1] = type.index; + return true; + } + } + if (!types.get(type.index)) { + types.set(type.index); + return true; + } + return false; + } + public void propagate(DependencyType type) { if (type.getDependencyChecker() != dependencyChecker) { throw new IllegalArgumentException("The given type does not belong to the same dependency checker"); @@ -48,11 +81,7 @@ 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 (addType(type)) { if (DependencyChecker.shouldLog) { System.out.println(tag + " -> " + type.getName()); } @@ -64,30 +93,30 @@ public class DependencyNode implements ValueDependencyInfo { } } - public void propagate(DependencyType[] agentTypes) { - DependencyType[] types = new DependencyType[agentTypes.length]; + public void propagate(DependencyType[] newTypes) { + DependencyType[] types = new DependencyType[newTypes.length]; int j = 0; - for (int i = 0; i < agentTypes.length; ++i) { - DependencyType type = agentTypes[i]; + for (int i = 0; i < newTypes.length; ++i) { + DependencyType type = newTypes[i]; if (type.getDependencyChecker() != dependencyChecker) { throw new IllegalArgumentException("The given type does not belong to the same dependency checker"); } - if (this.types == null || !this.types.get(type.index)) { + if (addType(type)) { types[j++] = type; } } - if (this.types == null) { - this.types = new BitSet(); + if (j == 0) { + return; } - for (int i = 0; i < j; ++i) { - this.types.set(types[i].index); - if (DependencyChecker.shouldLog) { + if (DependencyChecker.shouldLog) { + for (int i = 0; i < j; ++i) { System.out.println(tag + " -> " + types[i].getName()); } } if (followers != null) { + types = Arrays.copyOf(types, j); for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) { - dependencyChecker.schedulePropagation(consumer, Arrays.copyOf(types, j)); + dependencyChecker.schedulePropagation(consumer, types); } } } @@ -106,6 +135,12 @@ public class DependencyNode implements ValueDependencyInfo { types.add(dependencyChecker.types.get(index)); } dependencyChecker.schedulePropagation(consumer, types.toArray(new DependencyType[types.size()])); + } else if (this.smallTypes != null) { + DependencyType[] types = new DependencyType[smallTypes.length]; + for (int i = 0; i < types.length; ++i) { + types[i] = dependencyChecker.types.get(smallTypes[i]); + } + dependencyChecker.schedulePropagation(consumer, types); } } @@ -158,10 +193,18 @@ public class DependencyNode implements ValueDependencyInfo { @Override public boolean hasArrayType() { - return arrayItemNode != null && arrayItemNode.types != null && !arrayItemNode.types.isEmpty(); + return arrayItemNode != null && (arrayItemNode.types != null || arrayItemNode.smallTypes != null); } public boolean hasType(DependencyType type) { + if (smallTypes != null) { + for (int i = 0; i < smallTypes.length; ++i) { + if (smallTypes[i] == type.index) { + return true; + } + } + return false; + } return types != null && type.getDependencyChecker() == dependencyChecker && types.get(type.index); } @@ -172,6 +215,13 @@ public class DependencyNode implements ValueDependencyInfo { @Override public String[] getTypes() { + if (smallTypes != null) { + String[] result = new String[smallTypes.length]; + for (int i = 0; i < result.length; ++i) { + result[i] = dependencyChecker.types.get(smallTypes[i]).getName(); + } + return result; + } if (types == null) { return new String[0]; }