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 static org.jikesrvm.compilers.opt.ir.Operators.GOTO;
016    import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_OSR;
017    
018    import java.util.Enumeration;
019    
020    import org.jikesrvm.VM;
021    import org.jikesrvm.compilers.opt.OptOptions;
022    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
023    import org.jikesrvm.compilers.opt.ir.BasicBlock;
024    import org.jikesrvm.compilers.opt.ir.Goto;
025    import org.jikesrvm.compilers.opt.ir.IR;
026    import org.jikesrvm.compilers.opt.ir.InlineGuard;
027    import org.jikesrvm.compilers.opt.ir.Instruction;
028    
029    /**
030     * Static splitting based on very simple hints left by
031     * guarded inlining (off blocks marked as infrequent)
032     * and semantic knowledge of tests.
033     * The goal of this pass is to create 'common-case' traces.
034     * This is done by eliminating merge points where
035     * uncommon-case code merges back into common case code
036     * by code duplication. We rely on a later pass to
037     * eliminate redundant tests on the common-case trace.
038     * <p>
039     * We use semantic knowledge of the tests to reduce the
040     * code replicated.  The key idea is that for a guarded
041     * inlining, it is correct to take the 'off' branch even
042     * if test would select the on-branch.  Therefore we can
043     * avoid replicating the on-branch code downstream of the
044     * replicated test, at the possible cost of trapping an
045     * execution in the uncommon-case trace that might have
046     * been able to use a subset of to common-case trace.
047     * <p>
048     */
049    public class StaticSplitting extends CompilerPhase {
050    
051      private static final boolean DEBUG = false;
052      private final BranchOptimizations branchOpts;
053    
054      public StaticSplitting() {
055        branchOpts = new BranchOptimizations(-1, false, false);
056      }
057    
058      /**
059       * Return this instance of this phase. This phase contains no
060       * per-compilation instance fields.
061       * @param ir not used
062       * @return this
063       */
064      @Override
065      public CompilerPhase newExecution(IR ir) {
066        return this;
067      }
068    
069      @Override
070      public String getName() { return "Static Splitting"; }
071    
072      @Override
073      public boolean shouldPerform(OptOptions options) {
074        return options.CONTROL_STATIC_SPLITTING;
075      }
076    
077      @Override
078      public boolean printingEnabled(OptOptions options, boolean before) {
079        return DEBUG;
080      }
081    
082      /**
083       * Do simplistic static splitting to create hot traces
084       * with that do not have incoming edges from
085       * blocks that are statically predicted to be cold.
086       *
087       * @param ir   The IR on which to apply the phase
088       */
089      @Override
090      public void perform(IR ir) {
091        // (1) Find candidates to split
092        simpleCandidateSearch(ir);
093    
094        // (2) Split them
095        boolean needCleanup = haveCandidates();
096        while (haveCandidates()) {
097          splitCandidate(nextCandidate(), ir);
098        }
099    
100        // (3) If something was split optimize the CFG
101        if (needCleanup) {
102          branchOpts.perform(ir);
103        }
104      }
105    
106      /**
107       * Identify candidate blocks by using a very
108       * simplistic algorithm.
109       * <ul>
110       * <li> Find all blocks that end in a test that
111       *      is statically known to be likely to
112       *      create a common case trace. For example,
113       *      blocks that end in IG_METHOD_TEST, IG_CLASS_TEST
114       *      and IG_PATCH_POINT. Note that these tests also
115       *      have the property that it is correct
116       *      (but less desirable) to execute the off branch
117       *      when the test would have selected the on branch.
118       * <li> If such a block has a control flow predecessor
119       *      that is marked as infrequent, and if the block
120       *      is relatively small, then it is almost certainly
121       *      profitable to duplicate the block and transfer
122       *      the infrequent predecessor to go to
123       *      the cloned block.  This has the effect of freeing
124       *      the common-case path from the pollution of the
125       *      infrequently executed block. Therefore we identify
126       *      the block as a splitting candidate.
127       * </ul>
128       */
129      private void simpleCandidateSearch(IR ir) {
130        for (Enumeration<BasicBlock> e = ir.getBasicBlocks(); e.hasMoreElements();) {
131          BasicBlock cand = e.nextElement();
132          if (cand.isExceptionHandlerBasicBlock()) continue;
133          Instruction candTest = getCandidateTest(cand);
134          if (candTest == null) continue;
135          BasicBlock coldPrev = findColdPrev(cand);
136          if (coldPrev == null) continue;
137          if (tooBig(cand, ir.options.CONTROL_STATIC_SPLITTING_MAX_COST)) continue;
138          BasicBlock coldSucc = findColdSucc(cand, candTest);
139          if (containsOSRPoint(coldSucc)) continue;
140          if (DEBUG) {
141            VM.sysWrite("Found candidate \n");
142            VM.sysWrite("\tTest is " + candTest + "\n");
143            VM.sysWrite("\tcoldPrev is " + coldPrev + "\n");
144            VM.sysWrite("\tcoldSucc is " + coldSucc + "\n");
145            cand.printExtended();
146          }
147          pushCandidate(cand, coldPrev, coldSucc, candTest);
148        }
149      }
150    
151      /**
152       * Split a node where we can safely not
153       * replicate the on-branch in the cloned node.
154       * @param ci description of the split candidate.
155       */
156      private void splitCandidate(CandInfo ci, IR ir) {
157        BasicBlock cand = ci.candBB;
158        BasicBlock prev = ci.prevBB;
159        BasicBlock succ = ci.succBB;
160        BasicBlock clone = cand.copyWithoutLinks(ir);
161    
162        // Redirect clone to always stay on cold path.
163        Instruction s = clone.lastRealInstruction();
164        while (s.isBranch()) {
165          s = s.remove();
166        }
167        clone.appendInstruction(Goto.create(GOTO, succ.makeJumpTarget()));
168    
169        // inject clone in code order;
170        // force prev to go to clone instead of cand.
171        prev.redirectOuts(cand, clone, ir);
172        clone.recomputeNormalOut(ir);
173        ir.cfg.addLastInCodeOrder(clone);
174        clone.setInfrequent();
175      }
176    
177      /**
178       * Return the candidate test in b, or <code>null</code> if
179       * b does not have one.
180       */
181      private Instruction getCandidateTest(BasicBlock bb) {
182        Instruction test = null;
183        for (Enumeration<Instruction> e = bb.enumerateBranchInstructions(); e.hasMoreElements();) {
184          Instruction branch = e.nextElement();
185          if (InlineGuard.conforms(branch)) {
186            if (test != null) return null; // found multiple tests!
187            test = branch;
188          } else if (branch.operator() != GOTO) {
189            return null;
190          }
191        }
192        return test;
193      }
194    
195      /**
196       * Return the cold predecessor to the argument block.
197       * If there is not exactly 1, return {@code null}.
198       */
199      private BasicBlock findColdPrev(BasicBlock bb) {
200        BasicBlock cold = null;
201        for (java.util.Enumeration<BasicBlock> e = bb.getInNodes(); e.hasMoreElements();) {
202          BasicBlock p = e.nextElement();
203          if (p.getInfrequent()) {
204            if (cold != null) return null;
205            cold = p;
206          }
207        }
208        return cold;
209      }
210    
211      /**
212       * Return the off-trace successor of b
213       * (on and off relative to the argument test)
214       */
215      private BasicBlock findColdSucc(BasicBlock bb, Instruction test) {
216        return test.getBranchTarget();
217      }
218    
219      /**
220       * Simplistic cost estimate; since we
221       * are doing the splitting based on
222       * static hints, we are only willing to
223       * copy a very small amount of code.
224       */
225      private boolean tooBig(BasicBlock bb, int maxCost) {
226        int cost = 0;
227        for (Enumeration<Instruction> e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
228          Instruction s = e.nextElement();
229          if (s.isCall()) {
230            cost += 3;
231          } else if (s.isAllocation()) {
232            cost += 6;
233          } else {
234            cost++;
235          }
236          if (cost > maxCost) return true;
237        }
238        return false;
239      }
240    
241      private boolean containsOSRPoint(BasicBlock bb) {
242        for (Enumeration<Instruction> e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
243          Instruction s = e.nextElement();
244          if (s.operator() == YIELDPOINT_OSR) {
245            return true;
246          }
247        }
248        return false;
249      }
250    
251      /*
252       * Support for remembering candidates
253       */
254      private CandInfo cands;
255    
256      private static final class CandInfo {
257        final BasicBlock candBB;
258        final BasicBlock prevBB;
259        final BasicBlock succBB;
260        final Instruction test;
261        final CandInfo next;
262    
263        CandInfo(BasicBlock c, BasicBlock p, BasicBlock s, Instruction t, CandInfo n) {
264          candBB = c;
265          prevBB = p;
266          succBB = s;
267          test = t;
268          next = n;
269        }
270      }
271    
272      private void pushCandidate(BasicBlock cand, BasicBlock prev, BasicBlock succ, Instruction test) {
273        cands = new CandInfo(cand, prev, succ, test, cands);
274      }
275    
276      private boolean haveCandidates() {
277        return cands != null;
278      }
279    
280      private CandInfo nextCandidate() {
281        CandInfo res = cands;
282        cands = cands.next;
283        return res;
284      }
285    }