/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt.controlflow; import static org.jikesrvm.compilers.opt.ir.Operators.BBEND; import org.jikesrvm.VM; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.operand.BranchOperand; /** * Perform simple peephole optimizations for MIR branches. */ public final class MIRBranchOptimizations extends BranchOptimizationDriver { /** * @param level the minimum optimization level at which the branch * optimizations should be performed. */ public MIRBranchOptimizations(int level) { super(level); } private static boolean isMIR_Branch(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_Branch.conforms(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_Branch.conforms(x); } } private static boolean isMIR_CondBranch(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch.conforms(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch.conforms(x); } } private static boolean isMIR_CondBranch2(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.conforms(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.conforms(x); } } private static BranchOperand MIR_Branch_getTarget(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_Branch.getTarget(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_Branch.getTarget(x); } } private static BranchOperand MIR_Branch_getClearTarget(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_Branch.getClearTarget(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_Branch.getClearTarget(x); } } private static void MIR_Branch_setTarget(Instruction x, BranchOperand y) { if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.ir.ia32.MIR_Branch.setTarget(x, y); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.ir.ppc.MIR_Branch.setTarget(x, y); } } private static void MIR_CondBranch_setTarget(Instruction x, BranchOperand y) { if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch.setTarget(x, y); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch.setTarget(x, y); } } private static BranchOperand MIR_CondBranch2_getTarget1(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.getTarget1(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.getTarget1(x); } } private static BranchOperand MIR_CondBranch2_getTarget2(Instruction x) { if (VM.BuildForIA32) { return org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.getTarget2(x); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); return org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.getTarget2(x); } } private static void MIR_CondBranch2_setTarget1(Instruction x, BranchOperand y) { if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.setTarget1(x, y); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.setTarget1(x, y); } } private static void MIR_CondBranch2_setTarget2(Instruction x, BranchOperand y) { if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.setTarget2(x, y); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.setTarget2(x, y); } } /** * This method actually does the work of attempting to * peephole optimize a branch instruction. * See Muchnick ~p.590 * @param ir the containing IR * @param s the branch instruction to optimize * @param bb the containing basic block * @return {@code true} if an optimization was applied, {@code false} otherwise */ @Override protected boolean optimizeBranchInstruction(IR ir, Instruction s, BasicBlock bb) { if (isMIR_Branch(s)) { return processGoto(ir, s, bb); } else if (isMIR_CondBranch(s)) { return processCondBranch(ir, s, bb); } else if (isMIR_CondBranch2(s)) { return processTwoTargetConditionalBranch(ir, s, bb); } else { return false; } } /** * Perform optimizations for an unconditonal branch. * * <p> Patterns: * <pre> * 1) GOTO A replaced by GOTO B * A: GOTO B * * 2) GOTO next instruction eliminated * 3) GOTO A replaced by GOTO B * A: LABEL * BBEND * B: * </pre> * * <p> Precondition: MIR_Branch.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(); 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 (isMIR_Branch(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; } BranchOperand top = (BranchOperand)(MIR_Branch_getTarget(targetInst).copy()); MIR_Branch_setTarget(g, top); bb.recomputeNormalOut(ir); // fix the CFG return true; } if (targetBlock.isEmpty()) { // GOTO an empty block. Change target to the next block. BasicBlock nextBlock = targetBlock.getFallThroughBlock(); MIR_Branch_setTarget(g, nextBlock.makeJumpTarget()); 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) IF .. GOTO A replaced by IF .. GOTO B * A: LABEL * BBEND * B: * 5) fallthrough to a goto: replicate goto to enable other optimizations. * </pre> * * <p> Precondition: MIR_CondBranch.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 processCondBranch(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 && isMIR_Branch(nextI)) { // replicate Goto cb.insertAfter(nextI.copyWithoutLinks()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } if (isMIR_Branch(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; } MIR_CondBranch_setTarget(cb, (BranchOperand)MIR_Branch_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(); BranchOperand newTarget = nextBlock.makeJumpTarget(); MIR_CondBranch_setTarget(cb, newTarget); 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 two way conditional branch. * * <pre> * 1) IF .. GOTO A replaced by IF .. GOTO B * ... * A: GOTO B * 2) conditional branch to next instruction eliminated * 3) IF .. GOTO A replaced by IF .. GOTO B * A: LABEL * BBEND * B: * 4) fallthrough to a goto: replicate goto to enable other optimizations. * </pre> * * <p> Precondition: MIR_CondBranch2.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 = MIR_CondBranch2_getTarget1(cb).target; Instruction target1Inst = firstRealInstructionFollowing(target1Label); Instruction nextLabel = firstLabelFollowing(cb); boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND; if (target1Inst != null && target1Inst != cb) { if (isMIR_Branch(target1Inst)) { // conditional branch to unconditional branch. // change conditional branch target to latter's target MIR_CondBranch2_setTarget1(cb, MIR_Branch_getTarget(target1Inst).copy().asBranch()); 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(); MIR_CondBranch2_setTarget1(cb, nextBlock.makeJumpTarget()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } // Second condition/target Instruction target2Label = MIR_CondBranch2_getTarget2(cb).target; Instruction target2Inst = firstRealInstructionFollowing(target2Label); if (target2Inst != null && target2Inst != cb) { if (isMIR_Branch(target2Inst)) { // conditional branch to unconditional branch. // change conditional branch target to latter's target MIR_CondBranch2_setTarget2(cb, MIR_Branch_getTarget(target2Inst).copy().asBranch()); bb.recomputeNormalOut(ir); // fix CFG return true; } if ((target2Label == nextLabel) && endsBlock) { // found a conditional branch to the next instruction. // Reduce to MIR_BranchCond if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch.mutate(cb, org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_JCC, org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.getCond1(cb), org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.getTarget1(cb), org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2.getBranchProfile1(cb)); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch.mutate(cb, org.jikesrvm.compilers.opt.ir.ppc.ArchOperators.PPC_BCOND, org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.getValue(cb), org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.getCond1(cb), org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.getTarget1(cb), org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch2.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(); MIR_CondBranch2_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 && isMIR_Branch(nextI)) { // replicate unconditional branch cb.insertAfter(nextI.copyWithoutLinks()); bb.recomputeNormalOut(ir); // fix the CFG return true; } } return false; } /** * Is a conditional branch a candidate to be flipped? * See comment 3) of processCondBranch * * <p> Precondition: MIR_CondBranch.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 (!isMIR_Branch(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; } /** * Flip a conditional branch and remove the trailing goto. * See comment 3) of processCondBranch * * <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 = MIR_Branch_getClearTarget(g); // now flip the test and set the new target if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch.setCond(cb, org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch.getCond(cb).flipCode()); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch.setCond(cb, org.jikesrvm.compilers.opt.ir.ppc.MIR_CondBranch.getCond(cb).flipCode()); } MIR_CondBranch_setTarget(cb, gTarget); // Remove the trailing GOTO instruction g.remove(); } }