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 bd952fef4..ea78e32dc 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphUtils.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphUtils.java @@ -122,6 +122,9 @@ public final class GraphUtils { while (!stack.isEmpty()) { int node = stack.pop(); if (visitIndex[node] > 0) { + if (headerIndex[node] > 0) { + continue; + } currentComponent.add(node); int hdr = visitIndex[node]; for (int successor : graph.outgoingEdges(node)) { diff --git a/teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java b/teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java index a4a6fe89d..73eec1dd7 100644 --- a/teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java +++ b/teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java @@ -147,11 +147,11 @@ public class DJGraph { } public boolean isBackJoin(int i, int j) { - return isJoinEdge(i, j) && !domTree.dominates(mergeRoot[j], mergeRoot[i]); + return isJoinEdge(i, j) && domTree.dominates(mergeRoot[j], mergeRoot[i]); } public boolean isCrossJoin(int i, int j) { - return isJoinEdge(i, j) && domTree.dominates(mergeRoot[j], mergeRoot[i]); + return isJoinEdge(i, j) && !domTree.dominates(mergeRoot[j], mergeRoot[i]); } public boolean isSpanningBack(int i, int j) { @@ -200,7 +200,7 @@ public class DJGraph { return mergeRoot[node]; } - public void collapse(int[] nodes) { + public int collapse(int[] nodes) { // Replace nodes with their classes and find common dominator among them IntSet set = new IntOpenHashSet(); int top = nodes[0]; @@ -244,5 +244,6 @@ public class DJGraph { cfg.detachNode(node.value); } } + return top; } } diff --git a/teavm-core/src/main/java/org/teavm/common/irreducible/IrreducibleGraphConverter.java b/teavm-core/src/main/java/org/teavm/common/irreducible/IrreducibleGraphConverter.java index 3c6f4579d..68d2cd91b 100644 --- a/teavm-core/src/main/java/org/teavm/common/irreducible/IrreducibleGraphConverter.java +++ b/teavm-core/src/main/java/org/teavm/common/irreducible/IrreducibleGraphConverter.java @@ -30,6 +30,7 @@ import org.teavm.common.*; */ public class IrreducibleGraphConverter { private Graph cfg; + private int totalNodeCount; private GraphSplittingBackend backend; public void convertToReducible(Graph cfg, int[] weight, GraphSplittingBackend backend) { @@ -39,6 +40,7 @@ public class IrreducibleGraphConverter { identityNodeMap[i] = new int[] { i }; } this.cfg = cfg; + totalNodeCount = cfg.size(); handleLoops(new DJGraph(cfg, weight), identityNodeMap); this.backend = null; } @@ -46,23 +48,34 @@ public class IrreducibleGraphConverter { private void handleLoops(DJGraph djGraph, int[][] nodeMap) { for (int level = djGraph.levelCount() - 1; level >= 0; --level) { boolean irreducible = false; + levelScan: for (int node : djGraph.level(level)) { for (int pred : djGraph.getGraph().incomingEdges(node)) { - if (djGraph.isCrossJoin(pred, node)) { - if (!irreducible && djGraph.isSpanningBack(node, pred)) { - irreducible = true; - } - } else if (djGraph.isBackJoin(node, pred)) { - djGraph.collapse(reachUnder(djGraph, pred, node)); + if (djGraph.isCrossJoin(pred, node) && djGraph.isSpanningBack(node, pred)) { + irreducible = true; + break levelScan; } } } - DJGraphNodeFilter filter = new DJGraphNodeFilter(djGraph, level); - int[][] sccs = GraphUtils.findStronglyConnectedComponents(djGraph.getGraph(), djGraph.level(level), filter); - for (int[] scc : sccs) { - if (scc.length > 1) { - handleStronglyConnectedComponent(djGraph, scc, nodeMap); - djGraph.collapse(scc); + if (irreducible) { + DJGraphNodeFilter filter = new DJGraphNodeFilter(djGraph, level); + int[][] sccs = GraphUtils.findStronglyConnectedComponents(djGraph.getGraph(), + djGraph.level(level), filter); + for (int[] scc : sccs) { + if (scc.length > 1) { + handleStronglyConnectedComponent(djGraph, scc, nodeMap); + int cls = djGraph.collapse(scc); + IntegerArray nodes = new IntegerArray(djGraph.getGraph().size()); + for (int representative : djGraph.classRepresentatives(cls)) { + for (int node : nodeMap[representative]) { + nodes.add(node); + } + } + for (int representative : djGraph.classRepresentatives(cls)) { + nodeMap[representative] = new int[0]; + } + nodeMap[cls] = nodes.getAll(); + } } } } @@ -93,14 +106,16 @@ public class IrreducibleGraphConverter { // Partition SCC into domains DisjointSet partitions = new DisjointSet(); + int[] sccBack = new int[djGraph.getGraph().size()]; for (int i = 0; i < scc.length; ++i) { partitions.create(); + sccBack[scc[i]] = i; } for (int i = 0; i < scc.length; ++i) { int node = scc[i]; int idom = djGraph.getDomTree().immediateDominatorOf(node); if (idom != sharedDom) { - partitions.union(node, idom); + partitions.union(i, sccBack[idom]); } } int[] domains = partitions.pack(scc.length); @@ -110,10 +125,10 @@ public class IrreducibleGraphConverter { } // For each domain calculate its weight - int[] domainWeight = new int [domainCount]; + int[] domainWeight = new int[domainCount]; for (int i = 0; i < scc.length; ++i) { int node = scc[i]; - domainWeight[domains[node]] += djGraph.weightOf(node); + domainWeight[domains[i]] += djGraph.weightOf(node); } // Find domain to split around @@ -130,7 +145,7 @@ public class IrreducibleGraphConverter { IntSet domainNodes = new IntOpenHashSet(scc.length); for (int i = 0; i < scc.length; ++i) { int node = scc[i]; - if (domains[node] == domain) { + if (domains[i] == domain) { domainNodes.add(node); } } @@ -144,6 +159,7 @@ public class IrreducibleGraphConverter { private void splitStronglyConnectedComponent(DJGraph djGraph, IntSet domain, int sharedDom, int[] scc, int[][] nodeMap) { + Arrays.sort(scc); // Find SCC \ domain int[][] mappedNonDomain = new int[scc.length - domain.size()][]; int[] domainNodes = new int[domain.size()]; @@ -166,10 +182,13 @@ public class IrreducibleGraphConverter { // Delegate splitting to domain int[][] newNodes = backend.split(mappedDomain, mappedNonDomain); + for (int[] nodes : newNodes) { + totalNodeCount += nodes.length; + } // Calculate mappings int[][] newNodeMap = new int[1 + scc.length + newNodes.length][]; - int[] newNodeBackMap = new int[cfg.size()]; + int[] newNodeBackMap = new int[totalNodeCount]; int[] mappedWeight = new int[newNodeMap.length]; Arrays.fill(newNodeBackMap, -1); newNodeMap[0] = nodeMap[sharedDom]; @@ -203,12 +222,12 @@ public class IrreducibleGraphConverter { } } for (int i = 1; i <= mappedDomain.length; ++i) { - for (int succ : djGraph.getCfg().outgoingEdges(domainNodes[i])) { + for (int succ : djGraph.getCfg().outgoingEdges(domainNodes[i - 1])) { int j = newNodeBackMap[succ]; if (j > mappedDomain.length) { - builder.addEdge(i, j); - } else if (j >= 0) { builder.addEdge(i, j + mappedNonDomain.length); + } else if (j >= 0) { + builder.addEdge(i, j); } } } @@ -219,9 +238,9 @@ public class IrreducibleGraphConverter { if (j >= 0) { builder.addEdge(i, j); if (j > mappedDomain.length) { - builder.addEdge(i + mappedNonDomain.length, j); - } else { builder.addEdge(i + mappedNonDomain.length, j + mappedNonDomain.length); + } else { + builder.addEdge(i + mappedNonDomain.length, j); } } } diff --git a/teavm-core/src/test/java/org/teavm/common/GraphTest.java b/teavm-core/src/test/java/org/teavm/common/GraphTest.java index d757cd7bb..bfb8911c6 100644 --- a/teavm-core/src/test/java/org/teavm/common/GraphTest.java +++ b/teavm-core/src/test/java/org/teavm/common/GraphTest.java @@ -48,12 +48,51 @@ public class GraphTest { builder.addEdge(12, 13); Graph graph = builder.build(); - int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, new int[] { 0 }, new GraphNodeFilter() { + int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, new int[] { 0 }, filter); + sortSccs(sccs); + + assertThat(sccs.length, is(6)); + assertThat(sccs[0], is(new int[] { 0 })); + assertThat(sccs[1], is(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 })); + assertThat(sccs[2], is(new int[] { 9 })); + assertThat(sccs[3], is(new int[] { 10 })); + assertThat(sccs[4], is(new int[] { 11, 12 })); + assertThat(sccs[5], is(new int[] { 13 })); + } + + @Test + public void stronglyConnectedComponentCalculated2() { + GraphBuilder builder = new GraphBuilder(); + builder.addEdge(0, 1); + builder.addEdge(0, 2); + builder.addEdge(0, 3); + builder.addEdge(1, 2); + builder.addEdge(2, 1); + builder.addEdge(3, 2); + builder.addEdge(2, 4); + builder.addEdge(4, 5); + builder.addEdge(4, 1); + builder.addEdge(5, 3); + Graph graph = builder.build(); + + int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, new int[] { 1, 2, 3 }, new GraphNodeFilter() { @Override public boolean match(int node) { - return true; + return node != 0; } }); + sortSccs(sccs); + assertThat(sccs.length, is(1)); + assertThat(sccs[0], is(new int[] { 1, 2, 3, 4, 5 })); + } + + private GraphNodeFilter filter = new GraphNodeFilter() { + @Override public boolean match(int node) { + return true; + } + }; + + private void sortSccs(int[][] sccs) { for (int i = 0; i < sccs.length; ++i) { Arrays.sort(sccs[i]); } @@ -62,12 +101,5 @@ public class GraphTest { return Integer.compare(o1[0], o2[0]); } }); - - assertThat(sccs[0], is(new int[] { 0 })); - assertThat(sccs[1], is(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 })); - assertThat(sccs[2], is(new int[] { 9 })); - assertThat(sccs[3], is(new int[] { 10 })); - assertThat(sccs[4], is(new int[] { 11, 12 })); - assertThat(sccs[5], is(new int[] { 13 })); } } \ No newline at end of file