/**
  * Transform to eliminate redundant branches passed on GVNs and dominator information.
  *
  * @param ir The IR on which to apply the phase
  */
 public void perform(IR ir) {
   // (1) Remove redundant conditional branches and locally fix the PHIs
   GlobalValueNumberState gvns = ir.HIRInfo.valueNumbers;
   DominatorTree dt = ir.HIRInfo.dominatorTree;
   for (BasicBlockEnumeration bbs = ir.getBasicBlocks(); bbs.hasMoreElements(); ) {
     BasicBlock candBB = bbs.next();
     Instruction candTest = candBB.firstBranchInstruction();
     if (candTest == null) continue;
     if (!(IfCmp.conforms(candTest) || InlineGuard.conforms(candTest))) continue;
     GVCongruenceClass cc = gvns.congruenceClass(candTest);
     if (cc.size() > 1) {
       for (ValueGraphVertex vertex : cc) {
         Instruction poss = (Instruction) vertex.getName();
         if (poss != candTest) {
           BasicBlock notTaken = getNotTakenBlock(poss);
           BasicBlock taken = poss.getBranchTarget();
           if (taken == notTaken) continue; // both go to same block, so we don't know anything!
           if (notTaken.hasOneIn() && dt.dominates(notTaken, candBB)) {
             if (DEBUG)
               VM.sysWrite(candTest + " is dominated by not-taken branch of " + poss + "\n");
             removeCondBranch(candBB, candTest, ir, poss);
             cc.removeVertex(gvns.valueGraph.getVertex(candTest));
             break;
           }
           if (taken.hasOneIn() && dt.dominates(taken, candBB)) {
             if (DEBUG)
               VM.sysWrite(candTest + " is dominated by taken branch of " + poss + "\n");
             takeCondBranch(candBB, candTest, ir);
             cc.removeVertex(gvns.valueGraph.getVertex(candTest));
             break;
           }
         }
       }
     }
   }
   // (2) perform a Depth-first search of the control flow graph,
   //     and remove any nodes we have made unreachable
   removeUnreachableCode(ir);
 }
  /**
   * Do a quick pass over the IR, and return types that are candidates for redundant load
   * elimination. Algorithm: return those types T where 1) there's a load L(i) of type T 2) there's
   * another load or store M(j) of type T, M!=L and V(i) == V(j)
   *
   * <p>The result contains objects of type RVMField and TypeReference, whose narrowest common
   * ancestor is Object.
   */
  @SuppressWarnings("unchecked")
  public static HashSet<Object> getCandidates(IR ir) {
    GlobalValueNumberState valueNumbers = ir.HIRInfo.valueNumbers;
    // which types have we seen loads for?
    HashSet<Object> seenLoad = new HashSet<Object>(10);
    // which static fields have we seen stores for?
    HashSet<RVMField> seenStore = new HashSet<RVMField>(10);
    HashSet<Object> resultSet = new HashSet<Object>(10);
    HashSet<FieldReference> forbidden = new HashSet<FieldReference>(10);
    // for each type T, indices(T) gives the set of value number (pairs)
    // that identify the indices seen in memory accesses to type T.
    HashMap indices = new HashMap(10);

    for (Enumeration be = ir.getBasicBlocks(); be.hasMoreElements(); ) {
      BasicBlock bb = (BasicBlock) be.nextElement();
      if (!ir.options.FREQ_FOCUS_EFFORT || !bb.getInfrequent()) {
        for (InstructionEnumeration e = bb.forwardInstrEnumerator(); e.hasMoreElements(); ) {
          Instruction s = e.next();
          switch (s.operator().opcode) {
            case GETFIELD_opcode:
              {
                Operand ref = GetField.getRef(s);
                FieldReference fr = GetField.getLocation(s).getFieldRef();
                RVMField f = fr.peekResolvedField();
                if (f == null) {
                  forbidden.add(fr);
                } else {
                  HashSet<Integer> numbers = findOrCreateIndexSet(indices, f);
                  int v = valueNumbers.getValueNumber(ref);
                  Integer V = v;
                  if (numbers.contains(V)) {
                    resultSet.add(f);
                  } else {
                    numbers.add(V);
                  }
                  seenLoad.add(f);
                }
              }
              break;
            case PUTFIELD_opcode:
              {
                Operand ref = PutField.getRef(s);
                FieldReference fr = PutField.getLocation(s).getFieldRef();
                RVMField f = fr.peekResolvedField();
                if (f == null) {
                  forbidden.add(fr);
                } else {
                  HashSet<Integer> numbers = findOrCreateIndexSet(indices, f);
                  int v = valueNumbers.getValueNumber(ref);
                  Integer V = v;
                  if (numbers.contains(V)) {
                    if (seenLoad.contains(f)) {
                      resultSet.add(f);
                    }
                  } else {
                    numbers.add(V);
                  }
                }
              }
              break;
            case GETSTATIC_opcode:
              {
                FieldReference fr = GetStatic.getLocation(s).getFieldRef();
                RVMField f = fr.peekResolvedField();
                if (f == null) {
                  forbidden.add(fr);
                } else {
                  if (seenLoad.contains(f) || seenStore.contains(f)) {
                    resultSet.add(f);
                  }
                  seenLoad.add(f);
                }
              }
              break;
            case PUTSTATIC_opcode:
              {
                FieldReference fr = PutStatic.getLocation(s).getFieldRef();
                RVMField f = fr.peekResolvedField();
                if (f == null) {
                  forbidden.add(fr);
                } else {
                  if (seenLoad.contains(f)) {
                    resultSet.add(f);
                  }
                  seenStore.add(f);
                }
              }
              break;
            case INT_ALOAD_opcode:
            case LONG_ALOAD_opcode:
            case FLOAT_ALOAD_opcode:
            case DOUBLE_ALOAD_opcode:
            case REF_ALOAD_opcode:
            case BYTE_ALOAD_opcode:
            case UBYTE_ALOAD_opcode:
            case USHORT_ALOAD_opcode:
            case SHORT_ALOAD_opcode:
              {
                Operand ref = ALoad.getArray(s);
                TypeReference type = ref.getType();
                if (type.isArrayType()) {
                  if (!type.getArrayElementType().isPrimitiveType()) {
                    type = TypeReference.JavaLangObjectArray;
                  }
                }
                Operand index = ALoad.getIndex(s);

                HashSet<ValueNumberPair> numbers = findOrCreateIndexSet(indices, type);
                int v1 = valueNumbers.getValueNumber(ref);
                int v2 = valueNumbers.getValueNumber(index);
                ValueNumberPair V = new ValueNumberPair(v1, v2);
                if (numbers.contains(V)) {
                  resultSet.add(type);
                } else {
                  numbers.add(V);
                }
                seenLoad.add(type);
              }

              break;

            case INT_ASTORE_opcode:
            case LONG_ASTORE_opcode:
            case FLOAT_ASTORE_opcode:
            case DOUBLE_ASTORE_opcode:
            case REF_ASTORE_opcode:
            case BYTE_ASTORE_opcode:
            case SHORT_ASTORE_opcode:
              {
                Operand ref = AStore.getArray(s);
                TypeReference type = ref.getType();
                if (type.isArrayType()) {
                  if (!type.getArrayElementType().isPrimitiveType()) {
                    type = TypeReference.JavaLangObjectArray;
                  }
                }
                Operand index = AStore.getIndex(s);

                HashSet<ValueNumberPair> numbers = findOrCreateIndexSet(indices, type);
                int v1 = valueNumbers.getValueNumber(ref);
                int v2 = valueNumbers.getValueNumber(index);
                ValueNumberPair V = new ValueNumberPair(v1, v2);

                if (numbers.contains(V)) {
                  if (seenLoad.contains(type)) {
                    resultSet.add(type);
                  }
                } else {
                  numbers.add(V);
                }
              }
              break;

            default:
              break;
          }
        }
      }
    }

    // If we have found an unresolved field reference, then conservatively
    // remove all fields that it might refer to from the resultSet.
    for (final FieldReference fieldReference : forbidden) {
      for (Iterator i2 = resultSet.iterator(); i2.hasNext(); ) {
        Object it = i2.next();
        if (it instanceof RVMField) {
          final RVMField field = (RVMField) it;
          if (!fieldReference.definitelyDifferent(field.getMemberRef().asFieldReference())) {
            i2.remove();
          }
        }
      }
    }

    return resultSet;
  }
  /**
   * Walk over each instruction. If its a USE (load) of a heap variable and the value is available,
   * then replace the load with a move from a register.
   *
   * <p>POSTCONDITION: sets up the mapping 'registers' from value number to temporary register
   *
   * @param ir the IR
   * @param available information on which values are available
   * @param registers a place to store information about temp registers
   */
  static UseRecordSet replaceLoads(
      IR ir, DF_Solution available, HashMap<UseRecord, Register> registers) {
    UseRecordSet result = new UseRecordSet();
    SSADictionary ssa = ir.HIRInfo.dictionary;
    GlobalValueNumberState valueNumbers = ir.HIRInfo.valueNumbers;
    for (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements(); ) {
      Instruction s = e.nextElement();
      if (!GetField.conforms(s) && !GetStatic.conforms(s) && !ALoad.conforms(s)) {
        continue;
      }
      // this instruction is a USE of heap variable H.
      // get the lattice cell that holds the available indices
      // for this heap variable
      HeapOperand<?>[] H = ssa.getHeapUses(s);
      if (H == null) {
        // this case can happen due to certain magics that insert
        // INT_LOAD instructions in HIR
        // TODO: clean up HIR representation of these magics
        continue;
      }
      if (H.length != 1) {
        throw new OptimizingCompilerException(
            "LoadElimination: load with wrong number of heap uses");
      }
      if (GetField.conforms(s) || GetStatic.conforms(s)) {
        int valueNumber = -1;
        if (GetField.conforms(s)) {
          Object address = GetField.getRef(s);
          valueNumber = valueNumbers.getValueNumber(address);
        } else {
          // for getStatic, always use the value number 0
          valueNumber = 0;
        }
        ObjectCell cell = (ObjectCell) available.lookup(H[0].getHeapVariable());
        if (cell == null) {
          continue; // nothing available
        }

        // .. if H{valueNumber} is available ...
        if (cell.contains(valueNumber)) {
          result.add(H[0].getHeapVariable(), valueNumber);
          TypeReference type = ResultCarrier.getResult(s).getType();
          Register r =
              findOrCreateRegister(H[0].getHeapType(), valueNumber, registers, ir.regpool, type);
          if (DEBUG) {
            System.out.println("ELIMINATING LOAD " + s);
          }
          replaceLoadWithMove(r, s);
        }
      } else { // ALoad.conforms(s)
        Object array = ALoad.getArray(s);
        Object index = ALoad.getIndex(s);
        ArrayCell cell = (ArrayCell) available.lookup(H[0].getHeapVariable());
        if (cell == null) {
          continue; // nothing available
        }
        int v1 = valueNumbers.getValueNumber(array);
        int v2 = valueNumbers.getValueNumber(index);
        // .. if H{<v1,v2>} is available ...
        if (cell.contains(v1, v2)) {
          result.add(H[0].getHeapVariable(), v1, v2);
          TypeReference type = ALoad.getResult(s).getType();
          Register r =
              findOrCreateRegister(
                  H[0].getHeapVariable().getHeapType(), v1, v2, registers, ir.regpool, type);
          if (DEBUG) {
            System.out.println("ELIMINATING LOAD " + s);
          }
          replaceLoadWithMove(r, s);
        }
      }
    }
    return result;
  }