diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index b8648786e..defac52d0 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -89,6 +89,7 @@ public class DependencyAnalyzer implements DependencyInfo { Map dependencyPlugins = new HashMap<>(); private boolean completing; private Map superClassFilters = new HashMap<>(); + private List allNodes = new ArrayList<>(); boolean asyncSupported; public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, @@ -153,8 +154,13 @@ public class DependencyAnalyzer implements DependencyInfo { return createNode(null); } - private DependencyNode createNode(ValueType typeFilter) { - return new DependencyNode(this, typeFilter); + DependencyNode createNode(ValueType typeFilter) { + if (typeFilter != null && typeFilter.isObject("java.lang.Object")) { + typeFilter = null; + } + DependencyNode node = new DependencyNode(this, typeFilter); + allNodes.add(node); + return node; } @Override @@ -266,8 +272,12 @@ public class DependencyAnalyzer implements DependencyInfo { } void schedulePropagation(DependencyNodeToNodeTransition consumer, DependencyType type) { + if (!consumer.destination.filter(type)) { + return; + } + if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD - && consumer.pointsToDomainOrigin()) { + && consumer.pointsToDomainOrigin() && consumer.destination.propagateCount < 20) { ++propagationDepth; consumer.consume(type); --propagationDepth; @@ -290,15 +300,16 @@ public class DependencyAnalyzer implements DependencyInfo { } if (consumer.pendingTypes == null && propagationDepth < PROPAGATION_STACK_THRESHOLD - && consumer.pointsToDomainOrigin()) { + && consumer.pointsToDomainOrigin() && consumer.destination.propagateCount < 20) { ++propagationDepth; consumer.consume(types); --propagationDepth; } else { if (consumer.pendingTypes == null) { pendingTransitions.add(consumer); - consumer.pendingTypes = new IntHashSet(50); + consumer.pendingTypes = new IntHashSet(Math.max(50, types.length)); } + consumer.pendingTypes.ensureCapacity(types.length + consumer.pendingTypes.size()); for (DependencyType type : types) { consumer.pendingTypes.add(type.index); } @@ -650,38 +661,44 @@ public class DependencyAnalyzer implements DependencyInfo { private void reportDependencies() { List report = new ArrayList<>(); - for (MethodReference reachableMethod : getReachableMethods()) { - MethodDependency dependency = getMethod(reachableMethod); - if (dependency == null) { - continue; - } - - for (int i = 0; i <= reachableMethod.parameterCount(); ++i) { - DependencyNode param = dependency.getVariable(i); - if (param == null) { - continue; - } - report.add(new ReportEntry(reachableMethod + " param " + i, param.getTypes().length)); - } - - if (dependency.getResult() != null) { - report.add(new ReportEntry(reachableMethod + " result", dependency.getResult().getTypes().length)); + int domainCount = 0; + for (DependencyNode node : allNodes) { + String tag = node.tag + ""; + if (node.typeSet != null && node.typeSet.origin == node) { + ++domainCount; + tag += "{*}"; } + report.add(new ReportEntry(tag, node.getTypes().length)); } report.sort(Comparator.comparingInt(n -> -n.count)); for (ReportEntry entry : report) { System.out.println(entry.title + ": " + entry.count); } + + System.out.println("Total nodes: " + allNodes.size()); + System.out.println("Total domains: " + domainCount); } public void cleanup() { + for (DependencyNode node : allNodes) { + node.followers = null; + node.transitions = null; + node.transitionList = null; + node.method = null; + if (node.typeSet != null) { + node.typeSet.cleanup(); + } + } + for (MethodReference reachableMethod : getReachableMethods()) { MethodDependency dependency = getMethod(reachableMethod); for (int i = dependency.getParameterCount() + 1; i < dependency.getVariableCount(); ++i) { dependency.variableNodes[i] = null; } } + + allNodes.clear(); } static class ReportEntry { diff --git a/core/src/main/java/org/teavm/dependency/DependencyNode.java b/core/src/main/java/org/teavm/dependency/DependencyNode.java index a6ff6da6d..22b291e8b 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -49,15 +49,11 @@ public class DependencyNode implements ValueDependencyInfo { ValueType typeFilter; private DependencyTypeFilter cachedTypeFilter; - boolean visitedFlag; + int splitCount; + public int propagateCount; DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter) { - this(dependencyAnalyzer, typeFilter, 0); - } - - private DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter, int degree) { this.dependencyAnalyzer = dependencyAnalyzer; - this.degree = degree; this.typeFilter = typeFilter; } @@ -65,7 +61,8 @@ public class DependencyNode implements ValueDependencyInfo { if (degree > DEGREE_THRESHOLD) { return; } - if (!hasType(type)) { + if (!hasType(type) && filter(type)) { + propagateCount++; moveToSeparateDomain(); typeSet.addType(type); scheduleSingleType(type, null); @@ -108,11 +105,19 @@ public class DependencyNode implements ValueDependencyInfo { return; } + if (newTypes.length == 0) { + return; + } + if (newTypes.length == 1) { + propagate(newTypes[0]); + return; + } + int j = 0; boolean copied = false; for (int i = 0; i < newTypes.length; ++i) { DependencyType type = newTypes[i]; - if (!hasType(type)) { + if (!hasType(type) && filter(type)) { newTypes[j++] = type; } else if (!copied) { copied = true; @@ -128,6 +133,7 @@ public class DependencyNode implements ValueDependencyInfo { return; } + propagateCount++; if (j < newTypes.length) { newTypes = Arrays.copyOf(newTypes, j); } @@ -222,7 +228,7 @@ public class DependencyNode implements ValueDependencyInfo { } } - private boolean filter(DependencyType type) { + boolean filter(DependencyType type) { if (typeFilter == null) { return true; } @@ -279,14 +285,22 @@ public class DependencyNode implements ValueDependencyInfo { if (typeSet != null) { if (typeSet == node.typeSet) { - typeSet.loopNodes = null; return; } if (typeSet.transitions != null) { typeSet.transitions.add(transition); } - node.propagate(filter != null ? getTypesInternal(filter) : getTypesInternal()); + DependencyType[] types = node.typeSet == null && filter == null && node.typeFilter == null + ? getTypesInternal() + : getTypesInternal(filter, this, node); + if (types.length > 0) { + if (node.typeSet == null) { + node.propagate(types); + } else { + dependencyAnalyzer.schedulePropagation(transition, types); + } + } } connectArrayItemNodes(node); @@ -372,10 +386,8 @@ public class DependencyNode implements ValueDependencyInfo { ValueType itemTypeFilter = typeFilter instanceof ValueType.Array ? ((ValueType.Array) typeFilter).getItemType() : null; - if (itemTypeFilter != null && itemTypeFilter.isObject("java.lang.Object")) { - itemTypeFilter = null; - } - arrayItemNode = new DependencyNode(dependencyAnalyzer, itemTypeFilter, degree + 1); + arrayItemNode = dependencyAnalyzer.createNode(itemTypeFilter); + arrayItemNode.degree = degree + 1; arrayItemNode.method = method; if (DependencyAnalyzer.shouldTag) { arrayItemNode.tag = tag + "["; @@ -387,7 +399,8 @@ public class DependencyNode implements ValueDependencyInfo { @Override public DependencyNode getClassValueNode() { if (classValueNode == null) { - classValueNode = new DependencyNode(dependencyAnalyzer, null, degree); + classValueNode = dependencyAnalyzer.createNode(); + classValueNode.degree = degree; classValueNode.classValueNode = classValueNode; classValueNode.classNodeParent = this; if (DependencyAnalyzer.shouldTag) { @@ -445,19 +458,12 @@ public class DependencyNode implements ValueDependencyInfo { return i == result.length ? result : Arrays.copyOf(result, i); } - DependencyType[] getTypesInternal(DependencyTypeFilter filter) { + DependencyType[] getTypesInternal(DependencyTypeFilter filter, DependencyNode sourceNode, + DependencyNode targetNode) { if (typeSet == null) { - return new DependencyType[0]; + return TypeSet.EMPTY_TYPES; } - DependencyType[] types = typeSet.getTypes(); - DependencyType[] result = new DependencyType[types.length]; - int i = 0; - for (DependencyType type : types) { - if (filter(type) && filter.match(type)) { - result[i++] = type; - } - } - return i == result.length ? result : Arrays.copyOf(result, i); + return typeSet.getTypesForNode(sourceNode, targetNode, filter); } public String getTag() { @@ -480,7 +486,7 @@ public class DependencyNode implements ValueDependencyInfo { return; } - if (typeSet.origin == this || typeSet.reachesOrigin(this)) { + if (typeSet.origin == this) { return; } @@ -497,12 +503,13 @@ public class DependencyNode implements ValueDependencyInfo { for (DependencyNode node : domain) { node.typeSet = typeSet; + node.splitCount++; } } Collection findDomain() { - Set visited = new LinkedHashSet<>(); - Deque stack = new ArrayDeque<>(); + Set visited = new LinkedHashSet<>(50); + Deque stack = new ArrayDeque<>(50); stack.push(this); while (!stack.isEmpty()) { @@ -510,6 +517,9 @@ public class DependencyNode implements ValueDependencyInfo { if (!visited.add(node)) { continue; } + if (visited.size() > 100) { + break; + } if (node.transitions != null) { for (ObjectCursor cursor : node.transitionList) { diff --git a/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java b/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java index b2a247516..a111d1f13 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java +++ b/core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java @@ -15,7 +15,7 @@ */ package org.teavm.dependency; -import com.carrotsearch.hppc.IntSet; +import com.carrotsearch.hppc.IntHashSet; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; @@ -27,7 +27,7 @@ class DependencyNodeToNodeTransition { DependencyNode destination; DependencyTypeFilter filter; private BitSet knownFilteredOffTypes; - IntSet pendingTypes; + IntHashSet pendingTypes; byte destSubsetOfSrc; DependencyNodeToNodeTransition(DependencyNode source, DependencyNode destination, DependencyTypeFilter filter) { @@ -37,7 +37,7 @@ class DependencyNodeToNodeTransition { } void consume(DependencyType type) { - if (!destination.hasType(type) && filterType(type)) { + if (!destination.hasType(type) && filterType(type) && destination.filter(type)) { propagate(type); } } @@ -85,8 +85,17 @@ class DependencyNodeToNodeTransition { if (destination.typeSet == null) { return true; } - if (destination.typeSet == source.typeSet || destination.typeSet.origin == destination - || destination.typeSet.typeCount() >= source.typeSet.typeCount()) { + if (destination.typeSet == source.typeSet || destination.typeSet.origin == source + || destination.typeSet.typeCount() > source.typeSet.typeCount()) { + return false; + } + + if (destination.splitCount > 4) { + return false; + } + + if (destination.typeSet.typeCount() == source.typeSet.typeCount() + && destination.typeSet.origin != destination) { return false; } @@ -106,7 +115,7 @@ class DependencyNodeToNodeTransition { if (filter == null) { for (DependencyType type : types) { boolean added = false; - if (!destination.hasType(type)) { + if (!destination.hasType(type) && destination.filter(type)) { types[j++] = type; added = true; } @@ -119,11 +128,9 @@ class DependencyNodeToNodeTransition { } else { for (DependencyType type : types) { boolean added = false; - if (filterType(type)) { - if (!destination.hasType(type)) { - types[j++] = type; - added = true; - } + if (filterType(type) && !destination.hasType(type) && destination.filter(type)) { + types[j++] = type; + added = true; } if (!added && !copied) { copied = true; diff --git a/core/src/main/java/org/teavm/dependency/TypeSet.java b/core/src/main/java/org/teavm/dependency/TypeSet.java index 6ed598864..15e81bd29 100644 --- a/core/src/main/java/org/teavm/dependency/TypeSet.java +++ b/core/src/main/java/org/teavm/dependency/TypeSet.java @@ -16,8 +16,6 @@ package org.teavm.dependency; import com.carrotsearch.hppc.ObjectArrayList; -import com.carrotsearch.hppc.ObjectHashSet; -import com.carrotsearch.hppc.ObjectStack; import com.carrotsearch.hppc.cursors.ObjectCursor; import java.util.ArrayList; import java.util.Arrays; @@ -28,7 +26,7 @@ import java.util.Set; class TypeSet { private static final int SMALL_TYPES_THRESHOLD = 3; - private static final DependencyType[] EMPTY_TYPES = new DependencyType[0]; + static final DependencyType[] EMPTY_TYPES = new DependencyType[0]; private DependencyAnalyzer dependencyAnalyzer; DependencyNode origin; private int[] smallTypes; @@ -36,7 +34,6 @@ class TypeSet { private int typesCount; Set domain = new LinkedHashSet<>(); - ObjectHashSet loopNodes; ObjectArrayList transitions; ArrayList consumers; @@ -93,6 +90,41 @@ class TypeSet { } } + DependencyType[] getTypesForNode(DependencyNode sourceNode, DependencyNode targetNode, + DependencyTypeFilter filter) { + int j = 0; + DependencyType[] types; + if (this.types != null) { + types = new DependencyType[this.types.cardinality()]; + for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) { + DependencyType type = dependencyAnalyzer.types.get(index); + if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type) + && (filter == null || filter.match(type))) { + types[j++] = type; + } + } + } else if (this.smallTypes != null) { + types = new DependencyType[smallTypes.length]; + for (int i = 0; i < types.length; ++i) { + DependencyType type = dependencyAnalyzer.types.get(smallTypes[i]); + if (sourceNode.filter(type) && !targetNode.hasType(type) && targetNode.filter(type) + && (filter == null || filter.match(type))) { + types[j++] = type; + } + } + } else { + return EMPTY_TYPES; + } + + if (j == 0) { + return EMPTY_TYPES; + } + if (j < types.length) { + types = Arrays.copyOf(types, j); + } + return types; + } + boolean hasType(DependencyType type) { if (smallTypes != null) { for (int i = 0; i < smallTypes.length; ++i) { @@ -120,7 +152,6 @@ class TypeSet { void invalidate() { transitions = null; consumers = null; - loopNodes = null; } ObjectArrayList getTransitions() { @@ -153,51 +184,14 @@ class TypeSet { return consumers; } - boolean reachesOrigin(DependencyNode node) { - if (loopNodes == null) { - findLoopNodes(); - } - return loopNodes.contains(node); - } - - private void findLoopNodes() { - if (domain.size() == 1) { - return; - } - - for (DependencyNode node : domain) { - node.visitedFlag = false; - } - - loopNodes = new ObjectHashSet<>(domain.size()); - loopNodes.add(origin); - - ObjectStack stack = new ObjectStack<>(domain.size()); - stack.push(origin); - - while (!stack.isEmpty()) { - DependencyNode next = stack.pop(); - if (next.visitedFlag) { - continue; - } - next.visitedFlag = true; - - if (next.transitions != null) { - for (ObjectCursor cursor : next.transitionList) { - DependencyNodeToNodeTransition transition = cursor.value; - if (transition.destination.typeSet == this) { - if (!transition.destination.visitedFlag) { - stack.push(transition.destination); - } else if (loopNodes.contains(transition.destination)) { - loopNodes.add(next); - } - } - } - } - } - } - int typeCount() { return smallTypes != null ? smallTypes.length : types != null ? typesCount : 0; } + + void cleanup() { + origin = null; + domain = null; + transitions = null; + consumers = null; + } }