Developing algorithm for node splitting that would maintain DJ-graph

incrementally
This commit is contained in:
konsoletyper 2015-02-23 22:39:13 +03:00
parent 7a573efde1
commit 5d1fb3f67f
8 changed files with 273 additions and 179 deletions

View File

@ -45,12 +45,12 @@ class DominatorTreeBuilder {
bucket = new IntegerArray[graph.size()];
}
public void build() {
public void build(int[] start) {
for (int i = 0; i < labels.length; ++i) {
labels[i] = i;
}
Arrays.fill(ancestors, -1);
dfs();
dfs(start);
for (int i = effectiveSize - 1; i >= 0; --i) {
int w = vertices[i];
if (parents[w] < 0) {
@ -120,15 +120,13 @@ class DominatorTreeBuilder {
return labels[v];
}
private void dfs() {
private void dfs(int[] start) {
Arrays.fill(semidominators, -1);
Arrays.fill(vertices, -1);
IntegerStack stack = new IntegerStack(graph.size());
for (int i = graph.size() - 1; i >= 0; --i) {
if (graph.incomingEdgesCount(i) == 0) {
stack.push(i);
parents[i] = -1;
}
for (int node : start) {
stack.push(node);
parents[node] = -1;
}
int i = 0;
while (!stack.isEmpty()) {

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.common;
/**
*
* @author Alexey Andreev
*/
public interface GraphNodeFilter {
boolean match(int node);
}

View File

@ -15,7 +15,9 @@
*/
package org.teavm.common;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -104,9 +106,67 @@ public final class GraphUtils {
return cnt;
}
/*
* Tarjan's algorithm
*/
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) {
List<int[]> 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());
for (int startNode : start) {
stack.push(startNode);
}
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);
for (int successor : graph.outgoingEdges(node)) {
if (!filter.match(successor)) {
continue;
}
stack.push(node);
}
}
}
return components.toArray(new int[0][]);
}
public static DominatorTree buildDominatorTree(Graph graph) {
return buildDominatorTree(graph, 0);
}
public static DominatorTree buildDominatorTree(Graph graph, int... start) {
DominatorTreeBuilder builder = new DominatorTreeBuilder(graph);
builder.build();
builder.build(start);
return new DefaultDominatorTree(builder.dominators, builder.vertices);
}

View File

@ -57,6 +57,17 @@ public class MutableDirectedGraph implements Graph {
predecessors.get(to).add(from);
}
public void detachNode(int node) {
for (IntCursor succ : successors.get(node)) {
predecessors.get(succ.value).removeAllOccurrences(node);
}
for (IntCursor pred : predecessors.get(node)) {
successors.get(pred.value).removeAllOccurrences(node);
}
predecessors.get(node).clear();
successors.get(node).clear();
}
@Override
public int[] incomingEdges(int node) {
return predecessors.get(node).toArray();

View File

@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.common;
package org.teavm.common.irreducible;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.cursors.IntCursor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.common.*;
/**
*
@ -25,36 +29,44 @@ import java.util.List;
*/
public class DJGraph {
private DominatorTree domTree;
private Graph graph;
private MutableDirectedGraph cfg;
private MutableDirectedGraph graph;
private LCATree spanningTree;
private int[] spanningTreeNode;
private int[] spanningTreeIndex;
private int[][] levelContent;
private int[] mergeRoot;
private IntegerArray[] mergeClasses;
public DJGraph(Graph src) {
domTree = GraphUtils.buildDominatorTree(src);
public DJGraph(Graph src, int top) {
this.cfg = new MutableDirectedGraph(src);
domTree = GraphUtils.buildDominatorTree(src, top);
buildGraph(src);
buildLevels();
dfs();
mergeRoot = new int[src.size()];
mergeClasses = new IntegerArray[src.size()];
for (int i = 0; i < mergeRoot.length; ++i) {
mergeRoot[i] = i;
mergeClasses[i] = IntegerArray.of(i);
}
}
private void buildGraph(Graph graph) {
GraphBuilder builder = new GraphBuilder(graph.size());
private void buildGraph(Graph src) {
graph = new MutableDirectedGraph();
// Add join edges
for (int i = 0; i < graph.size(); ++i) {
for (int j : graph.outgoingEdges(i)) {
builder.addEdge(i, j);
for (int i = 0; i < src.size(); ++i) {
for (int j : src.outgoingEdges(i)) {
graph.addEdge(i, j);
}
}
// Add dom edges
for (int i = 1; i < graph.size(); ++i) {
int j = domTree.immediateDominatorOf(i);
builder.addEdge(j, i);
graph.addEdge(j, i);
}
graph = builder.build();
}
private void buildLevels() {
@ -102,13 +114,17 @@ public class DJGraph {
return domTree;
}
public MutableDirectedGraph getCfg() {
return cfg;
}
public Graph getGraph() {
return graph;
}
public boolean isAncestorInSpanningTree(int anc, int node) {
anc = spanningTreeIndex[anc];
node = spanningTreeIndex[node];
anc = spanningTreeIndex[mergeRoot[anc]];
node = spanningTreeIndex[mergeRoot[node]];
if (anc < 0 || node < 0) {
return false;
}
@ -116,7 +132,7 @@ public class DJGraph {
}
public boolean isDomEdge(int i, int j) {
return domTree.immediateDominatorOf(j) == i;
return domTree.immediateDominatorOf(mergeRoot[j]) == mergeRoot[i];
}
public boolean isJoinEdge(int i, int j) {
@ -124,24 +140,28 @@ public class DJGraph {
}
public boolean isBackJoin(int i, int j) {
return isJoinEdge(i, j) && !domTree.dominates(j, i);
return isJoinEdge(i, j) && !domTree.dominates(mergeRoot[j], mergeRoot[i]);
}
public boolean isCrossJoin(int i, int j) {
return isJoinEdge(i, j) && domTree.dominates(j, i);
return isJoinEdge(i, j) && domTree.dominates(mergeRoot[j], mergeRoot[i]);
}
public boolean isSpanningBack(int i, int j) {
i = spanningTreeIndex[mergeRoot[i]];
j = spanningTreeIndex[mergeRoot[j]];
return spanningTree.lcaOf(i, j) == j;
}
public boolean isSpanningCross(int i, int j) {
i = spanningTreeIndex[mergeRoot[i]];
j = spanningTreeIndex[mergeRoot[j]];
int c = spanningTree.lcaOf(i, j);
return c != i && c != j;
}
public int levelOf(int node) {
return domTree.levelOf(node);
return domTree.levelOf(mergeRoot[node]);
}
public int[] level(int level) {
@ -152,4 +172,57 @@ public class DJGraph {
public int levelCount() {
return levelContent.length;
}
public int[] classRepresentatives(int node) {
return mergeClasses[node].getAll();
}
public int classOf(int node) {
return mergeRoot[node];
}
public void collapse(int[] nodes) {
// Replace nodes with their classes and find common dominator among them
IntSet set = new IntOpenHashSet();
int top = nodes[0];
for (int node : nodes) {
node = mergeRoot[node];
top = domTree.commonDominatorOf(top, node);
set.add(node);
}
if (!set.contains(top)) {
throw new IllegalArgumentException("All nodes must have one common dominator");
}
// Alter classes
IntegerArray cls = mergeClasses[top];
for (IntCursor node : set) {
mergeRoot[node.value] = top;
if (node.value != top) {
cls.addAll(mergeClasses[node.value].getAll());
mergeClasses[node.value].clear();
}
}
// Alter graphs
for (IntCursor node : set) {
if (node.value != top) {
for (int succ : graph.outgoingEdges(node.value)) {
graph.addEdge(top, succ);
}
for (int pred : graph.incomingEdges(node.value)) {
graph.addEdge(top, pred);
}
graph.detachNode(node.value);
for (int succ : cfg.outgoingEdges(node.value)) {
cfg.addEdge(top, succ);
}
for (int pred : cfg.incomingEdges(node.value)) {
cfg.addEdge(top, pred);
}
cfg.detachNode(node.value);
}
}
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.util;
package org.teavm.common.irreducible;
/**
*

View File

@ -0,0 +1,79 @@
/*
* 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.irreducible;
import com.carrotsearch.hppc.IntSet;
import org.teavm.common.Graph;
import org.teavm.common.GraphNodeFilter;
import org.teavm.common.GraphUtils;
/**
* <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
*/
public class IrreducibleGraphConverter {
private GraphSplittingBackend backend;
public void convertToReducible(Graph cfg, GraphSplittingBackend backend) {
this.backend = backend;
handleLoops(new DJGraph(cfg, 0));
this.backend = null;
}
private void handleLoops(DJGraph djGraph) {
for (int level = djGraph.levelCount() - 1; level >= 0; --level) {
boolean irreducible = false;
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));
}
}
}
DJGraphNodeFilter filter = new DJGraphNodeFilter(djGraph, level, null);
int[][] sccs = GraphUtils.findStronglyConnectedComponents(djGraph.getGraph(), djGraph.level(level), filter);
}
}
private int[] reachUnder(DJGraph djGraph, int top) {
// TODO: implement
return null;
}
static class DJGraphNodeFilter implements GraphNodeFilter {
private DJGraph graph;
private int level;
private IntSet nodes;
public DJGraphNodeFilter(DJGraph graph, int level, IntSet nodes) {
this.graph = graph;
this.level = level;
this.nodes = nodes;
}
@Override
public boolean match(int node) {
return nodes.contains(node) && graph.levelOf(node) >= level;
}
}
}

View File

@ -1,151 +0,0 @@
/*
* 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;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.common.*;
/**
* <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>
*/
public class IrreducibleGraphConverter {
private MutableDirectedGraph graph;
private DisjointSet nodeClasses = new DisjointSet();
private List<IntegerArray> classContents = new ArrayList<>();
private DJGraph djGraph;
private GraphSplittingBackend backend;
public void convertToReducible(Graph cfg, GraphSplittingBackend backend) {
this.backend = backend;
buildMutableCFG(cfg);
rebuildDJGraph();
splitLoops(0, allNodesOf(cfg));
this.backend = null;
}
private boolean splitLoops(int top, IntSet nodesToHandle) {
boolean hasCrossEdge = false;
for (int child : djGraph.getGraph().outgoingEdges(top)) {
if (!djGraph.isDomEdge(top, child)) {
continue;
}
hasCrossEdge |= nodesToHandle.contains(child) && splitLoops(child, nodesToHandle);
}
if (hasCrossEdge) {
handleIrreducibleLoopChildren(top, nodesToHandle);
}
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);
}
}