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