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.ssa;
014
015import java.util.ArrayList;
016import java.util.Enumeration;
017import java.util.HashMap;
018import java.util.Iterator;
019import java.util.Stack;
020import org.jikesrvm.VM;
021import org.jikesrvm.compilers.opt.ir.IR;
022import org.jikesrvm.compilers.opt.ir.Instruction;
023import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
024import org.jikesrvm.compilers.opt.util.GraphNode;
025
026/**
027 * This class holds the results of global value numbering.
028 * ala Alpern, Wegman and Zadeck, PoPL 88.
029 * See Muchnick p.348 for a discussion (which is quite buggy, should
030 * have stuck to the dragon book, which gives a high-level description of
031 * the correct algorithm on page 143).
032 */
033public final class GlobalValueNumberState {
034  /**
035   * Constant used to flag "unknown" value numbers
036   */
037  public static final int UNKNOWN = -1;
038  /**
039   * Print verbose debugging output?
040   */
041  private static final boolean DEBUG = false;
042  /**
043   * Assume parameters are not aliased?
044   */
045  private static final boolean NO_PARAM_ALIAS = false;
046
047  /**
048   * ArrayList of GVCongruenceClass, indexed by value number.
049   */
050  private final ArrayList<GVCongruenceClass> B;
051  /**
052   * The value graph.
053   */
054  final ValueGraph valueGraph;
055  /**
056   * Stack used for a work list.
057   */
058  private final Stack<GVCongruenceClass> workList;
059
060  /**
061   * Construct a structure to hold global value number results for an IR.
062   *
063   * @param ir governing IR
064   */
065  GlobalValueNumberState(IR ir) {
066    B = new ArrayList<GVCongruenceClass>();
067    workList = new Stack<GVCongruenceClass>();
068    valueGraph = new ValueGraph(ir);
069    globalValueNumber();
070  }
071
072  /**
073   * Compute node congruence over the value number graph.
074   *
075   * <p> Algorithm: Muchnick pp. 348-355
076   * <p> Note: the Muchnick algorithm is buggy.  In particular, it
077   * does not put all needed congruence classes on the worklist.
078   *
079   * <p> Two nodes in the value graph are congruent if one of the
080   * following holds:
081   * <ul>
082   *  <li> they are the same node
083   *  <li>  their labels are identical constants
084   *  <li>  they have the same operators and their operands are
085   *      congruent
086   * </ul>
087   *
088   *  <p> Optimistic algorithm:
089   *  <ul>
090   *    <li> Initially assume all nodes with the same label are congruent
091   *    <li> start a work list with all congruence classes that
092   *       have multiple operands
093   *    <li> choose a congruence class from the worklist.  partition its
094   *       elements into new congruence classes if we can discover that
095   *       they are not congruent.
096   *     <li>  Add any newly created congruence classes to the work list.
097   *     <li> (Here's the step Muchnick omits:)
098   *     For each class C which has a dependence on any of the newly
099   *       created congruence classes, add C to the work list
100   *     <li> repeat until work list is empty
101   *   </ul>
102   *
103   *  <p> The following method breaks Muchnick's algorithm, which will
104   *  assign m and n the same value number. Muchnick's problem is that
105   *  it does not put the congruence class for 'int_mul' back on the worklist
106   *  when we discover, for example, that i is not congruent to k
107   *  <pre>
108   *   public int foo(int a, int b, int c, int d, int e, int f, int g, int h) {
109   *      int i = a+b;
110   *      int j = c+d;
111   *      int k = e+f;
112   *      int l = g+h;
113   *      int m = i * j;
114   *      int n = k * l;
115   *      int o = m/n;
116   *      return o;
117   *   }
118   *  </pre>
119   */
120  private void globalValueNumber() {
121    if (DEBUG) {
122      VM.sysWrite(valueGraph.toString());
123    }
124    // initialize the congurence classes
125    initialize();
126    // initialize the work list
127    initializeWorkList();
128    // drain the work list
129    while (!workList.empty()) {
130      GVCongruenceClass partition = workList.pop();
131      partitionClass(partition);
132    }
133    // all done
134    if (DEBUG) {
135      printValueNumbers();
136    }
137  }
138
139  void mergeClasses(ValueGraphVertex v1, ValueGraphVertex v2) {
140    if (DEBUG) {
141      System.out.println("@@@@ mergeClasses called with v1 = " + v1 + " ; v2 = " + v2);
142    }
143
144    int val1 = v1.getValueNumber();
145    int val2 = v2.getValueNumber();
146    if (val1 == val2) return;
147
148    GVCongruenceClass class1 = B.get(val1);
149
150    while (true) {
151      GVCongruenceClass class2 = B.get(val2);
152      Iterator<ValueGraphVertex> i = class2.iterator();
153      if (!i.hasNext()) break;
154      ValueGraphVertex v = i.next();
155      if (DEBUG) {
156        System.out.println("@@@@ moving vertex " + v + " from class " + val2 + " to class " + val1);
157      }
158      class1.addVertex(v);
159      class2.removeVertex(v);
160      v.setValueNumber(val1);
161    }
162
163    // Null out entry for val2
164    B.set(val2, null);
165  }
166
167  /**
168   * Definitely-same relation.
169   * @param name1 first variable
170   * @param name2 second variable
171   * @return true iff the value numbers for two variables are equal
172   */
173  boolean DS(Object name1, Object name2) {
174    ValueGraphVertex v1 = valueGraph.getVertex(name1);
175    ValueGraphVertex v2 = valueGraph.getVertex(name2);
176    return v1.getValueNumber() == v2.getValueNumber();
177  }
178
179  /**
180   * Definitely-different relation for two value numbers.
181   * Returns true for the following cases:
182   * <ul>
183   *  <li> v1 and v2 are both constants, but don't match
184   *  <li> v1 and v2 both result from NEW statements, but don't match
185   *  <li> one of v1 and v2 is a parameter, and the other results from a
186   *      new statement
187   * </ul>
188   * <p> TODO: add more smarts
189   * @param v1 first value number
190   * @param v2 second value number
191   * @return true iff the value numbers for two variables are definitely
192   * different
193   */
194  boolean DD(int v1, int v2) {
195    if ((v1 == -1) || (v2 == -1)) {
196      return false;
197    }
198    GVCongruenceClass class1 = B.get(v1);
199    GVCongruenceClass class2 = B.get(v2);
200    Object label1 = class1.getLabel();
201    Object label2 = class2.getLabel();
202    // if one is a constant, they must both be ...
203    if (isConstant(label1) && !isConstant(label2)) {
204      return false;
205    }
206    if (!isConstant(label1) && isConstant(label2)) {
207      return false;
208    }
209    if (isConstant(label1)) {
210      return (v1 != v2);
211    }
212    // handle DD for allocations
213    if (isBornAtAllocation(label1)) {
214      if (isBornAtAllocation(label2)) {
215        // both are NEW.  Are they dd?
216        return (v1 != v2);
217      } else if (class2.containsParameter()) {
218        // one is NEW, other is parameter. They are DD.
219        return true;
220      }
221    } else {
222      if (isBornAtAllocation(label2)) {
223        if (class1.containsParameter()) {
224          // one is NEW, other is parameter. They are DD.
225          return true;
226        }
227      }
228    }
229    // assume parameters are not aliased?
230    if (NO_PARAM_ALIAS) {
231      if (v1 != v2) {
232        if (class1.containsParameter()) {
233          if (class2.containsParameter()) {
234            return true;
235          }
236        }
237      }
238    }
239
240    // if we haven't figured out they're DD, return false;
241    return false;
242  }
243
244  /**
245   * Definitely-different relation.
246   * Returns true for the following cases:
247   * <ul>
248   *  <li> name1 and name2 are both constants, but don't match
249   *  <li> name1 and name2 both result from NEW statements, but don't match
250   *  <li> one of name1 and name2 is a parameter, and the other results from a
251   *      new statement
252   * </ul>
253   * <p> TODO: add more smarts
254   * @param name1 name of first object to compare
255   * @param name2 name of second object to compare
256   * @return true iff the value numbers for two variables are definitely
257   * different
258   */
259  boolean DD(Object name1, Object name2) {
260    ValueGraphVertex v1 = valueGraph.getVertex(name1);
261    ValueGraphVertex v2 = valueGraph.getVertex(name2);
262    return DD(v1.getValueNumber(), v2.getValueNumber());
263  }
264
265  GVCongruenceClass congruenceClass(Object name) {
266    ValueGraphVertex v = valueGraph.getVertex(name);
267    return B.get(v.getValueNumber());
268  }
269
270  /**
271   * Return the (integer) value number for a given variable
272   *
273   * @param name name of the variable to look up
274   * @return its value number
275   */
276  int getValueNumber(Object name) {
277    ValueGraphVertex v = valueGraph.getVertex(name);
278    if (v == null) {
279      return UNKNOWN;
280    }
281    return v.getValueNumber();
282  }
283
284  /**
285   * Print the value numbers for each node in the value graph.
286   */
287  void printValueNumbers() {
288    for (Enumeration<GraphNode> e = valueGraph.enumerateVertices(); e.hasMoreElements();) {
289      ValueGraphVertex v = (ValueGraphVertex) e.nextElement();
290      int valueNumber = v.getValueNumber();
291      GVCongruenceClass c = B.get(valueNumber);
292      System.out.println(v.getName() + " " + valueNumber + " " + c.getLabel());
293    }
294  }
295
296  /**
297   * Initialize the congruence classes, assuming that all nodes
298   * with the same label are congruent.
299   */
300  private void initialize() {
301    // store a map from label -> congruenceClass
302    HashMap<Object, GVCongruenceClass> labelMap = new HashMap<Object, GVCongruenceClass>(10);
303    for (Enumeration<GraphNode> e = valueGraph.enumerateVertices(); e.hasMoreElements();) {
304      ValueGraphVertex v = (ValueGraphVertex) e.nextElement();
305      Object label = v.getLabel();
306      GVCongruenceClass c = findOrCreateCongruenceClass(label, labelMap);
307      // add this node to the congruence class
308      c.addVertex(v);
309      // set the value number for the node
310      v.setValueNumber(c.getValueNumber());
311    }
312  }
313
314  /**
315   * Given a label, return the congruence class for that label.
316   * Create one if needed.
317   *
318   * @param label the label of a congruence class
319   * @param labelMap a mapping from labels to congruence class
320   * @return the congruence class for the label.
321   */
322  private GVCongruenceClass findOrCreateCongruenceClass(Object label,
323                                                            HashMap<Object, GVCongruenceClass> labelMap) {
324    GVCongruenceClass result = labelMap.get(label);
325    if ((result == null) || (label == null)) {
326      result = createCongruenceClass(label);
327      labelMap.put(label, result);
328    }
329    return result;
330  }
331
332  /**
333   * Given a label, return a new congruence class for that label.
334   * @param label the label of a congruence class
335   * @return the congruence class for the label.
336   */
337  private GVCongruenceClass createCongruenceClass(Object label) {
338    // create a new congruence class, and update data structures
339    int index = B.size();
340    GVCongruenceClass result = new GVCongruenceClass(index, label);
341    B.add(result);
342    return result;
343  }
344
345  /**
346   * Initialize the work list.
347   * A congruence class gets put on the work list if any two nodes
348   * in the class point to corresponding targets in separate partitions.
349   */
350  private void initializeWorkList() {
351    for (GVCongruenceClass c : B) {
352      if (c.size() == 1) {
353        continue;
354      }
355      // store a reference to the first node in c
356      Iterator<ValueGraphVertex> i = c.iterator();
357      ValueGraphVertex first = i.next();
358      // now check that each other target matches the first element
359      // if not, add this class to the work list
360      //
361      while (i.hasNext()) {
362        ValueGraphVertex v = i.next();
363        if (!checkCongruence(first, v)) {
364          workList.push(c);
365          break;
366        }
367      }
368    }
369  }
370
371  /**
372   * Partition a congruence class.
373   * @param partition the class to partition
374   */
375  private void partitionClass(GVCongruenceClass partition) {
376    // store a reference to the first node in c, which will serve
377    // as a representative for this class
378    Iterator<ValueGraphVertex> i = partition.iterator();
379    ValueGraphVertex first = i.next();
380    ArrayList<GVCongruenceClass> newClasses = new ArrayList<GVCongruenceClass>();
381    // now check each other node in c, to see if it matches the
382    // representative
383    ArrayList<ValueGraphVertex> toRemove = new ArrayList<ValueGraphVertex>();
384    while (i.hasNext()) {
385      ValueGraphVertex v = i.next();
386      if (!checkCongruence(first, v)) {
387        // NOT CONGRUENT!!  split the partition.  first check if
388        // v fits in any other newly created congruence classes
389        int index = findCongruenceMatch(newClasses, v);
390        if (index > -1) {
391          // MATCH FOUND!! place v in newClasses[index]
392          GVCongruenceClass match = B.get(index);
393          match.addVertex(v);
394          v.setValueNumber(match.getValueNumber());
395        } else {
396          // NO MATCH FOUND!! create a new congruence class
397          // find the appropriate label for the new congruence class
398          // and create a new congruence class with this label
399          GVCongruenceClass c = createCongruenceClass(v);
400          newClasses.add(c);
401          c.addVertex(v);
402          v.setValueNumber(c.getValueNumber());
403        }
404        // mark v as to be removed from partition
405        // (Can't remove it yet while iterating over the set);
406        toRemove.add(v);
407      }
408    }
409    // remove necessary vertices
410    for (ValueGraphVertex v : toRemove) {
411      partition.removeVertex(v);
412    }
413    // if needed place the original partition back on the work list
414    if ((!newClasses.isEmpty()) && (partition.size() > 1)) {
415      workList.push(partition);
416    }
417    // place any new congruence classes with size > 1 on the worklist
418    // also place any classes which might indirectly be affected
419    for (GVCongruenceClass c : newClasses) {
420      if (c.size() > 1) {
421        workList.push(c);
422      }
423      addDependentClassesToWorklist(c);
424    }
425  }
426
427  /**
428   * Assuming congruence class c has changed: find all other classes
429   * that might be affected, and add them to the worklist
430   * @param c the congruence class that has changed
431   */
432  private void addDependentClassesToWorklist(GVCongruenceClass c) {
433    // for each element of this congruence class:
434    for (ValueGraphVertex v : c) {
435      // for each vertex which points to v in the value graph
436      for (Enumeration<GraphNode> e = v.inNodes(); e.hasMoreElements();) {
437        ValueGraphVertex in = (ValueGraphVertex) e.nextElement();
438        int vn = in.getValueNumber();
439        GVCongruenceClass x = B.get(vn);
440        workList.push(x);
441      }
442    }
443  }
444
445  /**
446   * Does vertex v belong to any congruence class in a vector?
447   * If so, returns the value number of the matching congruence class.
448   * If none found, returns -1.
449   * @param vector a vector of congruence classes
450   * @param v the vertex to search for
451   * @return the value number corresponding to the congruence class
452   * containing v.  -1 iff no such class is found.
453   */
454  private int findCongruenceMatch(ArrayList<GVCongruenceClass> vector, ValueGraphVertex v) {
455    for (GVCongruenceClass klass : vector) {
456      if (checkCongruence(v, klass)) {
457        return klass.getValueNumber();
458      }
459    }
460    return -1;
461  }
462
463  /**
464   * Does the current state of the algorithm optimistically assume
465   * that a vertex v is congruent to the vertices in a congruence
466   * class? Note: this can return true even if
467   * if the value numbers don't currently match.
468   * @param v the vertex in question
469   * @param c the congurence class to check
470   * @return true or false
471   */
472  private boolean checkCongruence(ValueGraphVertex v, GVCongruenceClass c) {
473    ValueGraphVertex r = c.getRepresentative();
474    boolean result = checkCongruence(r, v);
475    return result;
476  }
477
478  /**
479   * Does the current state of the algorithm optimistically assume
480   * that two nodes are congruent? Note: this can return false
481   * even if the value numbers are currently the same.
482   * @param v1 first vertex
483   * @param v2 second vertex
484   * @return whether the notes are assumed to be congruent
485   */
486  private boolean checkCongruence(ValueGraphVertex v1, ValueGraphVertex v2) {
487    if (v1 == v2) {
488      return true;
489    }
490    // make sure the two nodes have the same label
491    if (v1.getLabel() != v2.getLabel()) {
492      return false;
493    }
494    // make sure that the operands match
495    int arity = v1.getArity();
496    for (int i = 0; i < arity; i++) {
497      ValueGraphVertex target1 = v1.getTarget(i);
498      ValueGraphVertex target2 = v2.getTarget(i);
499      // if either target is null, then that particular control
500      // flow path is never realized, so assume TOP
501      if ((target1 == null) || (target2 == null)) {
502        continue;
503      }
504      if (target1.getValueNumber() != target2.getValueNumber()) {
505        return false;
506      }
507    }
508    return true;
509  }
510
511  /**
512   * @param label a label
513   * @return whether a given label indicates that the object has a constant value
514   */
515  private static boolean isConstant(Object label) {
516    return (label instanceof ConstantOperand);
517  }
518
519  /**
520   * @param label a label
521   * @return whether a given label indicates that the object is created at an
522   * allocation site
523
524   */
525  private static boolean isBornAtAllocation(Object label) {
526    return (label instanceof Instruction);
527  }
528}