001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.opt.ssa;
014    
015    import static org.jikesrvm.compilers.opt.ir.Operators.BBEND;
016    import static org.jikesrvm.compilers.opt.ir.Operators.GOTO;
017    import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
018    
019    import java.util.Enumeration;
020    
021    import org.jikesrvm.VM;
022    import org.jikesrvm.compilers.opt.OptOptions;
023    import org.jikesrvm.compilers.opt.controlflow.DominatorTree;
024    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
025    import org.jikesrvm.compilers.opt.driver.OptimizationPlanAtomicElement;
026    import org.jikesrvm.compilers.opt.driver.OptimizationPlanCompositeElement;
027    import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
028    import org.jikesrvm.compilers.opt.ir.BasicBlock;
029    import org.jikesrvm.compilers.opt.ir.Goto;
030    import org.jikesrvm.compilers.opt.ir.IR;
031    import org.jikesrvm.compilers.opt.ir.IfCmp;
032    import org.jikesrvm.compilers.opt.ir.InlineGuard;
033    import org.jikesrvm.compilers.opt.ir.Instruction;
034    import org.jikesrvm.compilers.opt.ir.Move;
035    
036    /**
037     * Redundant branch elimination based on SSA form, global value numbers,
038     * and dominance relationships.
039     * The following are sufficient conditions for a conditional branch cb1
040     * to be eliminated as redundant
041     * <ul>
042     * <li> It is equivalent (has the same value number) as another
043     *      conditional branch cb2
044     * <li> Either (a) the target of the taken branch of cb2 dominates cb1
045     *      and said target block has exactly one in edge or (b)
046     *      the not-taken continuation of cb2 dominates cb1 and
047     *      said continuation block has exactly one in edge.
048     * </ul>
049     * NOTE: the check for exactly one in edge is used to rule out
050     *       situations like the following:
051     * <pre>
052     *      if (C) goto L2              // cb2
053     *      x = x + 1;
054     *  L2: x = x + 1;
055     *      if (C) goto L3.            // cb1
056     * </pre>
057     * Consider redundant branch elimination for cb1.
058     * Here L2 (the target of cb2) dominates cb1, but it
059     * is not correct to eliminate cb1 because it is also
060     * reachable (but not dominated) from the continuation
061     * block of cb2!
062     */
063    public final class RedundantBranchElimination extends OptimizationPlanCompositeElement {
064    
065      @Override
066      public boolean shouldPerform(OptOptions options) {
067        return options.SSA_REDUNDANT_BRANCH_ELIMINATION;
068      }
069    
070      /**
071       * Create this phase element as a composite of other elements
072       */
073      public RedundantBranchElimination() {
074        super("RedundantBranchElimination", new OptimizationPlanElement[]{
075            // Stage 1: Require SSA form
076            new OptimizationPlanAtomicElement(new EnsureSSA()),
077    
078            // Stage2: Require GVNs
079            new OptimizationPlanAtomicElement(new GlobalValueNumber()),
080    
081            // Stage3: Do the optimization
082            new OptimizationPlanAtomicElement(new RBE()),});
083      }
084    
085      private static final class EnsureSSA extends CompilerPhase {
086    
087        @Override
088        public String getName() {
089          return "Ensure SSA";
090        }
091    
092        public boolean shouldPerform() {
093          return true;
094        }
095    
096        @Override
097        public void perform(IR ir) {
098          ir.desiredSSAOptions = new SSAOptions();
099          new EnterSSA().perform(ir);
100        }
101    
102        @Override
103        public CompilerPhase newExecution(IR ir) {
104          return this;
105        }
106      }
107    
108      private static final class RBE extends CompilerPhase {
109        private static final boolean DEBUG = false;
110    
111        @Override
112        public String getName() { return "RBE Transform"; }
113    
114        @Override
115        public boolean printingEnabled(OptOptions options, boolean before) {
116          return false && DEBUG;
117        }
118    
119        /**
120         * Return this instance of this phase. This phase contains
121         * no per-compilation instance fields.
122         * @param ir not used
123         * @return this
124         */
125        @Override
126        public CompilerPhase newExecution(IR ir) {
127          return this;
128        }
129    
130        /**
131         * Transform to eliminate redundant branches passed on
132         * GVNs and dominator information.
133         *
134         * @param ir   The IR on which to apply the phase
135         */
136        @Override
137        public void perform(IR ir) {
138          // (1) Remove redundant conditional branches and locally fix the PHIs
139          GlobalValueNumberState gvns = ir.HIRInfo.valueNumbers;
140          DominatorTree dt = ir.HIRInfo.dominatorTree;
141          for (Enumeration<BasicBlock> bbs = ir.getBasicBlocks(); bbs.hasMoreElements();) {
142            BasicBlock candBB = bbs.nextElement();
143            Instruction candTest = candBB.firstBranchInstruction();
144            if (candTest == null) continue;
145            if (!(IfCmp.conforms(candTest) || InlineGuard.conforms(candTest))) continue;
146            GVCongruenceClass cc = gvns.congruenceClass(candTest);
147            if (cc.size() > 1) {
148              for (ValueGraphVertex vertex : cc) {
149                Instruction poss = (Instruction) vertex.getName();
150                if (poss != candTest) {
151                  BasicBlock notTaken = getNotTakenBlock(poss);
152                  BasicBlock taken = poss.getBranchTarget();
153                  if (taken == notTaken) continue; // both go to same block, so we don't know anything!
154                  if (notTaken.hasOneIn() && dt.dominates(notTaken, candBB)) {
155                    if (DEBUG) VM.sysWrite(candTest + " is dominated by not-taken branch of " + poss + "\n");
156                    removeCondBranch(candBB, candTest, ir, poss);
157                    cc.removeVertex(gvns.valueGraph.getVertex(candTest));
158                    break;
159                  }
160                  if (taken.hasOneIn() && dt.dominates(taken, candBB)) {
161                    if (DEBUG) VM.sysWrite(candTest + " is dominated by taken branch of " + poss + "\n");
162                    takeCondBranch(candBB, candTest, ir);
163                    cc.removeVertex(gvns.valueGraph.getVertex(candTest));
164                    break;
165                  }
166                }
167              }
168            }
169          }
170          // (2) perform a Depth-first search of the control flow graph,
171          //     and remove any nodes we have made unreachable
172          removeUnreachableCode(ir);
173        }
174    
175        /**
176         * Remove unreachable code
177         *
178         * @param ir the IR to optimize
179         */
180        private void removeUnreachableCode(IR ir) {
181          boolean removedCode = false;
182          BasicBlock entry = ir.cfg.entry();
183          ir.cfg.clearDFS();
184          entry.sortDFS();
185          for (BasicBlock node = entry; node != null;) {
186            // save it now before removeFromCFGAndCodeOrder nulls it out!!!
187            BasicBlock nextNode = (BasicBlock) node.getNext();
188            if (!node.dfsVisited()) {
189              for (Enumeration<BasicBlock> e = node.getOut(); e.hasMoreElements();) {
190                BasicBlock target = e.nextElement();
191                if (target != node && !target.isExit() && target.dfsVisited()) {
192                  SSA.purgeBlockFromPHIs(node, target);
193                }
194              }
195              ir.cfg.removeFromCFGAndCodeOrder(node);
196              removedCode = true;
197            }
198            node = nextNode;
199          }
200          if (removedCode) {
201            ir.cfg.compactNodeNumbering();
202            ir.HIRInfo.dominatorTree = null;
203            ir.HIRInfo.dominatorsAreComputed = false;
204          }
205        }
206    
207        /**
208         * Return the basic block that s's block will goto if s is not taken.
209         */
210        private BasicBlock getNotTakenBlock(Instruction s) {
211          s = s.nextInstructionInCodeOrder();
212          if (Goto.conforms(s)) return s.getBranchTarget();
213          if (VM.VerifyAssertions) VM._assert(s.operator() == BBEND);
214          return s.getBasicBlock().nextBasicBlockInCodeOrder();
215        }
216    
217        /**
218         * Remove cb from source, updating PHI nodes to maintain SSA form.
219         *
220         * @param source basic block containing cb
221         * @param cb conditional branch to remove
222         * @param ir containing IR
223         * @param di branch that dominates cb
224         */
225        private void removeCondBranch(BasicBlock source, Instruction cb, IR ir, Instruction di) {
226          if (DEBUG) VM.sysWrite("Eliminating definitely not-taken branch " + cb + "\n");
227          if (IfCmp.conforms(cb) && IfCmp.hasGuardResult(cb)) {
228            cb.insertBefore(Move.create(GUARD_MOVE, IfCmp.getGuardResult(cb), IfCmp.getGuardResult(di).copy()));
229          }
230          BasicBlock deadBB = cb.getBranchTarget();
231          cb.remove();
232          source.recomputeNormalOut(ir);
233          if (!source.pointsOut(deadBB)) {
234            // there is no longer an edge from source to target;
235            // update any PHIs in target to reflect this.
236            SSA.purgeBlockFromPHIs(source, deadBB);
237          }
238        }
239    
240        /**
241         * Transform cb into a GOTO, updating PHI nodes to maintain SSA form.
242         */
243        private void takeCondBranch(BasicBlock source, Instruction cb, IR ir) {
244          if (DEBUG) VM.sysWrite("Eliminating definitely taken branch " + cb + "\n");
245          BasicBlock deadBB = source.nextBasicBlockInCodeOrder();
246          Instruction next = cb.nextInstructionInCodeOrder();
247          if (Goto.conforms(next)) {
248            deadBB = next.getBranchTarget();
249            next.remove();
250          }
251          Goto.mutate(cb, GOTO, cb.getBranchTarget().makeJumpTarget());
252          source.recomputeNormalOut(ir);
253          if (!source.pointsOut(deadBB)) {
254            // there is no longer an edge from source to target;
255            // update any PHIs in target to reflect this.
256            SSA.purgeBlockFromPHIs(source, deadBB);
257          }
258        }
259      }
260    }