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.BYTE_ALOAD_opcode;
016import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_ASTORE_opcode;
017import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_ALOAD_opcode;
018import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_ASTORE_opcode;
019import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ALOAD_opcode;
020import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ASTORE_opcode;
021import static org.jikesrvm.compilers.opt.ir.Operators.GETFIELD_opcode;
022import static org.jikesrvm.compilers.opt.ir.Operators.GETSTATIC_opcode;
023import static org.jikesrvm.compilers.opt.ir.Operators.INT_ALOAD_opcode;
024import static org.jikesrvm.compilers.opt.ir.Operators.INT_ASTORE_opcode;
025import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ALOAD_opcode;
026import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ASTORE_opcode;
027import static org.jikesrvm.compilers.opt.ir.Operators.PUTFIELD_opcode;
028import static org.jikesrvm.compilers.opt.ir.Operators.PUTSTATIC_opcode;
029import static org.jikesrvm.compilers.opt.ir.Operators.REF_ALOAD_opcode;
030import static org.jikesrvm.compilers.opt.ir.Operators.REF_ASTORE_opcode;
031import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_ALOAD_opcode;
032import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_ASTORE_opcode;
033import static org.jikesrvm.compilers.opt.ir.Operators.UBYTE_ALOAD_opcode;
034import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_ALOAD_opcode;
035
036import java.lang.reflect.Constructor;
037import java.util.Enumeration;
038import java.util.HashMap;
039import java.util.HashSet;
040import java.util.Iterator;
041
042import org.jikesrvm.classloader.RVMField;
043import org.jikesrvm.classloader.FieldReference;
044import org.jikesrvm.classloader.TypeReference;
045import org.jikesrvm.compilers.opt.OptOptions;
046import org.jikesrvm.compilers.opt.OptimizingCompilerException;
047import org.jikesrvm.compilers.opt.dfsolver.DF_Solution;
048import org.jikesrvm.compilers.opt.driver.CompilerPhase;
049import org.jikesrvm.compilers.opt.driver.OptimizationPlanAtomicElement;
050import org.jikesrvm.compilers.opt.driver.OptimizationPlanCompositeElement;
051import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
052import org.jikesrvm.compilers.opt.ir.ALoad;
053import org.jikesrvm.compilers.opt.ir.AStore;
054import org.jikesrvm.compilers.opt.ir.BasicBlock;
055import org.jikesrvm.compilers.opt.ir.GetField;
056import org.jikesrvm.compilers.opt.ir.GetStatic;
057import org.jikesrvm.compilers.opt.ir.GenericRegisterPool;
058import org.jikesrvm.compilers.opt.ir.IR;
059import org.jikesrvm.compilers.opt.ir.IRTools;
060import org.jikesrvm.compilers.opt.ir.Instruction;
061import org.jikesrvm.compilers.opt.ir.Move;
062import org.jikesrvm.compilers.opt.ir.PutField;
063import org.jikesrvm.compilers.opt.ir.PutStatic;
064import org.jikesrvm.compilers.opt.ir.Register;
065import org.jikesrvm.compilers.opt.ir.ResultCarrier;
066import org.jikesrvm.compilers.opt.ir.operand.HeapOperand;
067import org.jikesrvm.compilers.opt.ir.operand.Operand;
068import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
069import org.jikesrvm.compilers.opt.ssa.IndexPropagation.ArrayCell;
070import org.jikesrvm.compilers.opt.ssa.IndexPropagation.ObjectCell;
071
072/**
073 * This class implements the redundant load elimination by
074 * Fink, Knobe & Sarkar.  See SAS 2000 paper for details.
075 */
076public final class LoadElimination extends OptimizationPlanCompositeElement {
077
078  /**
079   * @param round which round of load elimination is this?
080   */
081  public LoadElimination(int round) {
082    super("Load Elimination",
083          new OptimizationPlanElement[]{new OptimizationPlanAtomicElement(new GVNPreparation(round)),
084                                            new OptimizationPlanAtomicElement(new EnterSSA()),
085                                            new OptimizationPlanAtomicElement(new GlobalValueNumber()),
086                                            new OptimizationPlanAtomicElement(new LoadEliminationPreparation(round)),
087                                            new OptimizationPlanAtomicElement(new EnterSSA()),
088                                            new OptimizationPlanAtomicElement(new IndexPropagation()),
089                                            new OptimizationPlanAtomicElement(new LoadEliminator())});
090    this.round = round;
091  }
092
093  static final boolean DEBUG = false;
094
095  @Override
096  public boolean shouldPerform(OptOptions options) {
097    return options.SSA_LOAD_ELIMINATION;
098  }
099
100  @Override
101  public String getName() {
102    return "Array SSA Load Elimination, round " + round;
103  }
104
105  /**
106   * which round of load elimination is this?
107   */
108  private final int round;
109
110  static final class LoadEliminator extends CompilerPhase {
111    @Override
112    public String getName() {
113      return "Load Eliminator";
114    }
115
116    /**
117     * Return this instance of this phase. This phase contains no
118     * per-compilation instance fields.
119     * @param ir not used
120     * @return this
121     */
122    @Override
123    public CompilerPhase newExecution(IR ir) {
124      return this;
125    }
126
127    /**
128     * main driver for redundant load elimination
129     * <p>
130     * Preconditions: Array SSA form and Global Value Numbers computed
131     * @param ir the governing IR
132     */
133    @Override
134    public void perform(IR ir) {
135
136      if (ir.desiredSSAOptions.getAbort()) return;
137
138      boolean didSomething = eliminateLoads(ir, ir.HIRInfo.indexPropagationSolution);
139      // Note that SSA is no longer valid!!!
140      // This will force construction of SSA next time we call EnterSSA
141      ir.actualSSAOptions.setScalarValid(false);
142      ir.actualSSAOptions.setHeapValid(false);
143      ir.HIRInfo.loadEliminationDidSomething = didSomething;
144
145      // clear the following field to avoid excess memory retention
146      ir.HIRInfo.indexPropagationSolution = null;
147    }
148  }
149
150  /**
151   * Eliminates redundant loads with respect to prior defs and prior
152   * uses.
153   *
154   * @param ir the governing IR
155   * @param available results of index propagation analysis
156   * @return {@code true} if any load is eliminated.
157   */
158  static boolean eliminateLoads(IR ir, DF_Solution available) {
159    // maintain a mapping from value number to temporary register
160    HashMap<UseRecord, Register> registers = new HashMap<UseRecord, Register>();
161    UseRecordSet UseRepSet = replaceLoads(ir, available, registers);
162    replaceDefs(ir, UseRepSet, registers);
163
164    return (UseRepSet.size() > 0);
165  }
166
167  /**
168   * Walk over each instruction.  If its a USE (load) of a heap
169   * variable and the value is available, then replace the load
170   * with a move from a register.
171   * <p>
172   * POSTCONDITION: sets up the mapping 'registers' from value number
173   *                 to temporary register
174   * @param ir the IR
175   * @param available information on which values are available
176   * @param registers a place to store information about temp registers
177   * @return mapping from heap variables to value numbers
178   */
179  static UseRecordSet replaceLoads(IR ir, DF_Solution available, HashMap<UseRecord, Register> registers) {
180    UseRecordSet result = new UseRecordSet();
181    SSADictionary ssa = ir.HIRInfo.dictionary;
182    GlobalValueNumberState valueNumbers = ir.HIRInfo.valueNumbers;
183    for (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
184      Instruction s = e.nextElement();
185      if (!GetField.conforms(s) && !GetStatic.conforms(s) && !ALoad.conforms(s)) {
186        continue;
187      }
188      // this instruction is a USE of heap variable H.
189      // get the lattice cell that holds the available indices
190      // for this heap variable
191      HeapOperand<?>[] H = ssa.getHeapUses(s);
192      if (H == null) {
193        // this case can happen due to certain magics that insert
194        // INT_LOAD instructions in HIR
195        // TODO: clean up HIR representation of these magics
196        continue;
197      }
198      if (H.length != 1) {
199        throw new OptimizingCompilerException("LoadElimination: load with wrong number of heap uses");
200      }
201      if (GetField.conforms(s) || GetStatic.conforms(s)) {
202        int valueNumber = -1;
203        if (GetField.conforms(s)) {
204          Object address = GetField.getRef(s);
205          valueNumber = valueNumbers.getValueNumber(address);
206        } else {
207          // for getStatic, always use the value number 0
208          valueNumber = 0;
209        }
210        ObjectCell cell = (ObjectCell) available.lookup(H[0].getHeapVariable());
211        if (cell == null) {
212          continue;           // nothing available
213        }
214
215        // .. if H{valueNumber} is available ...
216        if (cell.contains(valueNumber)) {
217          result.add(H[0].getHeapVariable(), valueNumber);
218          TypeReference type = ResultCarrier.getResult(s).getType();
219          Register r = findOrCreateRegister(H[0].getHeapType(), valueNumber, registers, ir.regpool, type);
220          if (DEBUG) {
221            System.out.println("ELIMINATING LOAD " + s);
222          }
223          replaceLoadWithMove(r, s);
224        }
225      } else {                  // ALoad.conforms(s)
226        Object array = ALoad.getArray(s);
227        Object index = ALoad.getIndex(s);
228        ArrayCell cell = (ArrayCell) available.lookup(H[0].getHeapVariable());
229        if (cell == null) {
230          continue;           // nothing available
231        }
232        int v1 = valueNumbers.getValueNumber(array);
233        int v2 = valueNumbers.getValueNumber(index);
234        // .. if H{<v1,v2>} is available ...
235        if (cell.contains(v1, v2)) {
236          result.add(H[0].getHeapVariable(), v1, v2);
237          TypeReference type = ALoad.getResult(s).getType();
238          Register r =
239              findOrCreateRegister(H[0].getHeapVariable().getHeapType(), v1, v2, registers, ir.regpool, type);
240          if (DEBUG) {
241            System.out.println("ELIMINATING LOAD " + s);
242          }
243          replaceLoadWithMove(r, s);
244        }
245      }
246    }
247    return result;
248  }
249
250  /**
251   * Replace a Load instruction s with a load from a scalar register r
252   * <p>
253   * TODO: factor this functionality out elsewhere
254   *
255   * @param r the register to load from
256   * @param load the load instruction
257   */
258  static void replaceLoadWithMove(Register r, Instruction load) {
259    RegisterOperand dest = ResultCarrier.getResult(load);
260    RegisterOperand rop = new RegisterOperand(r, dest.getType());
261    load.replace(Move.create(IRTools.getMoveOp(dest.getType()), dest, rop));
262  }
263
264  /**
265   * Perform scalar replacement actions for a Def of a heap variable.
266   * <p>
267   * NOTE: Even loads can def a heap variable.
268   *
269   * @param ir the governing IR
270   * @param UseRepSet stores the uses(loads) that have been eliminated
271   * @param registers mapping from valueNumber -&gt; temporary register
272   */
273  static void replaceDefs(IR ir, UseRecordSet UseRepSet, HashMap<UseRecord, Register> registers) {
274    SSADictionary ssa = ir.HIRInfo.dictionary;
275    for (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
276      Instruction s = e.nextElement();
277      if (!GetField.conforms(s) &&
278          !GetStatic.conforms(s) &&
279          !PutField.conforms(s) &&
280          !PutStatic.conforms(s) &&
281          !ALoad.conforms(s) &&
282          !AStore.conforms(s)) {
283        continue;
284      }
285      if (!ssa.defsHeapVariable(s)) {
286        continue;
287      }
288      // this instruction is a DEF of heap variable H.
289      // Check if UseRepSet needs the scalar assigned by this def
290      HeapOperand<?>[] H = ssa.getHeapDefs(s);
291      if (H.length != 1) {
292        throw new OptimizingCompilerException("LoadElimination: encountered a store with more than one def? " +
293                                                  s);
294      }
295      int valueNumber = -1;
296      Object index = null;
297      if (AStore.conforms(s)) {
298        Object address = AStore.getArray(s);
299        index = AStore.getIndex(s);
300        valueNumber = ir.HIRInfo.valueNumbers.getValueNumber(address);
301      } else if (GetField.conforms(s)) {
302        Object address = GetField.getRef(s);
303        valueNumber = ir.HIRInfo.valueNumbers.getValueNumber(address);
304      } else if (PutField.conforms(s)) {
305        Object address = PutField.getRef(s);
306        valueNumber = ir.HIRInfo.valueNumbers.getValueNumber(address);
307      } else if (GetStatic.conforms(s)) {
308        valueNumber = 0;
309      } else if (PutStatic.conforms(s)) {
310        valueNumber = 0;
311      } else if (ALoad.conforms(s)) {
312        Object address = ALoad.getArray(s);
313        valueNumber = ir.HIRInfo.valueNumbers.getValueNumber(address);
314        index = ALoad.getIndex(s);
315      }
316      if (index == null) {
317        // Load/Store
318        if (UseRepSet.containsMatchingUse(H[0].getHeapVariable(), valueNumber)) {
319          Operand value = null;
320          if (PutField.conforms(s)) {
321            value = PutField.getValue(s);
322          } else if (PutStatic.conforms(s)) {
323            value = PutStatic.getValue(s);
324          } else if (GetField.conforms(s) || GetStatic.conforms(s)) {
325            value = ResultCarrier.getResult(s);
326          }
327          TypeReference type = value.getType();
328          Register r = findOrCreateRegister(H[0].getHeapType(), valueNumber, registers, ir.regpool, type);
329          appendMove(r, value, s);
330        }
331      } else {
332        // ALoad / AStore
333        int v1 = valueNumber;
334        int v2 = ir.HIRInfo.valueNumbers.getValueNumber(index);
335        if (UseRepSet.containsMatchingUse(H[0].getHeapVariable(), v1, v2)) {
336          Operand value = null;
337          if (AStore.conforms(s)) {
338            value = AStore.getValue(s);
339          } else if (ALoad.conforms(s)) {
340            value = ALoad.getResult(s);
341          }
342          TypeReference type = value.getType();
343          Register r = findOrCreateRegister(H[0].getHeapType(), v1, v2, registers, ir.regpool, type);
344          appendMove(r, value, s);
345        }
346      }
347    }
348  }
349
350  /**
351   * Append a move instruction after a store instruction that caches
352   * value in register r.
353   *
354   * @param r move target
355   * @param src move source
356   * @param store the instruction after which the move will be inserted
357   */
358  static void appendMove(Register r, Operand src, Instruction store) {
359    TypeReference type = src.getType();
360    RegisterOperand rop = new RegisterOperand(r, type);
361    store.insertAfter(Move.create(IRTools.getMoveOp(type), rop, src.copy()));
362  }
363
364  /**
365   * Given a value number, return the temporary register allocated
366   * for that value number.  Create one if necessary.
367   *
368   * @param heapType a TypeReference or RVMField identifying the array SSA
369   *                    heap type
370   * @param valueNumber the value number
371   * @param registers a mapping from value number to temporary register
372   * @param pool register pool to allocate new temporaries from
373   * @param type the type to store in the new register
374   *
375   * @return the temporary register allocated for the value number
376   */
377  static Register findOrCreateRegister(Object heapType, int valueNumber, HashMap<UseRecord, Register> registers,
378                                       GenericRegisterPool pool, TypeReference type) {
379    UseRecord key = new UseRecord(heapType, valueNumber);
380    Register result = registers.get(key);
381    if (result == null) {
382      // create a new temp and insert it in the mapping
383      result = pool.makeTemp(type).getRegister();
384      registers.put(key, result);
385    }
386    return result;
387  }
388
389  /**
390   * Given a pair of value numbers, return the temporary register
391   * allocated for that pair.  Create one if necessary.
392   *
393   * @param heapType a TypeReference identifying the array SSA
394   *                    heap type
395   * @param v1 first value number
396   * @param v2 second value number
397   * @param registers a mapping from value number to temporary register
398   * @param pool register pool to allocate new temporaries from
399   * @param type the type to store in the new register
400   * @return the temporary register allocated for the pair
401   */
402  static Register findOrCreateRegister(Object heapType, int v1, int v2, HashMap<UseRecord, Register> registers,
403                                       GenericRegisterPool pool, TypeReference type) {
404    UseRecord key = new UseRecord(heapType, v1, v2);
405    Register result = registers.get(key);
406    if (result == null) {
407      // create a new temp and insert it in the mapping
408      result = pool.makeTemp(type).getRegister();
409      registers.put(key, result);
410    }
411    return result;
412  }
413
414  // A UseRecord represents a load that will be eliminated
415  static class UseRecord {
416    final Object type;        // may be either a TypeReference or a RVMField
417    final int v1;             // first value number (object pointer)
418    final int v2;             // second value number (array index)
419    static final int NONE = -2;
420
421    UseRecord(Object type, int valueNumber) {
422      this.type = type;
423      this.v1 = valueNumber;
424      this.v2 = NONE;
425    }
426
427    UseRecord(Object type, int v1, int v2) {
428      this.type = type;
429      this.v1 = v1;
430      this.v2 = v2;
431    }
432
433    @Override
434    public boolean equals(Object o) {
435      if (o instanceof UseRecord) {
436        UseRecord u = (UseRecord) o;
437        return ((u.type.equals(type)) && (u.v1 == v1) && (u.v2 == v2));
438      }
439      return false;
440    }
441
442    @Override
443    public int hashCode() {
444      return type.hashCode() + v1 + v2;
445    }
446  }
447
448  static final class UseRecordSet {
449    final HashSet<UseRecord> set = new HashSet<UseRecord>(10);
450
451    // Does this set contain a use that has the same type as H and
452    // the given value number?
453    boolean containsMatchingUse(HeapVariable<?> H, int valueNumber) {
454      Object type = H.getHeapType();
455      UseRecord u = new UseRecord(type, valueNumber);
456      return (set.contains(u));
457    }
458
459    // Does this set contain a use that has the same type as H and
460    // the given value number pair <v1,v2>?
461    boolean containsMatchingUse(HeapVariable<?> H, int v1, int v2) {
462      Object type = H.getHeapType();
463      UseRecord u = new UseRecord(type, v1, v2);
464      return (set.contains(u));
465    }
466
467    // add a USE to the set
468    void add(HeapVariable<?> H, int valueNumber) {
469      UseRecord u = new UseRecord(H.getHeapType(), valueNumber);
470      set.add(u);
471    }
472
473    void add(HeapVariable<?> H, int v1, int v2) {
474      UseRecord u = new UseRecord(H.getHeapType(), v1, v2);
475      set.add(u);
476    }
477
478    int size() {
479      return set.size();
480    }
481  }
482
483  /**
484   * @param <T> the type for the collection returned as value in the mapping
485   * @param map a mapping from key to HashSet
486   * @param key a key into the map
487   * @return the set map(key).  create one if none exists.
488   */
489  private static <T> HashSet<T> findOrCreateIndexSet(HashMap<Object, HashSet<T>> map, Object key) {
490    HashSet<T> result = map.get(key);
491    if (result == null) {
492      result = new HashSet<T>(5);
493      map.put(key, result);
494    }
495    return result;
496  }
497
498  /**
499   * Do a quick pass over the IR, and return types that are candidates
500   * for redundant load elimination.<p>
501   *
502   * Algorithm: return those types T where
503   * <ul>
504   *   <li>there's a load L(i) of type T
505   *   <li>there's another load or store M(j) of type T, M!=L and V(i) == V(j)
506   * </ul>
507   * <p>
508   * The result contains objects of type RVMField and TypeReference, whose
509   * narrowest common ancestor is Object.
510   *
511   * @param ir the governing IR
512   *
513   * @return the types that are candidates for redundant load elimination
514   */
515  @SuppressWarnings("unchecked")
516  public static HashSet<Object> getCandidates(IR ir) {
517    GlobalValueNumberState valueNumbers = ir.HIRInfo.valueNumbers;
518    // which types have we seen loads for?
519    HashSet<Object> seenLoad = new HashSet<Object>(10);
520    // which static fields have we seen stores for?
521    HashSet<RVMField> seenStore = new HashSet<RVMField>(10);
522    HashSet<Object> resultSet = new HashSet<Object>(10);
523    HashSet<FieldReference> forbidden = new HashSet<FieldReference>(10);
524    // for each type T, indices(T) gives the set of value number (pairs)
525    // that identify the indices seen in memory accesses to type T.
526    HashMap indices = new HashMap(10);
527
528    for (Enumeration be = ir.getBasicBlocks(); be.hasMoreElements();) {
529      BasicBlock bb = (BasicBlock) be.nextElement();
530      if (!ir.options.FREQ_FOCUS_EFFORT || !bb.getInfrequent()) {
531        for (Enumeration<Instruction> e = bb.forwardInstrEnumerator(); e.hasMoreElements();) {
532          Instruction s = e.nextElement();
533          switch (s.getOpcode()) {
534            case GETFIELD_opcode: {
535              Operand ref = GetField.getRef(s);
536              FieldReference fr = GetField.getLocation(s).getFieldRef();
537              RVMField f = fr.peekResolvedField();
538              if (f == null) {
539                forbidden.add(fr);
540              } else {
541                HashSet<Integer> numbers = findOrCreateIndexSet(indices, f);
542                int v = valueNumbers.getValueNumber(ref);
543                Integer V = v;
544                if (numbers.contains(V)) {
545                  resultSet.add(f);
546                } else {
547                  numbers.add(V);
548                }
549                seenLoad.add(f);
550              }
551            }
552            break;
553            case PUTFIELD_opcode: {
554              Operand ref = PutField.getRef(s);
555              FieldReference fr = PutField.getLocation(s).getFieldRef();
556              RVMField f = fr.peekResolvedField();
557              if (f == null) {
558                forbidden.add(fr);
559              } else {
560                HashSet<Integer> numbers = findOrCreateIndexSet(indices, f);
561                int v = valueNumbers.getValueNumber(ref);
562                Integer V = v;
563                if (numbers.contains(V)) {
564                  if (seenLoad.contains(f)) {
565                    resultSet.add(f);
566                  }
567                } else {
568                  numbers.add(V);
569                }
570              }
571            }
572            break;
573            case GETSTATIC_opcode: {
574              FieldReference fr = GetStatic.getLocation(s).getFieldRef();
575              RVMField f = fr.peekResolvedField();
576              if (f == null) {
577                forbidden.add(fr);
578              } else {
579                if (seenLoad.contains(f) || seenStore.contains(f)) {
580                  resultSet.add(f);
581                }
582                seenLoad.add(f);
583              }
584            }
585            break;
586            case PUTSTATIC_opcode: {
587              FieldReference fr = PutStatic.getLocation(s).getFieldRef();
588              RVMField f = fr.peekResolvedField();
589              if (f == null) {
590                forbidden.add(fr);
591              } else {
592                if (seenLoad.contains(f)) {
593                  resultSet.add(f);
594                }
595                seenStore.add(f);
596              }
597            }
598            break;
599            case INT_ALOAD_opcode:
600            case LONG_ALOAD_opcode:
601            case FLOAT_ALOAD_opcode:
602            case DOUBLE_ALOAD_opcode:
603            case REF_ALOAD_opcode:
604            case BYTE_ALOAD_opcode:
605            case UBYTE_ALOAD_opcode:
606            case USHORT_ALOAD_opcode:
607            case SHORT_ALOAD_opcode: {
608              Operand ref = ALoad.getArray(s);
609              TypeReference type = ref.getType();
610              if (type.isArrayType()) {
611                if (!type.getArrayElementType().isPrimitiveType()) {
612                  type = TypeReference.JavaLangObjectArray;
613                }
614              }
615              Operand index = ALoad.getIndex(s);
616
617              HashSet<ValueNumberPair> numbers = findOrCreateIndexSet(indices, type);
618              int v1 = valueNumbers.getValueNumber(ref);
619              int v2 = valueNumbers.getValueNumber(index);
620              ValueNumberPair V = new ValueNumberPair(v1, v2);
621              if (numbers.contains(V)) {
622                resultSet.add(type);
623              } else {
624                numbers.add(V);
625              }
626              seenLoad.add(type);
627            }
628
629            break;
630
631            case INT_ASTORE_opcode:
632            case LONG_ASTORE_opcode:
633            case FLOAT_ASTORE_opcode:
634            case DOUBLE_ASTORE_opcode:
635            case REF_ASTORE_opcode:
636            case BYTE_ASTORE_opcode:
637            case SHORT_ASTORE_opcode:
638
639            {
640              Operand ref = AStore.getArray(s);
641              TypeReference type = ref.getType();
642              if (type.isArrayType()) {
643                if (!type.getArrayElementType().isPrimitiveType()) {
644                  type = TypeReference.JavaLangObjectArray;
645                }
646              }
647              Operand index = AStore.getIndex(s);
648
649              HashSet<ValueNumberPair> numbers = findOrCreateIndexSet(indices, type);
650              int v1 = valueNumbers.getValueNumber(ref);
651              int v2 = valueNumbers.getValueNumber(index);
652              ValueNumberPair V = new ValueNumberPair(v1, v2);
653
654              if (numbers.contains(V)) {
655                if (seenLoad.contains(type)) {
656                  resultSet.add(type);
657                }
658              } else {
659                numbers.add(V);
660              }
661            }
662            break;
663
664            default:
665              break;
666          }
667        }
668      }
669    }
670
671    // If we have found an unresolved field reference, then conservatively
672    // remove all fields that it might refer to from the resultSet.
673    for (final FieldReference fieldReference : forbidden) {
674      for (Iterator i2 = resultSet.iterator(); i2.hasNext();) {
675        Object it = i2.next();
676        if (it instanceof RVMField) {
677          final RVMField field = (RVMField) it;
678          if (!fieldReference.definitelyDifferent(field.getMemberRef().asFieldReference())) {
679            i2.remove();
680          }
681        }
682      }
683    }
684
685    return resultSet;
686  }
687
688  /**
689   * This class sets up the IR state prior to entering SSA for load
690   * elimination
691   */
692  public static class LoadEliminationPreparation extends CompilerPhase {
693
694    public LoadEliminationPreparation(int round) {
695      super(new Object[]{round});
696      this.round = round;
697    }
698
699    /**
700     * Constructor for this compiler phase
701     */
702    private static final Constructor<CompilerPhase> constructor =
703        getCompilerPhaseConstructor(LoadEliminationPreparation.class, new Class[]{Integer.TYPE});
704
705    /**
706     * Get a constructor object for this compiler phase
707     * @return compiler phase constructor
708     */
709    @Override
710    public Constructor<CompilerPhase> getClassConstructor() {
711      return constructor;
712    }
713
714    @Override
715    public final boolean shouldPerform(OptOptions options) {
716      return options.SSA_LOAD_ELIMINATION;
717    }
718
719    @Override
720    public final String getName() {
721      return "Load Elimination Preparation";
722    }
723
724    private final int round;
725
726    @Override
727    public final void perform(IR ir) {
728      // register in the IR the SSA properties we need for load
729      // elimination
730      ir.desiredSSAOptions = new SSAOptions();
731      ir.desiredSSAOptions.setScalarsOnly(false);
732      ir.desiredSSAOptions.setBackwards(false);
733      ir.desiredSSAOptions.setInsertUsePhis(true);
734      ir.desiredSSAOptions.setHeapTypes(LoadElimination.getCandidates(ir));
735      ir.desiredSSAOptions.setAbort((round > ir.options.SSA_LOAD_ELIMINATION_ROUNDS) ||
736                                    (!ir.HIRInfo.loadEliminationDidSomething));
737    }
738  }
739
740  /**
741   * This class sets up the IR state prior to entering SSA for GVN.
742   */
743  public static class GVNPreparation extends CompilerPhase {
744
745    @Override
746    public final boolean shouldPerform(OptOptions options) {
747      return options.SSA_LOAD_ELIMINATION;
748    }
749
750    @Override
751    public final String getName() {
752      return "GVN Preparation";
753    }
754
755    private final int round;
756
757    public GVNPreparation(int round) {
758      super(new Object[]{round});
759      this.round = round;
760    }
761
762    /**
763     * Constructor for this compiler phase
764     */
765    private static final Constructor<CompilerPhase> constructor =
766        getCompilerPhaseConstructor(GVNPreparation.class, new Class[]{Integer.TYPE});
767
768    /**
769     * Get a constructor object for this compiler phase
770     * @return compiler phase constructor
771     */
772    @Override
773    public Constructor<CompilerPhase> getClassConstructor() {
774      return constructor;
775    }
776
777    @Override
778    public final void perform(IR ir) {
779      // register in the IR the SSA properties we need for load
780      // elimination
781      ir.desiredSSAOptions = new SSAOptions();
782      ir.desiredSSAOptions.setScalarsOnly(true);
783      ir.desiredSSAOptions.setBackwards(false);
784      ir.desiredSSAOptions.setInsertUsePhis(false);
785      ir.desiredSSAOptions.setAbort((round > ir.options.SSA_LOAD_ELIMINATION_ROUNDS) ||
786                                    (!ir.HIRInfo.loadEliminationDidSomething));
787    }
788  }
789}