mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
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();
|
||||
return GraphUtils.printToDot(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,11 +126,11 @@ public final class GraphUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static class FilteredGraph implements Graph {
|
||||
static class FilteredGraph implements Graph {
|
||||
final Graph innerGraph;
|
||||
final IntPredicate filter;
|
||||
|
||||
public FilteredGraph(Graph innerGraph, IntPredicate filter) {
|
||||
FilteredGraph(Graph innerGraph, IntPredicate filter) {
|
||||
this.innerGraph = innerGraph;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
@ -198,6 +198,11 @@ public final class GraphUtils {
|
|||
public int outgoingEdgesCount(int node) {
|
||||
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) {
|
||||
new IrreducibleGraphConverter().convertToReducible(graph, weights, backend);
|
||||
new IrreducibleGraphSplitter(backend, graph, weights).splitLoops();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import com.carrotsearch.hppc.IntArrayList;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MutableDirectedGraph implements Graph {
|
||||
private List<IntSet> successors = new ArrayList<>();
|
||||
private List<IntSet> predecessors = new ArrayList<>();
|
||||
private List<NodeSet> successors = new ArrayList<>();
|
||||
private List<NodeSet> predecessors = new ArrayList<>();
|
||||
|
||||
public MutableDirectedGraph() {
|
||||
}
|
||||
|
@ -36,18 +36,29 @@ public class MutableDirectedGraph implements Graph {
|
|||
addEdge(i, data[j]);
|
||||
}
|
||||
}
|
||||
while (successors.size() < graph.size()) {
|
||||
successors.add(new NodeSet());
|
||||
predecessors.add(new NodeSet());
|
||||
}
|
||||
}
|
||||
|
||||
public Graph copyToImmutable() {
|
||||
GraphBuilder builder = new GraphBuilder(successors.size());
|
||||
for (int i = 0; i < successors.size(); ++i) {
|
||||
for (IntCursor cursor : successors.get(i)) {
|
||||
builder.addEdge(i, cursor.value);
|
||||
for (IntCursor successor : successors.get(i).list) {
|
||||
builder.addEdge(i, successor.value);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public int addNode() {
|
||||
int index = successors.size();
|
||||
successors.add(new NodeSet());
|
||||
predecessors.add(new NodeSet());
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return successors.size();
|
||||
|
@ -56,41 +67,62 @@ public class MutableDirectedGraph implements Graph {
|
|||
public void addEdge(int from, int to) {
|
||||
int max = Math.max(from, to);
|
||||
while (max >= successors.size()) {
|
||||
successors.add(new IntHashSet(1));
|
||||
predecessors.add(new IntHashSet(1));
|
||||
successors.add(new NodeSet());
|
||||
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) {
|
||||
if (from >= successors.size() || to >= successors.size()) {
|
||||
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) {
|
||||
for (IntCursor succ : successors.get(node)) {
|
||||
predecessors.get(succ.value).removeAll(node);
|
||||
for (IntCursor succ : successors.get(node).list) {
|
||||
NodeSet predecessorNodes = predecessors.get(succ.value);
|
||||
predecessorNodes.set.removeAll(node);
|
||||
predecessorNodes.list.removeAll(node);
|
||||
}
|
||||
for (IntCursor pred : predecessors.get(node)) {
|
||||
successors.get(pred.value).removeAll(node);
|
||||
for (IntCursor pred : predecessors.get(node).list) {
|
||||
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
|
||||
public int[] incomingEdges(int node) {
|
||||
return predecessors.get(node).toArray();
|
||||
return predecessors.get(node).list.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int copyIncomingEdges(int node, int[] target) {
|
||||
int index = 0;
|
||||
for (IntCursor cursor : predecessors.get(node)) {
|
||||
for (IntCursor cursor : predecessors.get(node).list) {
|
||||
target[index++] = cursor.value;
|
||||
}
|
||||
return index;
|
||||
|
@ -98,13 +130,13 @@ public class MutableDirectedGraph implements Graph {
|
|||
|
||||
@Override
|
||||
public int[] outgoingEdges(int node) {
|
||||
return successors.get(node).toArray();
|
||||
return successors.get(node).list.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int copyOutgoingEdges(int node, int[] target) {
|
||||
int index = 0;
|
||||
for (IntCursor cursor : successors.get(node)) {
|
||||
for (IntCursor cursor : successors.get(node).list) {
|
||||
target[index++] = cursor.value;
|
||||
}
|
||||
return index;
|
||||
|
@ -112,11 +144,21 @@ public class MutableDirectedGraph implements Graph {
|
|||
|
||||
@Override
|
||||
public int incomingEdgesCount(int node) {
|
||||
return predecessors.get(node).size();
|
||||
return predecessors.get(node).list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||
assertTrue("Should be equialent", isEquialent(backend, graph));
|
||||
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -171,7 +171,7 @@ public class GraphTest {
|
|||
|
||||
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||
assertTrue("Should be equivalent", isEquialent(backend, graph));
|
||||
assertTrue("Should be equivalent", isEquivalent(backend, graph));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -203,10 +203,137 @@ public class GraphTest {
|
|||
|
||||
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||
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();
|
||||
for (int node = 0; node < graph.size(); ++node) {
|
||||
int nodeProto = backend.prototype(node);
|
||||
|
|
Loading…
Reference in New Issue
Block a user