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;
016
017import org.jikesrvm.compilers.opt.OptimizingCompilerException;
018
019/**
020 * SpaceEffGraphNode is a generic graph node. Extend this to implement
021 * specific graph node types.  A node has a list of out edges and a
022 * list of in edges.  We maintain both to support bidirectional traversal
023 * of the graph.
024 */
025public class SpaceEffGraphNode implements GraphNode {
026
027  /**
028   * The following word is used for various purposes. The first
029   * 8 bits are used for flags, and the remaining 24 bits for any
030   * node information (node number, for example)
031   */
032  protected int info;
033
034  static final int DFS_VISITED = 0x01000000;
035  static final int TOP_VISITED = 0x02000000;
036  static final int ON_STACK = 0x04000000;
037
038  static final int LOOP_HEADER = 0x08000000;
039
040  static final int INFO_MASK = 0x00ffffff;
041
042  public final boolean dfsVisited() {
043    return (info & DFS_VISITED) != 0;
044  }
045
046  public final boolean topVisited() {
047    return (info & TOP_VISITED) != 0;
048  }
049
050  public final boolean onStack() {
051    return (info & ON_STACK) != 0;
052  }
053
054  public final boolean flagsOn() {
055    return (info & (DFS_VISITED | TOP_VISITED | ON_STACK)) != 0;
056  }
057
058  public final boolean isLoopHeader() {
059    return (info & LOOP_HEADER) != 0;
060  }
061
062  public final void setDfsVisited() {
063    info |= DFS_VISITED;
064  }
065
066  public final void setTopVisited() {
067    info |= TOP_VISITED;
068  }
069
070  public final void setOnStack() {
071    info |= ON_STACK;
072  }
073
074  public final void setDfsVisitedOnStack() {
075    info |= (DFS_VISITED | ON_STACK);
076  }
077
078  public final void setLoopHeader() {
079    info |= LOOP_HEADER;
080  }
081
082  public final void clearDfsVisited() {
083    info &= ~DFS_VISITED;
084  }
085
086  public final void clearTopVisited() {
087    info &= ~TOP_VISITED;
088  }
089
090  public final void clearOnStack() {
091    info &= ~ON_STACK;
092  }
093
094  public final void clearFlags() {
095    info &= ~(DFS_VISITED | TOP_VISITED | ON_STACK);
096  }
097
098  public final void clearLoopHeader() {
099    info &= ~LOOP_HEADER;
100  }
101
102  public final void setNumber(int value) {
103    info = (info & ~INFO_MASK) | (value & INFO_MASK);
104  }
105
106  public final int getNumber() {
107    return info & INFO_MASK;
108  }
109
110  @Override
111  public final int getIndex() {
112    return getNumber();
113  }
114
115  @Override
116  public final void setIndex(int i) {
117    setNumber(i);
118  }
119
120  /////////////////
121  // The following is used by several node sorting schemes
122  /////////////////
123
124  public SpaceEffGraphNode nextSorted;
125
126  // return the first in/out edge
127
128  public final SpaceEffGraphEdge firstInEdge() {
129    return _inEdgeStart;
130  }
131
132  public final SpaceEffGraphEdge firstOutEdge() {
133    return _outEdgeStart;
134  }
135
136  public final SpaceEffGraphNode firstInNode() {
137    return _inEdgeStart.fromNode();
138  }
139
140  public final SpaceEffGraphNode firstOutNode() {
141    return _outEdgeStart.toNode();
142  }
143
144  /**
145   * clear the in set of edges
146   */
147  final void clearIn() {
148    _inEdgeStart = _inEdgeEnd = null;
149  }
150
151  /**
152   * clear the out set of edges
153   */
154  final void clearOut() {
155    _outEdgeStart = _outEdgeEnd = null;
156  }
157
158  // deletes all the in/out edges
159
160  public final void deleteIn() {
161    for (SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
162      e.fromNode().removeOut(e);
163    }
164    clearIn();
165  }
166
167  public final void deleteOut() {
168    for (SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
169      e.toNode().removeIn(e);
170    }
171    clearOut();
172  }
173
174  /* get number of in/out edges */
175
176  public final int getNumberOfIn() {
177    int count = 0;
178    for (SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
179      count++;
180    }
181    return count;
182  }
183
184  public final int getNumberOfOut() {
185    int count = 0;
186    for (SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
187      count++;
188    }
189    return count;
190  }
191
192  /* specialized versions */
193  public final boolean hasZeroOut() {
194    return _outEdgeStart == null;
195  }
196
197  public final boolean hasZeroIn() {
198    return _inEdgeStart == null;
199  }
200
201  public final boolean hasOneOut() {
202    SpaceEffGraphEdge first = _outEdgeStart;
203    return (first != null) && (first.nextOut == null);
204  }
205
206  public final boolean hasOneIn() {
207    SpaceEffGraphEdge first = _inEdgeStart;
208    return (first != null) && (first.nextIn == null);
209  }
210
211  /* returns true if points to the in/out set */
212
213  public final boolean pointsIn(SpaceEffGraphNode inNode) {
214    for (SpaceEffGraphEdge e = _inEdgeStart; e != null; e = e.nextIn) {
215      if (e.fromNode() == inNode) return true;
216    }
217    return false;
218  }
219
220  public final boolean pointsOut(SpaceEffGraphNode outNode) {
221    for (SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
222      if (e.toNode() == outNode) return true;
223    }
224    return false;
225  }
226
227  public final boolean hasIn(GraphNode in) {
228    return pointsIn((SpaceEffGraphNode) in);
229  }
230
231  public final boolean hasOut(GraphNode out) {
232    return pointsOut((SpaceEffGraphNode) out);
233  }
234
235  /*
236   * returns the out edge pointing to node n, if it exists.
237   * returns null otherwise
238   */
239  public final SpaceEffGraphEdge findOutEdgeTo(SpaceEffGraphNode n) {
240    for (SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
241      if (e.toNode() == n) return e;
242    }
243    return null;
244  }
245
246  /**
247   * Replaces the in edge matching e1 with e2.
248   * maintains the ordering of edges<p>
249   *
250   * @param e1 original edge
251   * @param e2 new edge
252   *
253   * TODO YUCK: this data structure is messy.  I assume this is in the name
254   * of efficiency, but it makes control flow graph manipulations
255   * a real pain. (SJF)
256   */
257  public final void replaceInEdge(SpaceEffGraphEdge e1, SpaceEffGraphEdge e2) {
258    // set the predecessor of e1 to point to e2
259    if (_inEdgeStart == e1) {
260      _inEdgeStart = e2;
261    } else {
262      // walk the list until we find the predecessor to e1
263      SpaceEffGraphEdge pred = null;
264      for (pred = _inEdgeStart; pred != null; pred = pred.nextIn) {
265        if (pred.nextIn == e1) break;
266      }
267      // if not found, there's an error
268      if (pred == null) {
269        throw new OptimizingCompilerException("SpaceEffGraphNode.replaceInEdge: called incorrectly");
270      }
271      pred.nextIn = e2;
272    }
273    // set e2 to point to e1.nextIn
274    e2.nextIn = e1.nextIn;
275
276    // fix up _inEdgeStart, _inEdgeEnd
277    if (_inEdgeStart == e1) _inEdgeStart = e2;
278    if (_inEdgeEnd == e1) _inEdgeEnd = e2;
279
280    // clear the links of e1
281    e1.nextIn = null;
282  }
283
284  /**
285   * @param inNode the node that might be the single predecessor
286   * @return {@code true} if the node is the single predecessor of this node
287   */
288  public final boolean hasOneIn(SpaceEffGraphNode inNode) {
289    SpaceEffGraphEdge first = _inEdgeStart;
290    return (first != null) && (first.nextIn == null) && (first.fromNode() == inNode);
291  }
292
293  /**
294   *
295   * @param outNode the node that might be the single successor
296   * @return {@code true} if the node is the single successor of this node
297   */
298  public final boolean hasOneOut(SpaceEffGraphNode outNode) {
299    SpaceEffGraphEdge first = _outEdgeStart;
300    return (first != null) && (first.nextOut == null) && (first.toNode() == outNode);
301  }
302
303  public final void replaceOut(SpaceEffGraphNode oldOut, SpaceEffGraphNode newOut) {
304    deleteOut(oldOut);
305    insertOut(newOut);
306  }
307
308  public final void insertOut(SpaceEffGraphNode to, SpaceEffGraphEdge e) {
309    this.appendOutEdge(e);
310    to.appendInEdge(e);
311  }
312
313  public final void insertOut(SpaceEffGraphNode to) {
314    if (this.pointsOut(to)) return;
315    SpaceEffGraphEdge e = new SpaceEffGraphEdge(this, to);
316    this.appendOutEdge(e);
317    to.appendInEdge(e);
318  }
319
320  public final void deleteOut(SpaceEffGraphNode node) {
321    SpaceEffGraphEdge edge = this.removeOut(node);
322    node.removeIn(edge);
323  }
324
325  public final void deleteOut(SpaceEffGraphEdge e) {
326    SpaceEffGraphNode to = e.toNode();
327    this.removeOut(e);
328    to.removeIn(e);
329  }
330
331  /* sort nodes according to DFS. result is a list of nodes with the current
332     as root.  Note: it assumes that the dfs flags have been cleared before */
333  public final void sortDFS() {
334    _sortDFS(null);
335  }
336
337  protected final void _sortDFS(SpaceEffGraphNode header) {
338    setDfsVisited();
339    for (SpaceEffGraphEdge e = _outEdgeStart; e != null; e = e.nextOut) {
340      SpaceEffGraphNode n = e.toNode();
341      if (!n.dfsVisited()) {
342        n._sortDFS(header);
343        header = n;
344      }
345    }
346    nextSorted = header;
347  }
348
349  /* clear all out/in flags */
350
351  public final void clearOutFlags() {
352    clearFlags();
353    for (SpaceEffGraphEdge e = firstOutEdge(); e != null; e = e.getNextOut()) {
354      SpaceEffGraphNode succ = e.toNode();
355      e.clearVisited();
356      if (succ.flagsOn()) {
357        succ.clearOutFlags();
358      }
359    }
360  }
361
362  public final void clearInFlags() {
363    clearFlags();
364    for (SpaceEffGraphEdge e = firstInEdge(); e != null; e = e.getNextIn()) {
365      SpaceEffGraphNode succ = e.fromNode();
366      e.clearVisited();
367      if (succ.flagsOn()) {
368        succ.clearInFlags();
369      }
370    }
371  }
372
373  /* topological sort of nodes. result is a list of nodes with the current
374     as root */
375
376  public final void sortTop() {
377    clearOutFlags();
378    setDfsVisitedOnStack();
379    nextSorted = _sortTop(null);
380  }
381
382  protected final SpaceEffGraphNode _sortTop(SpaceEffGraphNode tail) {
383    for (SpaceEffGraphEdge e = firstOutEdge(); e != null; e = e.getNextOut()) {
384      SpaceEffGraphNode succ = e.toNode();
385      if (!succ.dfsVisited()) {
386        succ.setDfsVisitedOnStack();
387        tail = succ._sortTop(tail);
388      } else if (succ.onStack() || succ == this) {
389        e.setVisited(); // back edge
390      }
391    }
392    clearOnStack();
393    for (SpaceEffGraphEdge e = firstOutEdge(); e != null; e = e.getNextOut()) {
394      SpaceEffGraphNode succ = e.toNode();
395      if (!succ.topVisited() && !e.visited()) {
396        succ.nextSorted = tail;
397        tail = succ;
398        succ.setTopVisited();
399      }
400    }
401    return tail;
402  }
403
404  /* reverse topological sort of nodes. result is a list of nodes with the
405     current as root */
406
407  public final void sortRevTop() {
408    clearInFlags();
409    setDfsVisitedOnStack();
410    nextSorted = _sortRevTop(null);
411  }
412
413  protected final SpaceEffGraphNode _sortRevTop(SpaceEffGraphNode tail) {
414    for (SpaceEffGraphEdge e = firstInEdge(); e != null; e = e.getNextIn()) {
415      SpaceEffGraphNode succ = e.fromNode();
416      if (!succ.dfsVisited()) {
417        succ.setDfsVisitedOnStack();
418        tail = succ._sortRevTop(tail);
419      } else if (succ.onStack() || succ == this) {
420        e.setVisited(); // forward edge
421      }
422    }
423    clearOnStack();
424    for (SpaceEffGraphEdge e = firstInEdge(); e != null; e = e.getNextIn()) {
425      SpaceEffGraphNode succ = e.fromNode();
426      if (!succ.topVisited() && !e.visited()) {
427        succ.nextSorted = tail;
428        tail = succ;
429        succ.setTopVisited();
430      }
431    }
432    return tail;
433  }
434
435  /* print sorted nodes starting from this */
436
437  final void printSorted() {
438    for (SpaceEffGraphNode n = this; n != null; n = n.nextSorted) {
439      System.out.println(n);
440    }
441  }
442
443  /**
444   * Revert the sequence of out edges
445   */
446  final void revertOuts() {
447    SpaceEffGraphEdge last = null;
448    SpaceEffGraphEdge e = firstOutEdge();
449    _outEdgeStart = _outEdgeEnd;
450    _outEdgeEnd = e;
451    while (e != null) {
452      SpaceEffGraphEdge next = e.getNextOut();
453      e.appendOut(last);
454      last = e;
455      e = next;
456    }
457  }
458
459  /* enumerations to get the nodes/edges */
460
461  public interface GraphEdgeEnumeration<T extends GraphEdge> extends Enumeration<T> {
462    // Same as nextElement but avoid the need to downcast from Object
463    T next();
464  }
465
466  public final InEdgeEnumeration inEdges() {
467    return new InEdgeEnumeration(this);
468  }
469
470  public final OutEdgeEnumeration outEdges() {
471    return new OutEdgeEnumeration(this);
472  }
473
474  @Override
475  public final Enumeration<GraphNode> inNodes() {
476    return new InNodeEnumeration(this);
477  }
478
479  @Override
480  public final Enumeration<GraphNode> outNodes() {
481    return new OutNodeEnumeration(this);
482  }
483
484  /* print utilities */
485
486  public void printInEdges() {
487    for (SpaceEffGraphEdge in = firstInEdge(); in != null; in = in.getNextIn()) {
488      System.out.println(in.fromNodeString());
489    }
490  }
491
492  public void printOutEdges() {
493    for (SpaceEffGraphEdge out = firstOutEdge(); out != null; out = out.getNextOut()) {
494      System.out.println(out.toNodeString());
495    }
496  }
497
498  public void printInNodes() {
499    for (SpaceEffGraphEdge in = firstInEdge(); in != null; in = in.getNextIn()) {
500      System.out.println(in.fromNode());
501    }
502  }
503
504  public void printOutNodes() {
505    for (SpaceEffGraphEdge out = firstOutEdge(); out != null; out = out.getNextOut()) {
506      System.out.println(out.toNode());
507    }
508  }
509
510  public void printExtended() {
511    System.out.println(this);
512  }
513
514  /////////////////
515  // Implementation: the following is not intended for general client use
516  /////////////////
517
518  protected SpaceEffGraphEdge _outEdgeStart;
519  protected SpaceEffGraphEdge _outEdgeEnd;
520  protected SpaceEffGraphEdge _inEdgeStart;
521  protected SpaceEffGraphEdge _inEdgeEnd;
522
523  //
524  // add an in/out edge from 'node' to this node.
525  //
526
527  // (SJF): I had to make these public to do SSA transformations.
528  // TODO: The CFG data structure should not depend this tightly
529  // on the underlying Graph implementation, but rather should be
530  // designed so that the SSA-like transformations are easy to do.
531
532  protected final void appendInEdge(SpaceEffGraphEdge e) {
533    SpaceEffGraphEdge inEdgeEnd = _inEdgeEnd;
534    if (inEdgeEnd != null) {
535      inEdgeEnd.appendIn(e);
536    } else {
537      _inEdgeStart = e;
538    }
539    _inEdgeEnd = e;
540  }
541
542  protected final void appendOutEdge(SpaceEffGraphEdge e) {
543    SpaceEffGraphEdge outEdgeEnd = _outEdgeEnd;
544    if (outEdgeEnd != null) {
545      outEdgeEnd.appendOut(e);
546    } else {
547      _outEdgeStart = e;
548    }
549    _outEdgeEnd = e;
550  }
551
552  /* remove and edge/node from the in/out set */
553
554  protected final void removeIn(SpaceEffGraphEdge InEdge) {
555    SpaceEffGraphEdge prev = null;
556    for (SpaceEffGraphEdge edge = _inEdgeStart; edge != null; prev = edge, edge = edge.nextIn) {
557      if (edge == InEdge) {
558        SpaceEffGraphEdge next = edge.nextIn;
559        if (prev == null) {
560          _inEdgeStart = next;
561        } else {
562          prev.appendIn(next);
563        }
564        if (next == null) {
565          _inEdgeEnd = prev;
566        }
567        break;
568      }
569    }
570  }
571
572  protected final SpaceEffGraphEdge removeIn(SpaceEffGraphNode InNode) {
573    SpaceEffGraphEdge edge, prev = null;
574    for (edge = _inEdgeStart; edge != null; prev = edge, edge = edge.nextIn) {
575      if (edge.fromNode() == InNode) {
576        SpaceEffGraphEdge next = edge.nextIn;
577        if (prev == null) {
578          _inEdgeStart = next;
579        } else {
580          prev.appendIn(next);
581        }
582        if (next == null) {
583          _inEdgeEnd = prev;
584        }
585        break;
586      }
587    }
588    return edge;
589  }
590
591  protected final void removeOut(SpaceEffGraphEdge OutEdge) {
592    SpaceEffGraphEdge edge, prev = null;
593    for (edge = _outEdgeStart; edge != null; prev = edge, edge = edge.nextOut) {
594      if (edge == OutEdge) {
595        SpaceEffGraphEdge next = edge.nextOut;
596        if (prev == null) {
597          _outEdgeStart = next;
598        } else {
599          prev.appendOut(next);
600        }
601        if (next == null) {
602          _outEdgeEnd = prev;
603        }
604        break;
605      }
606    }
607  }
608
609  protected final SpaceEffGraphEdge removeOut(SpaceEffGraphNode OutNode) {
610    SpaceEffGraphEdge edge, prev = null;
611    for (edge = _outEdgeStart; edge != null; prev = edge, edge = edge.nextOut) {
612      if (edge.toNode() == OutNode) {
613        SpaceEffGraphEdge next = edge.nextOut;
614        if (prev == null) {
615          _outEdgeStart = next;
616        } else {
617          prev.appendOut(next);
618        }
619        if (next == null) {
620          _outEdgeEnd = prev;
621        }
622        break;
623      }
624    }
625    return edge;
626  }
627
628  static final class InEdgeEnumeration implements GraphEdgeEnumeration<GraphEdge> {
629    private SpaceEffGraphEdge _edge;
630
631    InEdgeEnumeration(SpaceEffGraphNode n) {
632      _edge = n._inEdgeStart;
633    }
634
635    @Override
636    public boolean hasMoreElements() {
637      return _edge != null;
638    }
639
640    @Override
641    public SpaceEffGraphEdge nextElement() {
642      return next();
643    }
644
645    @Override
646    public SpaceEffGraphEdge next() {
647      SpaceEffGraphEdge e = _edge;
648      _edge = e.nextIn;
649      return e;
650    }
651  }
652
653  static final class InNodeEnumeration implements Enumeration<GraphNode> {
654    private SpaceEffGraphEdge _edge;
655
656    InNodeEnumeration(SpaceEffGraphNode n) {
657      _edge = n._inEdgeStart;
658    }
659
660    @Override
661    public boolean hasMoreElements() {
662      return _edge != null;
663    }
664
665    @Override
666    public GraphNode nextElement() {
667      SpaceEffGraphEdge e = _edge;
668      _edge = e.nextIn;
669      return e.fromNode();
670    }
671  }
672
673  public static final class OutEdgeEnumeration implements GraphEdgeEnumeration<GraphEdge> {
674    private SpaceEffGraphEdge _edge;
675
676    public OutEdgeEnumeration(SpaceEffGraphNode n) {
677      _edge = n._outEdgeStart;
678    }
679
680    @Override
681    public boolean hasMoreElements() {
682      return _edge != null;
683    }
684
685    @Override
686    public GraphEdge nextElement() {
687      return next();
688    }
689
690    @Override
691    public GraphEdge next() {
692      SpaceEffGraphEdge e = _edge;
693      _edge = e.nextOut;
694      return e;
695    }
696  }
697
698  static final class OutNodeEnumeration implements Enumeration<GraphNode> {
699    private SpaceEffGraphEdge _edge;
700
701    OutNodeEnumeration(SpaceEffGraphNode n) {
702      _edge = n._outEdgeStart;
703    }
704
705    @Override
706    public boolean hasMoreElements() {
707      return _edge != null;
708    }
709
710    @Override
711    public GraphNode nextElement() {
712      SpaceEffGraphEdge e = _edge;
713      _edge = e.nextOut;
714      return e.toNode();
715    }
716  }
717
718  /**
719   * Links inlined from DoublyLinkedListElement.
720   */
721  public SpaceEffGraphNode prev, next;
722
723  /**
724   * Get the next node.
725   * @return next node
726   */
727  public final SpaceEffGraphNode getNext() {
728    return next;
729  }
730
731  /**
732   * Get the previous node.
733   * @return previous node
734   */
735  public final SpaceEffGraphNode getPrev() {
736    return prev;
737  }
738
739  /**
740   * Append a given node after this node.
741   * @param n the node to append
742   */
743  public final void append(SpaceEffGraphNode n) {
744    next = n;
745    n.prev = this;
746  }
747
748  /**
749   * Remove this node from the list.
750   * @return the next node in the list
751   */
752  public final SpaceEffGraphNode remove() {
753    // copy old links
754    SpaceEffGraphNode Prev = prev, Next = next;
755
756    // reset old links
757    prev = null;
758    next = null;
759
760    // compute new links
761    if (Prev != null) Prev.next = Next;
762    if (Next != null) Next.prev = Prev;
763
764    // return next node
765    return Next;
766  }
767
768}