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