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 static org.jikesrvm.compilers.opt.ir.Operators.FENCE;
016import static org.jikesrvm.compilers.opt.ir.Operators.READ_CEILING;
017import static org.jikesrvm.compilers.opt.ir.Operators.WRITE_FLOOR;
018
019import java.util.Enumeration;
020
021import org.jikesrvm.classloader.TypeReference;
022import org.jikesrvm.compilers.opt.OptimizingCompilerException;
023import org.jikesrvm.compilers.opt.dfsolver.DF_Equation;
024import org.jikesrvm.compilers.opt.dfsolver.DF_LatticeCell;
025import org.jikesrvm.compilers.opt.dfsolver.DF_Operator;
026import org.jikesrvm.compilers.opt.dfsolver.DF_System;
027import org.jikesrvm.compilers.opt.ir.ALoad;
028import org.jikesrvm.compilers.opt.ir.AStore;
029import org.jikesrvm.compilers.opt.ir.Attempt;
030import org.jikesrvm.compilers.opt.ir.BasicBlock;
031import org.jikesrvm.compilers.opt.ir.CacheOp;
032import org.jikesrvm.compilers.opt.ir.Call;
033import org.jikesrvm.compilers.opt.ir.GetField;
034import org.jikesrvm.compilers.opt.ir.GetStatic;
035import org.jikesrvm.compilers.opt.ir.IR;
036import org.jikesrvm.compilers.opt.ir.IRTools;
037import org.jikesrvm.compilers.opt.ir.Instruction;
038import org.jikesrvm.compilers.opt.ir.MonitorOp;
039import org.jikesrvm.compilers.opt.ir.New;
040import org.jikesrvm.compilers.opt.ir.NewArray;
041import org.jikesrvm.compilers.opt.ir.Phi;
042import org.jikesrvm.compilers.opt.ir.Prepare;
043import org.jikesrvm.compilers.opt.ir.PutField;
044import org.jikesrvm.compilers.opt.ir.PutStatic;
045import org.jikesrvm.compilers.opt.ir.operand.HeapOperand;
046import org.jikesrvm.compilers.opt.ir.operand.Operand;
047import org.jikesrvm.compilers.opt.ssa.IndexPropagation.ArrayCell;
048import org.jikesrvm.compilers.opt.ssa.IndexPropagation.ObjectCell;
049
050/**
051 * Represents a set of dataflow equations used to solve the
052 * index propagation problem.
053 */
054class IndexPropagationSystem extends DF_System {
055
056  /**
057   * The governing IR.
058   */
059  private final IR ir;
060  /**
061   * Heap Array SSA lookaside information for the IR.
062   */
063  private final SSADictionary ssa;
064  /**
065   * Results of global value numbering
066   */
067  private final GlobalValueNumberState valueNumbers;
068  /**
069   * object representing the MEET operator
070   */
071  private static final MeetOperator MEET = new MeetOperator();
072
073  /**
074   * Set up the system of dataflow equations.
075   * @param _ir the IR
076   */
077  IndexPropagationSystem(IR _ir) {
078    ir = _ir;
079    ssa = ir.HIRInfo.dictionary;
080    valueNumbers = ir.HIRInfo.valueNumbers;
081    setupEquations();
082  }
083
084  /**
085   * Create an DF_LatticeCell corresponding to an HeapVariable
086   * @param o the heap variable
087   * @return a new lattice cell corresponding to this heap variable
088   */
089  @Override
090  protected DF_LatticeCell makeCell(Object o) {
091    if (!(o instanceof HeapVariable)) {
092      throw new OptimizingCompilerException("IndexPropagation:makeCell");
093    }
094    DF_LatticeCell result = null;
095    Object heapType = ((HeapVariable<?>) o).getHeapType();
096    if (heapType instanceof TypeReference) {
097      result = new ArrayCell((HeapVariable<?>) o);
098    } else {
099      result = new ObjectCell((HeapVariable<?>) o);
100    }
101    return result;
102  }
103
104  /**
105   * Initialize the lattice variables.
106   */
107  @Override
108  protected void initializeLatticeCells() {
109    // initially all lattice cells are set to TOP
110    // set the lattice cells that are exposed on entry to BOTTOM
111    for (DF_LatticeCell c : cells.values()) {
112      if (c instanceof ObjectCell) {
113        ObjectCell c1 = (ObjectCell) c;
114        HeapVariable<?> key = c1.getKey();
115        if (key.isExposedOnEntry()) {
116          c1.setBOTTOM();
117        }
118      } else {
119        ArrayCell c1 = (ArrayCell) c;
120        HeapVariable<?> key = c1.getKey();
121        if (key.isExposedOnEntry()) {
122          c1.setBOTTOM();
123        }
124      }
125    }
126  }
127
128  /**
129   * Initialize the work list for the dataflow equation system.
130   */
131  @Override
132  protected void initializeWorkList() {
133    // add all equations to the work list that contain a non-TOP
134    // variable
135    for (Enumeration<DF_Equation> e = getEquations(); e.hasMoreElements();) {
136      DF_Equation eq = e.nextElement();
137      for (DF_LatticeCell operand : eq.getOperands()) {
138        if (operand instanceof ObjectCell) {
139          if (!((ObjectCell) operand).isTOP()) {
140            addToWorkList(eq);
141            break;
142          }
143        } else {
144          if (!((ArrayCell) operand).isTOP()) {
145            addToWorkList(eq);
146            break;
147          }
148        }
149      }
150    }
151  }
152
153  /**
154   * Walk through the IR and add dataflow equations for each instruction
155   * that affects the values of Array SSA variables.
156   */
157  void setupEquations() {
158    for (Enumeration<BasicBlock> e = ir.getBasicBlocks(); e.hasMoreElements();) {
159      BasicBlock bb = e.nextElement();
160      for (SSADictionary.AllInstructionEnumeration e2 = ssa.getAllInstructions(bb); e2.hasMoreElements();) {
161        Instruction s = e2.nextElement();
162        // only consider instructions which use/def Array SSA variables
163        if (!ssa.usesHeapVariable(s) && !ssa.defsHeapVariable(s)) {
164          continue;
165        }
166        if (s.isDynamicLinkingPoint()) {
167          processCall(s);
168        } else if (GetStatic.conforms(s)) {
169          processLoad(s);
170        } else if (GetField.conforms(s)) {
171          processLoad(s);
172        } else if (PutStatic.conforms(s)) {
173          processStore(s);
174        } else if (PutField.conforms(s)) {
175          processStore(s);
176        } else if (New.conforms(s)) {
177          processNew(s);
178        } else if (NewArray.conforms(s)) {
179          processNew(s);
180        } else if (ALoad.conforms(s)) {
181          processALoad(s);
182        } else if (AStore.conforms(s)) {
183          processAStore(s);
184        } else if (Call.conforms(s)) {
185          processCall(s);
186        } else if (MonitorOp.conforms(s)) {
187          processCall(s);
188        } else if (Prepare.conforms(s)) {
189          processCall(s);
190        } else if (Attempt.conforms(s)) {
191          processCall(s);
192        } else if (CacheOp.conforms(s)) {
193          processCall(s);
194        } else if (Phi.conforms(s)) {
195          processPhi(s);
196        } else if (s.operator() == READ_CEILING) {
197          processCall(s);
198        } else if (s.operator() == WRITE_FLOOR) {
199          processCall(s);
200        } else if (s.operator() == FENCE) {
201          processCall(s);
202        }
203      }
204    }
205  }
206
207  /**
208   * Update the set of dataflow equations to account for the actions
209   * of a Load instruction
210   *
211   * <p> The load is of the form x = A[k].  let A_1 be the array SSA
212   * variable before the load, and A_2 the array SSA variable after
213   * the store.  Then we add the dataflow equation
214   * L(A_2) = updateUse(L(A_1), VALNUM(k))
215   *
216   * <p> Intuitively, this equation represents the fact that A[k] is available
217   * after the store
218   *
219   * @param s the Load instruction
220   */
221  void processLoad(Instruction s) {
222    HeapOperand<?>[] A1 = ssa.getHeapUses(s);
223    HeapOperand<?>[] A2 = ssa.getHeapDefs(s);
224    if ((A1.length != 1) || (A2.length != 1)) {
225      throw new OptimizingCompilerException(
226          "IndexPropagation.processLoad: load instruction defs or uses multiple heap variables?");
227    }
228    int valueNumber = -1;
229    if (GetField.conforms(s)) {
230      Object address = GetField.getRef(s);
231      valueNumber = valueNumbers.getValueNumber(address);
232    } else {      // GetStatic.conforms(s)
233      valueNumber = 0;
234    }
235    if (IRTools.mayBeVolatileFieldLoad(s) || ir.options.READS_KILL) {
236      // to obey JMM strictly, we must treat every load as a
237      // DEF
238      addUpdateObjectDefEquation(A2[0].getHeapVariable(), A1[0].getHeapVariable(), valueNumber);
239    } else {
240      // otherwise, don't have to treat every load as a DEF
241      addUpdateObjectUseEquation(A2[0].getHeapVariable(), A1[0].getHeapVariable(), valueNumber);
242    }
243  }
244
245  /**
246   * Update the set of dataflow equations to account for the actions
247   * of a Store instruction.
248   *
249   * <p> The store is of the form A[k] = val.  let A_1 be the array SSA
250   * variable before the store, and A_2 the array SSA variable after
251   * the store.  Then we add the dataflow equation
252   * L(A_2) = updateDef(L(A_1), VALNUM(k))
253   *
254   * <p> Intuitively, this equation represents the fact that A[k] is available
255   * after the store
256   *
257   * @param s the Store instruction
258   */
259  void processStore(Instruction s) {
260    HeapOperand<?>[] A1 = ssa.getHeapUses(s);
261    HeapOperand<?>[] A2 = ssa.getHeapDefs(s);
262    if ((A1.length != 1) || (A2.length != 1)) {
263      throw new OptimizingCompilerException(
264          "IndexPropagation.processStore: store instruction defs or uses multiple heap variables?");
265    }
266    int valueNumber = -1;
267    if (PutField.conforms(s)) {
268      Object address = PutField.getRef(s);
269      valueNumber = valueNumbers.getValueNumber(address);
270    } else {      // PutStatic.conforms(s)
271      valueNumber = 0;
272    }
273    addUpdateObjectDefEquation(A2[0].getHeapVariable(), A1[0].getHeapVariable(), valueNumber);
274  }
275
276  /**
277   * Update the set of dataflow equations to account for the actions
278   * of ALoad instruction s
279   *
280   * <p> The load is of the form x = A[k].  let A_1 be the array SSA
281   * variable before the load, and A_2 the array SSA variable after
282   * the load.  Then we add the dataflow equation
283   * L(A_2) = updateUse(L(A_1), VALNUM(k))
284   *
285   * <p> Intuitively, this equation represents the fact that A[k] is available
286   * after the store
287   *
288   * @param s the Aload instruction
289   */
290  void processALoad(Instruction s) {
291    HeapOperand<?>[] A1 = ssa.getHeapUses(s);
292    HeapOperand<?>[] A2 = ssa.getHeapDefs(s);
293    if ((A1.length != 1) || (A2.length != 1)) {
294      throw new OptimizingCompilerException(
295          "IndexPropagation.processALoad: aload instruction defs or uses multiple heap variables?");
296    }
297    Operand array = ALoad.getArray(s);
298    Operand index = ALoad.getIndex(s);
299    if (IRTools.mayBeVolatileFieldLoad(s) || ir.options.READS_KILL) {
300      // to obey JMM strictly, we must treat every load as a
301      // DEF
302      addUpdateArrayDefEquation(A2[0].getHeapVariable(), A1[0].getHeapVariable(), array, index);
303    } else {
304      // otherwise, don't have to treat every load as a DEF
305      addUpdateArrayUseEquation(A2[0].getHeapVariable(), A1[0].getHeapVariable(), array, index);
306    }
307  }
308
309  /**
310   * Update the set of dataflow equations to account for the actions
311   * of AStore instruction s
312   *
313   * <p> The store is of the form A[k] = val.  let A_1 be the array SSA
314   * variable before the store, and A_2 the array SSA variable after
315   * the store.  Then we add the dataflow equation
316   * L(A_2) = update(L(A_1), VALNUM(k))
317   *
318   * <p> Intuitively, this equation represents the fact that A[k] is available
319   * after the store
320   *
321   * @param s the Astore instruction
322   */
323  void processAStore(Instruction s) {
324    HeapOperand<?>[] A1 = ssa.getHeapUses(s);
325    HeapOperand<?>[] A2 = ssa.getHeapDefs(s);
326    if ((A1.length != 1) || (A2.length != 1)) {
327      throw new OptimizingCompilerException(
328          "IndexPropagation.processAStore: astore instruction defs or uses multiple heap variables?");
329    }
330    Operand array = AStore.getArray(s);
331    Operand index = AStore.getIndex(s);
332    addUpdateArrayDefEquation(A2[0].getHeapVariable(), A1[0].getHeapVariable(), array, index);
333  }
334
335  /**
336   * Update the set of dataflow equations to account for the actions
337   * of allocation instruction s
338   *
339   * @param s the New instruction
340   */
341  void processNew(Instruction s) {
342    /** Assume nothing is a available after a new. So, set
343     * each lattice cell defined by the NEW as BOTTOM.
344     * TODO: add logic that understands that after a
345     * NEW, all fields have their default values.
346     */
347    for (HeapOperand<?> def : ssa.getHeapDefs(s)) {
348      DF_LatticeCell c = findOrCreateCell(def.getHeapVariable());
349      if (c instanceof ObjectCell) {
350        ((ObjectCell) c).setBOTTOM();
351      } else {
352        ((ArrayCell) c).setBOTTOM();
353      }
354    }
355  }
356
357  /**
358   * Update the set of dataflow equations to account for the actions
359   * of CALL instruction.
360   *
361   * @param s the Call instruction
362   */
363  void processCall(Instruction s) {
364
365    /** set all lattice cells def'ed by the call to bottom */
366    for (HeapOperand<?> operand : ssa.getHeapDefs(s)) {
367      DF_LatticeCell c = findOrCreateCell(operand.getHeapVariable());
368      if (c instanceof ObjectCell) {
369        ((ObjectCell) c).setBOTTOM();
370      } else {
371        ((ArrayCell) c).setBOTTOM();
372      }
373    }
374  }
375
376  /**
377   * Update the set of dataflow equations to account for the actions
378   * of Phi instruction.
379   *
380   * <p> The instruction has the form A1 = PHI (A2, A3, A4);
381   * We add the dataflow equation
382   * L(A1) = MEET(L(A2), L(A3), L(A4))
383   *
384   * @param s the Phi instruction
385   */
386  void processPhi(Instruction s) {
387    Operand result = Phi.getResult(s);
388    if (!(result instanceof HeapOperand)) {
389      return;
390    }
391    HeapVariable<?> lhs = ((HeapOperand<?>) result).value;
392    DF_LatticeCell A1 = findOrCreateCell(lhs);
393    DF_LatticeCell[] rhs = new DF_LatticeCell[Phi.getNumberOfValues(s)];
394    for (int i = 0; i < rhs.length; i++) {
395      HeapOperand<?> op = (HeapOperand<?>) Phi.getValue(s, i);
396      rhs[i] = findOrCreateCell(op.value);
397    }
398    newEquation(A1, MEET, rhs);
399  }
400
401  /**
402   * Add an equation to the system of the form
403   * L(A1) = updateDef(L(A2), VALNUM(address))
404   *
405   * @param A1 variable in the equation
406   * @param A2 variable in the equation
407   * @param valueNumber value number of the address
408   */
409  void addUpdateObjectDefEquation(HeapVariable<?> A1, HeapVariable<?> A2, int valueNumber) {
410    DF_LatticeCell cell1 = findOrCreateCell(A1);
411    DF_LatticeCell cell2 = findOrCreateCell(A2);
412    UpdateDefObjectOperator op = new UpdateDefObjectOperator(valueNumber);
413    newEquation(cell1, op, cell2);
414  }
415
416  /**
417   * Add an equation to the system of the form
418   * <pre>
419   * L(A1) = updateUse(L(A2), VALNUM(address))
420   * </pre>
421   *
422   * @param A1 variable in the equation
423   * @param A2 variable in the equation
424   * @param valueNumber value number of address
425   */
426  void addUpdateObjectUseEquation(HeapVariable<?> A1, HeapVariable<?> A2, int valueNumber) {
427    DF_LatticeCell cell1 = findOrCreateCell(A1);
428    DF_LatticeCell cell2 = findOrCreateCell(A2);
429    UpdateUseObjectOperator op = new UpdateUseObjectOperator(valueNumber);
430    newEquation(cell1, op, cell2);
431  }
432
433  /**
434   * Add an equation to the system of the form
435   * <pre>
436   * L(A1) = updateDef(L(A2), &lt;VALNUM(array),VALNUM(index)&gt;)
437   * </pre>
438   *
439   * @param A1 variable in the equation
440   * @param A2 variable in the equation
441   * @param array variable in the equation
442   * @param index variable in the equation
443   */
444  void addUpdateArrayDefEquation(HeapVariable<?> A1, HeapVariable<?> A2, Object array, Object index) {
445    DF_LatticeCell cell1 = findOrCreateCell(A1);
446    DF_LatticeCell cell2 = findOrCreateCell(A2);
447    int arrayNumber = valueNumbers.getValueNumber(array);
448    int indexNumber = valueNumbers.getValueNumber(index);
449    UpdateDefArrayOperator op = new UpdateDefArrayOperator(arrayNumber, indexNumber);
450    newEquation(cell1, op, cell2);
451  }
452
453  /**
454   * Add an equation to the system of the form
455   * <pre>
456   * L(A1) = updateUse(L(A2), &lt;VALNUM(array),VALNUM(index)&gt;)
457   * </pre>
458   *
459   * @param A1 variable in the equation
460   * @param A2 variable in the equation
461   * @param array variable in the equation
462   * @param index variable in the equation
463   */
464  void addUpdateArrayUseEquation(HeapVariable<?> A1, HeapVariable<?> A2, Object array, Object index) {
465    DF_LatticeCell cell1 = findOrCreateCell(A1);
466    DF_LatticeCell cell2 = findOrCreateCell(A2);
467    int arrayNumber = valueNumbers.getValueNumber(array);
468    int indexNumber = valueNumbers.getValueNumber(index);
469    UpdateUseArrayOperator op = new UpdateUseArrayOperator(arrayNumber, indexNumber);
470    newEquation(cell1, op, cell2);
471  }
472
473  /**
474   * Represents a MEET function (intersection) over Cells.
475   */
476  static class MeetOperator extends DF_Operator {
477
478    /**
479     * @return "MEET"
480     */
481    @Override
482    public String toString() {
483      return "MEET";
484    }
485
486    /**
487     * Evaluate a dataflow equation with the MEET operator
488     * @param operands the operands of the dataflow equation
489     * @return true iff the value of the lhs changes
490     */
491    @Override
492    public boolean evaluate(DF_LatticeCell[] operands) {
493      DF_LatticeCell lhs = operands[0];
494      if (lhs instanceof ObjectCell) {
495        return evaluateObjectMeet(operands);
496      } else {
497        return evaluateArrayMeet(operands);
498      }
499    }
500
501    /**
502     * Evaluate a dataflow equation with the MEET operator
503     * for lattice cells representing field heap variables.
504     * @param operands the operands of the dataflow equation
505     * @return true iff the value of the lhs changes
506     */
507    boolean evaluateObjectMeet(DF_LatticeCell[] operands) {
508      ObjectCell lhs = (ObjectCell) operands[0];
509
510      // short-circuit if lhs is already bottom
511      if (lhs.isBOTTOM()) {
512        return false;
513      }
514
515      // short-circuit if any rhs is bottom
516      for (int j = 1; j < operands.length; j++) {
517        ObjectCell r = (ObjectCell) operands[j];
518        if (r.isBOTTOM()) {
519          // from the previous short-circuit, we know lhs was not already bottom, so ...
520          lhs.setBOTTOM();
521          return true;
522        }
523      }
524
525      boolean lhsWasTOP = lhs.isTOP();
526      int[] oldNumbers = null;
527
528      if (!lhsWasTOP) oldNumbers = lhs.copyValueNumbers();
529
530      lhs.clear();
531      // perform the intersections
532      if (operands.length > 1) {
533        int firstNonTopRHS = -1;
534        // find the first RHS lattice cell that is not TOP
535        for (int j = 1; j < operands.length; j++) {
536          ObjectCell r = (ObjectCell) operands[j];
537          if (!r.isTOP()) {
538            firstNonTopRHS = j;
539            break;
540          }
541        }
542        // if we did not find ANY non-top cell, then simply set
543        // lhs to top and return
544        if (firstNonTopRHS == -1) {
545          lhs.setTOP(true);
546          return false;
547        }
548        // if we get here, we found a non-top cell. Start merging
549        // here
550        int[] rhsNumbers = ((ObjectCell) operands[firstNonTopRHS]).copyValueNumbers();
551
552        if (rhsNumbers != null) {
553          for (int v : rhsNumbers) {
554            lhs.add(v);
555            for (int j = firstNonTopRHS + 1; j < operands.length; j++) {
556              ObjectCell r = (ObjectCell) operands[j];
557              if (!r.contains(v)) {
558                lhs.remove(v);
559                break;
560              }
561            }
562          }
563        }
564      }
565      // check if anything has changed
566      if (lhsWasTOP) return true;
567      int[] newNumbers = lhs.copyValueNumbers();
568
569      return ObjectCell.setsDiffer(oldNumbers, newNumbers);
570    }
571
572    /**
573     * Evaluate a dataflow equation with the MEET operator
574     * for lattice cells representing array heap variables.
575     * @param operands the operands of the dataflow equation
576     * @return true iff the value of the lhs changes
577     */
578    boolean evaluateArrayMeet(DF_LatticeCell[] operands) {
579      ArrayCell lhs = (ArrayCell) operands[0];
580
581      // short-circuit if lhs is already bottom
582      if (lhs.isBOTTOM()) {
583        return false;
584      }
585
586      // short-circuit if any rhs is bottom
587      for (int j = 1; j < operands.length; j++) {
588        ArrayCell r = (ArrayCell) operands[j];
589        if (r.isBOTTOM()) {
590          // from the previous short-circuit, we know lhs was not already bottom, so ...
591          lhs.setBOTTOM();
592          return true;
593        }
594      }
595
596      boolean lhsWasTOP = lhs.isTOP();
597      ValueNumberPair[] oldNumbers = null;
598      if (!lhsWasTOP) oldNumbers = lhs.copyValueNumbers();
599
600      lhs.clear();
601      // perform the intersections
602      if (operands.length > 1) {
603        int firstNonTopRHS = -1;
604        // find the first RHS lattice cell that is not TOP
605        for (int j = 1; j < operands.length; j++) {
606          ArrayCell r = (ArrayCell) operands[j];
607          if (!r.isTOP()) {
608            firstNonTopRHS = j;
609            break;
610          }
611        }
612        // if we did not find ANY non-top cell, then simply set
613        // lhs to top and return
614        if (firstNonTopRHS == -1) {
615          lhs.setTOP(true);
616          return false;
617        }
618        // if we get here, we found a non-top cell. Start merging
619        // here
620        ValueNumberPair[] rhsNumbers = ((ArrayCell) operands[firstNonTopRHS]).copyValueNumbers();
621        if (rhsNumbers != null) {
622          for (ValueNumberPair pair : rhsNumbers) {
623            int v1 = pair.v1;
624            int v2 = pair.v2;
625            lhs.add(v1, v2);
626            for (int j = firstNonTopRHS + 1; j < operands.length; j++) {
627              ArrayCell r = (ArrayCell) operands[j];
628              if (!r.contains(v1, v2)) {
629                lhs.remove(v1, v2);
630                break;
631              }
632            }
633          }
634        }
635      }
636      // check if anything has changed
637      if (lhsWasTOP) return true;
638      ValueNumberPair[] newNumbers = lhs.copyValueNumbers();
639
640      return ArrayCell.setsDiffer(oldNumbers, newNumbers);
641    }
642  }
643
644  /**
645   * Represents an UPDATE_DEF function over two ObjectCells.
646   * <p> Given a value number v, this function updates a heap variable
647   * lattice cell to indicate that element at address v is
648   * available, but kills any available indices that are not DD from v
649   */
650  class UpdateDefObjectOperator extends DF_Operator {
651    /**
652     * The value number used in the dataflow equation.
653     */
654    private final int valueNumber;
655
656    /**
657     * @return a String representation
658     */
659    @Override
660    public String toString() {
661      return "UPDATE-DEF<" + valueNumber + ">";
662    }
663
664    UpdateDefObjectOperator(int valueNumber) {
665      this.valueNumber = valueNumber;
666    }
667
668    /**
669     * Evaluate the dataflow equation with this operator.
670     * @param operands operands in the dataflow equation
671     * @return true iff the lhs changes from this evaluation
672     */
673    @Override
674    public boolean evaluate(DF_LatticeCell[] operands) {
675      ObjectCell lhs = (ObjectCell) operands[0];
676
677      if (lhs.isBOTTOM()) {
678        return false;
679      }
680
681      ObjectCell rhs = (ObjectCell) operands[1];
682      boolean lhsWasTOP = lhs.isTOP();
683      int[] oldNumbers = null;
684      if (!lhsWasTOP) oldNumbers = lhs.copyValueNumbers();
685      lhs.clear();
686      if (rhs.isTOP()) {
687        throw new OptimizingCompilerException("Unexpected lattice operation");
688      }
689      int[] numbers = rhs.copyValueNumbers();
690      // add all rhs numbers that are DD from valueNumber
691      if (numbers != null) {
692        for (int number : numbers) {
693          if (valueNumbers.DD(number, valueNumber)) {
694            lhs.add(number);
695          }
696        }
697      }
698      // add value number generated by this update
699      lhs.add(valueNumber);
700      // check if anything has changed
701      if (lhsWasTOP) return true;
702      int[] newNumbers = lhs.copyValueNumbers();
703
704      return ObjectCell.setsDiffer(oldNumbers, newNumbers);
705    }
706  }
707
708  /**
709   * Represents an UPDATE_USE function over two ObjectCells.
710   *
711   * <p> Given a value number v, this function updates a heap variable
712   * lattice cell to indicate that element at address v is
713   * available, and doesn't kill any available indices
714   */
715  static class UpdateUseObjectOperator extends DF_Operator {
716    /**
717     * The value number used in the dataflow equation.
718     */
719    private final int valueNumber;
720
721    /**
722     * @return "UPDATE-USE"
723     */
724    @Override
725    public String toString() {
726      return "UPDATE-USE<" + valueNumber + ">";
727    }
728
729    UpdateUseObjectOperator(int valueNumber) {
730      this.valueNumber = valueNumber;
731    }
732
733    /**
734     * Evaluate the dataflow equation with this operator.
735     * @param operands operands in the dataflow equation
736     * @return true iff the lhs changes from this evaluation
737     */
738    @Override
739    public boolean evaluate(DF_LatticeCell[] operands) {
740      ObjectCell lhs = (ObjectCell) operands[0];
741
742      if (lhs.isBOTTOM()) {
743        return false;
744      }
745
746      ObjectCell rhs = (ObjectCell) operands[1];
747      int[] oldNumbers = null;
748      boolean lhsWasTOP = lhs.isTOP();
749      if (!lhsWasTOP) oldNumbers = lhs.copyValueNumbers();
750      lhs.clear();
751      if (rhs.isTOP()) {
752        throw new OptimizingCompilerException("Unexpected lattice operation");
753      }
754      int[] numbers = rhs.copyValueNumbers();
755      // add all rhs numbers
756      if (numbers != null) {
757        for (int number : numbers) {
758          lhs.add(number);
759        }
760      }
761      // add value number generated by this update
762      lhs.add(valueNumber);
763      // check if anything has changed
764      if (lhsWasTOP) return true;
765      int[] newNumbers = lhs.copyValueNumbers();
766
767      return ObjectCell.setsDiffer(oldNumbers, newNumbers);
768    }
769  }
770
771  /**
772   * Represents an UPDATE_DEF function over two ArrayCells.
773   * Given two value numbers v1, v2, this function updates a heap variable
774   * lattice cell to indicate that element for array v1 at address v2 is
775   * available, but kills any available indices that are not DD from &lt;v1,v2&gt;
776   */
777  class UpdateDefArrayOperator extends DF_Operator {
778    /**
779     * The value number pair used in the dataflow equation.
780     */
781    private final ValueNumberPair v;
782
783    /**
784     * @return "UPDATE-DEF"
785     */
786    @Override
787    public String toString() {
788      return "UPDATE-DEF<" + v + ">";
789    }
790
791    /**
792     * Create an operator with a given value number pair
793     * @param     v1 first value number in the pari
794     * @param     v2 first value number in the pari
795     */
796    UpdateDefArrayOperator(int v1, int v2) {
797      v = new ValueNumberPair(v1, v2);
798    }
799
800    /**
801     * Evaluate the dataflow equation with this operator.
802     * @param operands operands in the dataflow equation
803     * @return true iff the lhs changes from this evaluation
804     */
805    @Override
806    public boolean evaluate(DF_LatticeCell[] operands) {
807      ArrayCell lhs = (ArrayCell) operands[0];
808
809      if (lhs.isBOTTOM()) {
810        return false;
811      }
812      ArrayCell rhs = (ArrayCell) operands[1];
813      ValueNumberPair[] oldNumbers = null;
814      boolean lhsWasTOP = lhs.isTOP();
815      if (!lhsWasTOP) oldNumbers = lhs.copyValueNumbers();
816      lhs.clear();
817      if (rhs.isTOP()) {
818        throw new OptimizingCompilerException("Unexpected lattice operation");
819      }
820      ValueNumberPair[] numbers = rhs.copyValueNumbers();
821      // add all rhs pairs that are DD from either v.v1 or v.v2
822      if (numbers != null) {
823        for (ValueNumberPair number : numbers) {
824          if (valueNumbers.DD(number.v1, v.v1)) {
825            lhs.add(number.v1, number.v2);
826          } else if (valueNumbers.DD(number.v2, v.v2)) {
827            lhs.add(number.v1, number.v2);
828          }
829        }
830      }
831      // add the value number pair generated by this update
832      lhs.add(v.v1, v.v2);
833      // check if anything has changed
834      if (lhsWasTOP) {
835        return true;
836      }
837      ValueNumberPair[] newNumbers = lhs.copyValueNumbers();
838
839      return ArrayCell.setsDiffer(oldNumbers, newNumbers);
840    }
841  }
842
843  /**
844   * Represents an UPDATE_USE function over two ArrayCells.
845   *
846   * <p> Given two value numbers v1, v2, this function updates a heap variable
847   * lattice cell to indicate that element at array v1 index v2 is
848   * available, and doesn't kill any available indices
849   */
850  static class UpdateUseArrayOperator extends DF_Operator {
851    /**
852     * The value number pair used in the dataflow equation.
853     */
854    private final ValueNumberPair v;
855
856    /**
857     * @return "UPDATE-USE"
858     */
859    @Override
860    public String toString() {
861      return "UPDATE-USE<" + v + ">";
862    }
863
864    /**
865     * Create an operator with a given value number pair
866     * @param     v1 first value number in the pair
867     * @param     v2 second value number in the pair
868     */
869    UpdateUseArrayOperator(int v1, int v2) {
870      v = new ValueNumberPair(v1, v2);
871    }
872
873    /**
874     * Evaluate the dataflow equation with this operator.
875     * @param operands operands in the dataflow equation
876     * @return true iff the lhs changes from this evaluation
877     */
878    @Override
879    public boolean evaluate(DF_LatticeCell[] operands) {
880      ArrayCell lhs = (ArrayCell) operands[0];
881
882      if (lhs.isBOTTOM()) {
883        return false;
884      }
885
886      ArrayCell rhs = (ArrayCell) operands[1];
887      ValueNumberPair[] oldNumbers = null;
888      boolean lhsWasTOP = lhs.isTOP();
889      if (!lhsWasTOP) oldNumbers = lhs.copyValueNumbers();
890      lhs.clear();
891      if (rhs.isTOP()) {
892        throw new OptimizingCompilerException("Unexpected lattice operation");
893      }
894      ValueNumberPair[] numbers = rhs.copyValueNumbers();
895      // add all rhs numbers
896      if (numbers != null) {
897        for (ValueNumberPair number : numbers) {
898          lhs.add(number.v1, number.v2);
899        }
900      }
901      // add value number generated by this update
902      lhs.add(v.v1, v.v2);
903      // check if anything has changed
904      if (lhsWasTOP) {
905        return true;
906      }
907      ValueNumberPair[] newNumbers = lhs.copyValueNumbers();
908
909      return ArrayCell.setsDiffer(oldNumbers, newNumbers);
910    }
911  }
912}