From f106afb0345f92a96d416ead889ede34a7b9866d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 1 Mar 2015 23:19:46 +0300 Subject: [PATCH] Complete node splitting algorithm. Add test case for SCC finder. --- .../java/org/teavm/common/GraphUtils.java | 52 ++++++------- .../IrreducibleGraphConverter.java | 22 +++++- .../test/java/org/teavm/common/GraphTest.java | 73 +++++++++++++++++++ 3 files changed, 113 insertions(+), 34 deletions(-) create mode 100644 teavm-core/src/test/java/org/teavm/common/GraphTest.java 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 925b6d3a2..bd952fef4 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphUtils.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphUtils.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; - /** * * @author Alexey Andreev @@ -111,9 +110,7 @@ public final class GraphUtils { */ public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) { List components = new ArrayList<>(); - boolean[] done = new boolean[graph.size()]; int[] visitIndex = new int[graph.size()]; - Arrays.fill(visitIndex, -1); int[] headerIndex = new int[graph.size()]; int lastIndex = 0; IntegerStack stack = new IntegerStack(graph.size()); @@ -124,36 +121,32 @@ public final class GraphUtils { IntegerArray currentComponent = new IntegerArray(1); while (!stack.isEmpty()) { int node = stack.pop(); - if (visitIndex[node] == 0) { - if (done[node]) { - currentComponent.add(node); - int hdr = node; - for (int successor : graph.outgoingEdges(node)) { - if (!filter.match(successor)) { - continue; - } - if (!done[successor]) { - hdr = Math.min(hdr, visitIndex[successor]); - } else { - hdr = Math.min(hdr, headerIndex[successor]); - } - } - if (hdr == node) { - components.add(currentComponent.getAll()); - currentComponent.clear(); - } - headerIndex[node] = hdr; - } else { - done[node] = true; - } - } else { - visitIndex[node] = ++lastIndex; - stack.push(node); + if (visitIndex[node] > 0) { + currentComponent.add(node); + int hdr = visitIndex[node]; for (int successor : graph.outgoingEdges(node)) { if (!filter.match(successor)) { continue; } - stack.push(node); + if (headerIndex[successor] == 0) { + hdr = Math.min(hdr, visitIndex[successor]); + } else { + hdr = Math.min(hdr, headerIndex[successor]); + } + } + if (hdr == visitIndex[node]) { + components.add(currentComponent.getAll()); + currentComponent.clear(); + } + headerIndex[node] = hdr; + } else { + visitIndex[node] = ++lastIndex; + stack.push(node); + for (int successor : graph.outgoingEdges(node)) { + if (!filter.match(successor) || visitIndex[successor] > 0) { + continue; + } + stack.push(successor); } } } @@ -243,7 +236,6 @@ public final class GraphUtils { return domFrontiers; } - private static int[] makeSet(IntegerArray array) { int[] items = array.getAll(); int[] set = new int[items.length]; 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 dcd576945..a91ac26fd 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 @@ -55,7 +55,7 @@ public class IrreducibleGraphConverter { irreducible = true; } } else if (djGraph.isBackJoin(node, pred)) { - djGraph.collapse(reachUnder(djGraph, pred)); + djGraph.collapse(reachUnder(djGraph, pred, node)); } } } @@ -68,9 +68,20 @@ public class IrreducibleGraphConverter { } } - private int[] reachUnder(DJGraph djGraph, int top) { - // TODO: implement - return null; + private int[] reachUnder(DJGraph djGraph, int back, int header) { + IntSet naturalLoop = IntOpenHashSet.from(header); + IntegerStack stack = new IntegerStack(djGraph.getGraph().size()); + stack.push(back); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (!naturalLoop.add(node)) { + continue; + } + for (int pred : djGraph.getGraph().incomingEdges(node)) { + stack.push(pred); + } + } + return naturalLoop.toArray(); } private void handleStronglyConnectedComponent(DJGraph djGraph, int[] scc, int[] nodeMap) { @@ -126,6 +137,9 @@ public class IrreducibleGraphConverter { // Split splitStronglyConnectedComponent(domainNodes, sharedDom, scc, nodeMap); + + // Collapse + djGraph.collapse(scc); } private void splitStronglyConnectedComponent(IntSet domain, int sharedDom, int[] scc, int[] nodeMap) { diff --git a/teavm-core/src/test/java/org/teavm/common/GraphTest.java b/teavm-core/src/test/java/org/teavm/common/GraphTest.java new file mode 100644 index 000000000..d757cd7bb --- /dev/null +++ b/teavm-core/src/test/java/org/teavm/common/GraphTest.java @@ -0,0 +1,73 @@ +/* + * 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.common; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import java.util.Arrays; +import java.util.Comparator; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class GraphTest { + @Test + public void stronglyConnectedComponentsCalculated() { + GraphBuilder builder = new GraphBuilder(); + builder.addEdge(0, 1); + builder.addEdge(1, 2); + builder.addEdge(2, 3); + builder.addEdge(2, 4); + builder.addEdge(3, 5); + builder.addEdge(4, 5); + builder.addEdge(5, 6); + builder.addEdge(6, 1); + builder.addEdge(6, 7); + builder.addEdge(7, 8); + builder.addEdge(7, 9); + builder.addEdge(8, 1); + builder.addEdge(9, 10); + builder.addEdge(10, 11); + builder.addEdge(11, 12); + builder.addEdge(12, 11); + builder.addEdge(12, 13); + Graph graph = builder.build(); + + int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, new int[] { 0 }, new GraphNodeFilter() { + @Override public boolean match(int node) { + return true; + } + }); + + for (int i = 0; i < sccs.length; ++i) { + Arrays.sort(sccs[i]); + } + Arrays.sort(sccs, new Comparator() { + @Override public int compare(int[] o1, int[] o2) { + 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