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.bc2ir; 014 015 import java.util.Enumeration; 016 import java.util.HashMap; 017 import java.util.HashSet; 018 import java.util.Iterator; 019 import java.util.Map; 020 021 import org.jikesrvm.ArchitectureSpecificOpt.RegisterPool; 022 import org.jikesrvm.VM; 023 import org.jikesrvm.classloader.RVMMethod; 024 import org.jikesrvm.classloader.NormalMethod; 025 import org.jikesrvm.classloader.RVMType; 026 import org.jikesrvm.classloader.TypeReference; 027 import org.jikesrvm.compilers.baseline.BranchProfile; 028 import org.jikesrvm.compilers.baseline.BranchProfiles; 029 import org.jikesrvm.compilers.baseline.ConditionalBranchProfile; 030 import org.jikesrvm.compilers.baseline.EdgeCounts; 031 import org.jikesrvm.compilers.baseline.SwitchBranchProfile; 032 import org.jikesrvm.compilers.common.CompiledMethod; 033 import org.jikesrvm.compilers.opt.ClassLoaderProxy; 034 import org.jikesrvm.compilers.opt.OptOptions; 035 import org.jikesrvm.compilers.opt.OptimizingCompilerException; 036 import org.jikesrvm.compilers.opt.inlining.InlineOracle; 037 import org.jikesrvm.compilers.opt.inlining.InlineSequence; 038 import org.jikesrvm.compilers.opt.ir.BasicBlock; 039 import org.jikesrvm.compilers.opt.ir.Call; 040 import org.jikesrvm.compilers.opt.ir.ControlFlowGraph; 041 import org.jikesrvm.compilers.opt.ir.Empty; 042 import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock; 043 import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlockBag; 044 import org.jikesrvm.compilers.opt.ir.IRTools; 045 import org.jikesrvm.compilers.opt.ir.Instruction; 046 import org.jikesrvm.compilers.opt.ir.MonitorOp; 047 import org.jikesrvm.compilers.opt.ir.Move; 048 import org.jikesrvm.compilers.opt.ir.Nullary; 049 import org.jikesrvm.compilers.opt.ir.Operators; 050 import org.jikesrvm.compilers.opt.ir.Prologue; 051 import org.jikesrvm.compilers.opt.ir.Register; 052 import org.jikesrvm.compilers.opt.ir.Return; 053 import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand; 054 import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand; 055 import org.jikesrvm.compilers.opt.ir.operand.ClassConstantOperand; 056 import org.jikesrvm.compilers.opt.ir.operand.MethodOperand; 057 import org.jikesrvm.compilers.opt.ir.operand.Operand; 058 import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; 059 import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand; 060 import org.jikesrvm.compilers.opt.ir.operand.TypeOperand; 061 import org.jikesrvm.runtime.Entrypoints; 062 import org.jikesrvm.runtime.Statics; 063 import org.vmmagic.unboxed.Offset; 064 065 /** 066 * Defines the context in which BC2IR will abstractly interpret 067 * a method's bytecodes and populate targetIR with instructions. 068 * 069 **/ 070 public final class GenerationContext implements org.jikesrvm.compilers.opt.driver.OptConstants, Operators { 071 072 ////////// 073 // These fields are used to communicate information from its 074 // caller to BC2IR 075 ////////// 076 /** 077 * The original method (root of the calling context tree) 078 */ 079 NormalMethod original_method; 080 081 /** 082 * The compiled method assigned for this compilation of original_method 083 */ 084 CompiledMethod original_cm; 085 086 /** 087 * The method to be generated 088 */ 089 public NormalMethod method; 090 091 /** 092 * The BranchProfile data for method, if available 093 */ 094 BranchProfiles branchProfiles; 095 096 /** 097 * The options to control the generation 098 */ 099 public OptOptions options; 100 101 /** 102 * The CFG object into which instructions should be generated. 103 */ 104 public ControlFlowGraph cfg; 105 106 /** 107 * The register pool to be used during generation 108 */ 109 public RegisterPool temps; 110 111 /** 112 * The parameters which BC2IR should use to seed the local state 113 * for the entry basic block. 114 */ 115 Operand[] arguments; 116 117 /** 118 * The basic block into which BC2IR's caller will generate a "prologue." 119 * BC2IR will add a CFG edge from prologue to the block containing the 120 * instructions generated for bytecode 0, but it is its caller's 121 * responsibility to populate the prologue with instructions. 122 * All blocks generated by BC2IR will be injected by BC2IR.doFinalPass 123 * immediately 124 * after prologue in the code ordering 125 * (ie prologue can assume it will fallthrough 126 * to the first basic block in the ir generated for method. 127 */ 128 public BasicBlock prologue; 129 130 /** 131 * The basic block into which BC2IR's caller will generate an epilogue. 132 * BC2IR will add CFG edges to this node, but it is its caller's 133 * responsibility to populate it with instructions. 134 * NOTE: After IR is generated one of two conditions will hold: 135 * <ul> 136 * <li> epilogue == cfg.lastInCodeOrder(): (if it is to be inlined, 137 * then the generated cfg 138 * is expecting to "fallthrough" 139 * to the next bblock) 140 * <li> epilogue == null: implies that there is no "normal" exit from 141 * the callee (all exits via throw) 142 * </ul> 143 * NOTE: BC2IR assumes that epilogue is a single basic block 144 * (i.e. it has no out edges) 145 */ 146 public BasicBlock epilogue; 147 148 /** 149 * The exit node of the outermost CFG 150 * (used by BC2IR for not-definitely caught athrows and by OSR_Yieldpoints) 151 */ 152 public BasicBlock exit; 153 154 /** 155 * A catch, unlock, and rethrow exception handler used for 156 * synchronized methods. 157 */ 158 BasicBlock unlockAndRethrow; 159 160 /** 161 * The Register to which BC2IR should assign the return value(s) 162 * of the method. It will be null when the method has a void return. 163 */ 164 Register resultReg; 165 166 /** 167 * The enclosing exception handlers (null if there are none). 168 */ 169 ExceptionHandlerBasicBlockBag enclosingHandlers; 170 171 /** 172 * Inlining context of the method to be generated 173 */ 174 public InlineSequence inlineSequence; 175 176 /** 177 * The InlineOracle to be consulted for all inlining decisions during 178 * the generation of this IR. 179 */ 180 InlineOracle inlinePlan; 181 182 ////////// 183 // These fields are used to communicate information from BC2IR to its caller 184 ////////// 185 /** 186 * Did BC2IR generate a reachable exception handler while generating 187 * the IR for this method 188 */ 189 public boolean generatedExceptionHandlers; 190 191 /** 192 * Did BC2IR encounter a magic that requires us to allocate a stack frame? 193 */ 194 public boolean allocFrame; 195 196 /** 197 * Used to communicate the meet of the return values back to the caller 198 * Mainly useful when BC2IR is doing inlining....allows the caller 199 * BC2IR object 200 * to exploit knowledge the callee BC2IR object had about the result. 201 */ 202 public Operand result; 203 /** 204 * Do we do check stores? 205 */ 206 boolean doesCheckStore; 207 208 ////////// 209 // Main public methods 210 ///////// 211 212 /** 213 * Use this constructor to create an outermost (non-inlined) 214 * GenerationContext. 215 * 216 * @param meth The NormalMethod whose IR will be generated 217 * @param params The known types of the parameters to the method. For method specialization. 218 * @param cm The compiled method id to be used for this compilation 219 * @param opts The Options to be used for the generation 220 * @param ip The InlineOracle to be used for the generation 221 */ 222 GenerationContext(NormalMethod meth, TypeReference[] params, CompiledMethod cm, OptOptions opts, InlineOracle ip) { 223 original_method = meth; 224 original_cm = cm; 225 method = meth; 226 if (opts.frequencyCounters() || opts.inverseFrequencyCounters()) { 227 branchProfiles = EdgeCounts.getBranchProfiles(meth); 228 } 229 options = opts; 230 inlinePlan = ip; 231 inlineSequence = new InlineSequence(meth); 232 doesCheckStore = !meth.hasNoCheckStoreAnnotation(); 233 234 // Create the CFG. Initially contains prologue, epilogue, and exit. 235 cfg = new ControlFlowGraph(0); 236 prologue = new BasicBlock(PROLOGUE_BLOCK_BCI, inlineSequence, cfg); 237 epilogue = new BasicBlock(EPILOGUE_BLOCK_BCI, inlineSequence, cfg); 238 cfg.addLastInCodeOrder(prologue); 239 cfg.addLastInCodeOrder(epilogue); 240 exit = cfg.exit(); 241 epilogue.insertOut(exit); 242 243 // Create register pool, initialize arguments, resultReg. 244 temps = new RegisterPool(meth); 245 _ncGuards = new HashMap<Register, RegisterOperand>(); 246 initLocalPool(); 247 TypeReference[] definedParams = meth.getParameterTypes(); 248 if (params == null) params = definedParams; 249 int numParams = params.length; 250 int argIdx = 0; 251 int localNum = 0; 252 arguments = new Operand[method.isStatic() ? numParams : numParams + 1]; 253 // Insert IR_PROLOGUE instruction. Loop below will fill in its operands 254 Instruction prologueInstr = Prologue.create(IR_PROLOGUE, arguments.length); 255 appendInstruction(prologue, prologueInstr, PROLOGUE_BCI); 256 257 if (!method.isStatic()) { 258 TypeReference thisType = meth.getDeclaringClass().getTypeRef(); 259 RegisterOperand thisOp = makeLocal(localNum, thisType); 260 // The this param of a virtual method is by definition non null 261 RegisterOperand guard = makeNullCheckGuard(thisOp.getRegister()); 262 BC2IR.setGuard(thisOp, guard); 263 appendInstruction(prologue, Move.create(GUARD_MOVE, guard.copyRO(), new TrueGuardOperand()), PROLOGUE_BCI); 264 thisOp.setDeclaredType(); 265 thisOp.setExtant(); 266 if (method.getDeclaringClass().isFinal()) { 267 thisOp.setPreciseType(); 268 } 269 arguments[0] = thisOp; 270 Prologue.setFormal(prologueInstr, 0, thisOp.copyU2D()); 271 argIdx++; 272 localNum++; 273 } 274 for (int paramIdx = 0; paramIdx < numParams; paramIdx++) { 275 TypeReference argType = params[paramIdx]; 276 RegisterOperand argOp = makeLocal(localNum, argType); 277 argOp.setDeclaredType(); 278 if (argType.isClassType()) { 279 argOp.setExtant(); 280 } 281 arguments[argIdx] = argOp; 282 Prologue.setFormal(prologueInstr, argIdx, argOp.copyU2D()); 283 argIdx++; 284 localNum++; 285 if (argType.isLongType() || argType.isDoubleType()) { 286 localNum++; // longs & doubles take two words of local space 287 } 288 } 289 TypeReference returnType = meth.getReturnType(); 290 if (returnType != TypeReference.Void) { 291 resultReg = temps.makeTemp(returnType).getRegister(); 292 } 293 294 enclosingHandlers = null; 295 296 completePrologue(true); 297 completeEpilogue(true); 298 completeExceptionHandlers(true); 299 } 300 301 /** 302 * Create a child generation context from parent & callerBB to 303 * generate IR for callsite. 304 * Make this 'static' to avoid confusing parent/child fields. 305 * 306 * @param parent the parent gc 307 * @param ebag the enclosing exception handlers (null if none) 308 * @param callee the callee method to be inlined 309 * (may _not_ be equal to Call.getMethod(callSite).method) 310 * @param callSite the Call instruction to be inlined. 311 * @return the child context 312 */ 313 public static GenerationContext createChildContext(GenerationContext parent, ExceptionHandlerBasicBlockBag ebag, 314 NormalMethod callee, Instruction callSite) { 315 GenerationContext child = new GenerationContext(); 316 child.method = callee; 317 if (parent.options.frequencyCounters() || parent.options.inverseFrequencyCounters()) { 318 child.branchProfiles = EdgeCounts.getBranchProfiles(callee); 319 } 320 child.original_method = parent.original_method; 321 child.original_cm = parent.original_cm; 322 323 // Some state gets directly copied to the child 324 child.options = parent.options; 325 child.temps = parent.temps; 326 child._ncGuards = parent._ncGuards; 327 child.exit = parent.exit; 328 child.inlinePlan = parent.inlinePlan; 329 330 // Now inherit state based on callSite 331 child.inlineSequence = new InlineSequence(child.method, callSite.position, callSite); 332 child.enclosingHandlers = ebag; 333 child.arguments = new Operand[Call.getNumberOfParams(callSite)]; 334 for (int i = 0; i < child.arguments.length; i++) { 335 child.arguments[i] = Call.getParam(callSite, i).copy(); // copy instead 336 // of clearing in case inlining aborts. 337 } 338 if (Call.hasResult(callSite)) { 339 child.resultReg = Call.getResult(callSite).copyD2D().getRegister(); 340 child.resultReg.setSpansBasicBlock(); // it will... 341 } 342 343 // Initialize the child CFG, prologue, and epilogue blocks 344 child.cfg = new ControlFlowGraph(parent.cfg.numberOfNodes()); 345 child.prologue = new BasicBlock(PROLOGUE_BCI, child.inlineSequence, child.cfg); 346 child.prologue.exceptionHandlers = ebag; 347 child.epilogue = new BasicBlock(EPILOGUE_BCI, child.inlineSequence, child.cfg); 348 child.epilogue.exceptionHandlers = ebag; 349 child.cfg.addLastInCodeOrder(child.prologue); 350 child.cfg.addLastInCodeOrder(child.epilogue); 351 352 // Set up the local pool 353 child.initLocalPool(); 354 355 // Insert moves from child.arguments to child's locals in prologue 356 TypeReference[] params = child.method.getParameterTypes(); 357 int numParams = params.length; 358 int argIdx = 0; 359 int localNum = 0; 360 if (!child.method.isStatic()) { 361 Operand receiver = child.arguments[argIdx]; 362 argIdx++; 363 RegisterOperand local = null; 364 if (receiver.isRegister()) { 365 RegisterOperand objPtr = receiver.asRegister(); 366 if (ClassLoaderProxy.includesType(child.method.getDeclaringClass().getTypeRef(), objPtr.getType()) != YES) { 367 // narrow type of actual to match formal static type implied by method 368 objPtr.clearPreciseType(); // Can be precise but not assignable if enough classes aren't loaded 369 objPtr.setDeclaredType(); 370 objPtr.setType(child.method.getDeclaringClass().getTypeRef()); 371 } 372 local = child.makeLocal(localNum, objPtr); 373 localNum++; 374 child.arguments[0] = local; // Avoid confusion in BC2IR of callee 375 // when objPtr is a local in the caller. 376 } else if (receiver.isConstant()) { 377 local = child.makeLocal(localNum, receiver.getType()); 378 localNum++; 379 local.setPreciseType(); 380 // Constants trivially non-null 381 RegisterOperand guard = child.makeNullCheckGuard(local.getRegister()); 382 BC2IR.setGuard(local, guard); 383 child.prologue.appendInstruction(Move.create(GUARD_MOVE, guard.copyRO(), new TrueGuardOperand())); 384 } else { 385 OptimizingCompilerException.UNREACHABLE("Unexpected receiver operand"); 386 } 387 Instruction s = Move.create(REF_MOVE, local, receiver); 388 s.bcIndex = PROLOGUE_BCI; 389 s.position = callSite.position; 390 child.prologue.appendInstruction(s); 391 } 392 for (int paramIdx = 0; paramIdx < numParams; paramIdx++, argIdx++) { 393 TypeReference argType = params[paramIdx]; 394 RegisterOperand formal; 395 Operand actual = child.arguments[argIdx]; 396 if (actual.isRegister()) { 397 RegisterOperand rActual = actual.asRegister(); 398 if (ClassLoaderProxy.includesType(argType, rActual.getType()) != YES) { 399 // narrow type of actual to match formal static type implied by method 400 rActual.clearPreciseType(); // Can be precise but not 401 // assignable if enough classes aren't loaded 402 rActual.setDeclaredType(); 403 rActual.setType(argType); 404 } 405 formal = child.makeLocal(localNum, rActual); 406 localNum++; 407 child.arguments[argIdx] = formal; // Avoid confusion in BC2IR of 408 // callee when arg is a local in the caller. 409 } else { 410 formal = child.makeLocal(localNum, argType); 411 localNum++; 412 } 413 Instruction s = Move.create(IRTools.getMoveOp(argType), formal, actual); 414 s.bcIndex = PROLOGUE_BCI; 415 s.position = callSite.position; 416 child.prologue.appendInstruction(s); 417 if (argType.isLongType() || argType.isDoubleType()) { 418 localNum++; // longs and doubles take two local words 419 } 420 } 421 422 child.completePrologue(false); 423 child.completeEpilogue(false); 424 child.completeExceptionHandlers(false); 425 426 return child; 427 } 428 429 /** 430 * Only for internal use by Inliner (when inlining multiple targets) 431 * This is probably not the prettiest way to handle this, but it requires 432 * no changes to BC2IR's & Inliner's high level control logic. 433 * 434 * @param parent the parent GC 435 * @param ebag the enclosing exception handlers (null if none) 436 * @return the synthetic context 437 */ 438 public static GenerationContext createSynthetic(GenerationContext parent, ExceptionHandlerBasicBlockBag ebag) { 439 // Create the CFG. Initially contains prologue and epilogue 440 GenerationContext child = new GenerationContext(); 441 442 child.cfg = new ControlFlowGraph(-100000); 443 444 // It may be wrong to use the parent inline sequence as the 445 // position here, but it seems to work out. This is a synthetic 446 // context that is just used as a container for multiple inlined 447 // targets, so in the cases that I've observed where the prologue 448 // and epilogue don't disappear, it was correct to have the 449 // parent's position. -- Matt 450 child.prologue = new BasicBlock(PROLOGUE_BCI, parent.inlineSequence, parent.cfg); 451 child.prologue.exceptionHandlers = ebag; 452 child.epilogue = new BasicBlock(EPILOGUE_BCI, parent.inlineSequence, parent.cfg); 453 child.epilogue.exceptionHandlers = ebag; 454 child.cfg.addLastInCodeOrder(child.prologue); 455 child.cfg.addLastInCodeOrder(child.epilogue); 456 457 // All other fields are intentionally left null. 458 // We are only really using this context to transfer a synthetic CFG 459 // from the low-level Inliner.execute back to its caller. 460 // TODO: Rewrite GenerationContext to be a subclass of a root 461 // class that is just a CFG wrapper. Then, have an instance of this 462 // new parent 463 // class be the return value for the main entrypoints in Inliner 464 // and create an instance of the root class instead of GC when 465 // inlining multiple targets. 466 467 return child; 468 } 469 470 /** 471 * Use this to transfer state back from a child context back to its parent. 472 * 473 * @param parent the parent context that will receive the state 474 * @param child the child context from which the state will be taken 475 */ 476 public static void transferState(GenerationContext parent, GenerationContext child) { 477 parent.cfg.setNumberOfNodes(child.cfg.numberOfNodes()); 478 if (child.generatedExceptionHandlers) { 479 parent.generatedExceptionHandlers = true; 480 } 481 if (child.allocFrame) { 482 parent.allocFrame = true; 483 } 484 } 485 486 /////////// 487 // Local variables 488 /////////// 489 490 // The registers to use for various types of locals. 491 // Note that "int" really means 32-bit gpr. 492 private Register[] intLocals; 493 private Register[] addressLocals; 494 private Register[] floatLocals; 495 private Register[] longLocals; 496 private Register[] doubleLocals; 497 498 private void initLocalPool() { 499 int numLocals = method.getLocalWords(); 500 intLocals = new Register[numLocals]; 501 addressLocals = new Register[numLocals]; 502 floatLocals = new Register[numLocals]; 503 longLocals = new Register[numLocals]; 504 doubleLocals = new Register[numLocals]; 505 } 506 507 private Register[] getPool(TypeReference type) { 508 if (type == TypeReference.Float) { 509 return floatLocals; 510 } else if (type == TypeReference.Long) { 511 return longLocals; 512 } else if (type == TypeReference.Double) { 513 return doubleLocals; 514 } else if (type.isReferenceType() || type.isWordLikeType()) { 515 return addressLocals; 516 } else { 517 return intLocals; 518 } 519 } 520 521 /** 522 * Return the Register used to for local i of TypeReference type 523 */ 524 public Register localReg(int i, TypeReference type) { 525 Register[] pool = getPool(type); 526 if (pool[i] == null) { 527 pool[i] = temps.getReg(type); 528 pool[i].setLocal(); 529 } 530 return pool[i]; 531 } 532 533 /** 534 * Should null checks be generated? 535 */ 536 boolean noNullChecks() { 537 return method.hasNoNullCheckAnnotation(); 538 } 539 540 /** 541 * Should bounds checks be generated? 542 */ 543 boolean noBoundsChecks() { 544 return method.hasNoBoundsCheckAnnotation(); 545 } 546 547 /** 548 * Make a register operand that refers to the given local variable number 549 * and has the given type. 550 * 551 * @param i local variable number 552 * @param type desired data type 553 */ 554 public RegisterOperand makeLocal(int i, TypeReference type) { 555 return new RegisterOperand(localReg(i, type), type); 556 } 557 558 /** 559 * Make a register operand that refers to the given local variable number, 560 * and inherits its properties (type, flags) from props 561 * 562 * @param i local variable number 563 * @param props RegisterOperand to inherit flags from 564 */ 565 RegisterOperand makeLocal(int i, RegisterOperand props) { 566 RegisterOperand local = makeLocal(i, props.getType()); 567 local.setInheritableFlags(props); 568 BC2IR.setGuard(local, BC2IR.getGuard(props)); 569 return local; 570 } 571 572 /** 573 * Get the local number for a given register 574 */ 575 public int getLocalNumberFor(Register reg, TypeReference type) { 576 Register[] pool = getPool(type); 577 for (int i = 0; i < pool.length; i++) { 578 if (pool[i] == reg) return i; 579 } 580 return -1; 581 } 582 583 /** 584 * Is the operand a particular bytecode local? 585 */ 586 public boolean isLocal(Operand op, int i, TypeReference type) { 587 if (op instanceof RegisterOperand) { 588 if (getPool(type)[i] == ((RegisterOperand) op).getRegister()) return true; 589 } 590 return false; 591 } 592 593 /////////// 594 // Validation operands (guards) 595 /////////// 596 597 // For each register, we always use the same register as a validation operand. 598 // This helps us avoid needlessly losing information at CFG join points. 599 private HashMap<Register, RegisterOperand> _ncGuards; 600 601 /** 602 * Make a register operand to use as a null check guard for the 603 * given register. 604 */ 605 RegisterOperand makeNullCheckGuard(Register ref) { 606 RegisterOperand guard = _ncGuards.get(ref); 607 if (guard == null) { 608 guard = temps.makeTempValidation(); 609 _ncGuards.put(ref, guard.copyRO()); 610 } else { 611 guard = guard.copyRO(); 612 } 613 return guard; 614 } 615 616 /////////// 617 // Profile data 618 /////////// 619 public BranchProfileOperand getConditionalBranchProfileOperand(int bcIndex, boolean backwards) { 620 float prob; 621 BranchProfile bp; 622 if (branchProfiles != null && ((bp = branchProfiles.getEntry(bcIndex)) != null)) { 623 prob = ((ConditionalBranchProfile) bp).getTakenProbability(); 624 } else { 625 if (branchProfiles != null) { 626 VM.sysWrite("Warning: conditional branch profile entry not found"); 627 } 628 if (backwards) { 629 prob = 0.9f; 630 } else { 631 prob = 0.5f; 632 } 633 } 634 // experimental option: flip the probability to see how bad things would be if 635 // we were completely wrong. 636 if (options.inverseFrequencyCounters()) { 637 prob = 1f - prob; 638 } 639 return new BranchProfileOperand(prob); 640 } 641 642 public SwitchBranchProfile getSwitchProfile(int bcIndex) { 643 if (branchProfiles != null) { 644 BranchProfile bp = branchProfiles.getEntry(bcIndex); 645 return (SwitchBranchProfile) bp; 646 } else { 647 return null; 648 } 649 } 650 651 /////////// 652 // Implementation 653 /////////// 654 655 /** 656 * for internal use only (in createInlinedContext) 657 */ 658 private GenerationContext() {} 659 660 /** 661 * Fill in the rest of the method prologue. 662 * PRECONDITION: arguments & temps have been setup/initialized. 663 */ 664 private void completePrologue(boolean isOutermost) { 665 // Deal with Uninteruptible code. 666 if (!isOutermost && requiresUnintMarker()) { 667 Instruction s = Empty.create(UNINT_BEGIN); 668 appendInstruction(prologue, s, PROLOGUE_BCI); 669 } 670 671 // Deal with implicit monitorenter for synchronized methods. 672 // When working with the class writer do not expand static 673 // synchronization headers as there is no easy way to get at 674 // class object 675 676 // OSR: if this is a specialized method, no monitor enter at the beginging 677 // since it's the second time reenter 678 if (method.isForOsrSpecialization()) { 679 // do nothing 680 } else if (method.isSynchronized() && !options.ESCAPE_INVOKEE_THREAD_LOCAL) { 681 Operand lockObject = getLockObject(); 682 Instruction s = MonitorOp.create(MONITORENTER, lockObject, new TrueGuardOperand()); 683 appendInstruction(prologue, s, SYNCHRONIZED_MONITORENTER_BCI); 684 } 685 } 686 687 /** 688 * Fill in the rest of the method epilogue. 689 * PRECONDITION: arguments & temps have been setup/initialized. 690 */ 691 private void completeEpilogue(boolean isOutermost) { 692 // Deal with implicit monitorexit for synchronized methods. 693 if (method.isSynchronized() && !options.ESCAPE_INVOKEE_THREAD_LOCAL) { 694 Operand lockObject = getLockObject(); 695 Instruction s = MonitorOp.create(MONITOREXIT, lockObject, new TrueGuardOperand()); 696 appendInstruction(epilogue, s, SYNCHRONIZED_MONITOREXIT_BCI); 697 } 698 699 // Deal with Uninterruptible code. 700 if (!isOutermost && requiresUnintMarker()) { 701 Instruction s = Empty.create(UNINT_END); 702 appendInstruction(epilogue, s, EPILOGUE_BCI); 703 } 704 705 if (isOutermost) { 706 TypeReference returnType = method.getReturnType(); 707 Operand retVal = returnType.isVoidType() ? null : new RegisterOperand(resultReg, returnType); 708 Instruction s = Return.create(RETURN, retVal); 709 appendInstruction(epilogue, s, EPILOGUE_BCI); 710 } 711 } 712 713 /** 714 * If the method is synchronized then we wrap it in a 715 * synthetic exception handler that unlocks & rethrows 716 * PRECONDITION: cfg, arguments & temps have been setup/initialized. 717 */ 718 private void completeExceptionHandlers(boolean isOutermost) { 719 if (method.isSynchronized() && !options.ESCAPE_INVOKEE_THREAD_LOCAL) { 720 ExceptionHandlerBasicBlock rethrow = 721 new ExceptionHandlerBasicBlock(SYNTH_CATCH_BCI, 722 inlineSequence, 723 new TypeOperand(RVMType.JavaLangThrowableType), 724 cfg); 725 rethrow.exceptionHandlers = enclosingHandlers; 726 RegisterOperand ceo = temps.makeTemp(TypeReference.JavaLangThrowable); 727 Instruction s = Nullary.create(GET_CAUGHT_EXCEPTION, ceo); 728 appendInstruction(rethrow, s, SYNTH_CATCH_BCI); 729 Operand lockObject = getLockObject(); 730 731 RVMMethod target = Entrypoints.unlockAndThrowMethod; 732 MethodOperand methodOp = MethodOperand.STATIC(target); 733 methodOp.setIsNonReturningCall(true); // Used to keep cfg correct 734 s = 735 Call.create2(CALL, 736 null, 737 new AddressConstantOperand(target.getOffset()), 738 methodOp, 739 lockObject, 740 ceo.copyD2U()); 741 appendInstruction(rethrow, s, RUNTIME_SERVICES_BCI); 742 743 cfg.insertBeforeInCodeOrder(epilogue, rethrow); 744 745 // May be overly conservative 746 // (if enclosed by another catch of Throwable...) 747 if (enclosingHandlers != null) { 748 for (Enumeration<BasicBlock> e = enclosingHandlers.enumerator(); e.hasMoreElements();) { 749 BasicBlock eh = e.nextElement(); 750 rethrow.insertOut(eh); 751 } 752 } 753 rethrow.setCanThrowExceptions(); 754 rethrow.setMayThrowUncaughtException(); 755 rethrow.insertOut(exit); 756 757 // save a reference to this block so we can discard it if unused. 758 unlockAndRethrow = rethrow; 759 760 ExceptionHandlerBasicBlock[] sh = new ExceptionHandlerBasicBlock[1]; 761 sh[0] = rethrow; 762 enclosingHandlers = new ExceptionHandlerBasicBlockBag(sh, enclosingHandlers); 763 generatedExceptionHandlers = true; 764 } 765 } 766 767 /** 768 * Get the object for locking for synchronized methods. 769 * either the class object or the this ptr. 770 */ 771 private Operand getLockObject() { 772 if (method.isStatic()) { 773 Class<?> klass = method.getDeclaringClass().getClassForType(); 774 Offset offs = Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(klass)); 775 return new ClassConstantOperand(klass, offs); 776 } else { 777 return makeLocal(0, arguments[0].getType()); 778 } 779 } 780 781 private void appendInstruction(BasicBlock b, Instruction s, int bcIndex) { 782 s.position = inlineSequence; 783 s.bcIndex = bcIndex; 784 b.appendInstruction(s); 785 } 786 787 private boolean requiresUnintMarker() { 788 if (method.isInterruptible()) return false; 789 790 // supress redundant markers by detecting when we're inlining 791 // one Uninterruptible method into another one. 792 for (InlineSequence p = inlineSequence.getCaller(); p != null; p = p.getCaller()) { 793 if (!p.getMethod().isInterruptible()) return false; 794 } 795 796 return true; 797 } 798 799 /** 800 * Make sure, the generation context is still in sync with the IR, even if we applied some 801 * optimizations. This method should be called before hir2lir conversions 802 * which might trigger inlining. 803 */ 804 public void resync() { 805 //make sure the _ncGuards contain no dangling mappings 806 resync_ncGuards(); 807 } 808 809 /** 810 * This method makes sure that _ncGuard only maps to registers that 811 * are actually in the IRs register pool. 812 */ 813 private void resync_ncGuards() { 814 HashSet<Register> regPool = new HashSet<Register>(); 815 816 for (Register r = temps.getFirstSymbolicRegister(); r != null; r = r.getNext()) { 817 regPool.add(r); 818 } 819 820 Iterator<Map.Entry<Register, RegisterOperand>> i = _ncGuards.entrySet().iterator(); 821 while (i.hasNext()) { 822 Map.Entry<Register, RegisterOperand> entry = i.next(); 823 if (!(regPool.contains(entry.getValue()))) i.remove(); 824 } 825 } 826 827 /** 828 * Kill ncGuards, so we do not use outdated mappings unintendedly later on 829 */ 830 public void close() { 831 _ncGuards = null; 832 } 833 834 } 835