@Override public void cleanUpAndInsertEpilogue() { Instruction inst = ir.firstInstructionInCodeOrder().nextInstructionInCodeOrder(); for (; inst != null; inst = inst.nextInstructionInCodeOrder()) { switch (inst.getOpcode()) { case IA32_MOV_opcode: // remove frivolous moves Operand result = MIR_Move.getResult(inst); Operand val = MIR_Move.getValue(inst); if (result.similar(val)) { inst = inst.remove(); } break; case IA32_FMOV_opcode: case IA32_MOVSS_opcode: case IA32_MOVSD_opcode: // remove frivolous moves result = MIR_Move.getResult(inst); val = MIR_Move.getValue(inst); if (result.similar(val)) { inst = inst.remove(); } break; case IA32_RET_opcode: if (frameIsRequired()) { insertEpilogue(inst); } default: break; } } // now that the frame size is fixed, fix up the spill location code rewriteStackLocations(); }
private boolean canModifyEFLAGS(Instruction s) { if (PhysicalDefUse.usesEFLAGS(s.operator())) { return false; } if (PhysicalDefUse.definesEFLAGS(s.operator())) { return true; } if (s.operator() == BBEND) return true; return canModifyEFLAGS(s.nextInstructionInCodeOrder()); }
/** * 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; }
/** * Flip a conditional branch and remove the trailing goto. See comment 3) of * processConditionalBranch * * <p>Precondition isFlipCandidate(cb) * * @param cb the conditional branch instruction */ private void flipConditionalBranch(Instruction cb) { // get the trailing GOTO instruction Instruction g = cb.nextInstructionInCodeOrder(); BranchOperand gTarget = (BranchOperand) (Goto.getTarget(g).copy()); // now flip the test and set the new target IfCmp.setCond(cb, IfCmp.getCond(cb).flipCode()); IfCmp.setTarget(cb, gTarget); // Update the branch probability. It is now the opposite cb.flipBranchProbability(); // finally, remove the trailing GOTO instruction g.remove(); }
/** * Is a conditional branch a candidate to be flipped? See comment 3) of processConditionalBranch * * <p>Precondition: IfCmp.conforms(cb) * * @param cb the conditional branch instruction * @param target the target instruction (real instruction) of the conditional branch * @return boolean result */ private boolean isFlipCandidate(Instruction cb, Instruction target) { // condition 1: is next instruction a GOTO? Instruction next = cb.nextInstructionInCodeOrder(); if (!Goto.conforms(next)) { return false; } // condition 2: is the target of the conditional branch the // next instruction after the GOTO? next = firstRealInstructionFollowing(next); if (next != target) { return false; } // got this far. It's a candidate. return true; }
/** Transform cb into a GOTO, updating PHI nodes to maintain SSA form. */ private void takeCondBranch(BasicBlock source, Instruction cb, IR ir) { if (DEBUG) VM.sysWrite("Eliminating definitely taken branch " + cb + "\n"); BasicBlock deadBB = source.nextBasicBlockInCodeOrder(); Instruction next = cb.nextInstructionInCodeOrder(); if (Goto.conforms(next)) { deadBB = next.getBranchTarget(); next.remove(); } Goto.mutate(cb, GOTO, cb.getBranchTarget().makeJumpTarget()); source.recomputeNormalOut(ir); if (!source.pointsOut(deadBB)) { // there is no longer an edge from source to target; // update any PHIs in target to reflect this. SSA.purgeBlockFromPHIs(source, deadBB); } }
/** * Insert the prologue for a normal method. * * <p>Assume we are inserting the prologue for method B called from method A. * * <ul> * <li>Perform a stack overflow check. * <li>Store a back pointer to A's frame * <li>Store B's compiled method id * <li>Adjust frame pointer to point to B's frame * <li>Save any used non-volatile registers * </ul> */ @Override public void insertNormalPrologue() { PhysicalRegisterSet phys = (PhysicalRegisterSet) ir.regpool.getPhysicalRegisterSet(); Register ESP = phys.getESP(); MemoryOperand fpHome = MemoryOperand.BD( ir.regpool.makeTROp(), ArchEntrypoints.framePointerField.getOffset(), (byte) WORDSIZE, null, null); // the prologue instruction Instruction plg = ir.firstInstructionInCodeOrder().nextInstructionInCodeOrder(); // inst is the instruction immediately after the IR_PROLOGUE // instruction Instruction inst = plg.nextInstructionInCodeOrder(); int frameFixedSize = getFrameFixedSize(); ir.compiledMethod.setFrameFixedSize(frameFixedSize); // I. Buy a stackframe (including overflow check) // NOTE: We play a little game here. If the frame we are buying is // very small (less than 256) then we can be sloppy with the // stackoverflow check and actually allocate the frame in the guard // region. We'll notice when this frame calls someone and take the // stackoverflow in the callee. We can't do this if the frame is too big, // because growing the stack in the callee and/or handling a hardware trap // in this frame will require most of the guard region to complete. // See libvm.C. if (frameFixedSize >= 256) { // 1. Insert Stack overflow check. insertBigFrameStackOverflowCheck(plg); // 2. Save caller's frame pointer inst.insertBefore(MIR_UnaryNoRes.create(IA32_PUSH, fpHome)); // 3. Set my frame pointer to current value of stackpointer inst.insertBefore( MIR_Move.create( IA32_MOV, fpHome.copy(), new RegisterOperand(ESP, PRIMITIVE_TYPE_FOR_WORD))); // 4. Store my compiled method id int cmid = ir.compiledMethod.getId(); inst.insertBefore(MIR_UnaryNoRes.create(IA32_PUSH, VM.BuildFor32Addr ? IC(cmid) : LC(cmid))); } else { // 1. Save caller's frame pointer inst.insertBefore(MIR_UnaryNoRes.create(IA32_PUSH, fpHome)); // 2. Set my frame pointer to current value of stackpointer inst.insertBefore( MIR_Move.create( IA32_MOV, fpHome.copy(), new RegisterOperand(ESP, PRIMITIVE_TYPE_FOR_WORD))); // 3. Store my compiled method id int cmid = ir.compiledMethod.getId(); inst.insertBefore(MIR_UnaryNoRes.create(IA32_PUSH, VM.BuildFor32Addr ? IC(cmid) : LC(cmid))); // 4. Insert Stack overflow check. insertNormalStackOverflowCheck(plg); } // II. Save any used volatile and non-volatile registers if (ir.compiledMethod.isSaveVolatile()) { saveVolatiles(inst); saveFloatingPointState(inst); } saveNonVolatiles(inst); }
/** * 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; }
/** * 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 }
/** * @param ir the IR to expand * @return return value is garbage for IA32 */ public static int expand(IR ir) { PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet().asIA32(); MachineCodeOffsets mcOffsets = ir.MIRInfo.mcOffsets; for (Instruction next, p = ir.firstInstructionInCodeOrder(); p != null; p = next) { next = p.nextInstructionInCodeOrder(); mcOffsets.setMachineCodeOffset(p, -1); switch (p.getOpcode()) { case IA32_MOVAPS_opcode: // a reg-reg move turned into a memory move where we can't guarantee alignment if (MIR_Move.getResult(p).isMemory() || MIR_Move.getValue(p).isMemory()) { MIR_Move.mutate(p, IA32_MOVSS, MIR_Move.getClearResult(p), MIR_Move.getClearValue(p)); } break; case IA32_MOVAPD_opcode: // a reg-reg move turned into a memory move where we can't guarantee alignment if (MIR_Move.getResult(p).isMemory() || MIR_Move.getValue(p).isMemory()) { MIR_Move.mutate(p, IA32_MOVSD, MIR_Move.getClearResult(p), MIR_Move.getClearValue(p)); } break; case IA32_TEST_opcode: // don't bother telling rest of compiler that memory operand // must be first; we can just commute it here. if (MIR_Test.getVal2(p).isMemory()) { Operand tmp = MIR_Test.getClearVal1(p); MIR_Test.setVal1(p, MIR_Test.getClearVal2(p)); MIR_Test.setVal2(p, tmp); } break; case NULL_CHECK_opcode: { // mutate this into a TRAPIF, and then fall through to the the // TRAP_IF case. Operand ref = NullCheck.getRef(p); MIR_TrapIf.mutate( p, IA32_TRAPIF, null, ref.copy(), IC(0), IA32ConditionOperand.EQ(), TrapCodeOperand.NullPtr()); } // There is no break statement here on purpose! case IA32_TRAPIF_opcode: { // split the basic block right before the IA32_TRAPIF BasicBlock thisBlock = p.getBasicBlock(); BasicBlock trap = thisBlock.createSubBlock(p.getBytecodeIndex(), ir, 0f); thisBlock.insertOut(trap); BasicBlock nextBlock = thisBlock.splitNodeWithLinksAt(p, ir); thisBlock.insertOut(trap); TrapCodeOperand tc = MIR_TrapIf.getClearTrapCode(p); p.remove(); mcOffsets.setMachineCodeOffset(nextBlock.firstInstruction(), -1); // add code to thisBlock to conditionally jump to trap Instruction cmp = MIR_Compare.create(IA32_CMP, MIR_TrapIf.getVal1(p), MIR_TrapIf.getVal2(p)); if (p.isMarkedAsPEI()) { // The trap if was explictly marked, which means that it has // a memory operand into which we've folded a null check. // Actually need a GC map for both the compare and the INT. cmp.markAsPEI(); cmp.copyPosition(p); ir.MIRInfo.gcIRMap.insertTwin(p, cmp); } thisBlock.appendInstruction(cmp); thisBlock.appendInstruction( MIR_CondBranch.create( IA32_JCC, MIR_TrapIf.getCond(p), trap.makeJumpTarget(), null)); // add block at end to hold trap instruction, and // insert trap sequence ir.cfg.addLastInCodeOrder(trap); if (tc.isArrayBounds()) { // attempt to store index expression in processor object for // C trap handler Operand index = MIR_TrapIf.getVal2(p); if (!(index instanceof RegisterOperand || index instanceof IntConstantOperand)) { index = IC(0xdeadbeef); // index was spilled, and // we can't get it back here. } MemoryOperand mo = MemoryOperand.BD( ir.regpool.makeTROp(), ArchEntrypoints.arrayIndexTrapParamField.getOffset(), (byte) 4, null, null); trap.appendInstruction(MIR_Move.create(IA32_MOV, mo, index.copy())); } // NOTE: must make p the trap instruction: it is the GC point! // IMPORTANT: must also inform the GCMap that the instruction has // been moved!!! trap.appendInstruction(MIR_Trap.mutate(p, IA32_INT, null, tc)); ir.MIRInfo.gcIRMap.moveToEnd(p); if (tc.isStackOverflow()) { // only stackoverflow traps resume at next instruction. trap.appendInstruction(MIR_Branch.create(IA32_JMP, nextBlock.makeJumpTarget())); } } break; case IA32_FMOV_ENDING_LIVE_RANGE_opcode: { Operand result = MIR_Move.getResult(p); Operand value = MIR_Move.getValue(p); if (result.isRegister() && value.isRegister()) { if (result.similar(value)) { // eliminate useless move p.remove(); } else { int i = PhysicalRegisterSet.getFPRIndex(result.asRegister().getRegister()); int j = PhysicalRegisterSet.getFPRIndex(value.asRegister().getRegister()); if (i == 0) { MIR_XChng.mutate(p, IA32_FXCH, result, value); } else if (j == 0) { MIR_XChng.mutate(p, IA32_FXCH, value, result); } else { expandFmov(p, phys); } } } else { expandFmov(p, phys); } break; } case DUMMY_DEF_opcode: case DUMMY_USE_opcode: case REQUIRE_ESP_opcode: case ADVISE_ESP_opcode: p.remove(); break; case IA32_FMOV_opcode: expandFmov(p, phys); break; case IA32_MOV_opcode: // Replace result = IA32_MOV 0 with result = IA32_XOR result, result if (MIR_Move.getResult(p).isRegister() && MIR_Move.getValue(p).isIntConstant() && MIR_Move.getValue(p).asIntConstant().value == 0) { // Calculate what flags are defined in coming instructions before a use of a flag or // BBend Instruction x = next; int futureDefs = 0; while (!BBend.conforms(x) && !PhysicalDefUse.usesEFLAGS(x.operator())) { futureDefs |= x.operator().implicitDefs; x = x.nextInstructionInCodeOrder(); } // If the flags will be destroyed prior to use or we reached the end of the basic block if (BBend.conforms(x) || (futureDefs & PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) == PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) { Operand result = MIR_Move.getClearResult(p); MIR_BinaryAcc.mutate(p, IA32_XOR, result, result.copy()); } } break; case IA32_SET__B_opcode: // Replace <cmp>, set__b, movzx__b with xor, <cmp>, set__b if (MIR_Set.getResult(p).isRegister() && MIR_Unary.conforms(next) && (next.operator() == IA32_MOVZX__B) && MIR_Unary.getResult(next).isRegister() && MIR_Unary.getVal(next).similar(MIR_Unary.getResult(next)) && MIR_Unary.getVal(next).similar(MIR_Set.getResult(p))) { // Find instruction in this basic block that defines flags Instruction x = p.prevInstructionInCodeOrder(); Operand result = MIR_Unary.getResult(next); boolean foundCmp = false; outer: while (!Label.conforms(x)) { Enumeration<Operand> e = x.getUses(); while (e.hasMoreElements()) { // We can't use an xor to clear the register if that register is // used by the <cmp> or intervening instruction if (e.nextElement().similar(result)) { break outer; } } if (PhysicalDefUse.definesEFLAGS(x.operator()) && !PhysicalDefUse.usesEFLAGS(x.operator())) { // we found a <cmp> that doesn't use the result or the flags // that would be clobbered by the xor foundCmp = true; break outer; } x = x.prevInstructionInCodeOrder(); } if (foundCmp) { // We found the <cmp>, mutate the movzx__b into an xor and insert it before the <cmp> next.remove(); MIR_BinaryAcc.mutate(next, IA32_XOR, result, MIR_Unary.getVal(next)); x.insertBefore(next); // get ready for the next instruction next = p.nextInstructionInCodeOrder(); } } break; case IA32_LEA_opcode: { // Sometimes we're over eager in BURS in using LEAs and after register // allocation we can simplify to the accumulate form // replace reg1 = LEA [reg1 + reg2] with reg1 = reg1 + reg2 // replace reg1 = LEA [reg1 + c1] with reg1 = reg1 + c1 // replace reg1 = LEA [reg1 << c1] with reg1 = reg1 << c1 MemoryOperand value = MIR_Lea.getValue(p); RegisterOperand result = MIR_Lea.getResult(p); if ((value.base != null && value.base.getRegister() == result.getRegister()) || (value.index != null && value.index.getRegister() == result.getRegister())) { // Calculate what flags are defined in coming instructions before a use of a flag or // BBend Instruction x = next; int futureDefs = 0; while (!BBend.conforms(x) && !PhysicalDefUse.usesEFLAGS(x.operator())) { futureDefs |= x.operator().implicitDefs; x = x.nextInstructionInCodeOrder(); } // If the flags will be destroyed prior to use or we reached the end of the basic // block if (BBend.conforms(x) || (futureDefs & PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) == PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) { if (value.base != null && value.index != null && value.index.getRegister() == result.getRegister() && value.disp.isZero() && value.scale == 0) { // reg1 = lea [base + reg1] -> add reg1, base MIR_BinaryAcc.mutate(p, IA32_ADD, result, value.base); } else if (value.base != null && value.base.getRegister() == result.getRegister() && value.index != null && value.disp.isZero() && value.scale == 0) { // reg1 = lea [reg1 + index] -> add reg1, index MIR_BinaryAcc.mutate(p, IA32_ADD, result, value.index); } else if (value.base != null && value.base.getRegister() == result.getRegister() && value.index == null) { if (VM.VerifyAssertions) VM._assert(fits(value.disp, 32)); // reg1 = lea [reg1 + disp] -> add reg1, disp MIR_BinaryAcc.mutate(p, IA32_ADD, result, IC(value.disp.toInt())); } else if (value.base == null && value.index != null && value.index.getRegister() == result.getRegister() && value.scale == 0) { if (VM.VerifyAssertions) VM._assert(fits(value.disp, 32)); // reg1 = lea [reg1 + disp] -> add reg1, disp MIR_BinaryAcc.mutate(p, IA32_ADD, result, IC(value.disp.toInt())); } else if (value.base == null && value.index != null && value.index.getRegister() == result.getRegister() && value.disp.isZero()) { // reg1 = lea [reg1 << scale] -> shl reg1, scale if (value.scale == 0) { p.remove(); } else if (value.scale == 1) { MIR_BinaryAcc.mutate(p, IA32_ADD, result, value.index); } else { MIR_BinaryAcc.mutate(p, IA32_SHL, result, IC(value.scale)); } } } } } break; case IA32_FCLEAR_opcode: expandFClear(p, ir); break; case IA32_JCC2_opcode: p.insertBefore( MIR_CondBranch.create( IA32_JCC, MIR_CondBranch2.getCond1(p), MIR_CondBranch2.getTarget1(p), MIR_CondBranch2.getBranchProfile1(p))); MIR_CondBranch.mutate( p, IA32_JCC, MIR_CondBranch2.getCond2(p), MIR_CondBranch2.getTarget2(p), MIR_CondBranch2.getBranchProfile2(p)); break; case CALL_SAVE_VOLATILE_opcode: p.changeOperatorTo(IA32_CALL); break; case IA32_LOCK_CMPXCHG_opcode: p.insertBefore(MIR_Empty.create(IA32_LOCK)); p.changeOperatorTo(IA32_CMPXCHG); break; case IA32_LOCK_CMPXCHG8B_opcode: p.insertBefore(MIR_Empty.create(IA32_LOCK)); p.changeOperatorTo(IA32_CMPXCHG8B); break; case YIELDPOINT_PROLOGUE_opcode: expandYieldpoint( p, ir, Entrypoints.optThreadSwitchFromPrologueMethod, IA32ConditionOperand.NE()); break; case YIELDPOINT_EPILOGUE_opcode: expandYieldpoint( p, ir, Entrypoints.optThreadSwitchFromEpilogueMethod, IA32ConditionOperand.NE()); break; case YIELDPOINT_BACKEDGE_opcode: expandYieldpoint( p, ir, Entrypoints.optThreadSwitchFromBackedgeMethod, IA32ConditionOperand.GT()); break; case YIELDPOINT_OSR_opcode: // must yield, does not check threadSwitch request expandUnconditionalYieldpoint(p, ir, Entrypoints.optThreadSwitchFromOsrOptMethod); break; } } return 0; }
/** Return the basic block that s's block will goto if s is not taken. */ private BasicBlock getNotTakenBlock(Instruction s) { s = s.nextInstructionInCodeOrder(); if (Goto.conforms(s)) return s.getBranchTarget(); if (VM.VerifyAssertions) VM._assert(s.operator() == BBEND); return s.getBasicBlock().nextBasicBlockInCodeOrder(); }