/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt; import org.jikesrvm.VM; import org.jikesrvm.compilers.opt.ir.Goto; import org.jikesrvm.compilers.opt.ir.IfCmp; import org.jikesrvm.compilers.opt.ir.InlineGuard; import org.jikesrvm.compilers.opt.ir.Move; import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock; import org.jikesrvm.compilers.opt.ir.OPT_BasicBlockEnumeration; import org.jikesrvm.compilers.opt.ir.OPT_IR; import org.jikesrvm.compilers.opt.ir.OPT_Instruction; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BBEND; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GOTO; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GUARD_MOVE; /** * Redundant branch elimination based on SSA form, global value numbers, * and dominance relationships. * The following are sufficient conditions for a conditional branch cb1 * to be eliminated as redundant * <ul> * <li> It is equivalent (has the same value number) as another * conditional branch cb2 * <li> Either (a) the target of the taken branch of cb2 dominates cb1 * and said target block has exactly one in edge or (b) * the not-taken continuation of cb2 dominates cb1 and * said continuation block has exactly one in edge. * </ul> * NOTE: the check for exactly one in edge is used to rule out * situations like the following: * <pre> [5~ * if (C) goto L2 // cb2 * x = x + 1; * L2: x = x + 1; * if (C) goto L3. // cb1 * </pre> * Here L2 (the target of cb2) dominates cb1, but it * is not correct to eliminate cb1 because it is also * reachable (but not dominated) from the continutation * block of cb2! */ final class OPT_RedundantBranchElimination extends OPT_OptimizationPlanCompositeElement { public boolean shouldPerform(OPT_Options options) { return options.REDUNDANT_BRANCH_ELIMINATION; } /** * Create this phase element as a composite of other elements */ OPT_RedundantBranchElimination() { super("RedundantBranchElimination", new OPT_OptimizationPlanElement[]{ // Stage 1: Require SSA form new OPT_OptimizationPlanAtomicElement(new EnsureSSA()), // Stage2: Require GVNs new OPT_OptimizationPlanAtomicElement(new OPT_GlobalValueNumber()), // Stage3: Do the optimization new OPT_OptimizationPlanAtomicElement(new RBE()),}); } private static final class EnsureSSA extends OPT_CompilerPhase { public String getName() { return "Ensure SSA"; } public boolean shouldPerform() { return true; } public void perform(OPT_IR ir) { ir.desiredSSAOptions = new OPT_SSAOptions(); new OPT_EnterSSA().perform(ir); } public OPT_CompilerPhase newExecution(OPT_IR ir) { return this; } } private static final class RBE extends OPT_CompilerPhase { private static final boolean DEBUG = false; public String getName() { return "RBE Transform"; } public boolean printingEnabled(OPT_Options options, boolean before) { return false && DEBUG; } /** * Return this instance of this phase. This phase contains * no per-compilation instance fields. * @param ir not used * @return this */ public OPT_CompilerPhase newExecution(OPT_IR ir) { return this; } /** * Transform to eliminate redundant branches passed on * GVNs and dominator information. * * @param ir The IR on which to apply the phase */ public void perform(OPT_IR ir) { // (1) Remove redundant conditional branches and locally fix the PHIs OPT_GlobalValueNumberState gvns = ir.HIRInfo.valueNumbers; OPT_DominatorTree dt = ir.HIRInfo.dominatorTree; for (OPT_BasicBlockEnumeration bbs = ir.getBasicBlocks(); bbs.hasMoreElements();) { OPT_BasicBlock candBB = bbs.next(); OPT_Instruction candTest = candBB.firstBranchInstruction(); if (candTest == null) continue; if (!(IfCmp.conforms(candTest) || InlineGuard.conforms(candTest))) continue; OPT_GVCongruenceClass cc = gvns.congruenceClass(candTest); if (cc.size() > 1) { for (OPT_ValueGraphVertex vertex : cc) { OPT_Instruction poss = (OPT_Instruction) vertex.getName(); if (poss != candTest) { OPT_BasicBlock notTaken = getNotTakenBlock(poss); OPT_BasicBlock taken = poss.getBranchTarget(); if (taken == notTaken) continue; // both go to same block, so we don't know anything! if (notTaken.hasOneIn() && dt.dominates(notTaken, candBB)) { if (DEBUG) VM.sysWrite(candTest + " is dominated by not-taken branch of " + poss + "\n"); removeCondBranch(candBB, candTest, ir, poss); cc.removeVertex(gvns.valueGraph.getVertex(candTest)); break; } if (taken.hasOneIn() && dt.dominates(taken, candBB)) { if (DEBUG) VM.sysWrite(candTest + " is dominated by taken branch of " + poss + "\n"); takeCondBranch(candBB, candTest, ir); cc.removeVertex(gvns.valueGraph.getVertex(candTest)); break; } } } } } // (2) perform a Depth-first search of the control flow graph, // and remove any nodes we have made unreachable removeUnreachableCode(ir); } /** * Remove unreachable code * * @param ir the IR to optimize */ private void removeUnreachableCode(OPT_IR ir) { boolean removedCode = false; OPT_BasicBlock entry = ir.cfg.entry(); ir.cfg.clearDFS(); entry.sortDFS(); for (OPT_BasicBlock node = entry; node != null;) { // save it now before removeFromCFGAndCodeOrder nulls it out!!! OPT_BasicBlock nextNode = (OPT_BasicBlock) node.getNext(); if (!node.dfsVisited()) { for (OPT_BasicBlockEnumeration e = node.getOut(); e.hasMoreElements();) { OPT_BasicBlock target = e.next(); if (target != node && !target.isExit() && target.dfsVisited()) { OPT_SSA.purgeBlockFromPHIs(node, target); } } ir.cfg.removeFromCFGAndCodeOrder(node); removedCode = true; } node = nextNode; } if (removedCode) { ir.cfg.compactNodeNumbering(); ir.HIRInfo.dominatorTree = null; ir.HIRInfo.dominatorsAreComputed = false; } } /** * Return the basic block that s's block will goto if s is not taken. */ private OPT_BasicBlock getNotTakenBlock(OPT_Instruction s) { s = s.nextInstructionInCodeOrder(); if (Goto.conforms(s)) return s.getBranchTarget(); if (VM.VerifyAssertions) VM._assert(s.operator() == BBEND); return s.getBasicBlock().nextBasicBlockInCodeOrder(); } /** * Remove cb from source, updating PHI nodes to maintain SSA form. * * @param source basic block containing cb * @param cb conditional branch to remove * @param ir containing IR * @param di branch that dominates cb */ private void removeCondBranch(OPT_BasicBlock source, OPT_Instruction cb, OPT_IR ir, OPT_Instruction di) { if (DEBUG) VM.sysWrite("Eliminating definitely not-taken branch " + cb + "\n"); if (IfCmp.conforms(cb) && IfCmp.hasGuardResult(cb)) { cb.insertBefore(Move.create(GUARD_MOVE, IfCmp.getGuardResult(cb), IfCmp.getGuardResult(di).copy())); } OPT_BasicBlock deadBB = cb.getBranchTarget(); cb.remove(); 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. OPT_SSA.purgeBlockFromPHIs(source, deadBB); } } /** * Transform cb into a GOTO, updating PHI nodes to maintain SSA form. */ private void takeCondBranch(OPT_BasicBlock source, OPT_Instruction cb, OPT_IR ir) { if (DEBUG) VM.sysWrite("Eliminating definitely taken branch " + cb + "\n"); OPT_BasicBlock deadBB = source.nextBasicBlockInCodeOrder(); OPT_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. OPT_SSA.purgeBlockFromPHIs(source, deadBB); } } } }