diff --git a/teavm-core/pom.xml b/teavm-core/pom.xml index 330d08ecc..b120bf433 100644 --- a/teavm-core/pom.xml +++ b/teavm-core/pom.xml @@ -40,6 +40,11 @@ org.ow2.asm asm-debug-all + + com.carrotsearch + hppc + 0.6.1 + TeaVM core diff --git a/teavm-core/src/main/java/org/teavm/common/DJGraph.java b/teavm-core/src/main/java/org/teavm/common/DJGraph.java index 0ece52948..04319ad89 100644 --- a/teavm-core/src/main/java/org/teavm/common/DJGraph.java +++ b/teavm-core/src/main/java/org/teavm/common/DJGraph.java @@ -15,6 +15,10 @@ */ package org.teavm.common; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * * @author Alexey Andreev @@ -22,39 +26,57 @@ package org.teavm.common; public class DJGraph { private DominatorTree domTree; private Graph graph; - private Graph backEdges; + private LCATree spanningTree; + private int[] spanningTreeNode; + private int[] spanningTreeIndex; + private int[][] levelContent; public DJGraph(Graph src) { domTree = GraphUtils.buildDominatorTree(src); buildGraph(src); + buildLevels(); dfs(); } private void buildGraph(Graph graph) { GraphBuilder builder = new GraphBuilder(graph.size()); + + // Add join edges for (int i = 0; i < graph.size(); ++i) { for (int j : graph.outgoingEdges(i)) { builder.addEdge(i, j); } } + + // Add dom edges for (int i = 1; i < graph.size(); ++i) { int j = domTree.immediateDominatorOf(i); - boolean needsDomEdge = true; - for (int k : graph.incomingEdges(i)) { - if (k == j) { - needsDomEdge = false; - break; - } - } - if (needsDomEdge) { - builder.addEdge(j, i); - } + builder.addEdge(j, i); } + graph = builder.build(); } + private void buildLevels() { + List 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() { - 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()]; IntegerStack stack = new IntegerStack(graph.size() * 2); stack.push(0); @@ -63,16 +85,17 @@ public class DJGraph { int node = stack.pop(); int source = stack.pop(); if (visited[node]) { - builder.addEdge(node, source); continue; } + int index = spanningTree.addNode(spanningTreeIndex[source]); + spanningTreeNode[index] = node; + spanningTreeIndex[node] = index; visited[node] = true; for (int succ : graph.outgoingEdges(node)) { stack.push(node); stack.push(succ); } } - backEdges = builder.build(); } public DominatorTree getDomTree() { @@ -83,7 +106,50 @@ public class DJGraph { return graph; } - public int[] getSpanningTreeBackEdges(int node) { - return backEdges.outgoingEdges(node); + public boolean isAncestorInSpanningTree(int anc, int 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; } } diff --git a/teavm-core/src/main/java/org/teavm/common/DefaultDominatorTree.java b/teavm-core/src/main/java/org/teavm/common/DefaultDominatorTree.java index 4c51cab8b..43013b224 100644 --- a/teavm-core/src/main/java/org/teavm/common/DefaultDominatorTree.java +++ b/teavm-core/src/main/java/org/teavm/common/DefaultDominatorTree.java @@ -21,42 +21,42 @@ package org.teavm.common; */ class DefaultDominatorTree implements DominatorTree { private LCATree lcaTree; + private int[] indexes; private int[] nodes; - private int[] unodes; public DefaultDominatorTree(int[] dominators, int[] vertices) { lcaTree = new LCATree(dominators.length + 1); + indexes = new int[dominators.length + 1]; nodes = new int[dominators.length + 1]; - unodes = new int[dominators.length + 1]; - nodes[0] = -1; + indexes[0] = -1; for (int i = 0; i < dominators.length; ++i) { int v = vertices[i]; if (v < 0) { continue; } - int dom = nodes[dominators[v] + 1]; + int dom = indexes[dominators[v] + 1]; int node = lcaTree.addNode(dom); - nodes[v + 1] = node; - unodes[node] = v; + indexes[v + 1] = node; + nodes[node] = v; } } @Override public boolean directlyDominates(int a, int b) { - a = nodes[a + 1]; - b = nodes[b + 1]; + a = indexes[a + 1]; + b = indexes[b + 1]; return lcaTree.lcaOf(a, b) == a; } @Override 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 public boolean dominates(int a, int b) { - a = nodes[a + 1]; - b = nodes[b + 1]; + a = indexes[a + 1]; + b = indexes[b + 1]; return lcaTree.lcaOf(a, b) == a; } @@ -65,7 +65,13 @@ class DefaultDominatorTree implements DominatorTree { if (a == 0) { return -1; } - int result = lcaTree.parentOf(nodes[a + 1]); - return result >= 0 ? unodes[result] : -1; + int result = lcaTree.parentOf(indexes[a + 1]); + return result >= 0 ? nodes[result] : -1; + } + + @Override + public int levelOf(int a) { + int index = indexes[a]; + return lcaTree.depthOf(index); } } diff --git a/teavm-core/src/main/java/org/teavm/common/DominatorTree.java b/teavm-core/src/main/java/org/teavm/common/DominatorTree.java index 268ca50a0..fd0ea7736 100644 --- a/teavm-core/src/main/java/org/teavm/common/DominatorTree.java +++ b/teavm-core/src/main/java/org/teavm/common/DominatorTree.java @@ -27,4 +27,6 @@ public interface DominatorTree { boolean dominates(int a, int b); int immediateDominatorOf(int a); + + int levelOf(int a); } diff --git a/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java b/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java index 9180c965c..fad4816e2 100644 --- a/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/common/GraphBuilder.java @@ -15,6 +15,8 @@ */ package org.teavm.common; +import com.carrotsearch.hppc.IntOpenHashSet; +import com.carrotsearch.hppc.IntSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -26,14 +28,14 @@ import java.util.List; */ public class GraphBuilder { private GraphImpl builtGraph; - private List addedEdges = new ArrayList<>(); + private List addedEdges = new ArrayList<>(); private int sz; public GraphBuilder() { } public GraphBuilder(int sz) { - addedEdges.addAll(Collections.nCopies(sz, null)); + addedEdges.addAll(Collections.nCopies(sz, null)); this.sz = sz; } @@ -49,14 +51,14 @@ public class GraphBuilder { sz = Math.max(sz, Math.max(from, to) + 1); builtGraph = null; if (addedEdges.size() == from) { - addedEdges.add(IntegerArray.of(to)); + addedEdges.add(IntOpenHashSet.from(to)); } else if (addedEdges.size() <= from) { - addedEdges.addAll(Collections.nCopies(from - addedEdges.size(), null)); - addedEdges.add(IntegerArray.of(to)); + addedEdges.addAll(Collections.nCopies(from - addedEdges.size(), null)); + addedEdges.add(IntOpenHashSet.from(to)); } else { - IntegerArray set = addedEdges.get(from); + IntSet set = addedEdges.get(from); if (set == null) { - addedEdges.set(from, IntegerArray.of(to)); + addedEdges.set(from, IntOpenHashSet.from(to)); } else { set.add(to); } @@ -65,14 +67,15 @@ public class GraphBuilder { public Graph build() { if (builtGraph == null) { - IntegerArray[] incomingEdges = new IntegerArray[sz]; + IntSet[] incomingEdges = new IntSet[sz]; for (int i = 0; i < sz; ++i) { - incomingEdges[i] = new IntegerArray(1); + incomingEdges[i] = new IntOpenHashSet(); } int[][] outgoingEdgeList = new int[sz][]; for (int i = 0; i < addedEdges.size(); ++i) { - IntegerArray edgeList = addedEdges.get(i); - outgoingEdgeList[i] = edgeList != null ? edgeList.getAll() : new int[0]; + IntSet edgeList = addedEdges.get(i); + outgoingEdgeList[i] = edgeList != null ? edgeList.toArray() : new int[0]; + Arrays.sort(outgoingEdgeList[i]); for (int j : outgoingEdgeList[i]) { incomingEdges[j].add(i); } @@ -82,7 +85,8 @@ public class GraphBuilder { } int[][] incomingEdgeList = new int[sz][]; 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); } diff --git a/teavm-core/src/main/java/org/teavm/common/LCATree.java b/teavm-core/src/main/java/org/teavm/common/LCATree.java index 9938d299b..8134e5868 100644 --- a/teavm-core/src/main/java/org/teavm/common/LCATree.java +++ b/teavm-core/src/main/java/org/teavm/common/LCATree.java @@ -61,6 +61,10 @@ public class LCATree { return path.length > 0 ? path[0] : -1; } + public int depthOf(int node) { + return depths[node]; + } + public int lcaOf(int a, int b) { if (a == b) { return a; diff --git a/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java b/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java new file mode 100644 index 000000000..d8981af54 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/common/MutableDirectedGraph.java @@ -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 + */ +public class MutableDirectedGraph implements Graph { + private List successors = new ArrayList<>(); + private List 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(); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/util/GraphSplittingBackend.java b/teavm-core/src/main/java/org/teavm/model/util/GraphSplittingBackend.java new file mode 100644 index 000000000..2616d4bfd --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/util/GraphSplittingBackend.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.model.util; + +/** + * + * @author Alexey Andreev + */ +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); + } }