Improve DJ-graph implementation. Add Tarjan's SCC finding algorithm. Add

HPCC dependency.
This commit is contained in:
konsoletyper 2015-02-22 22:19:35 +04:00
parent 11437af5ae
commit 14ce9e23a4
9 changed files with 367 additions and 55 deletions

View File

@ -40,6 +40,11 @@
<groupId>org.ow2.asm</groupId> <groupId>org.ow2.asm</groupId>
<artifactId>asm-debug-all</artifactId> <artifactId>asm-debug-all</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.carrotsearch</groupId>
<artifactId>hppc</artifactId>
<version>0.6.1</version>
</dependency>
</dependencies> </dependencies>
<name>TeaVM core</name> <name>TeaVM core</name>

View File

@ -15,6 +15,10 @@
*/ */
package org.teavm.common; package org.teavm.common;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** /**
* *
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
@ -22,39 +26,57 @@ package org.teavm.common;
public class DJGraph { public class DJGraph {
private DominatorTree domTree; private DominatorTree domTree;
private Graph graph; private Graph graph;
private Graph backEdges; private LCATree spanningTree;
private int[] spanningTreeNode;
private int[] spanningTreeIndex;
private int[][] levelContent;
public DJGraph(Graph src) { public DJGraph(Graph src) {
domTree = GraphUtils.buildDominatorTree(src); domTree = GraphUtils.buildDominatorTree(src);
buildGraph(src); buildGraph(src);
buildLevels();
dfs(); dfs();
} }
private void buildGraph(Graph graph) { private void buildGraph(Graph graph) {
GraphBuilder builder = new GraphBuilder(graph.size()); GraphBuilder builder = new GraphBuilder(graph.size());
// Add join edges
for (int i = 0; i < graph.size(); ++i) { for (int i = 0; i < graph.size(); ++i) {
for (int j : graph.outgoingEdges(i)) { for (int j : graph.outgoingEdges(i)) {
builder.addEdge(i, j); builder.addEdge(i, j);
} }
} }
// Add dom edges
for (int i = 1; i < graph.size(); ++i) { for (int i = 1; i < graph.size(); ++i) {
int j = domTree.immediateDominatorOf(i); int j = domTree.immediateDominatorOf(i);
boolean needsDomEdge = true; builder.addEdge(j, i);
for (int k : graph.incomingEdges(i)) {
if (k == j) {
needsDomEdge = false;
break;
}
}
if (needsDomEdge) {
builder.addEdge(j, i);
}
} }
graph = builder.build(); graph = builder.build();
} }
private void buildLevels() {
List<IntegerArray> builder = new ArrayList<>();
for (int i = 0; i < graph.size(); ++i) {
int level = domTree.levelOf(i);
while (level >= builder.size()) {
builder.add(new IntegerArray(1));
}
builder.get(level).add(i);
}
levelContent = new int[builder.size()][];
for (int i = 0; i < builder.size(); ++i) {
levelContent[i] = builder.get(i).getAll();
}
}
private void dfs() { private void dfs() {
GraphBuilder builder = new GraphBuilder(); spanningTreeNode = new int[graph.size()];
spanningTreeIndex = new int[graph.size()];
Arrays.fill(spanningTreeIndex, -1);
Arrays.fill(spanningTreeNode, -1);
boolean[] visited = new boolean[graph.size()]; boolean[] visited = new boolean[graph.size()];
IntegerStack stack = new IntegerStack(graph.size() * 2); IntegerStack stack = new IntegerStack(graph.size() * 2);
stack.push(0); stack.push(0);
@ -63,16 +85,17 @@ public class DJGraph {
int node = stack.pop(); int node = stack.pop();
int source = stack.pop(); int source = stack.pop();
if (visited[node]) { if (visited[node]) {
builder.addEdge(node, source);
continue; continue;
} }
int index = spanningTree.addNode(spanningTreeIndex[source]);
spanningTreeNode[index] = node;
spanningTreeIndex[node] = index;
visited[node] = true; visited[node] = true;
for (int succ : graph.outgoingEdges(node)) { for (int succ : graph.outgoingEdges(node)) {
stack.push(node); stack.push(node);
stack.push(succ); stack.push(succ);
} }
} }
backEdges = builder.build();
} }
public DominatorTree getDomTree() { public DominatorTree getDomTree() {
@ -83,7 +106,50 @@ public class DJGraph {
return graph; return graph;
} }
public int[] getSpanningTreeBackEdges(int node) { public boolean isAncestorInSpanningTree(int anc, int node) {
return backEdges.outgoingEdges(node); anc = spanningTreeIndex[anc];
node = spanningTreeIndex[node];
if (anc < 0 || node < 0) {
return false;
}
return spanningTree.lcaOf(anc, node) == anc;
}
public boolean isDomEdge(int i, int j) {
return domTree.immediateDominatorOf(j) == i;
}
public boolean isJoinEdge(int i, int j) {
return !isDomEdge(i, j);
}
public boolean isBackJoin(int i, int j) {
return isJoinEdge(i, j) && !domTree.dominates(j, i);
}
public boolean isCrossJoin(int i, int j) {
return isJoinEdge(i, j) && domTree.dominates(j, i);
}
public boolean isSpanningBack(int i, int j) {
return spanningTree.lcaOf(i, j) == j;
}
public boolean isSpanningCross(int i, int j) {
int c = spanningTree.lcaOf(i, j);
return c != i && c != j;
}
public int levelOf(int node) {
return domTree.levelOf(node);
}
public int[] level(int level) {
int[] result = levelContent[level];
return Arrays.copyOf(result, result.length);
}
public int levelCount() {
return levelContent.length;
} }
} }

View File

@ -21,42 +21,42 @@ package org.teavm.common;
*/ */
class DefaultDominatorTree implements DominatorTree { class DefaultDominatorTree implements DominatorTree {
private LCATree lcaTree; private LCATree lcaTree;
private int[] indexes;
private int[] nodes; private int[] nodes;
private int[] unodes;
public DefaultDominatorTree(int[] dominators, int[] vertices) { public DefaultDominatorTree(int[] dominators, int[] vertices) {
lcaTree = new LCATree(dominators.length + 1); lcaTree = new LCATree(dominators.length + 1);
indexes = new int[dominators.length + 1];
nodes = new int[dominators.length + 1]; nodes = new int[dominators.length + 1];
unodes = new int[dominators.length + 1]; indexes[0] = -1;
nodes[0] = -1;
for (int i = 0; i < dominators.length; ++i) { for (int i = 0; i < dominators.length; ++i) {
int v = vertices[i]; int v = vertices[i];
if (v < 0) { if (v < 0) {
continue; continue;
} }
int dom = nodes[dominators[v] + 1]; int dom = indexes[dominators[v] + 1];
int node = lcaTree.addNode(dom); int node = lcaTree.addNode(dom);
nodes[v + 1] = node; indexes[v + 1] = node;
unodes[node] = v; nodes[node] = v;
} }
} }
@Override @Override
public boolean directlyDominates(int a, int b) { public boolean directlyDominates(int a, int b) {
a = nodes[a + 1]; a = indexes[a + 1];
b = nodes[b + 1]; b = indexes[b + 1];
return lcaTree.lcaOf(a, b) == a; return lcaTree.lcaOf(a, b) == a;
} }
@Override @Override
public int commonDominatorOf(int a, int b) { public int commonDominatorOf(int a, int b) {
return unodes[lcaTree.lcaOf(nodes[a + 1], nodes[b + 1])]; return nodes[lcaTree.lcaOf(indexes[a + 1], indexes[b + 1])];
} }
@Override @Override
public boolean dominates(int a, int b) { public boolean dominates(int a, int b) {
a = nodes[a + 1]; a = indexes[a + 1];
b = nodes[b + 1]; b = indexes[b + 1];
return lcaTree.lcaOf(a, b) == a; return lcaTree.lcaOf(a, b) == a;
} }
@ -65,7 +65,13 @@ class DefaultDominatorTree implements DominatorTree {
if (a == 0) { if (a == 0) {
return -1; return -1;
} }
int result = lcaTree.parentOf(nodes[a + 1]); int result = lcaTree.parentOf(indexes[a + 1]);
return result >= 0 ? unodes[result] : -1; return result >= 0 ? nodes[result] : -1;
}
@Override
public int levelOf(int a) {
int index = indexes[a];
return lcaTree.depthOf(index);
} }
} }

View File

@ -27,4 +27,6 @@ public interface DominatorTree {
boolean dominates(int a, int b); boolean dominates(int a, int b);
int immediateDominatorOf(int a); int immediateDominatorOf(int a);
int levelOf(int a);
} }

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.common; package org.teavm.common;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -26,14 +28,14 @@ import java.util.List;
*/ */
public class GraphBuilder { public class GraphBuilder {
private GraphImpl builtGraph; private GraphImpl builtGraph;
private List<IntegerArray> addedEdges = new ArrayList<>(); private List<IntSet> addedEdges = new ArrayList<>();
private int sz; private int sz;
public GraphBuilder() { public GraphBuilder() {
} }
public GraphBuilder(int sz) { public GraphBuilder(int sz) {
addedEdges.addAll(Collections.<IntegerArray>nCopies(sz, null)); addedEdges.addAll(Collections.<IntSet>nCopies(sz, null));
this.sz = sz; this.sz = sz;
} }
@ -49,14 +51,14 @@ public class GraphBuilder {
sz = Math.max(sz, Math.max(from, to) + 1); sz = Math.max(sz, Math.max(from, to) + 1);
builtGraph = null; builtGraph = null;
if (addedEdges.size() == from) { if (addedEdges.size() == from) {
addedEdges.add(IntegerArray.of(to)); addedEdges.add(IntOpenHashSet.from(to));
} else if (addedEdges.size() <= from) { } else if (addedEdges.size() <= from) {
addedEdges.addAll(Collections.<IntegerArray>nCopies(from - addedEdges.size(), null)); addedEdges.addAll(Collections.<IntSet>nCopies(from - addedEdges.size(), null));
addedEdges.add(IntegerArray.of(to)); addedEdges.add(IntOpenHashSet.from(to));
} else { } else {
IntegerArray set = addedEdges.get(from); IntSet set = addedEdges.get(from);
if (set == null) { if (set == null) {
addedEdges.set(from, IntegerArray.of(to)); addedEdges.set(from, IntOpenHashSet.from(to));
} else { } else {
set.add(to); set.add(to);
} }
@ -65,14 +67,15 @@ public class GraphBuilder {
public Graph build() { public Graph build() {
if (builtGraph == null) { if (builtGraph == null) {
IntegerArray[] incomingEdges = new IntegerArray[sz]; IntSet[] incomingEdges = new IntSet[sz];
for (int i = 0; i < sz; ++i) { for (int i = 0; i < sz; ++i) {
incomingEdges[i] = new IntegerArray(1); incomingEdges[i] = new IntOpenHashSet();
} }
int[][] outgoingEdgeList = new int[sz][]; int[][] outgoingEdgeList = new int[sz][];
for (int i = 0; i < addedEdges.size(); ++i) { for (int i = 0; i < addedEdges.size(); ++i) {
IntegerArray edgeList = addedEdges.get(i); IntSet edgeList = addedEdges.get(i);
outgoingEdgeList[i] = edgeList != null ? edgeList.getAll() : new int[0]; outgoingEdgeList[i] = edgeList != null ? edgeList.toArray() : new int[0];
Arrays.sort(outgoingEdgeList[i]);
for (int j : outgoingEdgeList[i]) { for (int j : outgoingEdgeList[i]) {
incomingEdges[j].add(i); incomingEdges[j].add(i);
} }
@ -82,7 +85,8 @@ public class GraphBuilder {
} }
int[][] incomingEdgeList = new int[sz][]; int[][] incomingEdgeList = new int[sz][];
for (int i = 0; i < sz; ++i) { for (int i = 0; i < sz; ++i) {
incomingEdgeList[i] = incomingEdges[i].getAll(); incomingEdgeList[i] = incomingEdges[i].toArray();
Arrays.sort(incomingEdgeList);
} }
builtGraph = new GraphImpl(incomingEdgeList, outgoingEdgeList); builtGraph = new GraphImpl(incomingEdgeList, outgoingEdgeList);
} }

View File

@ -61,6 +61,10 @@ public class LCATree {
return path.length > 0 ? path[0] : -1; return path.length > 0 ? path[0] : -1;
} }
public int depthOf(int node) {
return depths[node];
}
public int lcaOf(int a, int b) { public int lcaOf(int a, int b) {
if (a == b) { if (a == b) {
return a; return a;

View File

@ -0,0 +1,97 @@
/*
* 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 com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.cursors.IntCursor;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class MutableDirectedGraph implements Graph {
private List<IntSet> successors = new ArrayList<>();
private List<IntSet> predecessors = new ArrayList<>();
public MutableDirectedGraph() {
}
public MutableDirectedGraph(Graph graph) {
int[] data = new int[graph.size()];
for (int i = 0; i < graph.size(); ++i) {
int sz = graph.copyOutgoingEdges(i, data);
for (int j = 0; j < sz; ++j) {
addEdge(i, data[j]);
}
}
}
@Override
public int size() {
return successors.size();
}
public void addEdge(int from, int to) {
int max = Math.max(from, to);
while (max >= successors.size()) {
successors.add(new IntOpenHashSet(1));
predecessors.add(new IntOpenHashSet(1));
}
successors.get(from).add(to);
predecessors.get(to).add(from);
}
@Override
public int[] incomingEdges(int node) {
return predecessors.get(node).toArray();
}
@Override
public int copyIncomingEdges(int node, int[] target) {
int index = 0;
for (IntCursor cursor : predecessors.get(node)) {
target[index++] = cursor.value;
}
return index;
}
@Override
public int[] outgoingEdges(int node) {
return successors.get(node).toArray();
}
@Override
public int copyOutgoingEdges(int node, int[] target) {
int index = 0;
for (IntCursor cursor : successors.get(node)) {
target[index++] = cursor.value;
}
return index;
}
@Override
public int incomingEdgesCount(int node) {
return predecessors.get(node).size();
}
@Override
public int outgoingEdgesCount(int node) {
return successors.get(node).size();
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.model.util;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface GraphSplittingBackend {
int[] split(int[] nodes);
}

View File

@ -15,33 +15,137 @@
*/ */
package org.teavm.model.util; package org.teavm.model.util;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.teavm.common.DisjointSet; import org.teavm.common.*;
import org.teavm.common.Graph;
import org.teavm.common.MutableGraphNode;
/** /**
* <p>Converts irreducible graph to reducible one using node splitting algorithm described at
* the paper &ldquo;Handling irreducible loops: optimized node splitting vs. DJ-graphs&rdquo; by
* Sebastian Unger and Frank Mueller.</p>
* *
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public class IrreducibleGraphConverter { public class IrreducibleGraphConverter {
List<MutableGraphNode> nodes = new ArrayList<>(); private MutableDirectedGraph graph;
DisjointSet nodeClasses = new DisjointSet(); private DisjointSet nodeClasses = new DisjointSet();
private List<IntegerArray> classContents = new ArrayList<>();
private DJGraph djGraph;
private GraphSplittingBackend backend;
public void convertToReducible(Graph graph) { public void convertToReducible(Graph cfg, GraphSplittingBackend backend) {
buildMutableCFG(graph); this.backend = backend;
buildMutableCFG(cfg);
rebuildDJGraph();
splitLoops(0, allNodesOf(cfg));
this.backend = null;
} }
private void buildMutableCFG(Graph graph) { private boolean splitLoops(int top, IntSet nodesToHandle) {
nodes.clear(); boolean hasCrossEdge = false;
for (int i = 0; i < graph.size(); ++i) { for (int child : djGraph.getGraph().outgoingEdges(top)) {
nodes.add(new MutableGraphNode(i)); if (!djGraph.isDomEdge(top, child)) {
continue;
}
hasCrossEdge |= nodesToHandle.contains(child) && splitLoops(child, nodesToHandle);
} }
for (int i = 0; i < graph.size(); ++i) { if (hasCrossEdge) {
for (int j : graph.outgoingEdges(i)) { handleIrreducibleLoopChildren(top, nodesToHandle);
nodes.get(i).connect(nodes.get(j)); }
for (int pred : graph.incomingEdges(top)) {
if (djGraph.isSpanningBack(pred, top) && djGraph.isCrossJoin(top, pred)) {
return true;
}
}
return false;
}
private void handleIrreducibleLoopChildren(int top, IntSet nodesToHandle) {
List<int[]> sccs = findStronglyConnectedComponents(top, nodesToHandle, djGraph.levelOf(top));
for (int[] scc : sccs) {
if (scc.length > 1) {
handleStronglyConnectedComponent(top, scc);
} }
} }
} }
private void handleStronglyConnectedComponent(int top, int[] nodes) {
}
/*
* Tarjan's algorithm
*/
private List<int[]> findStronglyConnectedComponents(int start, IntSet nodesToHandle, int topLevel) {
List<int[]> components = new ArrayList<>();
boolean[] done = new boolean[djGraph.getGraph().size()];
int[] visitIndex = new int[djGraph.getGraph().size()];
Arrays.fill(visitIndex, -1);
int[] headerIndex = new int[djGraph.getGraph().size()];
int lastIndex = 0;
IntegerStack stack = new IntegerStack(nodesToHandle.size());
stack.push(-1);
stack.push(start);
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 : djGraph.getGraph().outgoingEdges(node)) {
if (!nodesToHandle.contains(successor) || djGraph.levelOf(node) < topLevel) {
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);
for (int successor : djGraph.getGraph().outgoingEdges(node)) {
if (!nodesToHandle.contains(successor) || djGraph.levelOf(node) >= topLevel) {
continue;
}
stack.push(node);
}
}
}
return components;
}
private void buildMutableCFG(Graph cfg) {
graph = new MutableDirectedGraph(cfg);
for (int i = 0; i < cfg.size(); ++i) {
nodeClasses.create();
classContents.add(IntegerArray.of(i));
}
}
private IntSet allNodesOf(Graph cfg) {
int[] allNodes = new int[cfg.size()];
for (int i = 0; i < cfg.size(); ++i) {
allNodes[i] = i;
}
return IntOpenHashSet.from(allNodes);
}
private void rebuildDJGraph() {
djGraph = new DJGraph(graph);
}
} }