/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import java.util.Enumeration;
import java.util.HashSet;
/**
* OPT_SpaceEffGraph package implements a generic directed graph that can
* be a multigraph. It uses Lists to model nodes and edges information.
*
* OPT_SpaceEffGraph is a generic graph. Extend to implement specific
* graph types.
*/
public class OPT_SpaceEffGraph implements OPT_Graph, OPT_VCGGraph, OPT_TopSortInterface {
/**
* First node
*/
protected OPT_SpaceEffGraphNode _firstNode;
/**
* Last node
*/
protected OPT_SpaceEffGraphNode _lastNode;
/**
* Nodes with no predecessors
*/
private OPT_SpaceEffGraphNodeListHeader _rootNodes;
/**
* Topological sort order
*/
private OPT_SpaceEffGraphNodeListHeader _topSortNodes; // top sort order.
/**
* Number of nodes
*/
protected int numberOfNodes;
/**
* Get number of nodes
* @return number of nodes
*/
public final int numberOfNodes() { return numberOfNodes; }
/**
* Set number of nodes
* @param n new number of nodes
*/
public final void setNumberOfNodes(int n) { numberOfNodes = n; }
/**
* Get the next node number
* @return the node number
*/
public final int allocateNodeNumber() { return numberOfNodes++; }
/**
* Renumber the nodes densely from 0...numberOfNodes-1.
*/
public void compactNodeNumbering() {
int number = 0;
for (OPT_SpaceEffGraphNode n = _firstNode; n != null; n = n.getNext()) {
n.setNumber(number++);
}
numberOfNodes = number;
}
/**
* Enumerate the nodes in no particular order
*/
public OPT_GraphNodeEnumeration enumerateNodes() {
return new NodeEnumeration(_firstNode);
}
//////////////////
// The following are to implement OPT_TopSortInterface.
//////////////////
public OPT_SortedGraphNode startNode(boolean forward) {
if (forward) {
return (OPT_SortedGraphNode) _firstNode;
} else {
return (OPT_SortedGraphNode) _lastNode;
}
}
public boolean isTopSorted(boolean forward) {
if (forward) {
return forwardTopSorted;
} else {
return backwardTopSorted;
}
}
public void setTopSorted(boolean forward) {
if (forward) {
forwardTopSorted = true;
} else {
backwardTopSorted = true;
}
}
public void resetTopSorted() {
forwardTopSorted = false;
backwardTopSorted = false;
}
public boolean forwardTopSorted = false, backwardTopSorted = false;
//////////////////
// End of OPT_TopSortInterface implementation
//////////////////
/**
* Add a node to the graph.
* @param inode node to add
*/
public final void addGraphNode(OPT_GraphNode inode) {
OPT_SpaceEffGraphNode node = (OPT_SpaceEffGraphNode) inode;
//_nodes.add(node);
if (_firstNode == null) {
_firstNode = node;
_lastNode = node;
} else {
_lastNode.append(node); // this is cheaper than add() call.
_lastNode = node;
}
numberOfNodes++;
}
/**
* Remove a node from the graph.
* @param node node to remove
*/
public final void removeGraphNode(OPT_SpaceEffGraphNode node) {
if (node == _firstNode) {
if (node == _lastNode) {
_firstNode = _lastNode = null;
} else {
_firstNode = node.getNext();
}
} else if (node == _lastNode) {
_lastNode = node.getPrev();
}
node.remove();
numberOfNodes--;
}
/**
* Add an edge to the graph.
* @param from start node
* @param to end node
* @see #addGraphEdge(OPT_SpaceEffGraphEdge)
*/
public void addGraphEdge(OPT_GraphNode from, OPT_GraphNode to) {
((OPT_SpaceEffGraphNode) from).insertOut((OPT_SpaceEffGraphNode) to);
}
/**
* Add an edge to the graph.
* @param e edge to insert
* @see #addGraphEdge(OPT_GraphNode,OPT_GraphNode)
*/
void addGraphEdge(OPT_SpaceEffGraphEdge e) {
e.fromNode().appendOutEdge(e);
e.toNode().appendInEdge(e);
}
/**
* Reset the list of nodes of the graph.
* WARNING!!! Use with caution if you know what you are doing.
* @param firstNode new value of the node list
*/
final void setFirstNode(OPT_SpaceEffGraphNode firstNode) {
_firstNode = firstNode;
}
/**
* Get the list of nodes.
* @return list of nodes
*/
public final OPT_SpaceEffGraphNode firstNode() {
return _firstNode;
}
/**
* Get the end of the list of nodes.
* @return end of the list of nodes
*/
public final OPT_SpaceEffGraphNode lastNode() {
return _lastNode;
}
/**
* Add a root node to the graph.
* @param root a node to add
*/
public final void addRootNode(OPT_SpaceEffGraphNode root) {
//_rootNodes.add(root);
if (_rootNodes == null) {
_rootNodes = new OPT_SpaceEffGraphNodeListHeader();
}
_rootNodes.append(root);
}
/**
* Get the list of root nodes.
* @return list of root nodes
*/
public final OPT_SpaceEffGraphNodeList rootNodes() {
return _rootNodes.first();
}
/**
* Get the topological order of nodes.
* @return topological order of nodes
*/
public final OPT_SpaceEffGraphNodeList topSortOrder() {
return _topSortNodes.first();
}
/**
* Clear the DFS flags.
*/
public final void clearDFS() {
for (OPT_SpaceEffGraphNode n = firstNode(); n != null; n = n.getNext()) {
n.clearDfsVisited();
}
}
/**
* Build a topological sort of this graph
*/
public void buildTopSort() {
if (!forwardTopSorted) {
//OPT_SortedGraphNode node =
OPT_TopSort.buildTopological(this, true);
// currently, no one cares about the return value, so we don't return it
}
}
/**
* Build a reverse topological sort of this graph
* @return a node if we build a new order, null if we reused the old
*/
public OPT_SortedGraphNode buildRevTopSort() {
if (!backwardTopSorted) {
return OPT_TopSort.buildTopological(this, false);
} else {
return null;
}
}
///////////////////////
// Starting with the root nodes, topologically sort them using
// the out edge information. Builds the _topSortNodes list.
// TODO: figure out how this works and add comments (IP)
///////////////////////
protected void initTopSort() {
_topSortNodes = new OPT_SpaceEffGraphNodeListHeader();
}
protected void addTopSortNode(OPT_SpaceEffGraphNode node) {
_topSortNodes.append(node);
}
public void topSort() {
initTopSort();
for (OPT_SpaceEffGraphNode n = firstNode(); n != null; n = n.getNext()) {
if (n.firstInEdge() == null) { // no predecessors
n.setDfsVisited();
n.setOnStack();
dfs(n);
addTopSortNode(n);
}
}
}
private void dfs(OPT_SpaceEffGraphNode node) {
for (OPT_SpaceEffGraphEdge edge = node.firstOutEdge(); edge != null; edge = edge.getNextOut()) {
OPT_SpaceEffGraphNode succ = edge.toNode();
if (!succ.dfsVisited()) {
succ.setDfsVisited();
succ.setOnStack();
dfs(succ);
} else if (succ.onStack() || succ == node) {
edge.setBackEdge();
}
}
node.clearOnStack();
for (OPT_SpaceEffGraphEdge edge = node.firstOutEdge(); edge != null; edge = edge.getNextOut()) {
OPT_SpaceEffGraphNode succ = edge.toNode();
if (!succ.topVisited() && !edge.backEdge()) {
addTopSortNode(succ);
succ.setTopVisited();
}
}
}
/**
* Return a string representation of this graph.
* @return a string representation of the graph
*/
public String toString() {
StringBuilder res = new StringBuilder();
for (OPT_SpaceEffGraphNode n = firstNode(); n != null; n = n.getNext()) {
HashSet<OPT_SpaceEffGraphEdge> visitedNodes = new HashSet<OPT_SpaceEffGraphEdge>();
int duplicatedNodes = 0;
res.append("\nNode: ").append(n).append("\n");
res.append("In nodes:\n");
for (OPT_SpaceEffGraphEdge inEdge = n.firstInEdge(); inEdge != null; inEdge = inEdge.getNextIn()) {
if (visitedNodes.contains(inEdge)) {
duplicatedNodes ++;
res.append("(Duplicated edge " + inEdge.toNodeString() + ")");
if (duplicatedNodes > 5) {
break;
}
} else {
visitedNodes.add(inEdge);
res.append(inEdge.getTypeString());
res.append(" ");
res.append(inEdge.fromNode());
res.append("\n");
}
}
res.append("\n");
visitedNodes.clear();
duplicatedNodes=0;
res.append("Out nodes:\n");
for (OPT_SpaceEffGraphEdge out = n.firstOutEdge(); out != null; out = out.getNextOut()) {
if (visitedNodes.contains(out)) {
duplicatedNodes ++;
res.append("(Duplicated edge " + out.toNodeString() + ")");
if (duplicatedNodes > 5) {
break;
}
} else {
res.append(out.getTypeString());
res.append(" ");
res.append(out.toNode());
res.append("\n");
}
}
if (res.length() > 50000) {
res.append("....(giving up too long)\n");
break;
}
}
return res.toString();
}
////////////////////
// Return a breadth-first enumeration of the nodes in this CFG.
// Note that this is different than topological ordering.
// TODO: figure out how this works and add comments (IP)
////////////////////
int markNumber;
final int getNewMark() {
return ++markNumber;
}
/**
* Print, to System.out, the basic blocks in depth first order.
*/
public void printDepthFirst() {
markNumber = getNewMark();
print(new OPT_DepthFirstEnumerator(_firstNode, markNumber));
}
/**
* Print, to System.out, the basic blocks in the order given in
* the supplied enumeration.
* @param e enumeration order to print blocks
*/
private void print(Enumeration<OPT_GraphNode> e) {
while (e.hasMoreElements()) {
OPT_SpaceEffGraphNode bb = (OPT_SpaceEffGraphNode) e.nextElement();
bb.printExtended();
}
}
private static final class NodeEnumeration implements OPT_GraphNodeEnumeration {
private OPT_SpaceEffGraphNode _node;
public NodeEnumeration(OPT_SpaceEffGraphNode n) { _node = n; }
public boolean hasMoreElements() { return _node != null; }
public OPT_GraphNode nextElement() { return next(); }
public OPT_GraphNode next() {
OPT_SpaceEffGraphNode n = _node;
_node = n.getNext();
return n;
}
}
protected static final class VCGNodeEnumeration implements Enumeration<OPT_VCGNode> {
private OPT_SpaceEffGraphNode _node;
public VCGNodeEnumeration(OPT_SpaceEffGraphNode n) { _node = n; }
public boolean hasMoreElements() { return _node != null; }
public OPT_VCGNode nextElement() {
OPT_SpaceEffGraphNode n = _node;
_node = n.getNext();
return n;
}
}
/**
* Returns the nodes of the graph.
* @return the enumeration that would list the nodes of the graph
* @see OPT_VCGGraph#nodes
*/
public Enumeration<OPT_VCGNode> nodes() {
return new VCGNodeEnumeration(firstNode());
}
/**
* Returns a VCG descriptor for the graph which will provide VCG-relevant
* information for the graph.
* @return graph descriptor
* @see OPT_VCGGraph#getVCGDescriptor
*/
public OPT_VCGGraph.GraphDesc getVCGDescriptor() { return defaultVCGDesc; }
}