001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.compilers.opt.util;
014
015import java.util.Enumeration;
016import java.util.HashSet;
017
018
019/**
020 * SpaceEffGraph package implements a generic directed graph that can
021 * be a multigraph. It uses Lists to model nodes and edges information.<p>
022 *
023 * SpaceEffGraph is a generic graph.  Extend to implement specific
024 * graph types.
025 */
026public class SpaceEffGraph implements Graph, TopSortInterface {
027  /**
028   * First node
029   */
030  protected SpaceEffGraphNode _firstNode;
031  /**
032   * Last node
033   */
034  protected SpaceEffGraphNode _lastNode;
035  /**
036   * Nodes with no predecessors
037   */
038  private SpaceEffGraphNodeListHeader _rootNodes;
039  /**
040   * Topological sort order
041   */
042  private SpaceEffGraphNodeListHeader _topSortNodes; // top sort order.
043
044  /**
045   * Number of nodes
046   */
047  protected int numberOfNodes;
048
049  @Override
050  public final int numberOfNodes() {
051    return numberOfNodes;
052  }
053
054  /**
055   * Set number of nodes
056   * @param n new number of nodes
057   */
058  public final void setNumberOfNodes(int n) {
059    numberOfNodes = n;
060  }
061
062  /**
063   * Get the next node number
064   * @return the node number
065   */
066  public final int allocateNodeNumber() {
067    return numberOfNodes++;
068  }
069
070  /**
071   * Renumber the nodes densely from 0...numberOfNodes-1.
072   */
073  @Override
074  public void compactNodeNumbering() {
075    int number = 0;
076    for (SpaceEffGraphNode n = _firstNode; n != null; n = n.getNext()) {
077      n.setNumber(number++);
078    }
079    numberOfNodes = number;
080  }
081
082  /**
083   * Enumerate the nodes in no particular order
084   */
085  @Override
086  public Enumeration<GraphNode> enumerateNodes() {
087    return new NodeEnumeration(_firstNode);
088  }
089
090  //////////////////
091  // The following are to implement TopSortInterface.
092  //////////////////
093
094  @Override
095  public SortedGraphNode startNode(boolean forward) {
096    if (forward) {
097      return (SortedGraphNode) _firstNode;
098    } else {
099      return (SortedGraphNode) _lastNode;
100    }
101  }
102
103  @Override
104  public boolean isTopSorted(boolean forward) {
105    if (forward) {
106      return forwardTopSorted;
107    } else {
108      return backwardTopSorted;
109    }
110  }
111
112  @Override
113  public void setTopSorted(boolean forward) {
114    if (forward) {
115      forwardTopSorted = true;
116    } else {
117      backwardTopSorted = true;
118    }
119  }
120
121  @Override
122  public void resetTopSorted() {
123    forwardTopSorted = false;
124    backwardTopSorted = false;
125  }
126
127  public boolean forwardTopSorted = false, backwardTopSorted = false;
128
129  //////////////////
130  // End of TopSortInterface implementation
131  //////////////////
132
133  /**
134   * @param inode node to add
135   */
136  @Override
137  public final void addGraphNode(GraphNode inode) {
138    SpaceEffGraphNode node = (SpaceEffGraphNode) inode;
139    //_nodes.add(node);
140    if (_firstNode == null) {
141      _firstNode = node;
142      _lastNode = node;
143    } else {
144      _lastNode.append(node);  // this is cheaper than add() call.
145      _lastNode = node;
146    }
147    numberOfNodes++;
148  }
149
150  /**
151   * Remove a node from the graph.
152   * @param node node to remove
153   */
154  public final void removeGraphNode(SpaceEffGraphNode node) {
155    if (node == _firstNode) {
156      if (node == _lastNode) {
157        _firstNode = _lastNode = null;
158      } else {
159        _firstNode = node.getNext();
160      }
161    } else if (node == _lastNode) {
162      _lastNode = node.getPrev();
163    }
164    node.remove();
165    numberOfNodes--;
166  }
167
168  /**
169   * Add an edge to the graph.
170   * @param from start node
171   * @param to end node
172   * @see #addGraphEdge(SpaceEffGraphEdge)
173   */
174  @Override
175  public void addGraphEdge(GraphNode from, GraphNode to) {
176    ((SpaceEffGraphNode) from).insertOut((SpaceEffGraphNode) to);
177  }
178
179  /**
180   * Add an edge to the graph.
181   * @param e edge to insert
182   * @see #addGraphEdge(GraphNode,GraphNode)
183   */
184  public void addGraphEdge(SpaceEffGraphEdge e) {
185    e.fromNode().appendOutEdge(e);
186    e.toNode().appendInEdge(e);
187  }
188
189  /**
190   * Reset the list of nodes of the graph.
191   * WARNING!!!  Use with caution if you know what you are doing.
192   * @param firstNode new value of the node list
193   */
194  public final void setFirstNode(SpaceEffGraphNode firstNode) {
195    _firstNode = firstNode;
196  }
197
198  /**
199   * Reset the list of nodes of the graph.
200   * WARNING!!!  Use with caution if you know what you are doing.
201   * @param lastNode new value of the node list
202   */
203  public final void setLastNode(SpaceEffGraphNode lastNode) {
204    _lastNode = lastNode;
205  }
206  /**
207   * Get the list of nodes.
208   * @return list of nodes
209   */
210  public final SpaceEffGraphNode firstNode() {
211    return _firstNode;
212  }
213
214  /**
215   * Get the end of the list of nodes.
216   * @return end of the list of nodes
217   */
218  public final SpaceEffGraphNode lastNode() {
219    return _lastNode;
220  }
221
222  /**
223   * Add a root node to the graph.
224   * @param root a node to add
225   */
226  public final void addRootNode(SpaceEffGraphNode root) {
227    //_rootNodes.add(root);
228    if (_rootNodes == null) {
229      _rootNodes = new SpaceEffGraphNodeListHeader();
230    }
231    _rootNodes.append(root);
232  }
233
234  /**
235   * Get the list of root nodes.
236   * @return list of root nodes
237   */
238  public final SpaceEffGraphNodeList rootNodes() {
239    return _rootNodes.first();
240  }
241
242  /**
243   * Get the topological order of nodes.
244   * @return topological order of nodes
245   */
246  public final SpaceEffGraphNodeList topSortOrder() {
247    return _topSortNodes.first();
248  }
249
250  /**
251   * Clear the DFS flags.
252   */
253  public final void clearDFS() {
254    for (SpaceEffGraphNode n = firstNode(); n != null; n = n.getNext()) {
255      n.clearDfsVisited();
256    }
257  }
258
259  /**
260   * Build a topological sort of this graph
261   */
262  public void buildTopSort() {
263    if (!forwardTopSorted) {
264      //SortedGraphNode node =
265      TopSort.buildTopological(this, true);
266      // currently, no one cares about the return value, so we don't return it
267    }
268  }
269
270  /**
271   * Build a reverse topological sort of this graph
272   * @return a node if we build a new order, null if we reused the old
273   */
274  public SortedGraphNode buildRevTopSort() {
275    if (!backwardTopSorted) {
276      return TopSort.buildTopological(this, false);
277    } else {
278      return null;
279    }
280  }
281
282  ///////////////////////
283  // Starting with the root nodes, topologically sort them using
284  // the out edge information. Builds the _topSortNodes list.
285  // TODO: figure out how this works and add comments (IP)
286  ///////////////////////
287
288  protected void initTopSort() {
289    _topSortNodes = new SpaceEffGraphNodeListHeader();
290  }
291
292  protected void addTopSortNode(SpaceEffGraphNode node) {
293    _topSortNodes.append(node);
294  }
295
296  public void topSort() {
297    initTopSort();
298    for (SpaceEffGraphNode n = firstNode(); n != null; n = n.getNext()) {
299      if (n.firstInEdge() == null) { // no predecessors
300        n.setDfsVisited();
301        n.setOnStack();
302        dfs(n);
303        addTopSortNode(n);
304      }
305    }
306  }
307
308  private void dfs(SpaceEffGraphNode node) {
309    for (SpaceEffGraphEdge edge = node.firstOutEdge(); edge != null; edge = edge.getNextOut()) {
310      SpaceEffGraphNode succ = edge.toNode();
311      if (!succ.dfsVisited()) {
312        succ.setDfsVisited();
313        succ.setOnStack();
314        dfs(succ);
315      } else if (succ.onStack() || succ == node) {
316        edge.setBackEdge();
317      }
318    }
319    node.clearOnStack();
320    for (SpaceEffGraphEdge edge = node.firstOutEdge(); edge != null; edge = edge.getNextOut()) {
321      SpaceEffGraphNode succ = edge.toNode();
322      if (!succ.topVisited() && !edge.backEdge()) {
323        addTopSortNode(succ);
324        succ.setTopVisited();
325      }
326    }
327  }
328
329  /**
330   * Return a string representation of this graph.
331   * @return a string representation of the graph
332   */
333  @Override
334  public String toString() {
335    StringBuilder res = new StringBuilder();
336    for (SpaceEffGraphNode n = firstNode(); n != null; n = n.getNext()) {
337      HashSet<SpaceEffGraphEdge> visitedNodes = new HashSet<SpaceEffGraphEdge>();
338      int duplicatedNodes = 0;
339      res.append("\nNode: ").append(n).append("\n");
340      res.append("In nodes:\n");
341      for (SpaceEffGraphEdge inEdge = n.firstInEdge(); inEdge != null; inEdge = inEdge.getNextIn()) {
342        if (visitedNodes.contains(inEdge)) {
343          duplicatedNodes ++;
344          res.append("(Duplicated edge " + inEdge.toNodeString() + ")");
345          if (duplicatedNodes > 5) {
346            break;
347          }
348        } else {
349          visitedNodes.add(inEdge);
350          res.append(inEdge.getTypeString());
351          res.append(" ");
352          res.append(inEdge.fromNode());
353          res.append("\n");
354        }
355      }
356      res.append("\n");
357      visitedNodes.clear();
358      duplicatedNodes = 0;
359      res.append("Out nodes:\n");
360      for (SpaceEffGraphEdge out = n.firstOutEdge(); out != null; out = out.getNextOut()) {
361        if (visitedNodes.contains(out)) {
362          duplicatedNodes ++;
363          res.append("(Duplicated edge " + out.toNodeString() + ")");
364          if (duplicatedNodes > 5) {
365            break;
366          }
367        } else {
368          res.append(out.getTypeString());
369          res.append(" ");
370          res.append(out.toNode());
371          res.append("\n");
372        }
373      }
374      if (res.length() > 50000) {
375        res.append("....(giving up too long)\n");
376        break;
377      }
378    }
379    return res.toString();
380  }
381
382  ////////////////////
383  // Return a breadth-first enumeration of the nodes in this CFG.
384  // Note that this is different than topological ordering.
385  // TODO: figure out how this works and add comments (IP)
386  ////////////////////
387
388  /**
389   * Print, to System.out, the basic blocks in depth first order.
390   */
391  public void printDepthFirst() {
392    print(new DepthFirstEnumerator(_firstNode));
393  }
394
395  /**
396   * Print, to System.out, the basic blocks in the order given in
397   * the supplied enumeration.
398   * @param e enumeration order to print blocks
399   */
400  private void print(Enumeration<GraphNode> e) {
401    while (e.hasMoreElements()) {
402      SpaceEffGraphNode bb = (SpaceEffGraphNode) e.nextElement();
403      bb.printExtended();
404    }
405  }
406
407  private static final class NodeEnumeration implements Enumeration<GraphNode> {
408    private SpaceEffGraphNode _node;
409
410    NodeEnumeration(SpaceEffGraphNode n) {
411      _node = n;
412    }
413
414    @Override
415    public boolean hasMoreElements() {
416      return _node != null;
417    }
418
419    @Override
420    public GraphNode nextElement() {
421      SpaceEffGraphNode n = _node;
422      _node = n.getNext();
423      return n;
424    }
425  }
426}
427