mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Rewrite node splitting algorithm
This commit is contained in:
parent
b34e25414e
commit
9972fe0c29
|
@ -1,252 +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.common;
|
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
|
||||||
import com.carrotsearch.hppc.IntSet;
|
|
||||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DJGraph {
|
|
||||||
private DominatorTree domTree;
|
|
||||||
private MutableDirectedGraph cfg;
|
|
||||||
private MutableDirectedGraph graph;
|
|
||||||
private LCATree spanningTree;
|
|
||||||
private int[] spanningTreeNode;
|
|
||||||
private int[] spanningTreeIndex;
|
|
||||||
private int[][] levelContent;
|
|
||||||
private int[] mergeRoot;
|
|
||||||
private int[] weight;
|
|
||||||
private IntegerArray[] mergeClasses;
|
|
||||||
|
|
||||||
public DJGraph(Graph src, int[] weight) {
|
|
||||||
if (src.size() != weight.length) {
|
|
||||||
throw new IllegalArgumentException("Node count " + src.size() + " is not equal to weight array "
|
|
||||||
+ weight.length);
|
|
||||||
}
|
|
||||||
this.cfg = new MutableDirectedGraph(src);
|
|
||||||
domTree = GraphUtils.buildDominatorTree(src);
|
|
||||||
buildGraph(src);
|
|
||||||
buildLevels();
|
|
||||||
spanningTree = new LCATree(src.size());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
this.weight = Arrays.copyOf(weight, weight.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildGraph(Graph src) {
|
|
||||||
graph = new MutableDirectedGraph();
|
|
||||||
|
|
||||||
// Add join edges
|
|
||||||
for (int i = 0; i < src.size(); ++i) {
|
|
||||||
for (int j : src.outgoingEdges(i)) {
|
|
||||||
graph.addEdge(i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add dom edges
|
|
||||||
for (int i = 0; i < graph.size(); ++i) {
|
|
||||||
int j = domTree.immediateDominatorOf(i);
|
|
||||||
if (j >= 0) {
|
|
||||||
graph.addEdge(j, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() - 1][];
|
|
||||||
for (int i = 1; i < builder.size(); ++i) {
|
|
||||||
levelContent[i - 1] = builder.get(i).getAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dfs() {
|
|
||||||
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(-1);
|
|
||||||
stack.push(0);
|
|
||||||
while (!stack.isEmpty()) {
|
|
||||||
int node = stack.pop();
|
|
||||||
int source = stack.pop();
|
|
||||||
if (visited[node]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int index = source >= 0 ? spanningTree.addNode(spanningTreeIndex[source]) : 0;
|
|
||||||
spanningTreeNode[index] = node;
|
|
||||||
spanningTreeIndex[node] = index;
|
|
||||||
visited[node] = true;
|
|
||||||
for (int succ : graph.outgoingEdges(node)) {
|
|
||||||
stack.push(node);
|
|
||||||
stack.push(succ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MutableDirectedGraph getCfg() {
|
|
||||||
return cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Graph getGraph() {
|
|
||||||
return graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAncestorInSpanningTree(int anc, int node) {
|
|
||||||
anc = spanningTreeIndex[mergeRoot[anc]];
|
|
||||||
node = spanningTreeIndex[mergeRoot[node]];
|
|
||||||
if (anc < 0 || node < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return spanningTree.lcaOf(anc, node) == anc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDomEdge(int i, int j) {
|
|
||||||
return immediateDominatorOf(j) == mergeRoot[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int immediateDominatorOf(int node) {
|
|
||||||
int dom = domTree.immediateDominatorOf(mergeRoot[node]);
|
|
||||||
return dom >= 0 ? mergeRoot[dom] : dom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int commonDominatorOf(int a, int b) {
|
|
||||||
int dom = domTree.commonDominatorOf(mergeRoot[a], mergeRoot[b]);
|
|
||||||
return dom >= 0 ? mergeRoot[dom] : dom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isJoinEdge(int i, int j) {
|
|
||||||
return !isDomEdge(i, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBackJoin(int i, int j) {
|
|
||||||
return isJoinEdge(i, j) && domTree.dominates(mergeRoot[j], mergeRoot[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCrossJoin(int i, int j) {
|
|
||||||
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 weightOf(int node) {
|
|
||||||
return weight[node];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int weightOf(int... nodes) {
|
|
||||||
int result = 0;
|
|
||||||
for (int node : nodes) {
|
|
||||||
result += weight[node];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int levelOf(int node) {
|
|
||||||
return domTree.levelOf(mergeRoot[node]) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] level(int level) {
|
|
||||||
int[] result = levelContent[level];
|
|
||||||
return Arrays.copyOf(result, result.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int levelCount() {
|
|
||||||
return levelContent.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] classRepresentatives(int node) {
|
|
||||||
return mergeClasses[node].getAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int classOf(int node) {
|
|
||||||
return mergeRoot[node];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int collapse(int[] nodes) {
|
|
||||||
// Replace nodes with their classes and find common dominator among them
|
|
||||||
IntSet set = new IntHashSet();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
weight[top] += weight[node.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -151,24 +151,7 @@ public class GraphBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
return GraphUtils.printToDot(this);
|
||||||
sb.append("digraph {\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < size(); ++i) {
|
|
||||||
if (outgoingEdgesCount(i) > 0) {
|
|
||||||
sb.append(" ").append(i).append(" -> { ");
|
|
||||||
int[] outgoingEdges = outgoingEdges(i);
|
|
||||||
sb.append(outgoingEdges[0]);
|
|
||||||
for (int j = 1; j < outgoingEdges.length; ++j) {
|
|
||||||
sb.append(", ").append(outgoingEdges[j]);
|
|
||||||
}
|
|
||||||
sb.append(" }\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append("}");
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,11 +126,11 @@ public final class GraphUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FilteredGraph implements Graph {
|
static class FilteredGraph implements Graph {
|
||||||
final Graph innerGraph;
|
final Graph innerGraph;
|
||||||
final IntPredicate filter;
|
final IntPredicate filter;
|
||||||
|
|
||||||
public FilteredGraph(Graph innerGraph, IntPredicate filter) {
|
FilteredGraph(Graph innerGraph, IntPredicate filter) {
|
||||||
this.innerGraph = innerGraph;
|
this.innerGraph = innerGraph;
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,11 @@ public final class GraphUtils {
|
||||||
public int outgoingEdgesCount(int node) {
|
public int outgoingEdgesCount(int node) {
|
||||||
return outgoingEdges(node).length;
|
return outgoingEdges(node).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return GraphUtils.printToDot(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -349,7 +354,7 @@ public final class GraphUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void splitIrreducibleGraph(Graph graph, int[] weights, GraphSplittingBackend backend) {
|
public static void splitIrreducibleGraph(Graph graph, int[] weights, GraphSplittingBackend backend) {
|
||||||
new IrreducibleGraphConverter().convertToReducible(graph, weights, backend);
|
new IrreducibleGraphSplitter(backend, graph, weights).splitLoops();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int[][] findDominanceFrontiers(Graph cfg, DominatorTree domTree) {
|
public static int[][] findDominanceFrontiers(Graph cfg, DominatorTree domTree) {
|
||||||
|
|
|
@ -1,349 +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.common;
|
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
|
||||||
import com.carrotsearch.hppc.IntSet;
|
|
||||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.function.IntPredicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <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
|
|
||||||
*/
|
|
||||||
class IrreducibleGraphConverter {
|
|
||||||
private Graph cfg;
|
|
||||||
private int totalNodeCount;
|
|
||||||
private GraphSplittingBackend backend;
|
|
||||||
private IntSet[] nodeCopies;
|
|
||||||
private IntegerArray nodeOriginals;
|
|
||||||
|
|
||||||
void convertToReducible(Graph cfg, int[] weight, GraphSplittingBackend backend) {
|
|
||||||
this.backend = backend;
|
|
||||||
|
|
||||||
nodeCopies = new IntHashSet[cfg.size()];
|
|
||||||
nodeOriginals = new IntegerArray(cfg.size());
|
|
||||||
for (int i = 0; i < cfg.size(); ++i) {
|
|
||||||
nodeCopies[i] = new IntHashSet();
|
|
||||||
nodeOriginals.add(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
int[][] identityNodeMap = new int[cfg.size()][];
|
|
||||||
for (int i = 0; i < identityNodeMap.length; ++i) {
|
|
||||||
identityNodeMap[i] = new int[] { i };
|
|
||||||
}
|
|
||||||
this.cfg = cfg;
|
|
||||||
totalNodeCount = cfg.size();
|
|
||||||
handleLoops(new DJGraph(cfg, weight), identityNodeMap);
|
|
||||||
this.backend = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLoops(DJGraph djGraph, int[][] nodeMap) {
|
|
||||||
for (int level = djGraph.levelCount() - 1; level >= 0; --level) {
|
|
||||||
boolean irreducible = false;
|
|
||||||
levelScan:
|
|
||||||
for (int node : djGraph.level(level)) {
|
|
||||||
for (int pred : djGraph.getGraph().incomingEdges(node)) {
|
|
||||||
if (djGraph.isCrossJoin(pred, node) && djGraph.isSpanningBack(node, pred)) {
|
|
||||||
irreducible = true;
|
|
||||||
break levelScan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (irreducible) {
|
|
||||||
DJGraphNodeFilter filter = new DJGraphNodeFilter(djGraph, level);
|
|
||||||
Graph graph = GraphUtils.subgraph(djGraph.getGraph(), filter);
|
|
||||||
int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph);
|
|
||||||
for (int[] scc : sccs) {
|
|
||||||
if (scc.length > 1) {
|
|
||||||
handleStronglyConnectedComponent(djGraph, scc, nodeMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleStronglyConnectedComponent(DJGraph djGraph, int[] scc, int[][] nodeMap) {
|
|
||||||
// Find shared dominator
|
|
||||||
int sharedDom = scc[0];
|
|
||||||
for (int i = 1; i < scc.length; ++i) {
|
|
||||||
sharedDom = djGraph.commonDominatorOf(sharedDom, scc[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < scc.length; ++i) {
|
|
||||||
if (scc[i] == sharedDom) {
|
|
||||||
collapse(djGraph, scc, nodeMap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partition SCC into domains
|
|
||||||
DisjointSet partitions = new DisjointSet();
|
|
||||||
int[] sccBack = new int[djGraph.getGraph().size()];
|
|
||||||
for (int i = 0; i < scc.length; ++i) {
|
|
||||||
partitions.create();
|
|
||||||
sccBack[scc[i]] = i;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < scc.length; ++i) {
|
|
||||||
int node = scc[i];
|
|
||||||
int idom = djGraph.immediateDominatorOf(node);
|
|
||||||
if (idom != sharedDom) {
|
|
||||||
partitions.union(i, sccBack[idom]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int[] domains = partitions.pack(scc.length);
|
|
||||||
int domainCount = 0;
|
|
||||||
for (int domain : domains) {
|
|
||||||
domainCount = Math.max(domainCount, domain + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each domain calculate its weight
|
|
||||||
int[] domainWeight = new int[domainCount];
|
|
||||||
for (int i = 0; i < scc.length; ++i) {
|
|
||||||
int node = scc[i];
|
|
||||||
domainWeight[domains[i]] += djGraph.weightOf(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find domain to split around
|
|
||||||
int domain = 0;
|
|
||||||
int maxWeight = domainWeight[0];
|
|
||||||
for (int i = 1; i < domainWeight.length; ++i) {
|
|
||||||
if (domainWeight[i] > maxWeight) {
|
|
||||||
domain = i;
|
|
||||||
maxWeight = domainWeight[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find header of this domain
|
|
||||||
IntSet domainNodes = new IntHashSet(scc.length);
|
|
||||||
for (int i = 0; i < scc.length; ++i) {
|
|
||||||
int node = scc[i];
|
|
||||||
if (domains[i] == domain) {
|
|
||||||
domainNodes.add(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split
|
|
||||||
splitStronglyConnectedComponent(djGraph, domainNodes, sharedDom, scc, nodeMap);
|
|
||||||
|
|
||||||
// Collapse
|
|
||||||
int[] sccAndTop = Arrays.copyOf(scc, scc.length + 1);
|
|
||||||
sccAndTop[scc.length] = sharedDom;
|
|
||||||
collapse(djGraph, sccAndTop, nodeMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void splitStronglyConnectedComponent(DJGraph djGraph, IntSet domain, int sharedDom,
|
|
||||||
int[] scc, int[][] nodeMap) {
|
|
||||||
Arrays.sort(scc);
|
|
||||||
// Find SCC \ domain
|
|
||||||
int[][] mappedNonDomain = new int[scc.length - domain.size()][];
|
|
||||||
int[] domainNodes = new int[domain.size()];
|
|
||||||
int[] nonDomainNodes = new int[mappedNonDomain.length];
|
|
||||||
int index = 0;
|
|
||||||
for (int node : scc) {
|
|
||||||
if (!domain.contains(node)) {
|
|
||||||
mappedNonDomain[index] = nodeMap[node];
|
|
||||||
nonDomainNodes[index] = node;
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int[][] mappedDomain = new int[domain.size()][];
|
|
||||||
index = 0;
|
|
||||||
for (IntCursor cursor : domain) {
|
|
||||||
mappedDomain[index] = nodeMap[cursor.value];
|
|
||||||
domainNodes[index] = cursor.value;
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate splitting to domain
|
|
||||||
int[] nodesToCopy = withCopies(flatten(mappedNonDomain));
|
|
||||||
int[] copies = backend.split(withCopies(flatten(mappedDomain)), nodesToCopy);
|
|
||||||
registerCopies(nodesToCopy, copies);
|
|
||||||
|
|
||||||
int[][] newNodes = unflatten(withoutCopies(copies), mappedNonDomain);
|
|
||||||
for (int[] nodes : newNodes) {
|
|
||||||
totalNodeCount += nodes.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate mappings
|
|
||||||
int[][] newNodeMap = new int[1 + scc.length + newNodes.length][];
|
|
||||||
int[] newNodeBackMap = new int[totalNodeCount];
|
|
||||||
int[] mappedWeight = new int[newNodeMap.length];
|
|
||||||
Arrays.fill(newNodeBackMap, -1);
|
|
||||||
newNodeMap[0] = nodeMap[sharedDom];
|
|
||||||
newNodeBackMap[sharedDom] = 0;
|
|
||||||
mappedWeight[0] = djGraph.weightOf(sharedDom);
|
|
||||||
index = 1;
|
|
||||||
for (int i = 0; i < mappedDomain.length; ++i) {
|
|
||||||
newNodeMap[index] = mappedDomain[i];
|
|
||||||
newNodeBackMap[domainNodes[i]] = index;
|
|
||||||
mappedWeight[index] = djGraph.weightOf(domainNodes[i]);
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < mappedNonDomain.length; ++i) {
|
|
||||||
newNodeMap[index] = mappedNonDomain[i];
|
|
||||||
newNodeBackMap[nonDomainNodes[i]] = index;
|
|
||||||
mappedWeight[index] = djGraph.weightOf(nonDomainNodes[i]);
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < mappedNonDomain.length; ++i) {
|
|
||||||
newNodeMap[index] = newNodes[i];
|
|
||||||
mappedWeight[index] = djGraph.weightOf(nonDomainNodes[i]);
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build subgraph with new nodes
|
|
||||||
GraphBuilder builder = new GraphBuilder(newNodeMap.length);
|
|
||||||
for (int succ : cfg.outgoingEdges(sharedDom)) {
|
|
||||||
int j = newNodeBackMap[succ];
|
|
||||||
if (j >= 0) {
|
|
||||||
builder.addEdge(0, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 1; i <= mappedDomain.length; ++i) {
|
|
||||||
for (int succ : djGraph.getCfg().outgoingEdges(domainNodes[i - 1])) {
|
|
||||||
int j = newNodeBackMap[succ];
|
|
||||||
if (j > mappedDomain.length) {
|
|
||||||
builder.addEdge(i, j + mappedNonDomain.length);
|
|
||||||
} else if (j >= 0) {
|
|
||||||
builder.addEdge(i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index = 0;
|
|
||||||
for (int i = mappedDomain.length + 1; i <= scc.length; ++i) {
|
|
||||||
for (int succ : djGraph.getCfg().outgoingEdges(nonDomainNodes[index++])) {
|
|
||||||
int j = newNodeBackMap[succ];
|
|
||||||
if (j >= 0) {
|
|
||||||
builder.addEdge(i, j);
|
|
||||||
if (j > mappedDomain.length) {
|
|
||||||
builder.addEdge(i + mappedNonDomain.length, j + mappedNonDomain.length);
|
|
||||||
} else {
|
|
||||||
builder.addEdge(i + mappedNonDomain.length, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLoops(new DJGraph(builder.build(), mappedWeight), newNodeMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] withCopies(int[] nodes) {
|
|
||||||
IntegerArray nodesWithCopies = new IntegerArray(nodes.length);
|
|
||||||
for (int node : nodes) {
|
|
||||||
nodesWithCopies.add(node);
|
|
||||||
IntSet copies = nodeCopies[node];
|
|
||||||
if (copies != null) {
|
|
||||||
nodesWithCopies.addAll(copies.toArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodesWithCopies.getAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] withoutCopies(int[] nodesWithCopies) {
|
|
||||||
IntSet visited = new IntHashSet();
|
|
||||||
int[] nodes = new int[nodesWithCopies.length];
|
|
||||||
int sz = 0;
|
|
||||||
for (int node : nodesWithCopies) {
|
|
||||||
node = nodeOriginals.get(node);
|
|
||||||
if (visited.add(node)) {
|
|
||||||
nodes[sz++] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Arrays.copyOf(nodes, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerCopies(int[] originalNodes, int[] copies) {
|
|
||||||
for (int i = 0; i < originalNodes.length; ++i) {
|
|
||||||
int original = nodeOriginals.get(originalNodes[i]);
|
|
||||||
int copy = copies[i];
|
|
||||||
IntSet knownCopies = nodeCopies[original];
|
|
||||||
if (knownCopies == null) {
|
|
||||||
knownCopies = new IntHashSet();
|
|
||||||
nodeCopies[original] = knownCopies;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (knownCopies.add(copy)) {
|
|
||||||
while (nodeOriginals.size() <= copy) {
|
|
||||||
nodeOriginals.add(-1);
|
|
||||||
}
|
|
||||||
nodeOriginals.set(copy, original);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void collapse(DJGraph djGraph, int[] scc, int[][] nodeMap) {
|
|
||||||
int cls = djGraph.collapse(scc);
|
|
||||||
IntegerArray nodes = new IntegerArray(djGraph.getGraph().size());
|
|
||||||
for (int representative : djGraph.classRepresentatives(cls)) {
|
|
||||||
for (int node : nodeMap[representative]) {
|
|
||||||
nodes.add(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int representative : djGraph.classRepresentatives(cls)) {
|
|
||||||
nodeMap[representative] = new int[0];
|
|
||||||
}
|
|
||||||
nodeMap[cls] = nodes.getAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] flatten(int[][] array) {
|
|
||||||
int count = 0;
|
|
||||||
for (int i = 0; i < array.length; ++i) {
|
|
||||||
count += array[i].length;
|
|
||||||
}
|
|
||||||
int[] flat = new int[count];
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < array.length; ++i) {
|
|
||||||
int[] part = array[i];
|
|
||||||
for (int j = 0; j < part.length; ++j) {
|
|
||||||
flat[index++] = part[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flat;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[][] unflatten(int[] flat, int[][] pattern) {
|
|
||||||
int[][] rough = new int[pattern.length][];
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < rough.length; ++i) {
|
|
||||||
int[] part = new int[pattern[i].length];
|
|
||||||
for (int j = 0; j < part.length; ++j) {
|
|
||||||
part[j] = flat[index++];
|
|
||||||
}
|
|
||||||
rough[i] = part;
|
|
||||||
}
|
|
||||||
return rough;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class DJGraphNodeFilter implements IntPredicate {
|
|
||||||
private DJGraph graph;
|
|
||||||
private int level;
|
|
||||||
|
|
||||||
public DJGraphNodeFilter(DJGraph graph, int level) {
|
|
||||||
this.graph = graph;
|
|
||||||
this.level = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(int node) {
|
|
||||||
return graph.levelOf(node) >= level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,426 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.IntArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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>
|
||||||
|
*
|
||||||
|
* <p>Appendix A of the paper contains pseudocode. We refer to this pseudocode below.</p>
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
class IrreducibleGraphSplitter {
|
||||||
|
private GraphSplittingBackend backend;
|
||||||
|
private int[] idom;
|
||||||
|
private int[][] domNodes;
|
||||||
|
private MutableDirectedGraph cfg;
|
||||||
|
private int[] weights;
|
||||||
|
private IntArrayList[] realNodes;
|
||||||
|
private int[][] spBackEdges;
|
||||||
|
private int[] levels;
|
||||||
|
private int[] tmpArray;
|
||||||
|
private IntArrayList copiedRealNodes = new IntArrayList();
|
||||||
|
private int additionalWeight;
|
||||||
|
private int[] collapseMap;
|
||||||
|
|
||||||
|
IrreducibleGraphSplitter(GraphSplittingBackend backend, Graph src, int[] weights) {
|
||||||
|
this(backend, src, weights, initRealNodes(src.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[][] initRealNodes(int size) {
|
||||||
|
int[][] result = new int[size][];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
result[i] = new int[] { i };
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrreducibleGraphSplitter(GraphSplittingBackend backend, Graph src, int[] weights, int[][] realNodes) {
|
||||||
|
int size = src.size();
|
||||||
|
if (size != weights.length || size != realNodes.length) {
|
||||||
|
throw new IllegalArgumentException("Node count " + src.size() + " is not equal to weight array "
|
||||||
|
+ weights.length);
|
||||||
|
}
|
||||||
|
this.backend = backend;
|
||||||
|
tmpArray = new int[src.size()];
|
||||||
|
cfg = new MutableDirectedGraph(src);
|
||||||
|
DominatorTree domTree = GraphUtils.buildDominatorTree(src);
|
||||||
|
idom = new int[size];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
idom[i] = domTree.immediateDominatorOf(i);
|
||||||
|
}
|
||||||
|
collapseMap = new int[size];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
collapseMap[i] = i;
|
||||||
|
}
|
||||||
|
buildDomGraph();
|
||||||
|
buildLevels();
|
||||||
|
dfs();
|
||||||
|
this.realNodes = new IntArrayList[realNodes.length];
|
||||||
|
for (int i = 0; i < cfg.size(); ++i) {
|
||||||
|
this.realNodes[i] = IntArrayList.from(realNodes[i]);
|
||||||
|
}
|
||||||
|
this.weights = weights.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// n-th element of output array (domGraph) will contain nodes, directly dominated by node n.
|
||||||
|
private void buildDomGraph() {
|
||||||
|
int size = cfg.size();
|
||||||
|
int[] domGraphCount = new int[size];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int j = idom[i];
|
||||||
|
if (j >= 0) {
|
||||||
|
domGraphCount[j]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int[][] domGraph = new int[size][];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
domGraph[i] = new int[domGraphCount[i]];
|
||||||
|
domGraphCount[i] = 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int j = idom[i];
|
||||||
|
if (j >= 0) {
|
||||||
|
domGraph[j][domGraphCount[j]++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.domNodes = domGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
// n-th element of output array (levels) will contain length of the path from root to node node N
|
||||||
|
// (paper calls this 'level').
|
||||||
|
private void buildLevels() {
|
||||||
|
int size = cfg.size();
|
||||||
|
levels = new int[size];
|
||||||
|
Arrays.fill(levels, -1);
|
||||||
|
levels[0] = 0;
|
||||||
|
for (int i = 1; i < size; ++i) {
|
||||||
|
if (levels[i] >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int node = i;
|
||||||
|
int depth = 0;
|
||||||
|
while (levels[node] < 0) {
|
||||||
|
node = idom[node];
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int level = depth + levels[node];
|
||||||
|
node = i;
|
||||||
|
while (levels[node] < 0) {
|
||||||
|
levels[node] = level--;
|
||||||
|
node = idom[node];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find back edges.
|
||||||
|
// The n-th element of output array (sbBackEdges) will contain null if there is no back edges leading to n,
|
||||||
|
// or array of nodes m_i, where each edge m_i -> n is a back edge in spanning tree
|
||||||
|
// (m_i -> n is called 'SB back edge' in the paper).
|
||||||
|
private void dfs() {
|
||||||
|
int size = cfg.size();
|
||||||
|
|
||||||
|
spBackEdges = new int[size][];
|
||||||
|
int[] spBackEdgeCount = new int[size];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int count = cfg.incomingEdgesCount(i);
|
||||||
|
if (count > 0) {
|
||||||
|
spBackEdges[i] = new int[cfg.incomingEdgesCount(i)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] state = new int[size];
|
||||||
|
int[] stack = new int[size * 2];
|
||||||
|
int top = 0;
|
||||||
|
stack[top++] = 0;
|
||||||
|
|
||||||
|
while (top > 0) {
|
||||||
|
int node = stack[--top];
|
||||||
|
switch (state[node]) {
|
||||||
|
case 0:
|
||||||
|
state[node] = 1;
|
||||||
|
stack[top++] = node;
|
||||||
|
for (int successor : cfg.outgoingEdges(node)) {
|
||||||
|
if (state[successor] == 0) {
|
||||||
|
stack[top++] = successor;
|
||||||
|
} else if (state[successor] == 1) {
|
||||||
|
spBackEdges[successor][spBackEdgeCount[successor]++] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
state[node] = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int[] back = spBackEdges[i];
|
||||||
|
if (back == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int count = spBackEdgeCount[i];
|
||||||
|
if (count == 0) {
|
||||||
|
spBackEdges[i] = null;
|
||||||
|
} else if (count < spBackEdges[i].length) {
|
||||||
|
spBackEdges[i] = Arrays.copyOf(back, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an implementation of 'split_loop' function from the paper.
|
||||||
|
// It does not take 'top' and 'set' parameter.
|
||||||
|
// Instead, it always starts with 0 node as top and assumes that all of the 'set' nodes are in the graph
|
||||||
|
// We rewrote this method to use stack instead of recursion. The only place where we need recursion
|
||||||
|
// is handleScc. We build a new instance of this class with corresponding subgraph.
|
||||||
|
void splitLoops() {
|
||||||
|
int size = cfg.size();
|
||||||
|
boolean[] cross = new boolean[size];
|
||||||
|
int[] stack = new int[size * 4];
|
||||||
|
int head = 0;
|
||||||
|
|
||||||
|
stack[head++] = 0;
|
||||||
|
stack[head++] = 0;
|
||||||
|
while (head > 0) {
|
||||||
|
int state = stack[--head];
|
||||||
|
int node = stack[--head];
|
||||||
|
if (state == 0) {
|
||||||
|
stack[head++] = node;
|
||||||
|
stack[head++] = 1;
|
||||||
|
int[] successors = domNodes[node];
|
||||||
|
for (int i = successors.length - 1; i >= 0; --i) {
|
||||||
|
stack[head++] = successors[i];
|
||||||
|
stack[head++] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cross[node]) {
|
||||||
|
for (int successor : domNodes[node]) {
|
||||||
|
collapse(successor);
|
||||||
|
}
|
||||||
|
handleIrreducibleChildren(node);
|
||||||
|
}
|
||||||
|
int[] back = spBackEdges[node];
|
||||||
|
int parent = idom[node];
|
||||||
|
if (back != null && parent >= 0) {
|
||||||
|
for (int predecessor : back) {
|
||||||
|
if (!dominates(node, predecessor)) {
|
||||||
|
cross[parent] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleIrreducibleChildren(int top) {
|
||||||
|
Graph levelSubgraph = GraphUtils.subgraph(cfg, node -> node == top || idom[node] == top);
|
||||||
|
int[][] sccs = GraphUtils.findStronglyConnectedComponents(levelSubgraph);
|
||||||
|
for (int[] scc : sccs) {
|
||||||
|
if (scc.length > 1) {
|
||||||
|
handleStronglyConnectedComponent(top, scc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStronglyConnectedComponent(int top, int[] scc) {
|
||||||
|
// Find header node
|
||||||
|
int domain = scc[0];
|
||||||
|
int maxWeight = weights[domain];
|
||||||
|
for (int i = 1; i < scc.length; ++i) {
|
||||||
|
int node = scc[i];
|
||||||
|
if (weights[node] > maxWeight) {
|
||||||
|
maxWeight = weights[node];
|
||||||
|
domain = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] realDomainNodes = realNodes[domain].toArray();
|
||||||
|
int realNodesToCopyCount = 0;
|
||||||
|
for (int node : scc) {
|
||||||
|
if (node != domain) {
|
||||||
|
realNodesToCopyCount += realNodes[node].size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int[] realNodesToCopy = new int[realNodesToCopyCount];
|
||||||
|
realNodesToCopyCount = 0;
|
||||||
|
for (int node : scc) {
|
||||||
|
if (node != domain) {
|
||||||
|
int[] nodes = realNodes[node].toArray();
|
||||||
|
System.arraycopy(nodes, 0, realNodesToCopy, realNodesToCopyCount, nodes.length);
|
||||||
|
realNodesToCopyCount += nodes.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] realNodesCopies = backend.split(realDomainNodes, realNodesToCopy);
|
||||||
|
copiedRealNodes.add(realNodesCopies);
|
||||||
|
realNodes[top].add(realNodesCopies);
|
||||||
|
int copyWeight = 0;
|
||||||
|
for (int node : scc) {
|
||||||
|
if (node != domain) {
|
||||||
|
copyWeight += weights[node];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.additionalWeight += copyWeight;
|
||||||
|
weights[top] += copyWeight;
|
||||||
|
|
||||||
|
int subgraphSize = scc.length * 2;
|
||||||
|
GraphBuilder subgraph = new GraphBuilder(subgraphSize);
|
||||||
|
int[][] subgraphRealNodes = new int[subgraphSize][];
|
||||||
|
int[] subgraphWeights = new int[subgraphSize];
|
||||||
|
int[] map = new int[cfg.size()];
|
||||||
|
int[] copyMap = new int[cfg.size()];
|
||||||
|
Arrays.fill(map, -1);
|
||||||
|
Arrays.fill(copyMap, -1);
|
||||||
|
|
||||||
|
map[top] = 0;
|
||||||
|
subgraphRealNodes[0] = realNodes[top].toArray();
|
||||||
|
subgraphWeights[0] = weights[top];
|
||||||
|
for (int i = 0; i < scc.length; ++i) {
|
||||||
|
int node = scc[i];
|
||||||
|
map[node] = i + 1;
|
||||||
|
subgraphRealNodes[i + 1] = realNodes[node].toArray();
|
||||||
|
subgraphWeights[i + 1] = weights[node];
|
||||||
|
}
|
||||||
|
int copyIndex = scc.length + 1;
|
||||||
|
int realNodeCopiesIndex = 0;
|
||||||
|
for (int node : scc) {
|
||||||
|
if (node == domain) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
copyMap[node] = copyIndex;
|
||||||
|
int realNodeCount = realNodes[node].size();
|
||||||
|
subgraphRealNodes[copyIndex] = Arrays.copyOfRange(realNodesCopies, realNodeCopiesIndex,
|
||||||
|
realNodeCopiesIndex + realNodeCount);
|
||||||
|
realNodeCopiesIndex += realNodeCount;
|
||||||
|
subgraphWeights[copyIndex] = weights[node];
|
||||||
|
copyIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < scc.length; ++i) {
|
||||||
|
subgraph.addEdge(0, i + 1);
|
||||||
|
}
|
||||||
|
for (int node : scc) {
|
||||||
|
int subgraphNode = map[node];
|
||||||
|
int subgraphNodeCopy = copyMap[node];
|
||||||
|
int[] successors = cfg.outgoingEdges(node);
|
||||||
|
for (int successor : successors) {
|
||||||
|
// (x, y) = (node, successor)
|
||||||
|
int subgraphSuccessor = map[successor];
|
||||||
|
int subgraphSuccessorCopy = copyMap[successor];
|
||||||
|
|
||||||
|
if (subgraphSuccessorCopy >= 0) {
|
||||||
|
// y in S
|
||||||
|
if (subgraphNodeCopy >= 0) {
|
||||||
|
// x in S
|
||||||
|
subgraph.addEdge(subgraphNodeCopy, subgraphSuccessorCopy); // 8.4
|
||||||
|
if (subgraphSuccessor >= 0) {
|
||||||
|
subgraph.addEdge(subgraphNode, subgraphSuccessor); // 8.1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// x !in S (x in domain(h))
|
||||||
|
subgraph.addEdge(subgraphNode, subgraphSuccessorCopy); // 8.2
|
||||||
|
}
|
||||||
|
} else if (subgraphSuccessor >= 0) {
|
||||||
|
// y !in S (y in N\S)
|
||||||
|
if (subgraphNodeCopy >= 0) {
|
||||||
|
subgraph.addEdge(subgraphNodeCopy, subgraphSuccessor); // 8.3
|
||||||
|
}
|
||||||
|
subgraph.addEdge(subgraphNode, subgraphSuccessor); // 8.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IrreducibleGraphSplitter subgraphSplitter = new IrreducibleGraphSplitter(backend, subgraph.build(),
|
||||||
|
subgraphWeights, subgraphRealNodes);
|
||||||
|
subgraphSplitter.splitLoops();
|
||||||
|
copiedRealNodes.addAll(subgraphSplitter.copiedRealNodes);
|
||||||
|
realNodes[top].addAll(subgraphSplitter.copiedRealNodes);
|
||||||
|
additionalWeight += subgraphSplitter.additionalWeight;
|
||||||
|
weights[top] += subgraphSplitter.additionalWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean dominates(int dominator, int node) {
|
||||||
|
int targetLevel = levels[dominator];
|
||||||
|
int level = levels[node];
|
||||||
|
while (level-- > targetLevel) {
|
||||||
|
node = idom[node];
|
||||||
|
}
|
||||||
|
return node == dominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collapse(int top) {
|
||||||
|
if (domNodes[top] == null || domNodes[top].length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int count = findAllDominatedNodes(top);
|
||||||
|
int[] nodes = tmpArray;
|
||||||
|
|
||||||
|
IntArrayList topRealNodes = realNodes[top];
|
||||||
|
for (int i = 1; i < count; ++i) {
|
||||||
|
int node = nodes[i];
|
||||||
|
topRealNodes.addAll(realNodes[node]);
|
||||||
|
realNodes[node] = null;
|
||||||
|
weights[top] += weights[node];
|
||||||
|
collapseMap[node] = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alter graphs
|
||||||
|
for (int i = 1; i < count; ++i) {
|
||||||
|
int node = nodes[i];
|
||||||
|
for (int succ : cfg.outgoingEdges(node)) {
|
||||||
|
int mappedSucc = collapseMap[succ];
|
||||||
|
if (mappedSucc != top || succ == top) {
|
||||||
|
cfg.addEdge(top, mappedSucc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int pred : cfg.incomingEdges(node)) {
|
||||||
|
int mappedPred = collapseMap[pred];
|
||||||
|
if (mappedPred != top) {
|
||||||
|
cfg.addEdge(mappedPred, top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfg.detachNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
domNodes[top] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findAllDominatedNodes(int top) {
|
||||||
|
int[] result = tmpArray;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
int head = 0;
|
||||||
|
result[count++] = top;
|
||||||
|
while (head < count) {
|
||||||
|
int[] successors = domNodes[result[head]];
|
||||||
|
if (successors != null && successors.length > 0) {
|
||||||
|
System.arraycopy(successors, 0, result, count, successors.length);
|
||||||
|
count += successors.length;
|
||||||
|
}
|
||||||
|
++head;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,15 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.common;
|
package org.teavm.common;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntArrayList;
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
import com.carrotsearch.hppc.IntHashSet;
|
||||||
import com.carrotsearch.hppc.IntSet;
|
|
||||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MutableDirectedGraph implements Graph {
|
public class MutableDirectedGraph implements Graph {
|
||||||
private List<IntSet> successors = new ArrayList<>();
|
private List<NodeSet> successors = new ArrayList<>();
|
||||||
private List<IntSet> predecessors = new ArrayList<>();
|
private List<NodeSet> predecessors = new ArrayList<>();
|
||||||
|
|
||||||
public MutableDirectedGraph() {
|
public MutableDirectedGraph() {
|
||||||
}
|
}
|
||||||
|
@ -36,18 +36,29 @@ public class MutableDirectedGraph implements Graph {
|
||||||
addEdge(i, data[j]);
|
addEdge(i, data[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while (successors.size() < graph.size()) {
|
||||||
|
successors.add(new NodeSet());
|
||||||
|
predecessors.add(new NodeSet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Graph copyToImmutable() {
|
public Graph copyToImmutable() {
|
||||||
GraphBuilder builder = new GraphBuilder(successors.size());
|
GraphBuilder builder = new GraphBuilder(successors.size());
|
||||||
for (int i = 0; i < successors.size(); ++i) {
|
for (int i = 0; i < successors.size(); ++i) {
|
||||||
for (IntCursor cursor : successors.get(i)) {
|
for (IntCursor successor : successors.get(i).list) {
|
||||||
builder.addEdge(i, cursor.value);
|
builder.addEdge(i, successor.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int addNode() {
|
||||||
|
int index = successors.size();
|
||||||
|
successors.add(new NodeSet());
|
||||||
|
predecessors.add(new NodeSet());
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return successors.size();
|
return successors.size();
|
||||||
|
@ -56,41 +67,62 @@ public class MutableDirectedGraph implements Graph {
|
||||||
public void addEdge(int from, int to) {
|
public void addEdge(int from, int to) {
|
||||||
int max = Math.max(from, to);
|
int max = Math.max(from, to);
|
||||||
while (max >= successors.size()) {
|
while (max >= successors.size()) {
|
||||||
successors.add(new IntHashSet(1));
|
successors.add(new NodeSet());
|
||||||
predecessors.add(new IntHashSet(1));
|
predecessors.add(new NodeSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeSet successorNodes = successors.get(from);
|
||||||
|
if (successorNodes.set.add(to)) {
|
||||||
|
successorNodes.list.add(to);
|
||||||
|
NodeSet predecessorNodes = predecessors.get(to);
|
||||||
|
predecessorNodes.set.add(from);
|
||||||
|
predecessorNodes.list.add(from);
|
||||||
}
|
}
|
||||||
successors.get(from).add(to);
|
|
||||||
predecessors.get(to).add(from);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteEdge(int from, int to) {
|
public void deleteEdge(int from, int to) {
|
||||||
if (from >= successors.size() || to >= successors.size()) {
|
if (from >= successors.size() || to >= successors.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
successors.get(from).removeAll(to);
|
|
||||||
predecessors.get(to).removeAll(from);
|
NodeSet successorNodes = successors.get(from);
|
||||||
|
if (successorNodes.set.removeAll(to) > 0) {
|
||||||
|
successorNodes.list.removeAll(to);
|
||||||
|
NodeSet predecessorNodes = predecessors.get(to);
|
||||||
|
predecessorNodes.set.removeAll(from);
|
||||||
|
predecessorNodes.list.removeAll(from);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void detachNode(int node) {
|
public void detachNode(int node) {
|
||||||
for (IntCursor succ : successors.get(node)) {
|
for (IntCursor succ : successors.get(node).list) {
|
||||||
predecessors.get(succ.value).removeAll(node);
|
NodeSet predecessorNodes = predecessors.get(succ.value);
|
||||||
|
predecessorNodes.set.removeAll(node);
|
||||||
|
predecessorNodes.list.removeAll(node);
|
||||||
}
|
}
|
||||||
for (IntCursor pred : predecessors.get(node)) {
|
for (IntCursor pred : predecessors.get(node).list) {
|
||||||
successors.get(pred.value).removeAll(node);
|
NodeSet successorNodes = successors.get(pred.value);
|
||||||
|
successorNodes.set.removeAll(node);
|
||||||
|
successorNodes.list.removeAll(node);
|
||||||
}
|
}
|
||||||
predecessors.get(node).clear();
|
|
||||||
successors.get(node).clear();
|
NodeSet predecessorNodes = predecessors.get(node);
|
||||||
|
predecessorNodes.list.clear();
|
||||||
|
predecessorNodes.set.clear();
|
||||||
|
NodeSet successorNodes = successors.get(node);
|
||||||
|
successorNodes.list.clear();
|
||||||
|
predecessorNodes.list.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] incomingEdges(int node) {
|
public int[] incomingEdges(int node) {
|
||||||
return predecessors.get(node).toArray();
|
return predecessors.get(node).list.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int copyIncomingEdges(int node, int[] target) {
|
public int copyIncomingEdges(int node, int[] target) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (IntCursor cursor : predecessors.get(node)) {
|
for (IntCursor cursor : predecessors.get(node).list) {
|
||||||
target[index++] = cursor.value;
|
target[index++] = cursor.value;
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
|
@ -98,13 +130,13 @@ public class MutableDirectedGraph implements Graph {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] outgoingEdges(int node) {
|
public int[] outgoingEdges(int node) {
|
||||||
return successors.get(node).toArray();
|
return successors.get(node).list.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int copyOutgoingEdges(int node, int[] target) {
|
public int copyOutgoingEdges(int node, int[] target) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (IntCursor cursor : successors.get(node)) {
|
for (IntCursor cursor : successors.get(node).list) {
|
||||||
target[index++] = cursor.value;
|
target[index++] = cursor.value;
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
|
@ -112,11 +144,21 @@ public class MutableDirectedGraph implements Graph {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int incomingEdgesCount(int node) {
|
public int incomingEdgesCount(int node) {
|
||||||
return predecessors.get(node).size();
|
return predecessors.get(node).list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int outgoingEdgesCount(int node) {
|
public int outgoingEdgesCount(int node) {
|
||||||
return successors.get(node).size();
|
return successors.get(node).list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return GraphUtils.printToDot(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NodeSet {
|
||||||
|
IntHashSet set = new IntHashSet(1);
|
||||||
|
IntArrayList list = new IntArrayList(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ public class GraphTest {
|
||||||
|
|
||||||
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
assertTrue("Should be equialent", isEquialent(backend, graph));
|
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -171,7 +171,7 @@ public class GraphTest {
|
||||||
|
|
||||||
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
assertTrue("Should be equivalent", isEquialent(backend, graph));
|
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -203,10 +203,137 @@ public class GraphTest {
|
||||||
|
|
||||||
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
assertTrue("Should be equivalent", isEquialent(backend, graph));
|
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEquialent(DefaultGraphSplittingBackend backend, Graph proto) {
|
@Test
|
||||||
|
public void irreducibleGraphSplit4() {
|
||||||
|
GraphBuilder builder = new GraphBuilder();
|
||||||
|
|
||||||
|
builder.addEdge(0, 1);
|
||||||
|
builder.addEdge(0, 2);
|
||||||
|
builder.addEdge(0, 4);
|
||||||
|
builder.addEdge(1, 2);
|
||||||
|
builder.addEdge(2, 3);
|
||||||
|
builder.addEdge(3, 4);
|
||||||
|
builder.addEdge(4, 1);
|
||||||
|
|
||||||
|
Graph graph = builder.build();
|
||||||
|
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
|
||||||
|
int[] weights = new int[graph.size()];
|
||||||
|
Arrays.fill(weights, 1);
|
||||||
|
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
|
||||||
|
Graph result = backend.getGraph();
|
||||||
|
|
||||||
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
|
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void irreducibleGraphSplit5() {
|
||||||
|
GraphBuilder builder = new GraphBuilder();
|
||||||
|
|
||||||
|
addEdges(builder, 0, 1);
|
||||||
|
addEdges(builder, 1, 2, 3);
|
||||||
|
addEdges(builder, 2, 5);
|
||||||
|
addEdges(builder, 3, 2, 4);
|
||||||
|
addEdges(builder, 4, 5);
|
||||||
|
addEdges(builder, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22);
|
||||||
|
addEdges(builder, 6, 69, 70);
|
||||||
|
addEdges(builder, 7, 69);
|
||||||
|
addEdges(builder, 8, 32);
|
||||||
|
addEdges(builder, 9, 63);
|
||||||
|
addEdges(builder, 10, 61);
|
||||||
|
addEdges(builder, 11, 59);
|
||||||
|
addEdges(builder, 12, 57);
|
||||||
|
addEdges(builder, 13, 55);
|
||||||
|
addEdges(builder, 14, 53);
|
||||||
|
addEdges(builder, 15, 42);
|
||||||
|
addEdges(builder, 16, 44);
|
||||||
|
addEdges(builder, 17, 46);
|
||||||
|
addEdges(builder, 18, 48);
|
||||||
|
addEdges(builder, 19, 50);
|
||||||
|
addEdges(builder, 20, 65);
|
||||||
|
addEdges(builder, 21, 67);
|
||||||
|
addEdges(builder, 23, 24, 25);
|
||||||
|
addEdges(builder, 24, 67, 68);
|
||||||
|
addEdges(builder, 25, 26, 27);
|
||||||
|
addEdges(builder, 26, 28, 29);
|
||||||
|
addEdges(builder, 28, 30, 31);
|
||||||
|
addEdges(builder, 30, 65, 66);
|
||||||
|
addEdges(builder, 31, 32, 33);
|
||||||
|
addEdges(builder, 32, 34, 35, 36, 37, 38, 39, 40, 41);
|
||||||
|
addEdges(builder, 34, 63, 64);
|
||||||
|
addEdges(builder, 35, 61, 62);
|
||||||
|
addEdges(builder, 36, 59, 60);
|
||||||
|
addEdges(builder, 37, 57, 58);
|
||||||
|
addEdges(builder, 38, 55, 56);
|
||||||
|
addEdges(builder, 39, 53, 54);
|
||||||
|
addEdges(builder, 40, 42, 43);
|
||||||
|
addEdges(builder, 42, 44, 45);
|
||||||
|
addEdges(builder, 44, 46, 47);
|
||||||
|
addEdges(builder, 46, 48, 49);
|
||||||
|
addEdges(builder, 48, 50, 51);
|
||||||
|
addEdges(builder, 50, 52);
|
||||||
|
addEdges(builder, 52, 23);
|
||||||
|
addEdges(builder, 53, 52);
|
||||||
|
addEdges(builder, 55, 52);
|
||||||
|
addEdges(builder, 57, 52);
|
||||||
|
addEdges(builder, 59, 52);
|
||||||
|
addEdges(builder, 61, 52);
|
||||||
|
addEdges(builder, 63, 52);
|
||||||
|
addEdges(builder, 65, 52);
|
||||||
|
addEdges(builder, 69, 23);
|
||||||
|
|
||||||
|
Graph graph = builder.build();
|
||||||
|
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
|
||||||
|
int[] weights = new int[graph.size()];
|
||||||
|
Arrays.fill(weights, 1);
|
||||||
|
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
|
||||||
|
Graph result = backend.getGraph();
|
||||||
|
|
||||||
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
|
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void irreducibleGraphSplit6() {
|
||||||
|
GraphBuilder builder = new GraphBuilder();
|
||||||
|
addEdges(builder, 0, 1, 3, 6, 9);
|
||||||
|
addEdges(builder, 1, 2);
|
||||||
|
addEdges(builder, 2, 3);
|
||||||
|
addEdges(builder, 3, 4);
|
||||||
|
addEdges(builder, 4, 5, 8);
|
||||||
|
addEdges(builder, 5, 6);
|
||||||
|
addEdges(builder, 6, 7);
|
||||||
|
addEdges(builder, 7, 11);
|
||||||
|
addEdges(builder, 8, 9);
|
||||||
|
addEdges(builder, 9, 10);
|
||||||
|
addEdges(builder, 10, 11);
|
||||||
|
addEdges(builder, 11, 12);
|
||||||
|
addEdges(builder, 12, 1);
|
||||||
|
|
||||||
|
Graph graph = builder.build();
|
||||||
|
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
|
||||||
|
int[] weights = new int[graph.size()];
|
||||||
|
Arrays.fill(weights, 1);
|
||||||
|
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
|
||||||
|
Graph result = backend.getGraph();
|
||||||
|
|
||||||
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
|
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addEdges(GraphBuilder builder, int from, int... to) {
|
||||||
|
for (int target : to) {
|
||||||
|
builder.addEdge(from, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEquivalent(DefaultGraphSplittingBackend backend, Graph proto) {
|
||||||
Graph graph = backend.getGraph();
|
Graph graph = backend.getGraph();
|
||||||
for (int node = 0; node < graph.size(); ++node) {
|
for (int node = 0; node < graph.size(); ++node) {
|
||||||
int nodeProto = backend.prototype(node);
|
int nodeProto = backend.prototype(node);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user