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.GOTO;
016
017import java.util.Enumeration;
018
019import org.jikesrvm.VM;
020import org.jikesrvm.compilers.opt.OptOptions;
021import org.jikesrvm.compilers.opt.driver.CompilerPhase;
022import org.jikesrvm.compilers.opt.ir.BasicBlock;
023import org.jikesrvm.compilers.opt.ir.Goto;
024import org.jikesrvm.compilers.opt.ir.IR;
025import org.jikesrvm.compilers.opt.ir.Instruction;
026import org.jikesrvm.compilers.opt.ir.WeightedBranchTargets;
027import org.jikesrvm.compilers.opt.util.GraphNode;
028import org.jikesrvm.util.BitVector;
029
030/**
031 *  This Phase supports
032 *  <ul>
033 *    <li>transforming while into until loops,
034 *    <li>elimination of critical edges,
035 *  </ul>
036 */
037public class CFGTransformations extends CompilerPhase {
038
039  private static final boolean DEBUG = false;
040
041  /**
042   * Return this instance of this phase. This phase contains no
043   * per-compilation instance fields.
044   * @param ir not used
045   * @return this
046   */
047  @Override
048  public CompilerPhase newExecution(IR ir) {
049    return this;
050  }
051
052  @Override
053  public void perform(IR ir) {
054    staticPerform(ir);
055  }
056
057  static void staticPerform(IR ir) {
058    if (ir.hasReachableExceptionHandlers()) return;
059
060    // Note: the following unfactors the CFG.
061    DominatorsPhase dom = new DominatorsPhase(true);
062    boolean moreToCome = true;
063    while (moreToCome) {
064      dom.perform(ir);
065      moreToCome = turnWhilesIntoUntils(ir);
066    }
067
068    ensureLandingPads(ir);
069
070    dom.perform(ir);
071    ir.cfg.compactNodeNumbering();
072    ir.HIRInfo.dominatorsAreComputed = false; // compacting the node numbering destroys the dominator info
073  }
074
075  /**
076   * Should this phase be performed?
077   * @return <code>true</code> if the opt level is at least 2 and whiles should be turned into untils
078   */
079  @Override
080  public boolean shouldPerform(OptOptions options) {
081    if (options.getOptLevel() < 2) {
082      return false;
083    }
084    return options.CONTROL_TURN_WHILES_INTO_UNTILS;
085  }
086
087  /**
088   * Returns the name of the phase.
089   */
090  @Override
091  public String getName() {
092    return "Loop Normalization";
093  }
094
095  /**
096   * Returns {@code true} if the phase wants the IR dumped before and/or after it runs.
097   */
098  @Override
099  public boolean printingEnabled(OptOptions options, boolean before) {
100    return false;
101  }
102
103  //Implementation
104
105  private static boolean turnWhilesIntoUntils(IR ir) {
106    LSTGraph lstg = ir.HIRInfo.loopStructureTree;
107    if (lstg != null) {
108      return turnLoopTreeIntoUntils((LSTNode) lstg.firstNode(), ir);
109    }
110    return false;
111  }
112
113  private static boolean turnLoopTreeIntoUntils(LSTNode t, IR ir) {
114    Enumeration<GraphNode> e = t.outNodes();
115    while (e.hasMoreElements()) {
116      LSTNode n = (LSTNode) e.nextElement();
117      if (turnLoopTreeIntoUntils(n, ir)) {
118        return true;
119      }
120      if (turnLoopIntoUntil(n, ir)) {
121        return true;
122      }
123    }
124    return false;
125  }
126
127  private static void ensureLandingPads(IR ir) {
128    LSTGraph lstg = ir.HIRInfo.loopStructureTree;
129    if (lstg != null) {
130      ensureLandingPads((LSTNode) lstg.firstNode(), ir);
131    }
132  }
133
134  private static void ensureLandingPads(LSTNode t, IR ir) {
135    Enumeration<GraphNode> e = t.outNodes();
136    while (e.hasMoreElements()) {
137      LSTNode n = (LSTNode) e.nextElement();
138      ensureLandingPads(n, ir);
139      ensureLandingPad(n, ir);
140    }
141  }
142
143  private static float edgeFrequency(BasicBlock a, BasicBlock b) {
144    float prop = 0f;
145    WeightedBranchTargets ws = new WeightedBranchTargets(a);
146    while (ws.hasMoreElements()) {
147      if (ws.curBlock() == b) prop += ws.curWeight();
148      ws.advance();
149    }
150    return a.getExecutionFrequency() * prop;
151  }
152
153  private static void ensureLandingPad(LSTNode n, IR ir) {
154    BasicBlock[] ps = loopPredecessors(n);
155    if (ps.length == 1 && ps[0].getNumberOfOut() == 1) return;
156
157    float frequency = 0f;
158    for (BasicBlock bb : ps) {
159      frequency += edgeFrequency(bb, n.header);
160    }
161    BasicBlock newPred;
162    newPred = n.header.createSubBlock(n.header.firstInstruction().bcIndex, ir, 1f);
163    newPred.setLandingPad();
164    newPred.setExecutionFrequency(frequency);
165
166    BasicBlock p = n.header.prevBasicBlockInCodeOrder();
167    if (VM.VerifyAssertions) VM._assert(p != null);
168    p.killFallThrough();
169    ir.cfg.breakCodeOrder(p, n.header);
170    ir.cfg.linkInCodeOrder(p, newPred);
171    ir.cfg.linkInCodeOrder(newPred, n.header);
172
173    newPred.lastInstruction().insertBefore(Goto.create(GOTO, n.header.makeJumpTarget()));
174    newPred.recomputeNormalOut(ir);
175
176    for (BasicBlock bb : ps) {
177      bb.redirectOuts(n.header, newPred, ir);
178    }
179  }
180
181  /**
182   * Transforms a given loop.
183   *
184   * <p> Look for the set S of in-loop predecessors of the loop header h.
185   * Make a copy h' of the loop header and redirect all edges going from
186   * nodes in S to h. Make them point to h' instead.
187   *
188   * <p> As an effect of this transformation, the old header is now not anymore
189   * part of the loop, but guards it.
190   *
191   * @param n anode
192   * @param ir the governing IR
193   * @return whether anything was changed
194   */
195  private static boolean turnLoopIntoUntil(LSTNode n, IR ir) {
196    BasicBlock header = n.header;
197    BasicBlock newLoopTest = null;
198
199    int i = 0;
200    int exiters = 0;
201
202    Enumeration<BasicBlock> e = ir.getBasicBlocks(n.loop);
203    while (e.hasMoreElements()) {
204      BasicBlock b = e.nextElement();
205      if (!exitsLoop(b, n.loop)) {
206        // header doesn't exit: nothing to do
207        if (b == n.header) return false;
208      } else {
209        exiters++;
210      }
211      i++;
212    }
213    // all blocks exit: can't improve
214    if (i == exiters) return false;
215
216    // rewriting loops where the header has more than one in-loop
217    // successor will lead to irreducible control flow.
218    BasicBlock[] succ = inLoopSuccessors(n);
219    if (succ.length > 1) {
220      if (DEBUG) VM.sysWrite("unwhiling would lead to irreducible CFG\n");
221      return false;
222    }
223
224    BasicBlock[] pred = inLoopPredecessors(n);
225    float frequency = 0f;
226
227    if (pred.length > 0) {
228      frequency += edgeFrequency(pred[0], header);
229      // replicate the header as successor of pred[0]
230      BasicBlock p = header.prevBasicBlockInCodeOrder();
231      p.killFallThrough();
232      newLoopTest = pred[0].replicateThisOut(ir, header, p);
233    }
234    for (i = 1; i < pred.length; ++i) { // check for aditional back edges
235      frequency += edgeFrequency(pred[i], header);
236      pred[i].redirectOuts(header, newLoopTest, ir);
237    }
238    newLoopTest.setExecutionFrequency(frequency);
239    header.setExecutionFrequency(header.getExecutionFrequency() - frequency);
240    return true;
241  }
242
243  private static BasicBlock[] loopPredecessors(LSTNode n) {
244    BasicBlock header = n.header;
245    BitVector loop = n.loop;
246
247    int i = 0;
248    Enumeration<BasicBlock> be = header.getIn();
249    while (be.hasMoreElements()) if (!inLoop(be.nextElement(), loop)) i++;
250
251    BasicBlock[] res = new BasicBlock[i];
252
253    i = 0;
254    be = header.getIn();
255    while (be.hasMoreElements()) {
256      BasicBlock in = be.nextElement();
257      if (!inLoop(in, loop)) res[i++] = in;
258    }
259    return res;
260  }
261
262  private static BasicBlock[] inLoopPredecessors(LSTNode n) {
263    BasicBlock header = n.header;
264    BitVector loop = n.loop;
265
266    int i = 0;
267    Enumeration<BasicBlock> be = header.getIn();
268    while (be.hasMoreElements()) if (inLoop(be.nextElement(), loop)) i++;
269
270    BasicBlock[] res = new BasicBlock[i];
271
272    i = 0;
273    be = header.getIn();
274    while (be.hasMoreElements()) {
275      BasicBlock in = be.nextElement();
276      if (inLoop(in, loop)) res[i++] = in;
277    }
278    return res;
279  }
280
281  private static BasicBlock[] inLoopSuccessors(LSTNode n) {
282    BasicBlock header = n.header;
283    BitVector loop = n.loop;
284
285    int i = 0;
286    Enumeration<BasicBlock> be = header.getOut();
287    while (be.hasMoreElements()) if (inLoop(be.nextElement(), loop)) i++;
288
289    BasicBlock[] res = new BasicBlock[i];
290
291    i = 0;
292    be = header.getOut();
293    while (be.hasMoreElements()) {
294      BasicBlock in = be.nextElement();
295      if (inLoop(in, loop)) res[i++] = in;
296    }
297    return res;
298  }
299
300  static void killFallThroughs(IR ir, BitVector nloop) {
301    Enumeration<BasicBlock> bs = ir.getBasicBlocks(nloop);
302    while (bs.hasMoreElements()) {
303      BasicBlock block = bs.nextElement();
304      Enumeration<BasicBlock> bi = block.getIn();
305      while (bi.hasMoreElements()) {
306        BasicBlock in = bi.nextElement();
307        if (inLoop(in, nloop)) continue;
308        in.killFallThrough();
309      }
310      block.killFallThrough();
311    }
312  }
313
314  static boolean inLoop(BasicBlock b, BitVector nloop) {
315    int idx = b.getNumber();
316    if (idx >= nloop.length()) return false;
317    return nloop.get(idx);
318  }
319
320  private static boolean exitsLoop(BasicBlock b, BitVector loop) {
321    Enumeration<BasicBlock> be = b.getOut();
322    while (be.hasMoreElements()) {
323      if (!inLoop(be.nextElement(), loop)) return true;
324    }
325    return false;
326  }
327
328  /**
329   * Critical edge removal: if (a,b) is an edge in the cfg where `a' has more
330   * than one out-going edge and `b' has more than one in-coming edge,
331   * insert a new empty block `c' on the edge between `a' and `b'.
332   *
333   * <p> We do this to provide landing pads for loop-invariant code motion.
334   * So we split only edges, where `a' has a lower loop nesting depth than `b'.
335   *
336   * @param ir the IR to process
337   */
338  public static void splitCriticalEdges(IR ir) {
339    Enumeration<BasicBlock> e = ir.getBasicBlocks();
340    while (e.hasMoreElements()) {
341      BasicBlock b = e.nextElement();
342      int numberOfIns = b.getNumberOfIn();
343      //Exception handlers and blocks with less than two inputs
344      // are no candidates for `b'.
345      if (b.isExceptionHandlerBasicBlock() || numberOfIns <= 1) {
346        continue;
347      }
348      // copy the predecessors, since we will alter the incoming edges.
349      BasicBlock[] ins = new BasicBlock[numberOfIns];
350      Enumeration<BasicBlock> ie = b.getIn();
351      for (int i = 0; i < numberOfIns; ++i) {
352        ins[i] = ie.nextElement();
353      }
354      // skip blocks, that do not fulfill our requirements for `a'
355      for (int i = 0; i < numberOfIns; ++i) {
356        BasicBlock a = ins[i];
357        if (a.getNumberOfOut() <= 1) {
358          continue;
359        }
360        // insert pads only for moving code up to the start of the method
361        //if (a.getExecutionFrequency() >= b.getExecutionFrequency()) continue;
362
363        // create a new block as landing pad
364        BasicBlock landingPad;
365        Instruction firstInB = b.firstInstruction();
366        int bcIndex = firstInB != null ? firstInB.bcIndex : -1;
367        landingPad = b.createSubBlock(bcIndex, ir);
368        landingPad.setLandingPad();
369        landingPad.setExecutionFrequency(edgeFrequency(a, b));
370
371        // make the landing pad jump to `b'
372        Instruction g;
373        g = Goto.create(GOTO, b.makeJumpTarget());
374        landingPad.appendInstruction(g);
375        landingPad.recomputeNormalOut(ir);
376        // redirect a's outputs from b to the landing pad
377        a.redirectOuts(b, landingPad, ir);
378
379        a.killFallThrough();
380        BasicBlock aNext = a.nextBasicBlockInCodeOrder();
381        if (aNext != null) {
382          ir.cfg.breakCodeOrder(a, aNext);
383          ir.cfg.linkInCodeOrder(landingPad, aNext);
384        }
385        ir.cfg.linkInCodeOrder(a, landingPad);
386      }
387    }
388  }
389}