Mirror of the BLOAT repository
https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
731 lines
26 KiB
731 lines
26 KiB
/*
|
|
* Class: SSAGraph
|
|
* Version:
|
|
* Date:
|
|
*
|
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and
|
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
|
* Research Foundation of Purdue University. All rights reserved.
|
|
*
|
|
* <p>
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that this entire copyright notice is duplicated in all
|
|
* such copies, and that any documentation, announcements, and other
|
|
* materials related to such distribution and use acknowledge that the
|
|
* software was developed at Purdue University, West Lafayette, IN by
|
|
* @author Antony Hosking
|
|
* @author David Whitlock
|
|
* @author Nathaniel Nystrom
|
|
* No charge may be made for copies, derivations, or distributions of
|
|
* this material without the express written consent of the copyright
|
|
* holder. Neither the name of the University nor the name of the
|
|
* author may be used to endorse or promote products derived from this
|
|
* material without specific prior written permission. THIS SOFTWARE
|
|
* IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE.
|
|
*
|
|
* <p>
|
|
*
|
|
* Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was
|
|
* contributed by The Australian National University by
|
|
* @author Arrin Daley
|
|
* @author John N Zigman
|
|
*
|
|
* <p>
|
|
* Java is a trademark of Sun Microsystems, Inc.
|
|
*/
|
|
|
|
package edu.purdue.cs.bloat.ssa;
|
|
|
|
import java.util.*;
|
|
import java.io.*;
|
|
|
|
import edu.purdue.cs.bloat.cfg.Block;
|
|
import edu.purdue.cs.bloat.cfg.FlowGraph;
|
|
import edu.purdue.cs.bloat.tree.CheckExpr;
|
|
import edu.purdue.cs.bloat.tree.Node;
|
|
import edu.purdue.cs.bloat.tree.PhiStmt;
|
|
import edu.purdue.cs.bloat.tree.StackExpr;
|
|
import edu.purdue.cs.bloat.tree.StackManipStmt;
|
|
import edu.purdue.cs.bloat.tree.StoreExpr;
|
|
import edu.purdue.cs.bloat.tree.Tree;
|
|
import edu.purdue.cs.bloat.tree.TreeVisitor;
|
|
import edu.purdue.cs.bloat.tree.VarExpr;
|
|
import edu.purdue.cs.bloat.util.Assert;
|
|
|
|
/**
|
|
* The SSA graph (also called the value graph) represents the nesting of
|
|
* expression in a control flow graph. Each node in the SSA graph represents an
|
|
* expression. If the expression is a definition, the it is labeled with the
|
|
* variable it defines. Each node has directed edges to the nodes representing
|
|
* its operands.
|
|
*
|
|
* <p>
|
|
*
|
|
* <tt>SSAGraph</tt> is a representation of the definitions found in a CFG in
|
|
* the following form: Each node in the graph is an expression that defines a
|
|
* variable (a <tt>VarExpr</tt>, <tt>PhiStmt</tt>, or a
|
|
* <tt>StackManipStmt</tt>). Edges in the graph point to the nodes whose
|
|
* expressions define the operands of the expression in the source node.
|
|
*
|
|
* <p>
|
|
*
|
|
* This class is used primarily get the strongly connected components of the SSA
|
|
* graph in support of value numbering and induction variable analysis.
|
|
*
|
|
* <p>
|
|
*
|
|
* Nate warns: Do not modify the CFG while using the SSA graph! The effects of
|
|
* such modification are undefined and will probably lead to nasty things
|
|
* occuring.
|
|
*
|
|
* @see edu.purdue.cs.bloat.trans.ValueNumbering ValueNumbering
|
|
*/
|
|
public class SSAGraph {
|
|
public static boolean DEBUG = false;
|
|
|
|
FlowGraph cfg;
|
|
|
|
LinkedHashMap equiv; // A mapping between a Node and all its equivalent
|
|
// Nodes
|
|
|
|
/**
|
|
* Grumble.
|
|
*/
|
|
public SSAGraph(FlowGraph cfg, boolean useless) {
|
|
this(cfg);
|
|
}
|
|
|
|
/**
|
|
* Constructor. Traverse the control flow graph and determines which Nodes
|
|
* are of an equivalent Type.
|
|
*
|
|
* @param cfg
|
|
* The control flow graph to examine
|
|
*/
|
|
public SSAGraph(FlowGraph cfg) {
|
|
this.cfg = cfg;
|
|
this.equiv = new LinkedHashMap();
|
|
|
|
cfg.visit(new TreeVisitor() {
|
|
// The CheckExpr and the Expr is checks are equivalent.
|
|
public void visitCheckExpr(CheckExpr expr) {
|
|
expr.visitChildren(this);
|
|
makeEquiv(expr, expr.expr());
|
|
}
|
|
|
|
// The target of the PhiStmt and the PhiStmt are equivalent
|
|
public void visitPhiStmt(PhiStmt stmt) {
|
|
stmt.visitChildren(this);
|
|
makeEquiv(stmt.target(), stmt);
|
|
}
|
|
|
|
// The use of a variable and its defining variable are
|
|
// equivalent
|
|
public void visitVarExpr(VarExpr expr) {
|
|
if (!expr.isDef()) {
|
|
VarExpr def = (VarExpr) expr.def();
|
|
|
|
if (def != null) {
|
|
makeEquiv(expr, def);
|
|
}
|
|
}
|
|
}
|
|
|
|
// With StackManipStmts the stack slot (StackExpr) after the
|
|
// StackManipStmt is equivalent to its corresponding slot before
|
|
// the StackManipStmt.
|
|
public void visitStackManipStmt(StackManipStmt stmt) {
|
|
StackExpr[] target = stmt.target();
|
|
StackExpr[] source = stmt.source();
|
|
|
|
switch (stmt.kind()) {
|
|
case StackManipStmt.SWAP:
|
|
// 0 1 -> 1 0
|
|
Assert.isTrue(source.length == 2 && target.length == 2,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 1, 0 });
|
|
break;
|
|
case StackManipStmt.DUP:
|
|
// 0 -> 0 0
|
|
Assert.isTrue(source.length == 1 && target.length == 2,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 0, 0 });
|
|
break;
|
|
case StackManipStmt.DUP_X1:
|
|
// 0 1 -> 1 0 1
|
|
Assert.isTrue(source.length == 2 && target.length == 3,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 1, 0, 1 });
|
|
break;
|
|
case StackManipStmt.DUP_X2:
|
|
if (source.length == 3) {
|
|
// 0 1 2 -> 2 0 1 2
|
|
Assert.isTrue(source.length == 3 && target.length == 4,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 2, 0, 1, 2 });
|
|
} else {
|
|
// 0-1 2 -> 2 0-1 2
|
|
Assert.isTrue(source.length == 2 && target.length == 3,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 1, 0, 1 });
|
|
}
|
|
break;
|
|
case StackManipStmt.DUP2:
|
|
if (source.length == 2) {
|
|
// 0 1 -> 0 1 0 1
|
|
Assert.isTrue(target.length == 4, "Illegal statement: "
|
|
+ stmt);
|
|
manip(source, target, new int[] { 0, 1, 0, 1 });
|
|
} else {
|
|
// 0-1 -> 0-1 0-1
|
|
Assert.isTrue(source.length == 1 && target.length == 2,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 0, 0 });
|
|
}
|
|
break;
|
|
case StackManipStmt.DUP2_X1:
|
|
if (source.length == 3) {
|
|
// 0 1 2 -> 1 2 0 1 2
|
|
Assert.isTrue(target.length == 5, "Illegal statement: "
|
|
+ stmt);
|
|
manip(source, target, new int[] { 1, 2, 0, 1, 2 });
|
|
} else {
|
|
// 0 1-2 -> 1-2 0 1-2
|
|
Assert.isTrue(source.length == 2 && target.length == 3,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 1, 0, 1 });
|
|
}
|
|
break;
|
|
case StackManipStmt.DUP2_X2:
|
|
if (source.length == 4) {
|
|
// 0 1 2 3 -> 2 3 0 1 2 3
|
|
Assert.isTrue(target.length == 6, "Illegal statement: "
|
|
+ stmt);
|
|
manip(source, target, new int[] { 2, 3, 0, 1, 2, 3 });
|
|
} else if (source.length == 3) {
|
|
if (target.length == 5) {
|
|
// 0-1 2 3 -> 2 3 0-1 2 3
|
|
manip(source, target, new int[] { 1, 2, 0, 1, 2 });
|
|
} else {
|
|
// 0 1 2-3 -> 2-3 0 1 2-3
|
|
Assert.isTrue(target.length == 4,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 2, 0, 1, 2 });
|
|
}
|
|
} else {
|
|
// 0-1 2-3 -> 2-3 0-1 2-3
|
|
Assert.isTrue(source.length == 2 && target.length == 3,
|
|
"Illegal statement: " + stmt);
|
|
manip(source, target, new int[] { 1, 0, 1 });
|
|
}
|
|
break;
|
|
}
|
|
|
|
stmt.visitChildren(this);
|
|
}
|
|
|
|
// Determines equivalence of the StackExprs invovled in a
|
|
// StackManipStmt. Recall that StackManipStmt are things like
|
|
// the dup and swap instructions. So, elements (StackExprs) of
|
|
// the "new" stack will be equivalent to elements of the "old"
|
|
// stack. The s array defines the transformation.
|
|
private void manip(StackExpr[] source, StackExpr[] target, int[] s) {
|
|
for (int i = 0; i < s.length; i++) {
|
|
makeEquiv(target[i], source[s[i]]);
|
|
}
|
|
}
|
|
|
|
// The StoreExpr is equivalent to the expression being stored.
|
|
public void visitStoreExpr(StoreExpr expr) {
|
|
expr.visitChildren(this);
|
|
makeEquiv(expr, expr.expr());
|
|
|
|
if (expr.target() instanceof VarExpr) {
|
|
makeEquiv(expr.target(), expr.expr());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns the <tt>FlowGraph</tt> that this <tt>SSAGraph</tt> is built
|
|
* around.
|
|
*/
|
|
public FlowGraph cfg() {
|
|
return (this.cfg);
|
|
}
|
|
|
|
/**
|
|
* Returns a set of nodes whose value is equivalent to a given node. For
|
|
* example, the LHS and RHS of an assignment are equivalent. As are all
|
|
* local variables with the same definition.
|
|
*/
|
|
public Set equivalent(Node node) {
|
|
Set s = (Set) equiv.get(node);
|
|
|
|
if (s == null) {
|
|
s = new LinkedHashSet(1);
|
|
s.add(node); // A node is equivalent to itself
|
|
equiv.put(node, s);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Makes node1 equivalent to node2 by adding the equivlance Set of node2 to
|
|
* the equivalance Set of node1, and vice versa.
|
|
*/
|
|
void makeEquiv(Node node1, Node node2) {
|
|
Set s1 = equivalent(node1);
|
|
Set s2 = equivalent(node2);
|
|
|
|
if (s1 != s2) {
|
|
s1.addAll(s2);
|
|
|
|
Iterator iter = s2.iterator();
|
|
|
|
while (iter.hasNext()) {
|
|
Node n = (Node) iter.next();
|
|
equiv.put(n, s1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the children (that is, the operands) of a given Node in the SSA
|
|
* Graph.
|
|
*/
|
|
public List children(Node node) {
|
|
final ArrayList c = new ArrayList();
|
|
|
|
if (node instanceof StoreExpr) {
|
|
StoreExpr store = (StoreExpr) node;
|
|
|
|
// Add the grand children of RHS. The RHS is equivalent to
|
|
// this node.
|
|
store.expr().visitChildren(new TreeVisitor() {
|
|
public void visitNode(Node node) {
|
|
c.add(node);
|
|
}
|
|
});
|
|
|
|
// The LHS is equivalent to this node if it is a VarExpr and not
|
|
// a child.
|
|
if (!(store.target() instanceof VarExpr)) {
|
|
c.add(store.target());
|
|
}
|
|
|
|
} else if (node instanceof PhiStmt) {
|
|
PhiStmt phi = (PhiStmt) node;
|
|
c.addAll(phi.operands());
|
|
|
|
} else {
|
|
node.visitChildren(new TreeVisitor() {
|
|
public void visitNode(Node node) {
|
|
c.add(node);
|
|
}
|
|
});
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* Returns the Sets of Nodes whose values are equivalent.
|
|
*/
|
|
public Collection equivalences() {
|
|
return equiv.values();
|
|
}
|
|
|
|
class Count {
|
|
int value = 0;
|
|
}
|
|
|
|
/**
|
|
* Calculates the strongly connected components (SCC) of the SSA graph. SSCs
|
|
* are represented by a List of <tt>Node</tt>s. The SCCs are then visited
|
|
* by the ComponentVistor.
|
|
*/
|
|
public void visitComponents(final ComponentVisitor visitor) {
|
|
// Number the nodes reverse post order (i.e. topological order).
|
|
|
|
final Count count = new Count();
|
|
|
|
final List postOrder = cfg.postOrder();
|
|
ListIterator iter = postOrder.listIterator(postOrder.size());
|
|
|
|
// Perform a depth-first ordering of the nodes in the CFG to give
|
|
// each node a unique identifier. This is accomplished by
|
|
// visiting the blocks in the CFG in post-order and numbering the
|
|
// Nodes in the block's expression Tree in depth-first order.
|
|
while (iter.hasPrevious()) {
|
|
Block block = (Block) iter.previous();
|
|
|
|
block.visit(new TreeVisitor() {
|
|
public void visitTree(Tree tree) {
|
|
tree.visitChildren(this);
|
|
}
|
|
|
|
public void visitNode(Node node) {
|
|
node.visitChildren(this);
|
|
node.setKey(count.value++);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Build the (strongly connected) components and call
|
|
// visitor.visitComponent for each.
|
|
cfg.visit(new TreeVisitor() {
|
|
ArrayList stack = new ArrayList();
|
|
|
|
BitSet onStack = new BitSet(count.value + 1);
|
|
|
|
int[] low = new int[count.value + 1];
|
|
|
|
int[] dfs = new int[count.value + 1];
|
|
|
|
int dfsNumber = 1;
|
|
|
|
Node parent; // Parent in the SSA graph
|
|
|
|
// Visit the blocks in the CFG in reverse postorder
|
|
public void visitFlowGraph(FlowGraph cfg) {
|
|
ListIterator e = postOrder.listIterator(postOrder.size());
|
|
|
|
while (e.hasPrevious()) {
|
|
Block block = (Block) e.previous();
|
|
block.visit(this);
|
|
}
|
|
}
|
|
|
|
public void visitTree(Tree tree) {
|
|
parent = null;
|
|
tree.visitChildren(this);
|
|
}
|
|
|
|
// This method is essentially Figure 4.6 in Taylor Simpson's PhD
|
|
// Thesis: www.cs.rice.edu/~lts. The implementation is a little
|
|
// funky, though, because someone wanted to use visitors.
|
|
// Grumble.
|
|
public void visitNode(Node node) {
|
|
int dfn = dfs[node.key()];
|
|
// System.out.println("visit " + node + " key=" + node.key() +
|
|
// " dfn=" + dfn);
|
|
|
|
if (dfn == 0) {
|
|
// The node in question has not yet been visited. Assign
|
|
// it
|
|
// the next dfNumber and add it to the stack. Mark all
|
|
// nodes that are equivalent to the node in question as
|
|
// being visited.
|
|
|
|
dfn = dfsNumber++;
|
|
low[dfn] = dfn;
|
|
|
|
stack.add(node);
|
|
onStack.set(dfn);
|
|
|
|
Iterator equiv = equivalent(node).iterator();
|
|
|
|
while (equiv.hasNext()) {
|
|
Node e = (Node) equiv.next();
|
|
dfs[e.key()] = dfn;
|
|
}
|
|
|
|
// Again examine each node, e, equivalent to the node in
|
|
// question. Then recursively visit the children of e in
|
|
// the SSA Graph.
|
|
Node grandParent = parent;
|
|
parent = node;
|
|
|
|
equiv = equivalent(node).iterator();
|
|
|
|
while (equiv.hasNext()) {
|
|
Node e = (Node) equiv.next();
|
|
|
|
Iterator children = children(e).iterator();
|
|
|
|
while (children.hasNext()) {
|
|
Node child = (Node) children.next();
|
|
child.visit(this);
|
|
}
|
|
}
|
|
|
|
parent = grandParent; // Restore true parent
|
|
|
|
// Now we finally get to the point where we can
|
|
// construct a
|
|
// strongly connected component. Pop all of the nodes
|
|
// off
|
|
// the stack until the node in question is reached.
|
|
if (low[dfn] == dfn) {
|
|
ArrayList scc = new ArrayList();
|
|
|
|
while (!stack.isEmpty()) {
|
|
Node v = (Node) stack.remove(stack.size() - 1);
|
|
onStack.clear(dfs[v.key()]);
|
|
scc.addAll(equivalent(v));
|
|
|
|
if (v == node) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Sort the nodes in the SCC by their reverse
|
|
// post order numbers.
|
|
Collections.sort(scc, new Comparator() {
|
|
public int compare(Object a, Object b) {
|
|
int ka = ((Node) a).key();
|
|
int kb = ((Node) b).key();
|
|
return ka - kb;
|
|
}
|
|
});
|
|
|
|
if (DEBUG) {
|
|
System.out.print("SCC =");
|
|
|
|
Iterator e = scc.iterator();
|
|
|
|
while (e.hasNext()) {
|
|
Node v = (Node) e.next();
|
|
System.out.print(" " + v + "{" + v.key() + "}");
|
|
}
|
|
|
|
System.out.println();
|
|
}
|
|
|
|
// Visit the SCC with the visitor that was passed in.
|
|
visitor.visitComponent(scc);
|
|
}
|
|
|
|
if (parent != null) {
|
|
int parentDfn = dfs[parent.key()];
|
|
low[parentDfn] = Math.min(low[parentDfn], low[dfn]);
|
|
}
|
|
|
|
} else {
|
|
// We've already visited the node in question
|
|
if (parent != null) {
|
|
int parentDfn = dfs[parent.key()];
|
|
|
|
// (parent, node) is either a cross edge or a back edge.
|
|
if (dfn < parentDfn && onStack.get(dfn)) {
|
|
low[parentDfn] = Math.min(low[parentDfn], dfn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Visits the strongly connected component that contains a given
|
|
* <tt>Node</tt>.
|
|
*/
|
|
public void visitComponent(final Node startNode,
|
|
final ComponentVisitor visitor) {
|
|
// Number the nodes reverse post order (i.e. topological order).
|
|
|
|
final Count count = new Count();
|
|
|
|
final List postOrder = cfg.postOrder();
|
|
ListIterator iter = postOrder.listIterator(postOrder.size());
|
|
|
|
// Perform a depth-first ordering of the nodes in the CFG to give
|
|
// each node a unique identifier. This is accomplished by
|
|
// visiting the blocks in the CFG in post-order and numbering the
|
|
// Nodes in the block's expression Tree in depth-first order.
|
|
while (iter.hasPrevious()) {
|
|
Block block = (Block) iter.previous();
|
|
|
|
block.visit(new TreeVisitor() {
|
|
public void visitTree(Tree tree) {
|
|
tree.visitChildren(this);
|
|
}
|
|
|
|
public void visitNode(Node node) {
|
|
node.visitChildren(this);
|
|
node.setKey(count.value++);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Build the (strongly connected) components and call
|
|
// visitor.visitComponent for each.
|
|
cfg.visit(new TreeVisitor() {
|
|
ArrayList stack = new ArrayList();
|
|
|
|
BitSet onStack = new BitSet(count.value + 1);
|
|
|
|
int[] low = new int[count.value + 1];
|
|
|
|
int[] dfs = new int[count.value + 1];
|
|
|
|
int dfsNumber = 1;
|
|
|
|
Node parent; // Parent in the SSA graph
|
|
|
|
// Visit the blocks in the CFG in reverse postorder
|
|
public void visitFlowGraph(FlowGraph cfg) {
|
|
ListIterator e = postOrder.listIterator(postOrder.size());
|
|
|
|
while (e.hasPrevious()) {
|
|
Block block = (Block) e.previous();
|
|
block.visit(this);
|
|
}
|
|
}
|
|
|
|
public void visitTree(Tree tree) {
|
|
parent = null;
|
|
tree.visitChildren(this);
|
|
}
|
|
|
|
// This method is essentially Figure 4.6 in Taylor Simpson's PhD
|
|
// Thesis: www.cs.rice.edu/~lts. The implementation is a little
|
|
// funky, though, because someone wanted to use visitors.
|
|
// Grumble.
|
|
public void visitNode(Node node) {
|
|
int dfn = dfs[node.key()];
|
|
// System.out.println("visit " + node + " key=" + node.key() +
|
|
// " dfn=" + dfn);
|
|
|
|
if (dfn == 0) {
|
|
// If this node isn't equivalent to the node the care
|
|
// about,
|
|
// fergit it!
|
|
if (!equivalent(node).contains(startNode))
|
|
return;
|
|
|
|
// The node in question has not yet been visited. Assign
|
|
// it
|
|
// the next dfNumber and add it to the stack. Mark all
|
|
// nodes that are equivalent to the node in question as
|
|
// being visited.
|
|
|
|
dfn = dfsNumber++;
|
|
low[dfn] = dfn;
|
|
|
|
stack.add(node);
|
|
onStack.set(dfn);
|
|
|
|
Iterator equiv = equivalent(node).iterator();
|
|
|
|
while (equiv.hasNext()) {
|
|
Node e = (Node) equiv.next();
|
|
dfs[e.key()] = dfn;
|
|
}
|
|
|
|
// Again examine each node, e, equivalent to the node in
|
|
// question. Then recursively visit the children of e in
|
|
// the SSA Graph.
|
|
Node grandParent = parent;
|
|
parent = node;
|
|
|
|
equiv = equivalent(node).iterator();
|
|
|
|
while (equiv.hasNext()) {
|
|
Node e = (Node) equiv.next();
|
|
|
|
Iterator children = children(e).iterator();
|
|
|
|
while (children.hasNext()) {
|
|
Node child = (Node) children.next();
|
|
child.visit(this);
|
|
}
|
|
}
|
|
|
|
parent = grandParent; // Restore true parent
|
|
|
|
// Now we finally get to the point where we can
|
|
// construct a
|
|
// strongly connected component. Pop all of the nodes
|
|
// off
|
|
// the stack until the node in question is reached.
|
|
if (low[dfn] == dfn) {
|
|
ArrayList scc = new ArrayList();
|
|
|
|
while (!stack.isEmpty()) {
|
|
Node v = (Node) stack.remove(stack.size() - 1);
|
|
onStack.clear(dfs[v.key()]);
|
|
scc.addAll(equivalent(v));
|
|
|
|
if (v == node) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Sort the nodes in the SCC by their reverse
|
|
// post order numbers.
|
|
Collections.sort(scc, new Comparator() {
|
|
public int compare(Object a, Object b) {
|
|
int ka = ((Node) a).key();
|
|
int kb = ((Node) b).key();
|
|
return ka - kb;
|
|
}
|
|
});
|
|
|
|
if (DEBUG) {
|
|
System.out.print("SCC =");
|
|
|
|
Iterator e = scc.iterator();
|
|
|
|
while (e.hasNext()) {
|
|
Node v = (Node) e.next();
|
|
System.out.print(" " + v + "{" + v.key() + "}");
|
|
}
|
|
|
|
System.out.println();
|
|
}
|
|
|
|
// Visit the SCC with the visitor that was passed in.
|
|
visitor.visitComponent(scc);
|
|
}
|
|
|
|
if (parent != null) {
|
|
int parentDfn = dfs[parent.key()];
|
|
low[parentDfn] = Math.min(low[parentDfn], low[dfn]);
|
|
}
|
|
|
|
} else {
|
|
// We've already visited the node in question
|
|
if (parent != null) {
|
|
int parentDfn = dfs[parent.key()];
|
|
|
|
// (parent, node) is either a cross edge or a back edge.
|
|
if (dfn < parentDfn && onStack.get(dfn)) {
|
|
low[parentDfn] = Math.min(low[parentDfn], dfn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Prints a textual representation of the strongly connected components of
|
|
* the SSAGraph to a PrintWriter.
|
|
*/
|
|
public void printSCCs(PrintWriter pw) {
|
|
Collection equivs = this.equivalences(); // A Collection of Sets
|
|
Iterator iter = equivs.iterator();
|
|
|
|
pw.println("Strongly Connected Components of the SSAGraph");
|
|
|
|
for (int i = 1; iter.hasNext(); i++) {
|
|
Set scc = (Set) iter.next();
|
|
Iterator sccIter = scc.iterator();
|
|
|
|
pw.println(" Component " + i);
|
|
|
|
while (sccIter.hasNext()) {
|
|
Node node = (Node) sccIter.next();
|
|
pw.println(" " + node + " [VN = " + node.valueNumber()
|
|
+ ", ID = " + System.identityHashCode(node) + "]");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|