/** * Perform optimizations for an inline guard. * * <p>Precondition: InlineGuard.conforms(cb) * * @param ir the governing IR * @param cb the instruction to optimize * @param bb the basic block holding if * @return {@code true} iff made a transformation */ private boolean processInlineGuard(IR ir, Instruction cb, BasicBlock bb) { BasicBlock targetBlock = cb.getBranchTarget(); Instruction targetLabel = targetBlock.firstInstruction(); // get the first real instruction at the branch target // NOTE: this instruction is not necessarily in targetBlock, // iff targetBlock has no real instructions Instruction targetInst = firstRealInstructionFollowing(targetLabel); if (targetInst == null || targetInst == cb) { return false; } boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND; if (endsBlock) { Instruction nextLabel = firstLabelFollowing(cb); if (targetLabel == nextLabel) { // found a conditional branch to the next instruction. just remove it. cb.remove(); return true; } Instruction nextI = firstRealInstructionFollowing(nextLabel); if (nextI != null && Goto.conforms(nextI)) { // replicate Goto cb.insertAfter(nextI.copyWithoutLinks()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } // do we fall through to a block that has only a goto? BasicBlock fallThrough = bb.getFallThroughBlock(); if (fallThrough != null) { Instruction fallThroughInstruction = fallThrough.firstRealInstruction(); if ((fallThroughInstruction != null) && Goto.conforms(fallThroughInstruction)) { // copy goto to bb bb.appendInstruction(fallThroughInstruction.copyWithoutLinks()); bb.recomputeNormalOut(ir); } } if (Goto.conforms(targetInst)) { // conditional branch to unconditional branch. // change conditional branch target to latter's target InlineGuard.setTarget(cb, (BranchOperand) Goto.getTarget(targetInst).copy()); bb.recomputeNormalOut(ir); // fix the CFG return true; } if (targetBlock.isEmpty()) { // branch to an empty block. Change target to the next block. BasicBlock nextBlock = targetBlock.getFallThroughBlock(); InlineGuard.setTarget(cb, nextBlock.makeJumpTarget()); bb.recomputeNormalOut(ir); // fix the CFG return true; } return false; }
/** * Perform optimizations for a two way conditional branch. * * <p>Precondition: IfCmp2.conforms(cb) * * @param ir the governing IR * @param cb the instruction to optimize * @param bb the basic block holding if * @return {@code true} iff made a transformation */ private boolean processTwoTargetConditionalBranch(IR ir, Instruction cb, BasicBlock bb) { // First condition/target Instruction target1Label = IfCmp2.getTarget1(cb).target; Instruction target1Inst = firstRealInstructionFollowing(target1Label); Instruction nextLabel = firstLabelFollowing(cb); boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND; if (target1Inst != null && target1Inst != cb) { if (Goto.conforms(target1Inst)) { // conditional branch to unconditional branch. // change conditional branch target to latter's target IfCmp2.setTarget1(cb, (BranchOperand) Goto.getTarget(target1Inst).copy()); bb.recomputeNormalOut(ir); // fix CFG return true; } BasicBlock target1Block = target1Label.getBasicBlock(); if (target1Block.isEmpty()) { // branch to an empty block. Change target to the next block. BasicBlock nextBlock = target1Block.getFallThroughBlock(); IfCmp2.setTarget1(cb, nextBlock.makeJumpTarget()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } // Second condition/target Instruction target2Label = IfCmp2.getTarget2(cb).target; Instruction target2Inst = firstRealInstructionFollowing(target2Label); if (target2Inst != null && target2Inst != cb) { if (Goto.conforms(target2Inst)) { // conditional branch to unconditional branch. // change conditional branch target to latter's target IfCmp2.setTarget2(cb, (BranchOperand) Goto.getTarget(target2Inst).copy()); bb.recomputeNormalOut(ir); // fix CFG return true; } if ((target2Label == nextLabel) && endsBlock) { // found a conditional branch to the next instruction. Reduce to IfCmp if (VM.VerifyAssertions) VM._assert(cb.operator() == INT_IFCMP2); IfCmp.mutate( cb, INT_IFCMP, IfCmp2.getGuardResult(cb), IfCmp2.getVal1(cb), IfCmp2.getVal2(cb), IfCmp2.getCond1(cb), IfCmp2.getTarget1(cb), IfCmp2.getBranchProfile1(cb)); return true; } BasicBlock target2Block = target2Label.getBasicBlock(); if (target2Block.isEmpty()) { // branch to an empty block. Change target to the next block. BasicBlock nextBlock = target2Block.getFallThroughBlock(); IfCmp2.setTarget2(cb, nextBlock.makeJumpTarget()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } // if fall through to a goto; replicate the goto if (endsBlock) { Instruction nextI = firstRealInstructionFollowing(nextLabel); if (nextI != null && Goto.conforms(nextI)) { // replicate Goto cb.insertAfter(nextI.copyWithoutLinks()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } return false; }
/** * Perform optimizations for a conditional branch. * * <pre> * 1) IF .. GOTO A replaced by IF .. GOTO B * ... * A: GOTO B * 2) conditional branch to next instruction eliminated * 3) IF (condition) GOTO A replaced by IF (!condition) GOTO B * GOTO B A: ... * A: ... * 4) special case to generate Boolean compare opcode * 5) special case to generate conditional move sequence * 6) IF .. GOTO A replaced by IF .. GOTO B * A: LABEL * BBEND * B: * 7) fallthrough to a goto: replicate goto to enable other optimizations. * </pre> * * <p>Precondition: IfCmp.conforms(cb) * * @param ir the governing IR * @param cb the instruction to optimize * @param bb the basic block holding if * @return {@code true} iff made a transformation */ private boolean processConditionalBranch(IR ir, Instruction cb, BasicBlock bb) { BasicBlock targetBlock = cb.getBranchTarget(); // don't optimize jumps to a code motion landing pad if (targetBlock.getLandingPad()) return false; Instruction targetLabel = targetBlock.firstInstruction(); // get the first real instruction at the branch target // NOTE: this instruction is not necessarily in targetBlock, // iff targetBlock has no real instructions Instruction targetInst = firstRealInstructionFollowing(targetLabel); if (targetInst == null || targetInst == cb) { return false; } boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND; if (endsBlock) { Instruction nextLabel = firstLabelFollowing(cb); if (targetLabel == nextLabel) { // found a conditional branch to the next instruction. just remove it. cb.remove(); return true; } Instruction nextI = firstRealInstructionFollowing(nextLabel); if (nextI != null && Goto.conforms(nextI)) { // Check that the target is not the fall through (the goto itself). // If we add a goto to the next block, it will be removed by // processGoto and we will loop indefinitely. // This can be tripped by (strange) code such as: // if (condition) while (true); BasicBlock gotoTarget = nextI.getBranchTarget(); Instruction gotoLabel = gotoTarget.firstInstruction(); Instruction gotoInst = firstRealInstructionFollowing(gotoLabel); if (gotoInst != nextI) { // replicate Goto cb.insertAfter(nextI.copyWithoutLinks()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } } // attempt to generate boolean compare. if (generateBooleanCompare(ir, bb, cb, targetBlock)) { // generateBooleanCompare does all necessary CFG fixup. return true; } // attempt to generate a sequence using conditional moves if (generateCondMove(ir, bb, cb)) { // generateCondMove does all necessary CFG fixup. return true; } // do we fall through to a block that has only a goto? BasicBlock fallThrough = bb.getFallThroughBlock(); if (fallThrough != null) { Instruction fallThroughInstruction = fallThrough.firstRealInstruction(); if ((fallThroughInstruction != null) && Goto.conforms(fallThroughInstruction)) { // copy goto to bb bb.appendInstruction(fallThroughInstruction.copyWithoutLinks()); bb.recomputeNormalOut(ir); } } if (Goto.conforms(targetInst)) { // conditional branch to unconditional branch. // change conditional branch target to latter's target Instruction target2 = firstRealInstructionFollowing(targetInst.getBranchTarget().firstInstruction()); if (target2 == targetInst) { // Avoid an infinite recursion in the following scenario: // g: if (...) goto L // ... // L: goto L // This happens in GCUtil in some systems due to a while(true) {} return false; } IfCmp.setTarget(cb, (BranchOperand) Goto.getTarget(targetInst).copy()); bb.recomputeNormalOut(ir); // fix the CFG return true; } if (targetBlock.isEmpty()) { // branch to an empty block. Change target to the next block. BasicBlock nextBlock = targetBlock.getFallThroughBlock(); IfCmp.setTarget(cb, nextBlock.makeJumpTarget()); bb.recomputeNormalOut(ir); // fix the CFG return true; } if (isFlipCandidate(cb, targetInst)) { flipConditionalBranch(cb); bb.recomputeNormalOut(ir); // fix the CFG return true; } return false; }
/** * Perform optimizations for a Goto. * * <p> Patterns: * <pre> * 1) GOTO A replaced by GOTO B * A: GOTO B * * 2) GOTO A replaced by IF .. GOTO B * A: IF .. GOTO B GOTO C * C: ... * 3) GOTO next instruction eliminated * 4) GOTO A replaced by GOTO B * A: LABEL * BBEND * B: * 5) GOTO BBn where BBn has exactly one in edge * - move BBn immediately after the GOTO in the code order, * so that pattern 3) will create a fallthrough * <pre> * * <p> Precondition: Goto.conforms(g) * * @param ir governing IR * @param g the instruction to optimize * @param bb the basic block holding g * @return {@code true} if made a transformation */ private boolean processGoto(IR ir, Instruction g, BasicBlock bb) { BasicBlock targetBlock = g.getBranchTarget(); // don't optimize jumps to a code motion landing pad if (targetBlock.getLandingPad()) return false; Instruction targetLabel = targetBlock.firstInstruction(); // get the first real instruction at the g target // NOTE: this instruction is not necessarily in targetBlock, // iff targetBlock has no real instructions Instruction targetInst = firstRealInstructionFollowing(targetLabel); if (targetInst == null || targetInst == g) { return false; } Instruction nextLabel = firstLabelFollowing(g); if (targetLabel == nextLabel) { // found a GOTO to the next instruction. just remove it. g.remove(); return true; } if (Goto.conforms(targetInst)) { // unconditional branch to unconditional branch. // replace g with goto to targetInst's target Instruction target2 = firstRealInstructionFollowing(targetInst.getBranchTarget().firstInstruction()); if (target2 == targetInst) { // Avoid an infinite recursion in the following bizarre scenario: // g: goto L // ... // L: goto L // This happens in jByteMark.EmFloatPnt.denormalize() due to a while(true) {} return false; } Goto.setTarget(g, (BranchOperand) Goto.getTarget(targetInst).copy()); bb.recomputeNormalOut(ir); // fix the CFG return true; } if (targetBlock.isEmpty()) { // GOTO an empty basic block. Change target to the // next block. BasicBlock nextBlock = targetBlock.getFallThroughBlock(); Goto.setTarget(g, nextBlock.makeJumpTarget()); bb.recomputeNormalOut(ir); // fix the CFG return true; } if (mayDuplicateCondBranches && IfCmp.conforms(targetInst)) { // unconditional branch to a conditional branch. // If the Goto is the only branch instruction in its basic block // and the IfCmp is the only non-GOTO branch instruction // in its basic block then replace the goto with a copy of // targetInst and append another GOTO to the not-taken // target of targetInst's block. // We impose these additional restrictions to avoid getting // multiple conditional branches in a single basic block. if (!g.prevInstructionInCodeOrder().isBranch() && (targetInst.nextInstructionInCodeOrder().operator == BBEND || targetInst.nextInstructionInCodeOrder().operator == GOTO)) { Instruction copy = targetInst.copyWithoutLinks(); g.replace(copy); Instruction newGoto = targetInst.getBasicBlock().getNotTakenNextBlock().makeGOTO(); copy.insertAfter(newGoto); bb.recomputeNormalOut(ir); // fix the CFG return true; } } // try to create a fallthrough if (mayReorderCode && targetBlock.getNumberOfIn() == 1) { BasicBlock ftBlock = targetBlock.getFallThroughBlock(); if (ftBlock != null) { BranchOperand ftTarget = ftBlock.makeJumpTarget(); targetBlock.appendInstruction(CPOS(g, Goto.create(GOTO, ftTarget))); } ir.cfg.removeFromCodeOrder(targetBlock); ir.cfg.insertAfterInCodeOrder(bb, targetBlock); targetBlock.recomputeNormalOut(ir); // fix the CFG return true; } return false; }
/** * 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; }
/** Append an instruction after a store instruction that caches value in register r. */ static void appendMove(Register r, Operand src, Instruction store) { TypeReference type = src.getType(); RegisterOperand rop = new RegisterOperand(r, type); store.insertAfter(Move.create(IRTools.getMoveOp(type), rop, src.copy())); }