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.adaptive.recompilation.instrumentation; 014 015import java.lang.reflect.Constructor; 016import java.util.ArrayList; 017import java.util.Enumeration; 018import java.util.HashMap; 019import java.util.HashSet; 020import java.util.Map; 021import org.jikesrvm.VM; 022import org.jikesrvm.adaptive.AosEntrypoints; 023import org.jikesrvm.compilers.opt.DefUse; 024import org.jikesrvm.compilers.opt.OptOptions; 025import org.jikesrvm.compilers.opt.Simple; 026import org.jikesrvm.compilers.opt.controlflow.BranchOptimizations; 027import org.jikesrvm.compilers.opt.driver.CompilerPhase; 028import org.jikesrvm.compilers.opt.ir.Binary; 029import org.jikesrvm.compilers.opt.ir.GetStatic; 030import org.jikesrvm.compilers.opt.ir.Goto; 031import org.jikesrvm.compilers.opt.ir.IfCmp; 032import org.jikesrvm.compilers.opt.ir.InstrumentedCounter; 033import org.jikesrvm.compilers.opt.ir.Load; 034import org.jikesrvm.compilers.opt.ir.BasicBlock; 035import org.jikesrvm.compilers.opt.ir.IR; 036import org.jikesrvm.compilers.opt.ir.IRTools; 037import org.jikesrvm.compilers.opt.ir.Instruction; 038import org.jikesrvm.compilers.opt.ir.Operator; 039import static org.jikesrvm.compilers.opt.ir.Operators.GETSTATIC; 040import static org.jikesrvm.compilers.opt.ir.Operators.GOTO; 041import static org.jikesrvm.compilers.opt.ir.Operators.INT_ADD; 042import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP; 043import static org.jikesrvm.compilers.opt.ir.Operators.INT_LOAD; 044import static org.jikesrvm.compilers.opt.ir.Operators.INT_STORE; 045import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE; 046import static org.jikesrvm.compilers.opt.ir.Operators.LABEL; 047import static org.jikesrvm.compilers.opt.ir.Operators.PUTSTATIC; 048import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_BACKEDGE; 049import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_PROLOGUE; 050import org.jikesrvm.compilers.opt.ir.Register; 051import org.jikesrvm.compilers.opt.ir.PutStatic; 052import org.jikesrvm.compilers.opt.ir.Store; 053import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand; 054import org.jikesrvm.compilers.opt.ir.operand.BranchOperand; 055import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand; 056import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand; 057import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand; 058import org.jikesrvm.compilers.opt.ir.operand.LocationOperand; 059import org.jikesrvm.compilers.opt.ir.operand.Operand; 060import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; 061 062/** 063 * Transforms the method so that instrumentation is sampled, rather 064 * than executed exhaustively. Includes both the full-duplication 065 * and no-duplication variations. See Arnold-Ryder PLDI 2001, and 066 * the section 4.1 of Arnold's PhD thesis. 067 * <p> 068 * NOTE: This implementation uses yieldpoints to denote where checks 069 * should be placed (to transfer control into instrumented code). It 070 * is currently assumed that these are on method entries and 071 * backedges. When optimized yieldpoint placement exists either a) 072 * it should be turned off when using this phase, or b) this phase 073 * should detect its own check locations. 074 * <p> 075 * To avoid the complexity of duplicating exception handler maps, 076 * exception handler blocks are split and a check at the top of the 077 * handler. Thus exceptions from both the checking and duplicated 078 * code are handled by the same catch block, however the check at the 079 * top of the catch block ensures that the hander code has the 080 * opportunity to be sampled. 081 */ 082public final class InstrumentationSamplingFramework extends CompilerPhase { 083 084 /** 085 * These are local copies of optimizing compiler debug options that can be 086 * set via the command line arguments. 087 */ 088 private static boolean DEBUG = false; 089 private static boolean DEBUG2 = false; 090 091 /** 092 * Temporary variables 093 */ 094 private RegisterOperand cbsReg = null; 095 096 /** 097 * Constructor for this compiler phase 098 */ 099 private static final Constructor<CompilerPhase> constructor = 100 getCompilerPhaseConstructor(InstrumentationSamplingFramework.class); 101 102 /** 103 * Get a constructor object for this compiler phase 104 * @return compiler phase constructor 105 */ 106 @Override 107 public Constructor<CompilerPhase> getClassConstructor() { 108 return constructor; 109 } 110 111 @Override 112 public boolean shouldPerform(OptOptions options) { 113 return options.ADAPTIVE_INSTRUMENTATION_SAMPLING; 114 } 115 116 @Override 117 public String getName() { 118 return "InstrumentationSamplingFramework"; 119 } 120 121 @Override 122 public void perform(IR ir) { 123 124 DEBUG = ir.options.DEBUG_INSTRU_SAMPLING; 125 DEBUG2 = ir.options.DEBUG_INSTRU_SAMPLING_DETAIL; 126 127 // Temp code for selective debugging. Dump the whole IR if either DEBUG2 is set, or if a specific method name is specified. 128 if (DEBUG2 || (DEBUG && ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString()))) { 129 dumpIR(ir, "Before instrumentation sampling transformation"); 130 dumpCFG(ir); 131 } 132 133 // Perform the actual phase here. 134 if (ir.options.ADAPTIVE_NO_DUPLICATION) { 135 performVariationNoDuplication(ir); 136 } else { 137 performVariationFullDuplication(ir); 138 } 139 140 // Dump method again after phase completes 141 if (DEBUG2 || (DEBUG && ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString()))) { 142 dumpIR(ir, "After instrumentation sampling transformation"); 143 dumpCFG(ir); 144 } 145 146 cleanUp(ir); 147 148 } 149 150// /** 151// * Initialization to perform before the transformation is applied 152// */ 153// private static void initialize(IR ir) { 154// 155// } 156 157 // 158 159 /** 160 * Cleans up the IR after the transformation is applied. 161 * 162 * @param ir the IR to clean up 163 */ 164 private void cleanUp(IR ir) { 165 166 // Clean up the ir with simple optimizations 167 Simple simple = new Simple(-1, false, false, false, false); 168 simple.perform(ir); 169 170 // Perform branch optimizations (level 0 is passed because if we 171 // always want to call it if we've used the sampling framework). 172 BranchOptimizations branchOpt = new BranchOptimizations(0, true, true); 173 branchOpt.perform(ir); 174 175 // Clear the static variables 176 cbsReg = null; 177 178 // 179 // RegisterInfo.computeRegisterList(ir); 180 DefUse.recomputeSpansBasicBlock(ir); 181 DefUse.recomputeSSA(ir); 182 } 183 184 /** 185 * Perform the full duplication algorithm 186 * 187 * @param ir the governing IR 188 */ 189 private void performVariationFullDuplication(IR ir) { 190 191 // Initialize 192 193 // Used to store mapping from original to duplicated blocks 194 HashMap<BasicBlock, BasicBlock> origToDupMap = new HashMap<BasicBlock, BasicBlock>(); 195 // Used to remember all exception handler blocks 196 HashSet<BasicBlock> exceptionHandlerBlocks = new HashSet<BasicBlock>(); 197 // The register containing the counter value to check 198 cbsReg = ir.regpool.makeTempInt(); 199 200 // 1) Make a copy of all basic blocks 201 duplicateCode(ir, origToDupMap, exceptionHandlerBlocks); 202 203 // 2) Insert counter-based sampling checks (record blocks with YP's) 204 insertCBSChecks(ir, origToDupMap, exceptionHandlerBlocks); 205 206 // 3) Fix control flow edges in duplicated code 207 adjustPointersInDuplicatedCode(ir, origToDupMap); 208 209 // 4) Remove instrumentation from checking code 210 removeInstrumentationFromOrig(ir, origToDupMap); 211 212 if (ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString())) { 213 ir.verify("End of Framework"); 214 } 215 216 } 217 218 /** 219 * Make a duplicate of all basic blocks down at the bottom of the 220 * code order. For now, this is done in a slightly ackward way. 221 * After duplication, the control flow within the duplicated code is 222 * not complete because each block immediately transfers you back to 223 * the original code. This gets fixed in 224 * adjustPointersInDuplicatedCode 225 * 226 * @param ir the governing IR 227 * @param origToDupMap a map (initially empty) that will store the mapping 228 * of original blocks to their duplicates 229 * @param exceptionHandlerBlocks a set that (initially empty) that is used 230 * to remember the exception handlers. Those need to be known because they 231 * need special checks (see {@link #insertCBSChecks(IR, HashMap, HashSet)}. 232 */ 233 private void duplicateCode(IR ir, HashMap<BasicBlock, BasicBlock> origToDupMap, 234 HashSet<BasicBlock> exceptionHandlerBlocks) { 235 236 if (DEBUG) VM.sysWrite("In duplicate code\n"); 237 238 // Iterate through all blocks and duplicate them. While 239 // iterating, loop until you see the block that was the *original* 240 // last block. (Need to do it this way because we're adding 241 // blocks to the end as we loop.) 242 BasicBlock origLastBlock = ir.cfg.lastInCodeOrder(); 243 boolean done = false; 244 BasicBlock curBlock = ir.cfg.firstInCodeOrder(); 245 while (!done && curBlock != null) { 246 if (curBlock == origLastBlock) { 247 // Stop after this iteration 248 done = true; 249 } 250 251 // Don't duplicate the exit node 252 if (curBlock == ir.cfg.exit()) { 253 // Next block 254 curBlock = curBlock.nextBasicBlockInCodeOrder(); 255 continue; 256 } 257 258 // Check if the block needs to be split for later insertion of 259 // a check. We split the entry basic block (first basic block 260 // in the method) to insert the method-entry check, and also 261 // exception handler blocks to simplify handling 262 if (curBlock == ir.cfg.entry() || curBlock.isExceptionHandlerBasicBlock()) { 263 264 Instruction splitInstr = null; 265 266 if (curBlock == ir.cfg.entry()) { 267 splitInstr = getFirstInstWithOperator(IR_PROLOGUE, curBlock); 268 } else { 269 splitInstr = getFirstInstWithOperator(LABEL, curBlock); 270 } 271 272 // Split entry node so the split instr isn't duplicated, but rest 273 // of block is. 274 BasicBlock blockTail = curBlock.splitNodeWithLinksAt(splitInstr, ir); 275 curBlock.recomputeNormalOut(ir); 276 277 if (curBlock.isExceptionHandlerBasicBlock()) { 278 // Remember that the tail of the block we just split is an 279 // exception handler and we want to add a check here 280 exceptionHandlerBlocks.add(blockTail); 281 } 282 283 // proceed with the duplication using the second half of the split block. 284 curBlock = blockTail; 285 286 // Is this necessary? TODO: see if it can be removed. 287 DefUse.recomputeSpansBasicBlock(ir); 288 } 289 290 // Copy the basic block 291 BasicBlock dup = myCopyWithoutLinks(curBlock, ir); 292 dup.setInfrequent(); // duplicated code is known to be infrequent. 293 294 if (DEBUG2) { 295 VM.sysWrite("Copying bb: " + curBlock + " to be " + dup + "\n"); 296 } 297 298 ir.cfg.addLastInCodeOrder(dup); 299 300 // Attach a goto to the duplicated code block, so that it 301 // doesn't fall through within the duplicated code, and jumps 302 // back to the checking code. This is a bit of a convoluted way 303 // of doing things and could be cleaned up. It was done 304 // originally done to make the remaining passes simpler. 305 BasicBlock fallthrough = curBlock.getFallThroughBlock(); 306 if (fallthrough != null) { 307 308 Instruction g = Goto.create(GOTO, fallthrough.makeJumpTarget()); 309 dup.appendInstruction(g); 310 } 311 312 dup.recomputeNormalOut(ir); 313 314 origToDupMap.put(curBlock, dup); // Remember that basic block "dup" 315 316 // Next block 317 curBlock = curBlock.nextBasicBlockInCodeOrder(); 318 } 319 } 320 321 /** 322 * In the checking code, insert CBS checks at each yieldpoint, to 323 * transfer code into the duplicated (instrumented) code. 324 * For now, checks are inserted at all yieldpoints (See comment at 325 * top of file). 326 * 327 * @param ir the governing IR 328 * @param origToDupMap a mapping of baisc block blocks to their duplicates 329 * @param exceptionHandlerBlocks a set that of exception handler basic blocks 330 */ 331 private void insertCBSChecks(IR ir, HashMap<BasicBlock, BasicBlock> origToDupMap, 332 HashSet<BasicBlock> exceptionHandlerBlocks) { 333 334 // Iterate through the basic blocks in the original code 335 for (Map.Entry<BasicBlock, BasicBlock> entry : origToDupMap.entrySet()) { 336 BasicBlock bb = entry.getKey(); 337 BasicBlock dup = entry.getValue(); 338 339 if (dup == null) { 340 // Getting here means that for some reason the block was not duplicated. 341 if (DEBUG) { 342 VM.sysWrite("Debug: block " + bb + " was not duplicated\n"); 343 } 344 continue; 345 } 346 347 // If you have a yieldpoint, or if you are the predecessor of a 348 // split exception handler when duplicating code) then insert a 349 // check 350 351 if (getFirstInstWithYieldPoint(bb) != null || // contains yieldpoint 352 exceptionHandlerBlocks.contains(bb)) { // is exception handler 353 354 BasicBlock checkBB = null; 355 BasicBlock prev = bb.prevBasicBlockInCodeOrder(); 356 // Entry has been split, so any node containing a yieldpoint 357 // should not be first 358 if (VM.VerifyAssertions) { 359 VM._assert(prev != null); 360 } 361 362 // Make a new BB that contains the check itself (it needs to 363 // be a basic block because the duplicated code branches back 364 // to it). 365 checkBB = new BasicBlock(-1, null, ir.cfg); 366 367 // Break the code to insert the check logic 368 ir.cfg.breakCodeOrder(prev, bb); 369 ir.cfg.linkInCodeOrder(prev, checkBB); 370 ir.cfg.linkInCodeOrder(checkBB, bb); 371 if (DEBUG) { 372 VM.sysWrite("Creating check " + checkBB + " to preceed " + bb + " \n"); 373 } 374 375 // Step 2, Make all of bb's predecessors point to the check instead 376 for (Enumeration<BasicBlock> preds = bb.getIn(); preds.hasMoreElements();) { 377 BasicBlock pred = preds.nextElement(); 378 pred.redirectOuts(bb, checkBB, ir); 379 } 380 381 // Insert the check logic 382 createCheck(checkBB, bb, dup, false, ir); 383 384 } 385 } 386 } 387 388 /** 389 * Append a check to a basic block, and make it jump to the right places. 390 * 391 * @param checkBB The block to append the CBS check to. 392 * @param noInstBB The basic block to jump to if the CBS check fails 393 * @param instBB The basicBlock to jump to if the CBS check succeeds 394 * @param fallthroughToInstBB Should checkBB fallthrough to instBB 395 * (otherwise it must fallthrough to noInstBB) 396 * @param ir the IR that contains the blocks 397 */ 398 private void createCheck(BasicBlock checkBB, BasicBlock noInstBB, BasicBlock instBB, 399 boolean fallthroughToInstBB, IR ir) { 400 401 appendLoad(checkBB, ir); 402 403 // Depending on where you fallthrough, set the condition correctly 404 ConditionOperand cond = null; 405 BranchOperand target = null; 406 BranchProfileOperand profileOperand = null; 407 if (fallthroughToInstBB) { 408 // The instrumented basic block is the fallthrough of checkBB, 409 // so make the branch jump to the non-instrumented block. 410 cond = ConditionOperand.GREATER(); 411 target = noInstBB.makeJumpTarget(); 412 profileOperand = new BranchProfileOperand(1.0f); // Taken frequently 413 } else { 414 // The non-instrumented basic block is the fallthrough of checkBB, 415 // so make the branch jump to the instrumented block. 416 cond = ConditionOperand.LESS_EQUAL(); 417 target = instBB.makeJumpTarget(); 418 profileOperand = new BranchProfileOperand(0.0f); // Taken infrequently 419 } 420 RegisterOperand guard = ir.regpool.makeTempValidation(); 421 checkBB.appendInstruction(IfCmp.create(INT_IFCMP, 422 guard, 423 cbsReg.copyRO(), 424 new IntConstantOperand(0), 425 cond, 426 target, 427 profileOperand)); 428 checkBB.recomputeNormalOut(ir); 429 430 // Insert the decrement and store in the block that is the 431 // successor of the check 432 prependStore(noInstBB, ir); 433 prependDecrement(noInstBB, ir); 434 435 // Insert a counter reset in the duplicated block. 436 prependCounterReset(instBB, ir); 437 438 } 439 440 /** 441 * Append a load of the global counter to the given basic block. 442 * 443 * WARNING: Tested for LIR only! 444 * 445 * @param bb The block to append the load to 446 * @param ir The IR */ 447 private void appendLoad(BasicBlock bb, IR ir) { 448 449 if (DEBUG) VM.sysWrite("Adding load to " + bb + "\n"); 450 Instruction load = null; 451 if (ir.options.ADAPTIVE_PROCESSOR_SPECIFIC_COUNTER) { 452 // Use one CBS counter per processor (better for multi threaded apps) 453 454 if (ir.IRStage == IR.HIR) { 455 // NOT IMPLEMENTED 456 } else { 457 // Phase is being used in LIR 458 // Insert the load instruction. 459 load = 460 Load.create(INT_LOAD, 461 cbsReg.copyRO(), 462 ir.regpool.makeTROp(), 463 IRTools.AC(AosEntrypoints.threadCBSField.getOffset()), 464 new LocationOperand(AosEntrypoints.threadCBSField)); 465 466 bb.appendInstruction(load); 467 } 468 } else { 469 // Use global counter 470 if (ir.IRStage == IR.HIR) { 471 Operand offsetOp = new AddressConstantOperand(AosEntrypoints.globalCBSField.getOffset()); 472 load = 473 GetStatic.create(GETSTATIC, 474 cbsReg.copyRO(), 475 offsetOp, 476 new LocationOperand(AosEntrypoints.globalCBSField)); 477 bb.appendInstruction(load); 478 } else { 479 // LIR 480 Instruction dummy = Load.create(INT_LOAD, null, null, null, null); 481 482 bb.appendInstruction(dummy); 483 load = 484 Load.create(INT_LOAD, 485 cbsReg.copyRO(), 486 ir.regpool.makeJTOCOp(), 487 IRTools.AC(AosEntrypoints.globalCBSField.getOffset()), 488 new LocationOperand(AosEntrypoints.globalCBSField)); 489 490 dummy.insertBefore(load); 491 dummy.remove(); 492 } 493 } 494 } 495 496 /** 497 * Append a store of the global counter to the given basic block. 498 * 499 * WARNING: Tested in LIR only! 500 * 501 * @param bb The block to append the load to 502 * @param ir The IR */ 503 private void prependStore(BasicBlock bb, IR ir) { 504 505 if (DEBUG) VM.sysWrite("Adding store to " + bb + "\n"); 506 Instruction store = null; 507 if (ir.options.ADAPTIVE_PROCESSOR_SPECIFIC_COUNTER) { 508 store = 509 Store.create(INT_STORE, 510 cbsReg.copyRO(), 511 ir.regpool.makeTROp(), 512 IRTools.AC(AosEntrypoints.threadCBSField.getOffset()), 513 new LocationOperand(AosEntrypoints.threadCBSField)); 514 515 bb.prependInstruction(store); 516 } else { 517 if (ir.IRStage == IR.HIR) { 518 store = 519 PutStatic.create(PUTSTATIC, 520 cbsReg.copyRO(), 521 new AddressConstantOperand(AosEntrypoints.globalCBSField.getOffset()), 522 new LocationOperand(AosEntrypoints.globalCBSField)); 523 524 bb.prependInstruction(store); 525 } else { 526 Instruction dummy = Load.create(INT_LOAD, null, null, null, null); 527 528 bb.prependInstruction(dummy); 529 store = 530 Store.create(INT_STORE, 531 cbsReg.copyRO(), 532 ir.regpool.makeJTOCOp(), 533 IRTools.AC(AosEntrypoints.globalCBSField.getOffset()), 534 new LocationOperand(AosEntrypoints.globalCBSField)); 535 536 dummy.insertBefore(store); 537 dummy.remove(); 538 } 539 } 540 } 541 542 /** 543 * Append a decrement of the global counter to the given basic block. 544 * 545 * Tested in LIR only! 546 * 547 * @param bb The block to append the load to 548 * @param ir The IR 549 */ 550 private void prependDecrement(BasicBlock bb, IR ir) { 551 if (DEBUG) VM.sysWrite("Adding Increment to " + bb + "\n"); 552 553 RegisterOperand use = cbsReg.copyRO(); 554 RegisterOperand def = use.copyU2D(); 555 Instruction inc = Binary.create(INT_ADD, def, use, IRTools.IC(-1)); 556 bb.prependInstruction(inc); 557 } 558 559 /** 560 * Prepend the code to reset the global counter to the given basic 561 * block. 562 * 563 * Warning: Tested in LIR only! 564 * 565 * @param bb The block to append the load to 566 * @param ir The IR */ 567 private void prependCounterReset(BasicBlock bb, IR ir) { 568 Instruction load = null; 569 Instruction store = null; 570 571 if (ir.IRStage == IR.HIR) { 572 // Not tested 573 Operand offsetOp = new AddressConstantOperand(AosEntrypoints.cbsResetValueField.getOffset()); 574 load = 575 GetStatic.create(GETSTATIC, 576 cbsReg.copyRO(), 577 offsetOp, 578 new LocationOperand(AosEntrypoints.cbsResetValueField)); 579 store = 580 PutStatic.create(PUTSTATIC, 581 cbsReg.copyRO(), 582 new AddressConstantOperand(AosEntrypoints.globalCBSField.getOffset()), 583 new LocationOperand(AosEntrypoints.globalCBSField)); 584 bb.prependInstruction(store); 585 bb.prependInstruction(load); 586 } else { 587 // LIR 588 Instruction dummy = Load.create(INT_LOAD, null, null, null, null); 589 bb.prependInstruction(dummy); 590 // Load the reset value 591 load = 592 Load.create(INT_LOAD, 593 cbsReg.copyRO(), 594 ir.regpool.makeJTOCOp(), 595 IRTools.AC(AosEntrypoints.cbsResetValueField.getOffset()), 596 new LocationOperand(AosEntrypoints.cbsResetValueField)); 597 598 dummy.insertBefore(load); 599 600 // Store it in the counter register 601 if (ir.options.ADAPTIVE_PROCESSOR_SPECIFIC_COUNTER) { 602 store = 603 Store.create(INT_STORE, 604 cbsReg.copyRO(), 605 ir.regpool.makeTROp(), 606 IRTools.AC(AosEntrypoints.threadCBSField.getOffset()), 607 new LocationOperand(AosEntrypoints.threadCBSField)); 608 } else { 609 // Use global counter 610 store = 611 Store.create(INT_STORE, 612 cbsReg.copyRO(), 613 ir.regpool.makeJTOCOp(), 614 IRTools.AC(AosEntrypoints.globalCBSField.getOffset()), 615 new LocationOperand(AosEntrypoints.globalCBSField)); 616 } 617 dummy.insertBefore(store); 618 dummy.remove(); 619 } 620 621 } 622 623 /** 624 * Go through all instructions and find the first with the given 625 * operator. 626 * 627 * @param operator The operator to look for 628 * @param bb The basic block in which to look 629 * @return The first instruction in bb that has operator operator. */ 630 private static Instruction getFirstInstWithOperator(Operator operator, BasicBlock bb) { 631 for (Enumeration<Instruction> ie = bb.forwardInstrEnumerator(); ie.hasMoreElements();) { 632 Instruction i = ie.nextElement(); 633 634 if (i.operator() == operator) { 635 return i; 636 } 637 } 638 return null; 639 } 640 641 /** 642 * Go through all instructions and find one that is a yield point 643 * 644 * @param bb The basic block in which to look 645 * @return The first instruction in bb that has a yield point 646 */ 647 public static Instruction getFirstInstWithYieldPoint(BasicBlock bb) { 648 for (Enumeration<Instruction> ie = bb.forwardInstrEnumerator(); ie.hasMoreElements();) { 649 Instruction i = ie.nextElement(); 650 651 if (isYieldpoint(i)) { 652 return i; 653 } 654 } 655 return null; 656 } 657 658 /** 659 * Is the given instruction a yieldpoint? 660 * <p> 661 * For now we ignore epilogue yieldpoints since we are only concerned with 662 * method entries and backedges. 663 * 664 * @param i the instruction to examine 665 * @return whether the instruction is a non-epilogue yieldpoint 666 */ 667 public static boolean isYieldpoint(Instruction i) { 668 return i.operator() == YIELDPOINT_PROLOGUE || 669 // Skip epilogue yieldpoints. No checks needed for them 670 // i.operator() == YIELDPOINT_EPILOGUE || 671 i.operator() == YIELDPOINT_BACKEDGE; 672 673 } 674 675 /** 676 * Go through all blocks in duplicated code and adjust the edges as 677 * follows: 678 * <ol> 679 * <li>All edges (in duplicated code) that go into a block with a 680 * yieldpoint must jump to back to the original code. This is 681 * actually already satisfied because we appended goto's to all 682 * duplicated bb's (the goto's go back to orig code) 683 * <li>All edges that do NOT go into a yieldpoint block must stay 684 * (either fallthrough or jump to a block in) in the duplicated 685 * code. 686 * </ol> 687 * @param origToDupMap mapping of basic blocks to their duplicates 688 * @param ir the governing IR 689 */ 690 private static void adjustPointersInDuplicatedCode(IR ir, HashMap<BasicBlock, BasicBlock> origToDupMap) { 691 692 // Iterate over the original version of all duplicate blocks 693 for (BasicBlock dupBlock : origToDupMap.values()) { 694 695 // Look at the successors of duplicated Block. 696 for (Enumeration<BasicBlock> out = dupBlock.getNormalOut(); out.hasMoreElements();) { 697 BasicBlock origSucc = out.nextElement(); 698 699 BasicBlock dupSucc = origToDupMap.get(origSucc); 700 701 // If the successor is not in the duplicated code, then 702 // redirect it to stay in the duplicated code. (dupSucc != 703 // null implies that origSucc has a corresponding duplicated 704 // block, and thus origSucc is in the checking code. 705 706 if (dupSucc != null) { 707 708 dupBlock.redirectOuts(origSucc, dupSucc, ir); 709 if (DEBUG) { 710 VM.sysWrite("Source: " + dupBlock + "\n"); 711 VM.sysWrite("============= FROM " + origSucc + " =============\n"); 712 //origSucc.printExtended(); 713 VM.sysWrite("============= TO " + dupSucc + "=============\n"); 714 //dupSucc.printExtended(); 715 } 716 } else { 717 if (DEBUG) { 718 VM.sysWrite("Not adjusting pointer from " + dupBlock + " to " + origSucc + " because dupSucc is null\n"); 719 } 720 } 721 } 722 723 } 724 } 725 726 /** 727 * Remove instrumentation from the original version of all duplicated 728 * basic blocks.<p> 729 * 730 * The yieldpoints can also be removed from either the original or 731 * the duplicated code. If you remove them from the original, it 732 * will make it run faster (since it is executed more than the 733 * duplicated) however that means that you can't turn off the 734 * instrumentation or you could infinitely loop without executing 735 * yieldpoints! 736 * 737 * @param ir the governing IR 738 * @param origToDupMap mapping of basic blocks to their duplicates 739 */ 740 private static void removeInstrumentationFromOrig(IR ir, HashMap<BasicBlock, BasicBlock> origToDupMap) { 741 // Iterate over the original version of all duplicate blocks 742 743 for (BasicBlock origBlock : origToDupMap.keySet()) { 744 745 // Remove all instrumentation instructions. They already have 746 // been transfered to the duplicated code. 747 for (Enumeration<Instruction> ie = origBlock.forwardInstrEnumerator(); ie.hasMoreElements();) { 748 Instruction i = ie.nextElement(); 749 if (isInstrumentationInstruction(i) || (isYieldpoint(i) && ir.options.ADAPTIVE_REMOVE_YP_FROM_CHECKING)) { 750 751 if (DEBUG) VM.sysWrite("Removing " + i + "\n"); 752 i.remove(); 753 } 754 } 755 } 756 } 757 758 /** 759 * The same as BasicBlock.copyWithoutLinks except that it 760 * renames all temp variables that are never used outside the basic 761 * block. This 1) helps eliminate the artificially large live 762 * ranges that might have been created (assuming the reg allocator 763 * isn't too smart) and 2) it prevents BURS from crashing. 764 * <p> 765 * PRECONDITION: the spansBasicBlock bit must be correct by calling 766 * DefUse.recomputeSpansBasicBlock(IR); 767 * 768 * @param bb the basic block to process 769 * @param ir the IR that contains the block 770 * @return the copied block 771 */ 772 private static BasicBlock myCopyWithoutLinks(BasicBlock bb, IR ir) { 773 774 BasicBlock newBlock = bb.copyWithoutLinks(ir); 775 776 // Without this, there were sanity errors at one point. 777 updateTemps(newBlock, ir); 778 779 return newBlock; 780 } 781 782 /** 783 * Given an basic block, rename all of the temporary registers that are 784 * local to the block. 785 * 786 * @param bb the block 787 * @param ir the IR that contains the block 788 */ 789 private static void updateTemps(BasicBlock bb, IR ir) { 790 int capacity = ir.regpool.getNumberOfSymbolicRegisters() * 2; 791 HashMap<Register, Register> duplicates = new HashMap<Register, Register>(capacity); 792 // For each instruction in the block 793 for (Enumeration<Instruction> ie = bb.forwardInstrEnumerator(); ie.hasMoreElements();) { 794 Instruction inst = ie.nextElement(); 795 796 // Look at each register operand 797 int numOperands = inst.getNumberOfOperands(); 798 for (int i = 0; i < numOperands; i++) { 799 Operand op = inst.getOperand(i); 800 if (op instanceof RegisterOperand) { 801 RegisterOperand ro = (RegisterOperand) op; 802 if (ro.getRegister().isTemp() && !ro.getRegister().spansBasicBlock()) { 803 // This register does not span multiple basic blocks, so 804 // replace it with a temp. 805 RegisterOperand newReg = getOrCreateDupReg(ro, ir, duplicates); 806 if (DEBUG2) { 807 VM.sysWrite("Was " + ro + " and now it's " + newReg + "\n"); 808 } 809 inst.putOperand(i, newReg); 810 } 811 } 812 } 813 } 814 } 815 816 /** 817 * The given register a) does not span multiple basic block, and b) 818 * is used in a basic block that is being duplicated. It is 819 * therefore safe to replace all occurrences of the register with a 820 * new register. This method returns the duplicated 821 * register that is associated with the given register. If a 822 * duplicated register does not exist, it is created and recorded. 823 * 824 * @param ro the register operand to duplicate 825 * @param ir the IR that contains the register operand 826 * @param dupMappings the mappings of registers to duplicates 827 * @return a duplicated register operand 828 */ 829 private static RegisterOperand getOrCreateDupReg(RegisterOperand ro, IR ir, Map<Register, Register> dupMappings) { 830 831 // Check if the register associated with this regOperand already 832 // has a paralles operand 833 Register roReg = ro.getRegister(); 834 Register rosDupReg = dupMappings.get(roReg); 835 if (rosDupReg == null) { 836 // If no dup register exists, make a new one and remember it. 837 RegisterOperand duplicatedRegOp = ir.regpool.makeTemp(ro.getType()); 838 Register regFromDuplicatedRegOp = duplicatedRegOp.getRegister(); 839 dupMappings.put(roReg, regFromDuplicatedRegOp); 840 rosDupReg = regFromDuplicatedRegOp; 841 } 842 return new RegisterOperand(rosDupReg, ro.getType()); 843 } 844 845 /** 846 * Perform the NoDuplication version of the framework (see 847 * Arnold-Ryder PLDI 2001). Instrumentation operations are wrapped 848 * in a conditional, but no code duplication is performed. 849 * 850 * @param ir the IR to process 851 */ 852 private void performVariationNoDuplication(IR ir) { 853 // The register containing the counter value to check 854 cbsReg = ir.regpool.makeTempInt(); 855 856 ArrayList<Instruction> instrumentationOperations = new ArrayList<Instruction>(); 857 for (Enumeration<BasicBlock> allBB = ir.getBasicBlocks(); allBB.hasMoreElements();) { 858 BasicBlock bb = allBB.nextElement(); 859 860 for (Enumeration<Instruction> ie = bb.forwardInstrEnumerator(); ie.hasMoreElements();) { 861 Instruction i = ie.nextElement(); 862 863 // If it's an instrumentation operation, remember the instruction 864 if (isInstrumentationInstruction(i)) { 865 instrumentationOperations.add(i); 866 } 867 } 868 } 869 870 // for each instrumentation operation. 871 for (Instruction i : instrumentationOperations) { 872 BasicBlock bb = i.getBasicBlock(); 873 conditionalizeInstrumentationOperation(ir, i, bb); 874 } 875 } 876 877 /** 878 * Take an instrumentation operation (an instruction) and guard it 879 * with a counter-based check. 880 * <ol> 881 * <li>split the basic block before and after the i 882 * to get A -> B -> C 883 * <li>Add check to A, making it go to B if it succeeds, otherwise C 884 * </ol> 885 * 886 * @param ir the IR that contains the instruction 887 * @param i the instruction 888 * @param bb the basic block that contains the instruction 889 */ 890 private void conditionalizeInstrumentationOperation(IR ir, Instruction i, BasicBlock bb) { 891 892 // Create bb after instrumentation ('C', in comment above) 893 BasicBlock C = bb.splitNodeWithLinksAt(i, ir); 894 bb.recomputeNormalOut(ir); 895 896 // Create bb before instrumentation ('A', in comment above) 897 Instruction prev = i.prevInstructionInCodeOrder(); 898 899 BasicBlock B = null; 900 try { 901 B = bb.splitNodeWithLinksAt(prev, ir); 902 } catch (RuntimeException e) { 903 VM.sysWrite("Bombed when I: " + i + " prev: " + prev + "\n"); 904 bb.printExtended(); 905 throw e; 906 } 907 908 bb.recomputeNormalOut(ir); 909 910 BasicBlock A = bb; 911 912 // Now, add check to A, that jumps to C 913 createCheck(A, C, B, true, ir); 914 } 915 916 /** 917 * How to determine whether a given instruction is an 918 * "instrumentation instruction". In other words, it should be 919 * executed only when a sample is being taken. 920 * 921 * @param i an instruction 922 * @return {@code true} if and only if this is an instrumentation instruction 923 */ 924 private static boolean isInstrumentationInstruction(Instruction i) { 925 926 // if (i.bcIndex == INSTRUMENTATION_BCI && 927 // (Call.conforms(i) || 928 // if (InstrumentedCounter.conforms(i))) 929 930 return InstrumentedCounter.conforms(i); 931 } 932 933 public static void dumpCFG(IR ir) { 934 for (Enumeration<BasicBlock> allBB = ir.getBasicBlocks(); allBB.hasMoreElements();) { 935 BasicBlock curBB = allBB.nextElement(); 936 curBB.printExtended(); 937 } 938 } 939}