mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Improve DJ-graph implementation. Add Tarjan's SCC finding algorithm. Add
HPCC dependency.
This commit is contained in:
parent
11437af5ae
commit
14ce9e23a4
|
@ -40,6 +40,11 @@
|
||||||
<groupId>org.ow2.asm</groupId>
|
<groupId>org.ow2.asm</groupId>
|
||||||
<artifactId>asm-debug-all</artifactId>
|
<artifactId>asm-debug-all</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.carrotsearch</groupId>
|
||||||
|
<artifactId>hppc</artifactId>
|
||||||
|
<version>0.6.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<name>TeaVM core</name>
|
<name>TeaVM core</name>
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.common;
|
package org.teavm.common;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
|
@ -22,39 +26,57 @@ package org.teavm.common;
|
||||||
public class DJGraph {
|
public class DJGraph {
|
||||||
private DominatorTree domTree;
|
private DominatorTree domTree;
|
||||||
private Graph graph;
|
private Graph graph;
|
||||||
private Graph backEdges;
|
private LCATree spanningTree;
|
||||||
|
private int[] spanningTreeNode;
|
||||||
|
private int[] spanningTreeIndex;
|
||||||
|
private int[][] levelContent;
|
||||||
|
|
||||||
public DJGraph(Graph src) {
|
public DJGraph(Graph src) {
|
||||||
domTree = GraphUtils.buildDominatorTree(src);
|
domTree = GraphUtils.buildDominatorTree(src);
|
||||||
buildGraph(src);
|
buildGraph(src);
|
||||||
|
buildLevels();
|
||||||
dfs();
|
dfs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildGraph(Graph graph) {
|
private void buildGraph(Graph graph) {
|
||||||
GraphBuilder builder = new GraphBuilder(graph.size());
|
GraphBuilder builder = new GraphBuilder(graph.size());
|
||||||
|
|
||||||
|
// Add join edges
|
||||||
for (int i = 0; i < graph.size(); ++i) {
|
for (int i = 0; i < graph.size(); ++i) {
|
||||||
for (int j : graph.outgoingEdges(i)) {
|
for (int j : graph.outgoingEdges(i)) {
|
||||||
builder.addEdge(i, j);
|
builder.addEdge(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add dom edges
|
||||||
for (int i = 1; i < graph.size(); ++i) {
|
for (int i = 1; i < graph.size(); ++i) {
|
||||||
int j = domTree.immediateDominatorOf(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();
|
graph = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void buildLevels() {
|
||||||
|
List<IntegerArray> 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() {
|
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()];
|
boolean[] visited = new boolean[graph.size()];
|
||||||
IntegerStack stack = new IntegerStack(graph.size() * 2);
|
IntegerStack stack = new IntegerStack(graph.size() * 2);
|
||||||
stack.push(0);
|
stack.push(0);
|
||||||
|
@ -63,16 +85,17 @@ public class DJGraph {
|
||||||
int node = stack.pop();
|
int node = stack.pop();
|
||||||
int source = stack.pop();
|
int source = stack.pop();
|
||||||
if (visited[node]) {
|
if (visited[node]) {
|
||||||
builder.addEdge(node, source);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
int index = spanningTree.addNode(spanningTreeIndex[source]);
|
||||||
|
spanningTreeNode[index] = node;
|
||||||
|
spanningTreeIndex[node] = index;
|
||||||
visited[node] = true;
|
visited[node] = true;
|
||||||
for (int succ : graph.outgoingEdges(node)) {
|
for (int succ : graph.outgoingEdges(node)) {
|
||||||
stack.push(node);
|
stack.push(node);
|
||||||
stack.push(succ);
|
stack.push(succ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
backEdges = builder.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DominatorTree getDomTree() {
|
public DominatorTree getDomTree() {
|
||||||
|
@ -83,7 +106,50 @@ public class DJGraph {
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getSpanningTreeBackEdges(int node) {
|
public boolean isAncestorInSpanningTree(int anc, int node) {
|
||||||
return backEdges.outgoingEdges(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,42 +21,42 @@ package org.teavm.common;
|
||||||
*/
|
*/
|
||||||
class DefaultDominatorTree implements DominatorTree {
|
class DefaultDominatorTree implements DominatorTree {
|
||||||
private LCATree lcaTree;
|
private LCATree lcaTree;
|
||||||
|
private int[] indexes;
|
||||||
private int[] nodes;
|
private int[] nodes;
|
||||||
private int[] unodes;
|
|
||||||
|
|
||||||
public DefaultDominatorTree(int[] dominators, int[] vertices) {
|
public DefaultDominatorTree(int[] dominators, int[] vertices) {
|
||||||
lcaTree = new LCATree(dominators.length + 1);
|
lcaTree = new LCATree(dominators.length + 1);
|
||||||
|
indexes = new int[dominators.length + 1];
|
||||||
nodes = new int[dominators.length + 1];
|
nodes = new int[dominators.length + 1];
|
||||||
unodes = new int[dominators.length + 1];
|
indexes[0] = -1;
|
||||||
nodes[0] = -1;
|
|
||||||
for (int i = 0; i < dominators.length; ++i) {
|
for (int i = 0; i < dominators.length; ++i) {
|
||||||
int v = vertices[i];
|
int v = vertices[i];
|
||||||
if (v < 0) {
|
if (v < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int dom = nodes[dominators[v] + 1];
|
int dom = indexes[dominators[v] + 1];
|
||||||
int node = lcaTree.addNode(dom);
|
int node = lcaTree.addNode(dom);
|
||||||
nodes[v + 1] = node;
|
indexes[v + 1] = node;
|
||||||
unodes[node] = v;
|
nodes[node] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean directlyDominates(int a, int b) {
|
public boolean directlyDominates(int a, int b) {
|
||||||
a = nodes[a + 1];
|
a = indexes[a + 1];
|
||||||
b = nodes[b + 1];
|
b = indexes[b + 1];
|
||||||
return lcaTree.lcaOf(a, b) == a;
|
return lcaTree.lcaOf(a, b) == a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int commonDominatorOf(int a, int b) {
|
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
|
@Override
|
||||||
public boolean dominates(int a, int b) {
|
public boolean dominates(int a, int b) {
|
||||||
a = nodes[a + 1];
|
a = indexes[a + 1];
|
||||||
b = nodes[b + 1];
|
b = indexes[b + 1];
|
||||||
return lcaTree.lcaOf(a, b) == a;
|
return lcaTree.lcaOf(a, b) == a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,13 @@ class DefaultDominatorTree implements DominatorTree {
|
||||||
if (a == 0) {
|
if (a == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int result = lcaTree.parentOf(nodes[a + 1]);
|
int result = lcaTree.parentOf(indexes[a + 1]);
|
||||||
return result >= 0 ? unodes[result] : -1;
|
return result >= 0 ? nodes[result] : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int levelOf(int a) {
|
||||||
|
int index = indexes[a];
|
||||||
|
return lcaTree.depthOf(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,6 @@ public interface DominatorTree {
|
||||||
boolean dominates(int a, int b);
|
boolean dominates(int a, int b);
|
||||||
|
|
||||||
int immediateDominatorOf(int a);
|
int immediateDominatorOf(int a);
|
||||||
|
|
||||||
|
int levelOf(int a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.common;
|
package org.teavm.common;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntOpenHashSet;
|
||||||
|
import com.carrotsearch.hppc.IntSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -26,14 +28,14 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class GraphBuilder {
|
public class GraphBuilder {
|
||||||
private GraphImpl builtGraph;
|
private GraphImpl builtGraph;
|
||||||
private List<IntegerArray> addedEdges = new ArrayList<>();
|
private List<IntSet> addedEdges = new ArrayList<>();
|
||||||
private int sz;
|
private int sz;
|
||||||
|
|
||||||
public GraphBuilder() {
|
public GraphBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GraphBuilder(int sz) {
|
public GraphBuilder(int sz) {
|
||||||
addedEdges.addAll(Collections.<IntegerArray>nCopies(sz, null));
|
addedEdges.addAll(Collections.<IntSet>nCopies(sz, null));
|
||||||
this.sz = sz;
|
this.sz = sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,14 +51,14 @@ public class GraphBuilder {
|
||||||
sz = Math.max(sz, Math.max(from, to) + 1);
|
sz = Math.max(sz, Math.max(from, to) + 1);
|
||||||
builtGraph = null;
|
builtGraph = null;
|
||||||
if (addedEdges.size() == from) {
|
if (addedEdges.size() == from) {
|
||||||
addedEdges.add(IntegerArray.of(to));
|
addedEdges.add(IntOpenHashSet.from(to));
|
||||||
} else if (addedEdges.size() <= from) {
|
} else if (addedEdges.size() <= from) {
|
||||||
addedEdges.addAll(Collections.<IntegerArray>nCopies(from - addedEdges.size(), null));
|
addedEdges.addAll(Collections.<IntSet>nCopies(from - addedEdges.size(), null));
|
||||||
addedEdges.add(IntegerArray.of(to));
|
addedEdges.add(IntOpenHashSet.from(to));
|
||||||
} else {
|
} else {
|
||||||
IntegerArray set = addedEdges.get(from);
|
IntSet set = addedEdges.get(from);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
addedEdges.set(from, IntegerArray.of(to));
|
addedEdges.set(from, IntOpenHashSet.from(to));
|
||||||
} else {
|
} else {
|
||||||
set.add(to);
|
set.add(to);
|
||||||
}
|
}
|
||||||
|
@ -65,14 +67,15 @@ public class GraphBuilder {
|
||||||
|
|
||||||
public Graph build() {
|
public Graph build() {
|
||||||
if (builtGraph == null) {
|
if (builtGraph == null) {
|
||||||
IntegerArray[] incomingEdges = new IntegerArray[sz];
|
IntSet[] incomingEdges = new IntSet[sz];
|
||||||
for (int i = 0; i < sz; ++i) {
|
for (int i = 0; i < sz; ++i) {
|
||||||
incomingEdges[i] = new IntegerArray(1);
|
incomingEdges[i] = new IntOpenHashSet();
|
||||||
}
|
}
|
||||||
int[][] outgoingEdgeList = new int[sz][];
|
int[][] outgoingEdgeList = new int[sz][];
|
||||||
for (int i = 0; i < addedEdges.size(); ++i) {
|
for (int i = 0; i < addedEdges.size(); ++i) {
|
||||||
IntegerArray edgeList = addedEdges.get(i);
|
IntSet edgeList = addedEdges.get(i);
|
||||||
outgoingEdgeList[i] = edgeList != null ? edgeList.getAll() : new int[0];
|
outgoingEdgeList[i] = edgeList != null ? edgeList.toArray() : new int[0];
|
||||||
|
Arrays.sort(outgoingEdgeList[i]);
|
||||||
for (int j : outgoingEdgeList[i]) {
|
for (int j : outgoingEdgeList[i]) {
|
||||||
incomingEdges[j].add(i);
|
incomingEdges[j].add(i);
|
||||||
}
|
}
|
||||||
|
@ -82,7 +85,8 @@ public class GraphBuilder {
|
||||||
}
|
}
|
||||||
int[][] incomingEdgeList = new int[sz][];
|
int[][] incomingEdgeList = new int[sz][];
|
||||||
for (int i = 0; i < sz; ++i) {
|
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);
|
builtGraph = new GraphImpl(incomingEdgeList, outgoingEdgeList);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,10 @@ public class LCATree {
|
||||||
return path.length > 0 ? path[0] : -1;
|
return path.length > 0 ? path[0] : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int depthOf(int node) {
|
||||||
|
return depths[node];
|
||||||
|
}
|
||||||
|
|
||||||
public int lcaOf(int a, int b) {
|
public int lcaOf(int a, int b) {
|
||||||
if (a == b) {
|
if (a == b) {
|
||||||
return a;
|
return a;
|
||||||
|
|
|
@ -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 <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public class MutableDirectedGraph implements Graph {
|
||||||
|
private List<IntSet> successors = new ArrayList<>();
|
||||||
|
private List<IntSet> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <konsoletyper@gmail.com>
|
||||||
|
*/
|
||||||
|
public interface GraphSplittingBackend {
|
||||||
|
int[] split(int[] nodes);
|
||||||
|
}
|
|
@ -15,33 +15,137 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model.util;
|
package org.teavm.model.util;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntOpenHashSet;
|
||||||
|
import com.carrotsearch.hppc.IntSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.common.DisjointSet;
|
import org.teavm.common.*;
|
||||||
import org.teavm.common.Graph;
|
|
||||||
import org.teavm.common.MutableGraphNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <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>
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class IrreducibleGraphConverter {
|
public class IrreducibleGraphConverter {
|
||||||
List<MutableGraphNode> nodes = new ArrayList<>();
|
private MutableDirectedGraph graph;
|
||||||
DisjointSet nodeClasses = new DisjointSet();
|
private DisjointSet nodeClasses = new DisjointSet();
|
||||||
|
private List<IntegerArray> classContents = new ArrayList<>();
|
||||||
|
private DJGraph djGraph;
|
||||||
|
private GraphSplittingBackend backend;
|
||||||
|
|
||||||
public void convertToReducible(Graph graph) {
|
public void convertToReducible(Graph cfg, GraphSplittingBackend backend) {
|
||||||
buildMutableCFG(graph);
|
this.backend = backend;
|
||||||
|
buildMutableCFG(cfg);
|
||||||
|
rebuildDJGraph();
|
||||||
|
splitLoops(0, allNodesOf(cfg));
|
||||||
|
this.backend = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildMutableCFG(Graph graph) {
|
private boolean splitLoops(int top, IntSet nodesToHandle) {
|
||||||
nodes.clear();
|
boolean hasCrossEdge = false;
|
||||||
for (int i = 0; i < graph.size(); ++i) {
|
for (int child : djGraph.getGraph().outgoingEdges(top)) {
|
||||||
nodes.add(new MutableGraphNode(i));
|
if (!djGraph.isDomEdge(top, child)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < graph.size(); ++i) {
|
hasCrossEdge |= nodesToHandle.contains(child) && splitLoops(child, nodesToHandle);
|
||||||
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<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