diff --git a/teavm-core/src/main/java/org/teavm/common/DominatorTreeBuilder.java b/teavm-core/src/main/java/org/teavm/common/DominatorTreeBuilder.java index 06626673a..77ad2cde8 100644 --- a/teavm-core/src/main/java/org/teavm/common/DominatorTreeBuilder.java +++ b/teavm-core/src/main/java/org/teavm/common/DominatorTreeBuilder.java @@ -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()) { diff --git a/teavm-core/src/main/java/org/teavm/common/GraphNodeFilter.java b/teavm-core/src/main/java/org/teavm/common/GraphNodeFilter.java new file mode 100644 index 000000000..a7a9cb1f3 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/common/GraphNodeFilter.java @@ -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); +} 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 a320cff6d..eea85e356 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphUtils.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphUtils.java @@ -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 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); } diff --git a/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java b/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java index d8981af54..8036e16ae 100644 --- a/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java +++ b/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java @@ -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(); diff --git a/teavm-core/src/main/java/org/teavm/common/DJGraph.java b/teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java similarity index 52% rename from teavm-core/src/main/java/org/teavm/common/DJGraph.java rename to teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java index 04319ad89..bf8486266 100644 --- a/teavm-core/src/main/java/org/teavm/common/DJGraph.java +++ b/teavm-core/src/main/java/org/teavm/common/irreducible/DJGraph.java @@ -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); + } + } + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/GraphSplittingBackend.java b/teavm-core/src/main/java/org/teavm/common/irreducible/GraphSplittingBackend.java similarity index 95% rename from teavm-core/src/main/java/org/teavm/model/util/GraphSplittingBackend.java rename to teavm-core/src/main/java/org/teavm/common/irreducible/GraphSplittingBackend.java index 2616d4bfd..fbf791ceb 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/GraphSplittingBackend.java +++ b/teavm-core/src/main/java/org/teavm/common/irreducible/GraphSplittingBackend.java @@ -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; /** * 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 new file mode 100644 index 000000000..a84de998b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/common/irreducible/IrreducibleGraphConverter.java @@ -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; + +/** + *

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 { + 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; + } + } +} 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 deleted file mode 100644 index e4d8a8e45..000000000 --- a/teavm-core/src/main/java/org/teavm/model/util/IrreducibleGraphConverter.java +++ /dev/null @@ -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.*; - -/** - *

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 { - private MutableDirectedGraph graph; - private DisjointSet nodeClasses = new DisjointSet(); - private List 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 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); - } -}