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.controlflow;
014    
015    import org.jikesrvm.compilers.opt.ir.MIR_Branch;
016    import org.jikesrvm.compilers.opt.ir.MIR_CondBranch;
017    import org.jikesrvm.compilers.opt.ir.MIR_CondBranch2;
018    import org.jikesrvm.compilers.opt.ir.BasicBlock;
019    import org.jikesrvm.compilers.opt.ir.IR;
020    import org.jikesrvm.compilers.opt.ir.Instruction;
021    import org.jikesrvm.compilers.opt.ir.Operators;
022    import org.jikesrvm.compilers.opt.ir.operand.BranchOperand;
023    
024    import static org.jikesrvm.compilers.opt.ir.Operators.BBEND;
025    
026    /**
027     * Perform simple peephole optimizations for MIR branches.
028     */
029    public final class MIRBranchOptimizations extends BranchOptimizationDriver {
030    
031      /**
032       * @param level the minimum optimization level at which the branch
033       * optimizations should be performed.
034       */
035      public MIRBranchOptimizations(int level) {
036        super(level);
037      }
038    
039      /**
040       * This method actually does the work of attempting to
041       * peephole optimize a branch instruction.
042       * See Muchnick ~p.590
043       * @param ir the containing IR
044       * @param s the branch instruction to optimize
045       * @param bb the containing basic block
046       * @return {@code true} if an optimization was applied, {@code false} otherwise
047       */
048      @Override
049      protected boolean optimizeBranchInstruction(IR ir, Instruction s, BasicBlock bb) {
050        if (MIR_Branch.conforms(s)) {
051          return processGoto(ir, s, bb);
052        } else if (MIR_CondBranch.conforms(s)) {
053          return processCondBranch(ir, s, bb);
054        } else if (MIR_CondBranch2.conforms(s)) {
055          return processTwoTargetConditionalBranch(ir, s, bb);
056        } else {
057          return false;
058        }
059      }
060    
061      /**
062       * Perform optimizations for an unconditonal branch.
063       *
064       * <p> Patterns:
065       * <pre>
066       *    1)      GOTO A       replaced by  GOTO B
067       *         A: GOTO B
068       *
069       *    2)   GOTO next instruction eliminated
070       *    3)      GOTO A       replaced by  GOTO B
071       *         A: LABEL
072       *            BBEND
073       *         B:
074       * <pre>
075       *
076       * <p> Precondition: MIR_Branch.conforms(g)
077       *
078       * @param ir governing IR
079       * @param g the instruction to optimize
080       * @param bb the basic block holding g
081       * @return {@code true} if made a transformation
082       */
083      private boolean processGoto(IR ir, Instruction g, BasicBlock bb) {
084        BasicBlock targetBlock = g.getBranchTarget();
085        Instruction targetLabel = targetBlock.firstInstruction();
086        // get the first real instruction at the g target
087        // NOTE: this instruction is not necessarily in targetBlock,
088        // iff targetBlock has no real instructions
089        Instruction targetInst = firstRealInstructionFollowing(targetLabel);
090        if (targetInst == null || targetInst == g) {
091          return false;
092        }
093        Instruction nextLabel = firstLabelFollowing(g);
094        if (targetLabel == nextLabel) {
095          // found a GOTO to the next instruction.  just remove it.
096          g.remove();
097          return true;
098        }
099        if (MIR_Branch.conforms(targetInst)) {
100          // unconditional branch to unconditional branch.
101          // replace g with goto to targetInst's target
102          Instruction target2 = firstRealInstructionFollowing(targetInst.getBranchTarget().firstInstruction());
103          if (target2 == targetInst) {
104            // Avoid an infinite recursion in the following bizarre scenario:
105            // g: goto L
106            // ...
107            // L: goto L
108            // This happens in jByteMark.EmFloatPnt.denormalize() due to a while(true) {}
109            return false;
110          }
111          BranchOperand top = (BranchOperand) MIR_Branch.getTarget(targetInst).copy();
112          MIR_Branch.setTarget(g, top);
113          bb.recomputeNormalOut(ir); // fix the CFG
114          return true;
115        }
116        if (targetBlock.isEmpty()) {
117          // GOTO an empty block.  Change target to the next block.
118          BasicBlock nextBlock = targetBlock.getFallThroughBlock();
119          MIR_Branch.setTarget(g, nextBlock.makeJumpTarget());
120          bb.recomputeNormalOut(ir); // fix the CFG
121          return true;
122        }
123        return false;
124      }
125    
126      /**
127       * Perform optimizations for a conditional branch.
128       *
129       * <pre>
130       * 1)   IF .. GOTO A          replaced by  IF .. GOTO B
131       *      ...
132       *   A: GOTO B
133       * 2)   conditional branch to next instruction eliminated
134       * 3)   IF (condition) GOTO A  replaced by  IF (!condition) GOTO B
135       *      GOTO B                           A: ...
136       *   A: ...
137       * 4)   IF .. GOTO A       replaced by  IF .. GOTO B
138       *   A: LABEL
139       *      BBEND
140       *   B:
141       * 5)  fallthrough to a goto: replicate goto to enable other optimizations.
142       * </pre>
143       *
144       * <p> Precondition: MIR_CondBranch.conforms(cb)
145       *
146       * @param ir the governing IR
147       * @param cb the instruction to optimize
148       * @param bb the basic block holding if
149       * @return {@code true} iff made a transformation
150       */
151      private boolean processCondBranch(IR ir, Instruction cb, BasicBlock bb) {
152        BasicBlock targetBlock = cb.getBranchTarget();
153        Instruction targetLabel = targetBlock.firstInstruction();
154        // get the first real instruction at the branch target
155        // NOTE: this instruction is not necessarily in targetBlock,
156        // iff targetBlock has no real instructions
157        Instruction targetInst = firstRealInstructionFollowing(targetLabel);
158        if (targetInst == null || targetInst == cb) {
159          return false;
160        }
161        boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND;
162        if (endsBlock) {
163          Instruction nextLabel = firstLabelFollowing(cb);
164          if (targetLabel == nextLabel) {
165            // found a conditional branch to the next instruction.  just remove it.
166            cb.remove();
167            return true;
168          }
169          Instruction nextI = firstRealInstructionFollowing(nextLabel);
170          if (nextI != null && MIR_Branch.conforms(nextI)) {
171            // replicate Goto
172            cb.insertAfter(nextI.copyWithoutLinks());
173            bb.recomputeNormalOut(ir); // fix the CFG
174            return true;
175          }
176        }
177        if (MIR_Branch.conforms(targetInst)) {
178          // conditional branch to unconditional branch.
179          // change conditional branch target to latter's target
180          Instruction target2 = firstRealInstructionFollowing(targetInst.getBranchTarget().firstInstruction());
181          if (target2 == targetInst) {
182            // Avoid an infinite recursion in the following scenario:
183            // g: if (...) goto L
184            // ...
185            // L: goto L
186            // This happens in GCUtil in some systems due to a while(true) {}
187            return false;
188          }
189          MIR_CondBranch.setTarget(cb, (BranchOperand) (MIR_Branch.getTarget(targetInst).copy()));
190          bb.recomputeNormalOut(ir); // fix the CFG
191          return true;
192        }
193        if (targetBlock.isEmpty()) {
194          // branch to an empty block.  Change target to the next block.
195          BasicBlock nextBlock = targetBlock.getFallThroughBlock();
196          BranchOperand newTarget = nextBlock.makeJumpTarget();
197          MIR_CondBranch.setTarget(cb, newTarget);
198          bb.recomputeNormalOut(ir); // fix the CFG
199          return true;
200        }
201        if (isFlipCandidate(cb, targetInst)) {
202          flipConditionalBranch(cb);
203          bb.recomputeNormalOut(ir); // fix the CFG
204          return true;
205        }
206        return false;
207      }
208    
209      /**
210       * Perform optimizations for a two way conditional branch.
211       *
212       * <pre>
213       * 1)   IF .. GOTO A          replaced by  IF .. GOTO B
214       *      ...
215       *   A: GOTO B
216       * 2)   conditional branch to next instruction eliminated
217       * 3)   IF .. GOTO A       replaced by  IF .. GOTO B
218       *   A: LABEL
219       *      BBEND
220       *   B:
221       * 4)  fallthrough to a goto: replicate goto to enable other optimizations.
222       * </pre>
223       *
224       * <p> Precondition: MIR_CondBranch2.conforms(cb)
225       *
226       * @param ir the governing IR
227       * @param cb the instruction to optimize
228       * @param bb the basic block holding if
229       * @return {@code true} iff made a transformation
230       */
231      private boolean processTwoTargetConditionalBranch(IR ir, Instruction cb, BasicBlock bb) {
232        // First condition/target
233        Instruction target1Label = MIR_CondBranch2.getTarget1(cb).target;
234        Instruction target1Inst = firstRealInstructionFollowing(target1Label);
235        Instruction nextLabel = firstLabelFollowing(cb);
236        boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND;
237        if (target1Inst != null && target1Inst != cb) {
238          if (MIR_Branch.conforms(target1Inst)) {
239            // conditional branch to unconditional branch.
240            // change conditional branch target to latter's target
241            MIR_CondBranch2.setTarget1(cb, MIR_Branch.getTarget(target1Inst));
242            bb.recomputeNormalOut(ir); // fix CFG
243            return true;
244          }
245          BasicBlock target1Block = target1Label.getBasicBlock();
246          if (target1Block.isEmpty()) {
247            // branch to an empty block.  Change target to the next block.
248            BasicBlock nextBlock = target1Block.getFallThroughBlock();
249            MIR_CondBranch2.setTarget1(cb, nextBlock.makeJumpTarget());
250            bb.recomputeNormalOut(ir); // fix the CFG
251            return true;
252          }
253        }
254    
255        // Second condition/target
256        Instruction target2Label = MIR_CondBranch2.getTarget2(cb).target;
257        Instruction target2Inst = firstRealInstructionFollowing(target2Label);
258        if (target2Inst != null && target2Inst != cb) {
259          if (MIR_Branch.conforms(target2Inst)) {
260            // conditional branch to unconditional branch.
261            // change conditional branch target to latter's target
262            MIR_CondBranch2.setTarget2(cb, MIR_Branch.getTarget(target2Inst));
263            bb.recomputeNormalOut(ir); // fix CFG
264            return true;
265          }
266          if ((target2Label == nextLabel) && endsBlock) {
267            // found a conditional branch to the next instruction.
268            // Reduce to MIR_BranchCond
269            Operators.helper.mutateMIRCondBranch(cb);
270            return true;
271          }
272          BasicBlock target2Block = target2Label.getBasicBlock();
273          if (target2Block.isEmpty()) {
274            // branch to an empty block.  Change target to the next block.
275            BasicBlock nextBlock = target2Block.getFallThroughBlock();
276            MIR_CondBranch2.setTarget2(cb, nextBlock.makeJumpTarget());
277            bb.recomputeNormalOut(ir); // fix the CFG
278            return true;
279          }
280        }
281    
282        // if fall through to a goto; replicate the goto
283        if (endsBlock) {
284          Instruction nextI = firstRealInstructionFollowing(nextLabel);
285          if (nextI != null && MIR_Branch.conforms(nextI)) {
286            // replicate unconditional branch
287            cb.insertAfter(nextI.copyWithoutLinks());
288            bb.recomputeNormalOut(ir); // fix the CFG
289            return true;
290          }
291        }
292    
293        return false;
294      }
295    
296      /**
297       * Is a conditional branch a candidate to be flipped?
298       * See comment 3) of processCondBranch
299       *
300       * <p> Precondition: MIR_CondBranch.conforms(cb)
301       *
302       * @param cb the conditional branch instruction
303       * @param target the target instruction (real instruction) of the conditional
304       *               branch
305       * @return boolean result
306       */
307      private boolean isFlipCandidate(Instruction cb, Instruction target) {
308        // condition 1: is next instruction a GOTO?
309        Instruction next = cb.nextInstructionInCodeOrder();
310        if (!MIR_Branch.conforms(next)) {
311          return false;
312        }
313        // condition 2: is the target of the conditional branch the
314        //  next instruction after the GOTO?
315        next = firstRealInstructionFollowing(next);
316        if (next != target) {
317          return false;
318        }
319        // got this far.  It's a candidate.
320        return true;
321      }
322    
323      /**
324       * Flip a conditional branch and remove the trailing goto.
325       * See comment 3) of processCondBranch
326       *
327       * <p> Precondition isFlipCandidate(cb)
328       * @param cb the conditional branch instruction
329       */
330      private void flipConditionalBranch(Instruction cb) {
331        // get the trailing GOTO instruction
332        Instruction g = cb.nextInstructionInCodeOrder();
333        BranchOperand gTarget = MIR_Branch.getTarget(g);
334        // now flip the test and set the new target
335        MIR_CondBranch.setCond(cb, MIR_CondBranch.getCond(cb).flipCode());
336        MIR_CondBranch.setTarget(cb, gTarget);
337        // Remove the trailing GOTO instruction
338        g.remove();
339      }
340    }