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