/**
  * Generate a boolean operation opcode
  *
  * <pre>
  * 1) IF br != 0 THEN x=1 ELSE x=0       replaced by INT_MOVE x=br
  *    IF br == 0 THEN x=0 ELSE x=1
  * 2) IF br == 0 THEN x=1 ELSE x=0       replaced by BOOLEAN_NOT x=br
  *    IF br != 0 THEN x=0 ELSE x=1
  * 3) IF v1 ~ v2 THEN x=1 ELSE x=0       replaced by BOOLEAN_CMP x=v1,v2,~
  * </pre>
  *
  * @param cb conditional branch instruction
  * @param res the operand for result
  * @param val1 value being compared
  * @param val2 value being compared with
  * @param cond comparison condition
  */
 private void booleanCompareHelper(
     Instruction cb, RegisterOperand res, Operand val1, Operand val2, ConditionOperand cond) {
   if ((val1 instanceof RegisterOperand)
       && ((RegisterOperand) val1).getType().isBooleanType()
       && (val2 instanceof IntConstantOperand)) {
     int value = ((IntConstantOperand) val2).value;
     if (VM.VerifyAssertions && (value != 0) && (value != 1)) {
       throw new OptimizingCompilerException("Invalid boolean value");
     }
     int c = cond.evaluate(value, 0);
     if (c == ConditionOperand.TRUE) {
       Unary.mutate(cb, BOOLEAN_NOT, res, val1);
       return;
     } else if (c == ConditionOperand.FALSE) {
       Move.mutate(cb, INT_MOVE, res, val1);
       return;
     }
   }
   BooleanCmp.mutate(
       cb,
       (cb.operator() == REF_IFCMP) ? BOOLEAN_CMP_ADDR : BOOLEAN_CMP_INT,
       res,
       val1,
       val2,
       cond,
       new BranchProfileOperand());
 }
  /**
   * 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;
  }
  /**
   * Attempt to generate a boolean compare opcode from a conditional branch.
   *
   * <pre>
   * 1)   IF .. GOTO A          replaced by  BOOLEAN_CMP x=..
   *      x = 0
   *      GOTO B
   *   A: x = 1
   *   B: ...
   * </pre>
   *
   * <p>Precondition: <code>IfCmp.conforms(<i>cb</i>)</code>
   *
   * @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 generateBooleanCompare(IR ir, BasicBlock bb, Instruction cb, BasicBlock tb) {

    if ((cb.operator() != INT_IFCMP) && (cb.operator() != REF_IFCMP)) {
      return false;
    }
    // make sure this is the last branch in the block
    if (cb.nextInstructionInCodeOrder().operator() != BBEND) {
      return false;
    }
    Operand val1 = IfCmp.getVal1(cb);
    Operand val2 = IfCmp.getVal2(cb);
    ConditionOperand condition = IfCmp.getCond(cb);
    // "not taken" path
    BasicBlock fb = cb.getBasicBlock().getNotTakenNextBlock();
    // make sure it's a diamond
    if (tb.getNumberOfNormalOut() != 1) {
      return false;
    }
    if (fb.getNumberOfNormalOut() != 1) {
      return false;
    }
    BasicBlock jb = fb.getNormalOut().nextElement(); // join block
    // make sure it's a diamond
    if (!tb.pointsOut(jb)) {
      return false;
    }
    Instruction ti = tb.firstRealInstruction();
    Instruction fi = fb.firstRealInstruction();
    // make sure the instructions in target blocks are either both moves
    // or both returns
    if (ti == null || fi == null) {
      return false;
    }
    if (ti.operator() != fi.operator()) {
      return false;
    }
    if (ti.operator != RETURN && ti.operator() != INT_MOVE) {
      return false;
    }
    //
    // WARNING: This code is currently NOT exercised!
    //
    if (ti.operator() == RETURN) {
      // make sure each of the target blocks contains only one instruction
      if (ti != tb.lastRealInstruction()) {
        return false;
      }
      if (fi != fb.lastRealInstruction()) {
        return false;
      }
      Operand tr = Return.getVal(ti);
      Operand fr = Return.getVal(fi);
      // make sure we're returning constants
      if (!(tr instanceof IntConstantOperand) || !(fr instanceof IntConstantOperand)) {
        return false;
      }
      int tv = ((IntConstantOperand) tr).value;
      int fv = ((IntConstantOperand) fr).value;
      if (!((tv == 1 && fv == 0) || (tv == 1 && fv == 0))) {
        return false;
      }
      RegisterOperand t = ir.regpool.makeTemp(TypeReference.Boolean);
      // Cases 1) and 2)
      if (tv == 0) {
        condition = condition.flipCode();
      }
      booleanCompareHelper(cb, t, val1.copy(), val2.copy(), condition);
      cb.insertAfter(Return.create(RETURN, t.copyD2U()));
    } else { // (ti.operator() == INT_MOVE)
      // make sure each of the target blocks only does the move
      if (ti != tb.lastRealInstruction() && ti.nextInstructionInCodeOrder().operator() != GOTO) {
        return false;
      }
      if (fi != fb.lastRealInstruction() && fi.nextInstructionInCodeOrder().operator() != GOTO) {
        return false;
      }
      RegisterOperand t = Move.getResult(ti);
      // make sure both moves are to the same register
      if (t.getRegister() != Move.getResult(fi).getRegister()) {
        return false;
      }
      Operand tr = Move.getVal(ti);
      Operand fr = Move.getVal(fi);
      // make sure we're assigning constants
      if (!(tr instanceof IntConstantOperand) || !(fr instanceof IntConstantOperand)) {
        return false;
      }
      int tv = ((IntConstantOperand) tr).value;
      int fv = ((IntConstantOperand) fr).value;
      if (!((tv == 1 && fv == 0) || (tv == 0 && fv == 1))) {
        return false;
      }
      // Cases 3) and 4)
      if (tv == 0) {
        condition = condition.flipCode();
      }
      booleanCompareHelper(cb, t.copyRO(), val1.copy(), val2.copy(), condition);
      Instruction next = cb.nextInstructionInCodeOrder();
      if (next.operator() == GOTO) {
        Goto.setTarget(next, jb.makeJumpTarget());
      } else {
        cb.insertAfter(jb.makeGOTO());
      }
    }
    // fixup CFG
    bb.deleteOut(tb);
    bb.deleteOut(fb);
    bb.insertOut(jb); // Note: if we processed returns,
    // jb is the exit node.
    return true;
  }
  /**
   * Perform the transformation to replace conditional branch with a sequence using conditional
   * moves.
   *
   * @param ir governing IR
   * @param diamond the IR diamond structure to replace
   * @param cb conditional branch instruction at the head of the diamond
   */
  private void doCondMove(IR ir, Diamond diamond, Instruction cb) {
    BasicBlock taken = diamond.getTaken();
    BasicBlock notTaken = diamond.getNotTaken();

    // for each non-branch instruction s in the diamond,
    // copy s to a new instruction s'
    // and store a mapping from s to s'
    HashMap<Instruction, Instruction> takenInstructions = new HashMap<Instruction, Instruction>();
    Instruction[] takenInstructionList = copyAndMapInstructions(taken, takenInstructions);

    HashMap<Instruction, Instruction> notTakenInstructions =
        new HashMap<Instruction, Instruction>();
    Instruction[] notTakenInstructionList = copyAndMapInstructions(notTaken, notTakenInstructions);

    // Extract the values and condition from the conditional branch.
    Operand val1 = IfCmp.getVal1(cb);
    Operand val2 = IfCmp.getVal2(cb);
    ConditionOperand cond = IfCmp.getCond(cb);

    // Copy val1 and val2 to temporaries, just in case they're defined in
    // the diamond.  If they're not defined in the diamond, copy prop
    // should clean these moves up.
    RegisterOperand tempVal1 = ir.regpool.makeTemp(val1);
    Operator op = IRTools.getMoveOp(tempVal1.getType());
    cb.insertBefore(Move.create(op, tempVal1.copyRO(), val1.copy()));
    RegisterOperand tempVal2 = ir.regpool.makeTemp(val2);
    op = IRTools.getMoveOp(tempVal2.getType());
    cb.insertBefore(Move.create(op, tempVal2.copyRO(), val2.copy()));

    // For each instruction in each temporary set, rewrite it to def a new
    // temporary, and insert it before the branch.
    rewriteWithTemporaries(takenInstructionList, ir);
    rewriteWithTemporaries(notTakenInstructionList, ir);
    insertBefore(takenInstructionList, cb);
    insertBefore(notTakenInstructionList, cb);

    // For each register defined in the TAKEN branch, save a mapping to
    // the corresponding conditional move.
    HashMap<Register, Instruction> takenMap = new HashMap<Register, Instruction>();

    // Now insert conditional moves to replace each instruction in the diamond.
    // First handle the taken branch.
    if (taken != null) {
      for (Enumeration<Instruction> e = taken.forwardRealInstrEnumerator(); e.hasMoreElements(); ) {
        Instruction s = e.nextElement();
        if (s.isBranch()) continue;
        Operand def = s.getDefs().nextElement();
        // if the register does not span a basic block, it is a temporary
        // that will now be dead
        if (def.asRegister().getRegister().spansBasicBlock()) {
          Instruction tempS = takenInstructions.get(s);
          RegisterOperand temp = (RegisterOperand) tempS.getDefs().nextElement();
          op = IRTools.getCondMoveOp(def.asRegister().getType());
          Instruction cmov =
              CondMove.create(
                  op,
                  def.asRegister(),
                  tempVal1.copy(),
                  tempVal2.copy(),
                  cond.copy().asCondition(),
                  temp.copy(),
                  def.copy());
          takenMap.put(def.asRegister().getRegister(), cmov);
          cb.insertBefore(cmov);
        }
        s.remove();
      }
    }
    // For each register defined in the NOT-TAKEN branch, save a mapping to
    // the corresponding conditional move.
    HashMap<Register, Instruction> notTakenMap = new HashMap<Register, Instruction>();
    // Next handle the not taken branch.
    if (notTaken != null) {
      for (Enumeration<Instruction> e = notTaken.forwardRealInstrEnumerator();
          e.hasMoreElements(); ) {
        Instruction s = e.nextElement();
        if (s.isBranch()) continue;
        Operand def = s.getDefs().nextElement();
        // if the register does not span a basic block, it is a temporary
        // that will now be dead
        if (def.asRegister().getRegister().spansBasicBlock()) {
          Instruction tempS = notTakenInstructions.get(s);
          RegisterOperand temp = (RegisterOperand) tempS.getDefs().nextElement();

          Instruction prevCmov = takenMap.get(def.asRegister().getRegister());
          if (prevCmov != null) {
            // if this register was also defined in the taken branch, change
            // the previous cmov with a different 'False' Value
            CondMove.setFalseValue(prevCmov, temp.copy());
            notTakenMap.put(def.asRegister().getRegister(), prevCmov);
          } else {
            // create a new cmov instruction
            op = IRTools.getCondMoveOp(def.asRegister().getType());
            Instruction cmov =
                CondMove.create(
                    op,
                    def.asRegister(),
                    tempVal1.copy(),
                    tempVal2.copy(),
                    cond.copy().asCondition(),
                    def.copy(),
                    temp.copy());
            cb.insertBefore(cmov);
            notTakenMap.put(def.asRegister().getRegister(), cmov);
          }
        }
        s.remove();
      }
    }

    // Mutate the conditional branch into a GOTO.
    BranchOperand target = diamond.getBottom().makeJumpTarget();
    Goto.mutate(cb, GOTO, target);

    // Delete a potential GOTO after cb.
    Instruction next = cb.nextInstructionInCodeOrder();
    if (next.operator != BBEND) {
      next.remove();
    }

    // Recompute the CFG.
    diamond.getTop().recomputeNormalOut(ir); // fix the CFG
  }