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