+ */
+public interface GraphSplittingBackend {
+ int[] split(int[] nodes);
+}
diff --git a/teavm-core/src/main/java/org/teavm/model/util/IrreducibleGraphConverter.java b/teavm-core/src/main/java/org/teavm/model/util/IrreducibleGraphConverter.java
index 8f9190aa9..e4d8a8e45 100644
--- a/teavm-core/src/main/java/org/teavm/model/util/IrreducibleGraphConverter.java
+++ b/teavm-core/src/main/java/org/teavm/model/util/IrreducibleGraphConverter.java
@@ -15,33 +15,137 @@
*/
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.DisjointSet;
-import org.teavm.common.Graph;
-import org.teavm.common.MutableGraphNode;
+import org.teavm.common.*;
/**
+ * 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.
*
* @author Alexey Andreev
*/
public class IrreducibleGraphConverter {
- List nodes = new ArrayList<>();
- DisjointSet nodeClasses = new DisjointSet();
+ private MutableDirectedGraph graph;
+ private DisjointSet nodeClasses = new DisjointSet();
+ private List classContents = new ArrayList<>();
+ private DJGraph djGraph;
+ private GraphSplittingBackend backend;
- public void convertToReducible(Graph graph) {
- buildMutableCFG(graph);
+ public void convertToReducible(Graph cfg, GraphSplittingBackend backend) {
+ this.backend = backend;
+ buildMutableCFG(cfg);
+ rebuildDJGraph();
+ splitLoops(0, allNodesOf(cfg));
+ this.backend = null;
}
- private void buildMutableCFG(Graph graph) {
- nodes.clear();
- for (int i = 0; i < graph.size(); ++i) {
- nodes.add(new MutableGraphNode(i));
+ 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);
}
- for (int i = 0; i < graph.size(); ++i) {
- for (int j : graph.outgoingEdges(i)) {
- nodes.get(i).connect(nodes.get(j));
+ 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 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 findStronglyConnectedComponents(int start, IntSet nodesToHandle, int topLevel) {
+ List 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);
+ }
}