mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Developing algorithm for node splitting that would maintain DJ-graph
incrementally
This commit is contained in:
parent
7a573efde1
commit
5d1fb3f67f
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
*
|
|
@ -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 “Handling irreducible loops: optimized node splitting vs. DJ-graphs” 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 “Handling irreducible loops: optimized node splitting vs. DJ-graphs” 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user