예제 #1
0
  /**
   * Adds a move instruction after the phi insn block.
   *
   * @param result move destination
   * @param source move source
   */
  public void addMoveToBeginning(RegisterSpec result, RegisterSpec source) {
    if (result.getReg() == source.getReg()) {
      // Sometimes we end up with no-op moves. Ignore them here.
      return;
    }

    RegisterSpecList sources = RegisterSpecList.make(source);
    NormalSsaInsn toAdd =
        new NormalSsaInsn(
            new PlainInsn(Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, sources),
            this);

    insns.add(getCountPhiInsns(), toAdd);
    movesFromPhisAtBeginning++;
  }
예제 #2
0
  /**
   * Adds a move instruction to the end of this basic block, just before the last instruction. If
   * the result of the final instruction is the source in question, then the move is placed at the
   * beginning of the primary successor block. This is for unversioned registers.
   *
   * @param result move destination
   * @param source move source
   */
  public void addMoveToEnd(RegisterSpec result, RegisterSpec source) {

    if (result.getReg() == source.getReg()) {
      // Sometimes we end up with no-op moves. Ignore them here.
      return;
    }

    /*
     * The last Insn has to be a normal SSA insn: a phi can't branch or
     * return or cause an exception, etc.
     */
    NormalSsaInsn lastInsn;
    lastInsn = (NormalSsaInsn) insns.get(insns.size() - 1);

    if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) {
      /*
       * The final insn in this block has a source or result register, and
       * the moves we may need to place and schedule may interfere. We
       * need to insert this instruction at the beginning of the primary
       * successor block instead. We know this is safe, because when we
       * edge-split earlier, we ensured that each successor has only us as
       * a predecessor.
       */

      for (int i = successors.nextSetBit(0); i >= 0; i = successors.nextSetBit(i + 1)) {

        SsaBasicBlock succ;

        succ = parent.getBlocks().get(i);
        succ.addMoveToBeginning(result, source);
      }
    } else {
      /*
       * We can safely add a move to the end of the block just before the
       * last instruction, because the final insn does not assign to
       * anything.
       */
      RegisterSpecList sources = RegisterSpecList.make(source);
      NormalSsaInsn toAdd =
          new NormalSsaInsn(
              new PlainInsn(Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, sources),
              this);

      insns.add(insns.size() - 1, toAdd);

      movesFromPhisAtEnd++;
    }
  }
예제 #3
0
  /**
   * Sorts move instructions added via {@code addMoveToEnd} during phi removal so that results don't
   * overwrite sources that are used. For use after all phis have been removed and all calls to
   * addMoveToEnd() have been made.
   *
   * <p>This is necessary because copy-propogation may have left us in a state where the same basic
   * block has the same register as a phi operand and a result. In this case, the register in the
   * phi operand always refers value before any other phis have executed.
   */
  public void scheduleMovesFromPhis() {
    if (movesFromPhisAtBeginning > 1) {
      List<SsaInsn> toSchedule;

      toSchedule = insns.subList(0, movesFromPhisAtBeginning);

      scheduleUseBeforeAssigned(toSchedule);

      SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning);

      /*
       * TODO: It's actually possible that this case never happens,
       * because a move-exception block, having only one predecessor in
       * SSA form, perhaps is never on a dominance frontier.
       */
      if (firstNonPhiMoveInsn.isMoveException()) {
        if (true) {
          /*
           * We've yet to observe this case, and if it can occur the
           * code written to handle it probably does not work.
           */
          throw new RuntimeException("Unexpected: moves from " + "phis before move-exception");
        } else {
          /*
           * A move-exception insn must be placed first in this block
           * We need to move it there, and deal with possible
           * interference.
           */
          boolean moveExceptionInterferes = false;

          int moveExceptionResult = firstNonPhiMoveInsn.getResult().getReg();

          /*
           * Does the move-exception result reg interfere with the phi
           * moves?
           */
          for (SsaInsn insn : toSchedule) {
            if (insn.isResultReg(moveExceptionResult) || insn.isRegASource(moveExceptionResult)) {
              moveExceptionInterferes = true;
              break;
            }
          }

          if (!moveExceptionInterferes) {
            // This is the easy case.
            insns.remove(movesFromPhisAtBeginning);
            insns.add(0, firstNonPhiMoveInsn);
          } else {
            /*
             * We need to move the result to a spare reg and move it
             * back.
             */
            RegisterSpec originalResultSpec = firstNonPhiMoveInsn.getResult();
            int spareRegister = parent.borrowSpareRegister(originalResultSpec.getCategory());

            // We now move it to a spare register.
            firstNonPhiMoveInsn.changeResultReg(spareRegister);
            RegisterSpec tempSpec = firstNonPhiMoveInsn.getResult();

            insns.add(0, firstNonPhiMoveInsn);

            // And here we move it back.

            NormalSsaInsn toAdd =
                new NormalSsaInsn(
                    new PlainInsn(
                        Rops.opMove(tempSpec.getType()),
                        SourcePosition.NO_INFO,
                        originalResultSpec,
                        RegisterSpecList.make(tempSpec)),
                    this);

            /*
             * Place it immediately after the phi-moves, overwriting
             * the move-exception that was there.
             */
            insns.set(movesFromPhisAtBeginning + 1, toAdd);
          }
        }
      }
    }

    if (movesFromPhisAtEnd > 1) {
      scheduleUseBeforeAssigned(
          insns.subList(insns.size() - movesFromPhisAtEnd - 1, insns.size() - 1));
    }

    // Return registers borrowed here and in scheduleUseBeforeAssigned().
    parent.returnSpareRegisters();
  }
예제 #4
0
  /**
   * Ensures that all move operations in this block occur such that reads of any register happen
   * before writes to that register. NOTE: caller is expected to returnSpareRegisters()! TODO: See
   * Briggs, et al "Practical Improvements to the Construction and Destruction of Static Single
   * Assignment Form" section 5. a) This can be done in three passes.
   *
   * @param toSchedule List of instructions. Must consist only of moves.
   */
  private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) {
    BitSet regsUsedAsSources = new BitSet(parent.getRegCount());

    // TODO: Get rid of this.
    BitSet regsUsedAsResults = new BitSet(parent.getRegCount());

    int sz = toSchedule.size();

    int insertPlace = 0;

    while (insertPlace < sz) {
      int oldInsertPlace = insertPlace;

      // Record all registers used as sources in this block.
      for (int i = insertPlace; i < sz; i++) {
        setRegsUsed(regsUsedAsSources, toSchedule.get(i).getSources().get(0));

        setRegsUsed(regsUsedAsResults, toSchedule.get(i).getResult());
      }

      /*
       * If there are no circular dependencies, then there exists n
       * instructions where n > 1 whose result is not used as a source.
       */
      for (int i = insertPlace; i < sz; i++) {
        SsaInsn insn = toSchedule.get(i);

        /*
         * Move these n registers to the front, since they overwrite
         * nothing.
         */
        if (!checkRegUsed(regsUsedAsSources, insn.getResult())) {
          Collections.swap(toSchedule, i, insertPlace++);
        }
      }

      /*
       * If we've made no progress in this iteration, there's a circular
       * dependency. Split it using the temp reg.
       */
      if (oldInsertPlace == insertPlace) {

        SsaInsn insnToSplit = null;

        // Find an insn whose result is used as a source.
        for (int i = insertPlace; i < sz; i++) {
          SsaInsn insn = toSchedule.get(i);
          if (checkRegUsed(regsUsedAsSources, insn.getResult())
              && checkRegUsed(regsUsedAsResults, insn.getSources().get(0))) {

            insnToSplit = insn;
            /*
             * We're going to split this insn; move it to the front.
             */
            Collections.swap(toSchedule, insertPlace, i);
            break;
          }
        }

        // At least one insn will be set above.

        RegisterSpec result = insnToSplit.getResult();
        RegisterSpec tempSpec = result.withReg(parent.borrowSpareRegister(result.getCategory()));

        NormalSsaInsn toAdd =
            new NormalSsaInsn(
                new PlainInsn(
                    Rops.opMove(result.getType()),
                    SourcePosition.NO_INFO,
                    tempSpec,
                    insnToSplit.getSources()),
                this);

        toSchedule.add(insertPlace++, toAdd);

        RegisterSpecList newSources = RegisterSpecList.make(tempSpec);

        NormalSsaInsn toReplace =
            new NormalSsaInsn(
                new PlainInsn(
                    Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, newSources),
                this);

        toSchedule.set(insertPlace, toReplace);

        // The size changed.
        sz = toSchedule.size();
      }

      regsUsedAsSources.clear();
      regsUsedAsResults.clear();
    }
  }