コード例 #1
0
  /**
   * Attempt to generate a straight-line sequence using conditional move instructions, to replace a
   * diamond control flow structure.
   *
   * <p>Suppose we have the following code, where e{n} is an expression:
   *
   * <pre>
   * if (a op b) {
   *   x = e2;
   *   y = e3;
   * } else {
   *   z = e4;
   *   x = e5;
   * }
   * </pre>
   *
   * We would transform this to:
   *
   * <pre>
   * t1 = a;
   * t2 = b;
   * t3 = e2;
   * t4 = e3;
   * t5 = e4;
   * t6 = e5;
   * COND MOVE [if (t1 op t2) x := t3 else x := t6 ];
   * COND MOVE [if (t1 op t2) y := t4 else y := y];
   * COND MOVE [if (t1 op t2) z := z  else z := t5];
   * </pre>
   *
   * <p>Note that we rely on other optimizations (eg. copy propagation) to clean up some of this
   * unnecessary mess.
   *
   * <p>Note that in this example, we've increased the shortest path by 2 expression evaluations, 2
   * moves, and 3 cond moves, but eliminated one conditional branch.
   *
   * <p>We apply a cost heuristic to guide this transformation: We will eliminate a conditional
   * branch iff it increases the shortest path by no more than 'k' operations. Currently, we count
   * each instruction (alu, move, or cond move) as 1 evaluation. The parameter k is specified by
   * OPT\_Options.COND_MOVE_CUTOFF.
   *
   * <p>In the example above, since we've increased the shortest path by 6 instructions, we will
   * only perform the transformation if k >= 7.
   *
   * <p>TODO items
   *
   * <ul>
   *   <li>consider smarter cost heuristics
   *   <li>enhance downstream code generation to avoid redundant evaluation of condition codes.
   * </ul>
   *
   * @param ir governing IR
   * @param bb basic block of cb
   * @param cb conditional branch instruction
   * @return true if the transformation succeeds, false otherwise
   */
  private boolean generateCondMove(IR ir, BasicBlock bb, Instruction cb) {
    final boolean VERBOSE = false;
    if (!VM.BuildForIA32) return false;
    if (!IfCmp.conforms(cb)) return false;

    if (VERBOSE) System.out.println("CondMove: Looking to optimize " + cb);
    // Don't generate CMOVs for branches that can be folded.
    if (IfCmp.getVal1(cb).isConstant() && IfCmp.getVal2(cb).isConstant()) {
      if (VERBOSE) System.out.println("CondMove: fail - could be folded");
      return false;
    }

    // see if bb is the root of an if-then-else.
    Diamond diamond = Diamond.buildDiamond(bb);
    if (diamond == null) {
      if (VERBOSE) System.out.println("CondMove: fail - no diamond");
      return false;
    }
    BasicBlock taken = diamond.getTaken();
    BasicBlock notTaken = diamond.getNotTaken();

    // do not perform the transformation if either branch of the diamond
    // has a taboo instruction (eg., a PEI, store or divide).
    if (taken != null && hasCMTaboo(taken)) {
      if (VERBOSE) System.out.println("CondMove: fail - taken branch has taboo instruction");
      return false;
    }
    if (notTaken != null && hasCMTaboo(notTaken)) {
      if (VERBOSE) System.out.println("CondMove: fail - not taken branch has taboo instruction");
      return false;
    }

    ConditionOperand cond = IfCmp.getCond(cb);

    // Do not generate when we don't know the branch probability or
    // when branch probability is high. CMOVs reduce performance of
    // the out-of-order engine (Intel Optimization Guide -
    // Assembly/Compiler Coding Rule 2).
    // Ignore in the case of an abs() method as we can create tighter
    // instructions.
    BranchProfileOperand profile = IfCmp.getBranchProfile(cb);
    if ((Math.abs(profile.takenProbability - 0.5) >= ir.options.CONTROL_WELL_PREDICTED_CUTOFF)
        && !(cb.position != null
            && cb.position.method.getName() == ABS
            && cond.isFLOATINGPOINT())) {
      if (VERBOSE)
        System.out.println(
            "CondMove: fail - branch could be well predicted by branch predictor: "
                + profile.takenProbability);
      return false;
    }

    // if we must generate FCMP, make sure the condition code is OK
    if (cond.isFLOATINGPOINT()) {
      if (!fpConditionOK(cond)) {
        // Condition not OK, but maybe if we flip the operands
        if (!fpConditionOK(cond.flipOperands())) {
          // still not ok so flip operands back
          cond.flipOperands();
          // give up or for SSE2 check if this is a floating point compare
          // controlling just floating point moves
          if (!VM.BuildForSSE2Full
              || hasFloatingPointDef(taken, true)
              || hasFloatingPointDef(notTaken, true)) {
            if (VERBOSE) System.out.println("CondMove: fail - fp condition not OK: " + cond);
            return false;
          }
        } else {
          // flip operands
          Operand val1 = IfCmp.getVal1(cb);
          Operand val2 = IfCmp.getVal2(cb);
          IfCmp.setVal1(cb, val2);
          IfCmp.setVal2(cb, val1);
        }
      }
    }

    if (!cond.isFLOATINGPOINT()) {
      // Can only generate moves of floating point values for floating point
      // compares or for unsigned compares in x87
      if (VM.BuildForSSE2Full || !cond.isUNSIGNED()) {
        if (hasFloatingPointDef(taken, false) || hasFloatingPointDef(notTaken, false)) {
          if (VERBOSE)
            System.out.println(
                "CondMove: fail - not allowed integer condition controlling floating conditional move");
          return false;
        }
      }
    }

    // For now, do not generate CMOVs for longs.
    if (hasLongDef(taken) || hasLongDef(notTaken)) {
      return false;
    }

    // count the number of expression evaluations in each side of the
    // diamond
    int takenCost = 0;
    int notTakenCost = 0;
    if (taken != null) takenCost = evaluateCost(taken);
    if (notTaken != null) notTakenCost = evaluateCost(notTaken);

    // evaluate whether it's profitable.
    int shortestCost = Math.min(takenCost, notTakenCost);
    int xformCost = 2 * (takenCost + notTakenCost);
    int k = ir.options.CONTROL_COND_MOVE_CUTOFF;
    if (xformCost - shortestCost > k) {
      if (VERBOSE) System.out.println("CondMove: fail - cost too high");
      return false;
    }

    // Perform the transformation!
    doCondMove(ir, diamond, cb);
    return true;
  }