/** * 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 }