/*
* 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;
/**
* OPT_SpaceEffGraphNode is a generic graph node. Extend this to implement
* specific graph node types. A node has a list of out edges and a
* list of in edges. We maintain both to support bidirectional traversal
* of the graph.
*/
public class OPT_SpaceEffGraphNode implements OPT_GraphNode, OPT_VCGNode {
/** scratch field: optimizations use as they wish */
public Object scratchObject;
/** any optimization can use this for its own purposes */
public int scratch;
/**
* The following word is used for various purposes. The first
* 8 bits are used for flags, and the remaining 24 bits for any
* node information (node number, for example)
*/
protected int info;
static final int DFS_VISITED = 0x01000000;
static final int TOP_VISITED = 0x02000000;
static final int ON_STACK = 0x04000000;
static final int LOOP_HEADER = 0x08000000;
static final int INFO_MASK = 0x00ffffff;
public final boolean dfsVisited() { return (info & DFS_VISITED) != 0; }
public final boolean topVisited() { return (info & TOP_VISITED) != 0; }
public final boolean onStack() { return (info & ON_STACK) != 0; }
public final boolean flagsOn() { return (info & (DFS_VISITED | TOP_VISITED | ON_STACK)) != 0; }
public final boolean isLoopHeader() { return (info & LOOP_HEADER) != 0; }
public final void setDfsVisited() { info |= DFS_VISITED; }
public final void setTopVisited() { info |= TOP_VISITED; }
public final void setOnStack() { info |= ON_STACK; }
public final void setDfsVisitedOnStack() { info |= (DFS_VISITED | ON_STACK); }
public final void setLoopHeader() { info |= LOOP_HEADER; }
public final void clearDfsVisited() { info &= ~DFS_VISITED; }
public final void clearTopVisited() { info &= ~TOP_VISITED; }
public final void clearOnStack() { info &= ~ON_STACK; }
public final void clearFlags() { info &= ~(DFS_VISITED | TOP_VISITED | ON_STACK); }
public final void clearLoopHeader() { info &= ~LOOP_HEADER; }
public int getScratch() { return scratch; }
public int setScratch(int scratch) { return this.scratch = scratch; }
public final void setNumber(int value) {
info = (info & ~INFO_MASK) | (value & INFO_MASK);
}
public final int getNumber() {
return info & INFO_MASK;
}
public final int getIndex() {
return getNumber();
}
public final void setIndex(int i) {
setNumber(i);
}
/////////////////
// The following is used by several node sorting schemes
/////////////////
public OPT_SpaceEffGraphNode nextSorted;
// return the first in/out edge
public final OPT_SpaceEffGraphEdge firstInEdge() {
return _inEdgeStart;
}
public final OPT_SpaceEffGraphEdge firstOutEdge() {
return _outEdgeStart;
}
public final OPT_SpaceEffGraphNode firstInNode() {
return _inEdgeStart.fromNode();
}
public final OPT_SpaceEffGraphNode firstOutNode() {
return _outEdgeStart.toNode();
}
/**
* clear the in set of edges
*/
final void clearIn() {
_inEdgeStart = _inEdgeEnd = null;
}
/**
* clear the out set of edges
*/
final void clearOut() {
_outEdgeStart = _outEdgeEnd = null;
}
// deletes all the in/out edges
public final void deleteIn() {
for (OPT_SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
e.fromNode().removeOut(e);
}
clearIn();
}
public final void deleteOut() {
for (OPT_SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
e.toNode().removeIn(e);
}
clearOut();
}
/* get number of in/out edges */
public final int getNumberOfIn() {
int count = 0;
for (OPT_SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
count++;
}
return count;
}
public final int getNumberOfOut() {
int count = 0;
for (OPT_SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
count++;
}
return count;
}
/* specialized versions */
public final boolean hasZeroOut() {
return _outEdgeStart == null;
}
public final boolean hasZeroIn() {
return _inEdgeStart == null;
}
public final boolean hasOneOut() {
OPT_SpaceEffGraphEdge first = _outEdgeStart;
return (first != null) && (first.nextOut == null);
}
public final boolean hasOneIn() {
OPT_SpaceEffGraphEdge first = _inEdgeStart;
return (first != null) && (first.nextIn == null);
}
/* returns true if points to the in/out set */
public final boolean pointsIn(OPT_SpaceEffGraphNode inNode) {
for (OPT_SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
if (e.fromNode() == inNode) return true;
}
return false;
}
public final boolean pointsOut(OPT_SpaceEffGraphNode outNode) {
for (OPT_SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
if (e.toNode() == outNode) return true;
}
return false;
}
public final boolean hasIn(OPT_GraphNode in) {
return pointsIn((OPT_SpaceEffGraphNode) in);
}
public final boolean hasOut(OPT_GraphNode out) {
return pointsOut((OPT_SpaceEffGraphNode) out);
}
/*
* returns the out edge pointing to node n, if it exists.
* returns null otherwise
*/
public final OPT_SpaceEffGraphEdge findOutEdgeTo(OPT_SpaceEffGraphNode n) {
for (OPT_SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
if (e.toNode() == n) return e;
}
return null;
}
/*
* replaces the in edge matching e1 with e2.
* maintains the ordering of edges
* YUCK: this data structure is messy. I assume this is in the name
* of efficiency, but it makes control flow graph manipulations
* a real pain. (SJF)
*/
public final void replaceInEdge(OPT_SpaceEffGraphEdge e1, OPT_SpaceEffGraphEdge e2) {
// set the predecessor of e1 to point to e2
if (_inEdgeStart == e1) {
_inEdgeStart = e2;
} else {
// walk the list until we find the predecessor to e1
OPT_SpaceEffGraphEdge pred = null;
for (pred = _inEdgeStart; pred != null; pred = pred.nextIn) {
if (pred.nextIn == e1) break;
}
// if not found, there's an error
if (pred == null) {
throw new OPT_OptimizingCompilerException("OPT_SpaceEffGraphNode.replaceInEdge: called incorrectly");
}
pred.nextIn = e2;
}
// set e2 to point to e1.nextIn
e2.nextIn = e1.nextIn;
// fix up _inEdgeStart, _inEdgeEnd
if (_inEdgeStart == e1) _inEdgeStart = e2;
if (_inEdgeEnd == e1) _inEdgeEnd = e2;
// clear the links of e1
e1.nextIn = null;
}
/* returns true if the node is the single predecessor/successor of
this block */
public final boolean hasOneIn(OPT_SpaceEffGraphNode inNode) {
OPT_SpaceEffGraphEdge first = _inEdgeStart;
return (first != null) && (first.nextIn == null) && (first.fromNode() == inNode);
}
public final boolean hasOneOut(OPT_SpaceEffGraphNode outNode) {
OPT_SpaceEffGraphEdge first = _outEdgeStart;
return (first != null) && (first.nextOut == null) && (first.toNode() == outNode);
}
/* replaces an oldnode with a new node */
public final void replaceOut(OPT_SpaceEffGraphNode oldOut, OPT_SpaceEffGraphNode newOut) {
deleteOut(oldOut);
insertOut(newOut);
}
/* inserts an outgoing edge to a node 'to' */
public final void insertOut(OPT_SpaceEffGraphNode to, OPT_SpaceEffGraphEdge e) {
this.appendOutEdge(e);
to.appendInEdge(e);
}
/* same as before, if you don't care the edge type */
public final void insertOut(OPT_SpaceEffGraphNode to) {
if (this.pointsOut(to)) return;
OPT_SpaceEffGraphEdge e = new OPT_SpaceEffGraphEdge(this, to);
this.appendOutEdge(e);
to.appendInEdge(e);
}
/* delete an outgoing edge to a node */
public final void deleteOut(OPT_SpaceEffGraphNode node) {
OPT_SpaceEffGraphEdge edge = this.removeOut(node);
node.removeIn(edge);
}
/* delete an outgoing edge */
public final void deleteOut(OPT_SpaceEffGraphEdge e) {
OPT_SpaceEffGraphNode to = e.toNode();
this.removeOut(e);
to.removeIn(e);
}
/* mark nodes in a DFS manner, result written in 'scratch' */
/* NOTE: it assummes that the 'dfs' flag has been cleared before */
public final void markDFN(int DFN) {
setDfsVisited();
for (OPT_SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
OPT_SpaceEffGraphNode n = e.toNode();
if (!n.dfsVisited()) {
n.markDFN(DFN);
}
}
scratch = DFN - 1;
}
/* mark nodes according to the SCC (Strongly Connected Component Number),
result written in 'scratch'
NOTE: it assummes that the 'dfs' flag has been cleared before */
public final void markSCC(int currSCC) {
setDfsVisited();
scratch = currSCC;
for (OPT_SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
OPT_SpaceEffGraphNode n = e.fromNode();
if (!n.dfsVisited()) {
n.markSCC(currSCC);
}
}
}
/* sort nodes according to DFS. result is a list of nodes with the current
as root. Note: it assumes that the dfs flags have been cleared before */
public final void sortDFS() {
_sortDFS(null);
}
protected final void _sortDFS(OPT_SpaceEffGraphNode header) {
setDfsVisited();
for (OPT_SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
OPT_SpaceEffGraphNode n = e.toNode();
if (!n.dfsVisited()) {
n._sortDFS(header);
header = n;
}
}
nextSorted = header;
}
/* clear all out/in flags */
public final void clearOutFlags() {
clearFlags();
for (OPT_SpaceEffGraphEdge e = firstOutEdge(); e != null; e = e.getNextOut()) {
OPT_SpaceEffGraphNode succ = e.toNode();
e.clearVisited();
if (succ.flagsOn()) {
succ.clearOutFlags();
}
}
}
public final void clearInFlags() {
clearFlags();
for (OPT_SpaceEffGraphEdge e = firstInEdge(); e != null; e = e.getNextIn()) {
OPT_SpaceEffGraphNode succ = e.fromNode();
e.clearVisited();
if (succ.flagsOn()) {
succ.clearInFlags();
}
}
}
/* topological sort of nodes. result is a list of nodes with the current
as root */
public final void sortTop() {
clearOutFlags();
setDfsVisitedOnStack();
nextSorted = _sortTop(null);
}
protected final OPT_SpaceEffGraphNode _sortTop(OPT_SpaceEffGraphNode tail) {
for (OPT_SpaceEffGraphEdge e = firstOutEdge(); e != null; e = e.getNextOut()) {
OPT_SpaceEffGraphNode succ = e.toNode();
if (!succ.dfsVisited()) {
succ.setDfsVisitedOnStack();
tail = succ._sortTop(tail);
} else if (succ.onStack() || succ == this) {
e.setVisited(); // back edge
}
}
clearOnStack();
for (OPT_SpaceEffGraphEdge e = firstOutEdge(); e != null; e = e.getNextOut()) {
OPT_SpaceEffGraphNode succ = e.toNode();
if (!succ.topVisited() && !e.visited()) {
succ.nextSorted = tail;
tail = succ;
succ.setTopVisited();
}
}
return tail;
}
/* reverse topological sort of nodes. result is a list of nodes with the
current as root */
public final void sortRevTop() {
clearInFlags();
setDfsVisitedOnStack();
nextSorted = _sortRevTop(null);
}
protected final OPT_SpaceEffGraphNode _sortRevTop(OPT_SpaceEffGraphNode tail) {
for (OPT_SpaceEffGraphEdge e = firstInEdge(); e != null; e = e.getNextIn()) {
OPT_SpaceEffGraphNode succ = e.fromNode();
if (!succ.dfsVisited()) {
succ.setDfsVisitedOnStack();
tail = succ._sortRevTop(tail);
} else if (succ.onStack() || succ == this) {
e.setVisited(); // forward edge
}
}
clearOnStack();
for (OPT_SpaceEffGraphEdge e = firstInEdge(); e != null; e = e.getNextIn()) {
OPT_SpaceEffGraphNode succ = e.fromNode();
if (!succ.topVisited() && !e.visited()) {
succ.nextSorted = tail;
tail = succ;
succ.setTopVisited();
}
}
return tail;
}
/* print sorted nodes starting from this */
final void printSorted() {
for (OPT_SpaceEffGraphNode n = this; n != null; n = n.nextSorted) {
System.out.println(n);
}
}
/**
* Revert the sequence of out edges
*/
final void revertOuts() {
OPT_SpaceEffGraphEdge last = null;
OPT_SpaceEffGraphEdge e = firstOutEdge();
_outEdgeStart = _outEdgeEnd;
_outEdgeEnd = e;
while (e != null) {
OPT_SpaceEffGraphEdge next = e.getNextOut();
e.appendOut(last);
last = e;
e = next;
}
}
/* enumerations to get the nodes/edges */
public interface GraphEdgeEnumeration<T extends OPT_GraphEdge> extends Enumeration<T> {
// Same as nextElement but avoid the need to downcast from Object
T next();
}
public final InEdgeEnumeration inEdges() {
return new InEdgeEnumeration(this);
}
public final OutEdgeEnumeration outEdges() {
return new OutEdgeEnumeration(this);
}
public final OPT_GraphNodeEnumeration inNodes() {
return new InNodeEnumeration(this);
}
public final OPT_GraphNodeEnumeration outNodes() {
return new OutNodeEnumeration(this);
}
/* print utilities */
public void printInEdges() {
for (OPT_SpaceEffGraphEdge in = firstInEdge(); in != null; in = in.getNextIn()) {
System.out.println(in.fromNodeString());
}
}
public void printOutEdges() {
for (OPT_SpaceEffGraphEdge out = firstOutEdge(); out != null; out = out.getNextOut()) {
System.out.println(out.toNodeString());
}
}
public void printInNodes() {
for (OPT_SpaceEffGraphEdge in = firstInEdge(); in != null; in = in.getNextIn()) {
System.out.println(in.fromNode());
}
}
public void printOutNodes() {
for (OPT_SpaceEffGraphEdge out = firstOutEdge(); out != null; out = out.getNextOut()) {
System.out.println(out.toNode());
}
}
public void printExtended() {
System.out.println(this);
}
/////////////////
// Implementation: the following is not intended for general client use
/////////////////
protected OPT_SpaceEffGraphEdge _outEdgeStart;
protected OPT_SpaceEffGraphEdge _outEdgeEnd;
protected OPT_SpaceEffGraphEdge _inEdgeStart;
protected OPT_SpaceEffGraphEdge _inEdgeEnd;
//
// add an in/out edge from 'node' to this node.
//
// (SJF): I had to make these public to do SSA transformations.
// TODO: The CFG data structure should not depend this tightly
// on the underlying Graph implementation, but rather should be
// designed so that the SSA-like transformations are easy to do.
final void appendInEdge(OPT_SpaceEffGraphEdge e) {
OPT_SpaceEffGraphEdge inEdgeEnd = _inEdgeEnd;
if (inEdgeEnd != null) {
inEdgeEnd.appendIn(e);
} else {
_inEdgeStart = e;
}
_inEdgeEnd = e;
}
final void appendOutEdge(OPT_SpaceEffGraphEdge e) {
OPT_SpaceEffGraphEdge outEdgeEnd = _outEdgeEnd;
if (outEdgeEnd != null) {
outEdgeEnd.appendOut(e);
} else {
_outEdgeStart = e;
}
_outEdgeEnd = e;
}
/* remove and edge/node from the in/out set */
protected final void removeIn(OPT_SpaceEffGraphEdge InEdge) {
OPT_SpaceEffGraphEdge prev = null;
for (OPT_SpaceEffGraphEdge edge = _inEdgeStart; edge != null; prev = edge, edge = edge.nextIn) {
if (edge == InEdge) {
OPT_SpaceEffGraphEdge next = edge.nextIn;
if (prev == null) {
_inEdgeStart = next;
} else {
prev.appendIn(next);
}
if (next == null) {
_inEdgeEnd = prev;
}
break;
}
}
}
protected final OPT_SpaceEffGraphEdge removeIn(OPT_SpaceEffGraphNode InNode) {
OPT_SpaceEffGraphEdge edge, prev = null;
for (edge = _inEdgeStart; edge != null; prev = edge, edge = edge.nextIn) {
if (edge.fromNode() == InNode) {
OPT_SpaceEffGraphEdge next = edge.nextIn;
if (prev == null) {
_inEdgeStart = next;
} else {
prev.appendIn(next);
}
if (next == null) {
_inEdgeEnd = prev;
}
break;
}
}
return edge;
}
protected final void removeOut(OPT_SpaceEffGraphEdge OutEdge) {
OPT_SpaceEffGraphEdge edge, prev = null;
for (edge = _outEdgeStart; edge != null; prev = edge, edge = edge.nextOut) {
if (edge == OutEdge) {
OPT_SpaceEffGraphEdge next = edge.nextOut;
if (prev == null) {
_outEdgeStart = next;
} else {
prev.appendOut(next);
}
if (next == null) {
_outEdgeEnd = prev;
}
break;
}
}
}
protected final OPT_SpaceEffGraphEdge removeOut(OPT_SpaceEffGraphNode OutNode) {
OPT_SpaceEffGraphEdge edge, prev = null;
for (edge = _outEdgeStart; edge != null; prev = edge, edge = edge.nextOut) {
if (edge.toNode() == OutNode) {
OPT_SpaceEffGraphEdge next = edge.nextOut;
if (prev == null) {
_outEdgeStart = next;
} else {
prev.appendOut(next);
}
if (next == null) {
_outEdgeEnd = prev;
}
break;
}
}
return edge;
}
static final class InEdgeEnumeration implements GraphEdgeEnumeration<OPT_GraphEdge> {
private OPT_SpaceEffGraphEdge _edge;
public InEdgeEnumeration(OPT_SpaceEffGraphNode n) {
_edge = n._inEdgeStart;
}
public boolean hasMoreElements() { return _edge != null; }
public OPT_SpaceEffGraphEdge nextElement() { return next(); }
public OPT_SpaceEffGraphEdge next() {
OPT_SpaceEffGraphEdge e = _edge;
_edge = e.nextIn;
return e;
}
}
static final class InNodeEnumeration implements OPT_GraphNodeEnumeration {
private OPT_SpaceEffGraphEdge _edge;
public InNodeEnumeration(OPT_SpaceEffGraphNode n) {
_edge = n._inEdgeStart;
}
public boolean hasMoreElements() { return _edge != null; }
public OPT_GraphNode nextElement() { return next(); }
public OPT_GraphNode next() {
OPT_SpaceEffGraphEdge e = _edge;
_edge = e.nextIn;
return e.fromNode();
}
}
static final class OutEdgeEnumeration implements GraphEdgeEnumeration<OPT_GraphEdge> {
private OPT_SpaceEffGraphEdge _edge;
public OutEdgeEnumeration(OPT_SpaceEffGraphNode n) {
_edge = n._outEdgeStart;
}
public boolean hasMoreElements() { return _edge != null; }
public OPT_GraphEdge nextElement() { return next(); }
public OPT_GraphEdge next() {
OPT_SpaceEffGraphEdge e = _edge;
_edge = e.nextOut;
return e;
}
}
static final class OutNodeEnumeration implements OPT_GraphNodeEnumeration {
private OPT_SpaceEffGraphEdge _edge;
public OutNodeEnumeration(OPT_SpaceEffGraphNode n) {
_edge = n._outEdgeStart;
}
public boolean hasMoreElements() { return _edge != null; }
public OPT_GraphNode nextElement() { return next(); }
public OPT_GraphNode next() {
OPT_SpaceEffGraphEdge e = _edge;
_edge = e.nextOut;
return e.toNode();
}
}
/**
* Returns the out edges of the node.
* @return the enumeration that would list the out edges of the node
* @see OPT_VCGNode#edges
*/
public final Enumeration<OPT_VisEdge> edges() {
return new Enumeration<OPT_VisEdge>() {
OutEdgeEnumeration underlying = outEdges();
public boolean hasMoreElements() { return underlying.hasMoreElements(); }
public OPT_VisEdge nextElement() { return underlying.nextElement(); }
};
}
/**
* 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_VCGNode.NodeDesc getVCGDescriptor() {
final OPT_SpaceEffGraphNode node = this;
return new OPT_VCGNode.NodeDesc() {
public String getLabel() { return node.toString(); }
public String getShape() { return isLoopHeader() ? "triangle" : null; }
public int getBorderWidth() { return isLoopHeader() ? 2 : 1; }
};
}
/**
* Links inlined from DoublyLinkedListElement.
*/
public OPT_SpaceEffGraphNode prev, next;
/**
* Get the next node.
* @return next node
*/
public final OPT_SpaceEffGraphNode getNext() {
return next;
}
/**
* Get the previous node.
* @return previous node
*/
public final OPT_SpaceEffGraphNode getPrev() {
return prev;
}
/**
* Append a given node after this node.
* @param n the node to append
*/
public final void append(OPT_SpaceEffGraphNode n) {
next = n;
n.prev = this;
}
/**
* Remove this node from the list.
* @return the next node in the list
*/
public final OPT_SpaceEffGraphNode remove() {
// copy old links
OPT_SpaceEffGraphNode Prev = prev, Next = next;
// reset old links
prev = null;
next = null;
// compute new links
if (Prev != null) Prev.next = Next;
if (Next != null) Next.prev = Prev;
// return next node
return Next;
}
}