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.bc2ir.IRGenOptions.DBG_BB; 016import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_BBSET; 017import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_CFG; 018import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_EX; 019import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_FLATTEN; 020import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_INLINE_JSR; 021import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_LOCAL; 022import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_REGEN; 023import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_STACK; 024import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.MAX_RETURN_ADDRESSES; 025import static org.jikesrvm.compilers.opt.driver.OptConstants.RECTIFY_BCI; 026 027import java.util.Enumeration; 028import java.util.HashSet; 029import java.util.NoSuchElementException; 030 031import org.jikesrvm.VM; 032import org.jikesrvm.classloader.BytecodeStream; 033import org.jikesrvm.classloader.ExceptionHandlerMap; 034import org.jikesrvm.classloader.TypeReference; 035import org.jikesrvm.compilers.opt.OperationNotImplementedException; 036import org.jikesrvm.compilers.opt.ir.BasicBlock; 037import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock; 038import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlockBag; 039import org.jikesrvm.compilers.opt.ir.IRTools; 040import org.jikesrvm.compilers.opt.ir.Instruction; 041import org.jikesrvm.compilers.opt.ir.Move; 042import org.jikesrvm.compilers.opt.ir.operand.Operand; 043import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; 044import org.jikesrvm.compilers.opt.ir.operand.TypeOperand; 045 046/** 047 * Encapsulates the discovery and maintenance of the set of basic blocks that 048 * are being generated during construction of the IR. 049 * <p> 050 * The backing data store is a red/black tree, but there are a number of 051 * very specialized operations that are performed during search/insertion 052 * so we roll our own instead of using one from the standard library. 053 */ 054final class BBSet { 055 /** root of the backing red/black tree*/ 056 private BasicBlockLE root; 057 058 /** 059 * is it the case that we can ignore JSR processing because 060 * BC2IR has not yet generated a JSR bytecode in this method? 061 */ 062 private boolean noJSR = true; 063 064 /** entry block of the CFG */ 065 private final BasicBlockLE entry; 066 067 /** associated generation context */ 068 private final GenerationContext gc; 069 070 /** associated bytecodes */ 071 private final BytecodeStream bcodes; 072 073 // Fields to support generation/identification of catch blocks 074 /** Start bytecode index for each exception handler ranges */ 075 private int[] startPCs; 076 077 /** End bytecode index for each exception handler range */ 078 private int[] endPCs; 079 080 /** Start bytecode index of each the exception handler */ 081 private int[] handlerPCs; 082 083 /** Type of exception handled by each exception handler range. */ 084 private TypeOperand[] exceptionTypes; 085 086 /** 087 * Initialize the BBSet to handle basic block generation for the argument 088 * generation context and bytecode info. 089 * @param gc the generation context to generate blocks for 090 * @param bcodes the bytecodes of said generation context 091 * @param localState the state of the local variables for the block 092 * beginning at bytecode 0. 093 */ 094 BBSet(GenerationContext gc, BytecodeStream bcodes, Operand[] localState) { 095 this.gc = gc; 096 this.bcodes = bcodes; 097 098 // Set up internal data structures to deal with exception handlers 099 parseExceptionTables(); 100 101 // Create the entry block, setting root as a sideffect. 102 entry = _createBBLE(0, null, null, false); 103 entry.setStackKnown(); 104 entry.copyIntoLocalState(localState); 105 } 106 107 BasicBlockLE getEntry() { 108 return entry; 109 } 110 111 /** 112 * Notify the BBSet that BC2IR has encountered a JSR bytecode. 113 * This enables more complex logic in getOrCreateBlock to drive 114 * the basic block specialization that is the key to JSR inlining. 115 */ 116 void seenJSR() { 117 noJSR = false; 118 } 119 120 /** 121 * @return a enumeration of the BasicBlockLE's currently in the BBSet 122 */ 123 Enumeration<BasicBlockLE> contents() { 124 return TreeEnumerator.enumFromRoot(root); 125 } 126 127 /** 128 * Gets the bytecode index of the block in the set which has the 129 * next-higher bytecode index. 130 * 131 * @param x basic block to start at. 132 * @return the bytecode index of the block in the set with the 133 * next-higher bytecode index. If {@code x} is currently the block with 134 * the highest starting bytecode index, return {@code bcodes.length()}. 135 */ 136 int getNextBlockBytecodeIndex(BasicBlockLE x) { 137 BasicBlockLE nextBBLE = getSuccessor(x, x.low); 138 return nextBBLE == null ? bcodes.length() : nextBBLE.low; 139 } 140 141 /** 142 * Finds the next ungenerated block, starting at the argument 143 * block and searching forward, wrapping around to the beginning. 144 * 145 * @param start the basic block at which to start looking. 146 * @return {@code null} if all blocks are generated, the next 147 * ungenerated block otherwise 148 */ 149 BasicBlockLE getNextEmptyBlock(BasicBlockLE start) { 150 if (DBG_BBSET) db("getting the next empty block after " + start); 151 152 // Look for an ungenerated block after start. 153 BBSet.TreeEnumerator e = TreeEnumerator.enumFromNode(start); 154 while (e.hasMoreElements()) { 155 BasicBlockLE block = e.next(); 156 if (DBG_BBSET) { 157 db("Considering block " + block + " " + block.genState()); 158 } 159 if (block.isReadyToGenerate()) { 160 if (DBG_BBSET) db("block " + block + " is not yet generated"); 161 return block; 162 } 163 } 164 165 // There were none. Start looking from the beginning. 166 if (DBG_BBSET) db("at end of bytecodes, restarting at beginning"); 167 e = TreeEnumerator.enumFromRoot(root); 168 while (true) { 169 BasicBlockLE block = e.next(); 170 if (block == start) { 171 if (DBG_BBSET) db("wrapped around, no more empty blocks"); 172 return null; 173 } 174 if (DBG_BBSET) { 175 db("Considering block " + block + " " + block.genState()); 176 } 177 if (block.isReadyToGenerate()) { 178 if (DBG_BBSET) db("block " + block + " is not yet generated"); 179 return block; 180 } 181 } 182 } 183 184 /** 185 * Get or create a block at the specified target. 186 * If simStack is non-{@code null}, rectifies stack state with target stack state. 187 * If simLocals is non-{@code null}, rectifies local state with target local state. 188 * Any instructions needed to rectify stack/local state are appended to 189 * from. 190 * 191 * @param target target index 192 * @param from the block from which control is being transfered 193 * and to which rectification instructions are added. 194 * @param simStack stack state to rectify, or {@code null} 195 * @param simLocals local state to rectify, or {@code null} 196 * @return a block, never {@code null} 197 */ 198 BasicBlockLE getOrCreateBlock(int target, BasicBlockLE from, OperandStack simStack, Operand[] simLocals) { 199 if (DBG_BB || BC2IR.DBG_SELECTED) { 200 db("getting block " + 201 target + 202 ", match stack: " + 203 (simStack != null) + 204 " match locals: " + 205 (simLocals != null)); 206 } 207 return getOrCreateBlock(root, true, target, from, simStack, simLocals); 208 } 209 210 /** 211 * Mark a previously generated block for regeneration. 212 * We define this method here so that in the future 213 * we can implement a more efficient getNextEmptyBlock that 214 * (1) avoids generating lots of blocks when a CFG predecessor has a 215 * pending regeneration and (2) avoids the scan through all blocks when 216 * there are no more blocks left to generate. 217 * 218 * @param p the block to mark for regeneration 219 */ 220 private void markBlockForRegeneration(BasicBlockLE p) { 221 if (DBG_REGEN) db("marking " + p + " for regeneration"); 222 if (p.fallThrough != null && p.fallThrough instanceof InliningBlockLE) { 223 // if the fallthrough out edge of this block is an 224 // InlineMethodBasicBlock, then the inlined method must also be 225 // regenerated. In preparation for this, we must delete all out 226 // edges from the inlined method to the caller. 227 // (These arise from thrown/caught exceptions.) 228 InliningBlockLE imbb = (InliningBlockLE) p.fallThrough; 229 imbb.deleteAllOutEdges(); 230 } 231 // discard any "real" instructions in the block 232 if (!p.block.isEmpty()) { 233 p.block.discardInstructions(); 234 } 235 p.setSelfRegen(); 236 p.clearGenerated(); 237 p.fallThrough = null; 238 // If p had a non-empty stack on entry, we need to go through it 239 // and copy all of its operands (they may point to instructions 240 // we just blew away, but then again they may not (may not be in p), 241 // so we can't simply null out the instruction field); 242 if (p.stackState != null) { 243 int i = p.stackState.getSize(); 244 while (i-- > 0) { 245 Operand op = p.stackState.getFromTop(i); 246 p.stackState.replaceFromTop(i, op.copy()); 247 } 248 } 249 } 250 251 /** 252 * Rectify the given stack state with the state contained in the given 253 * BBLE, adding the necessary move instructions to the end of the given 254 * basic block to make register numbers agree and rectify mis-matched constants. 255 * <p> 256 * @param block basic block to append move instructions to 257 * @param stack stack to copy 258 * @param p BBLE to copy stack state into 259 */ 260 void rectifyStacks(BasicBlock block, OperandStack stack, BasicBlockLE p) { 261 if (stack == null || stack.isEmpty()) { 262 if (VM.VerifyAssertions) VM._assert(p.stackState == null); 263 if (!p.isStackKnown()) { 264 p.setStackKnown(); 265 } 266 if (DBG_STACK || BC2IR.DBG_SELECTED) { 267 db("Rectified empty expression stack into " + p + "(" + p.block + ")"); 268 } 269 return; 270 } 271 boolean generated = p.isGenerated(); 272 // (1) Rectify the stacks. 273 if (!p.isStackKnown()) { 274 // First time we reached p. Thus, its expression stack 275 // is implicitly top and the meet degenerates to a copy operation 276 // with possibly some register renaming. 277 // (We need to ensure that non-local registers appear at 278 // most once on each expression stack). 279 if (DBG_STACK || BC2IR.DBG_SELECTED) { 280 db("First stack rectifiction for " + p + "(" + p.block + ") simply saving"); 281 } 282 if (VM.VerifyAssertions) VM._assert(p.stackState == null); 283 p.stackState = stack.createEmptyOperandStackWithSameCapacity(); 284 for (int i = stack.getSize() - 1; i >= 0; i--) { 285 Operand op = stack.getFromTop(i); 286 if (op == BC2IR.DUMMY) { 287 p.stackState.push(BC2IR.DUMMY); 288 } else if (op instanceof RegisterOperand) { 289 RegisterOperand rop = op.asRegister(); 290 if (rop.getRegister().isLocal()) { 291 RegisterOperand temp = gc.getTemps().makeTemp(rop); 292 temp.setInheritableFlags(rop); 293 BC2IR.setGuardForRegOp(temp, BC2IR.copyGuardFromOperand(rop)); 294 Instruction move = Move.create(IRTools.getMoveOp(rop.getType()), temp, rop.copyRO()); 295 move.bcIndex = RECTIFY_BCI; 296 move.position = gc.getInlineSequence(); 297 block.appendInstructionRespectingTerminalBranch(move); 298 p.stackState.push(temp.copy()); 299 if (DBG_STACK || BC2IR.DBG_SELECTED) { 300 db("Inserted " + move + " into " + block + " to rename local"); 301 } 302 } else { 303 p.stackState.push(rop.copy()); 304 } 305 } else { 306 p.stackState.push(op.copy()); 307 } 308 } 309 p.setStackKnown(); 310 } else { 311 // A real rectification. 312 // We need to update mergedStack such that 313 // mergedStack[i] = meet(mergedStack[i], stack[i]). 314 if (DBG_STACK || BC2IR.DBG_SELECTED) db("rectifying stacks"); 315 try { 316 if (VM.VerifyAssertions) { 317 VM._assert(stack.getSize() == p.stackState.getSize()); 318 } 319 } catch (NullPointerException e) { 320 System.err.println("stack size " + stack.getSize()); 321 System.err.println(stack); 322 System.err.println(p.stackState); 323 System.err.println(gc.getMethod().toString()); 324 block.printExtended(); 325 p.block.printExtended(); 326 throw e; 327 } 328 for (int i = 0; i < stack.getSize(); ++i) { 329 Operand sop = stack.getFromTop(i); 330 Operand mop = p.stackState.getFromTop(i); 331 if ((sop == BC2IR.DUMMY) || (sop instanceof ReturnAddressOperand)) { 332 if (VM.VerifyAssertions) VM._assert(mop.similar(sop)); 333 continue; 334 } else if (sop.isConstant() || mop.isConstant()) { 335 if (mop.similar(sop)) { 336 continue; // constants are similar; so we don't have to do anything. 337 } 338 // sigh. Non-similar constants. 339 if (mop.isConstant()) { 340 // Insert move instructions in all predecessor 341 // blocks except 'block' to move mop into a register. 342 RegisterOperand mopTmp = gc.getTemps().makeTemp(mop); 343 if (DBG_STACK || BC2IR.DBG_SELECTED) db("Merged stack has constant operand " + mop); 344 for (Enumeration<BasicBlock> preds = p.block.getIn(); preds.hasMoreElements();) { 345 BasicBlock pred = preds.nextElement(); 346 if (pred == block) continue; 347 injectMove(pred, mopTmp.copyRO(), mop.copy()); 348 } 349 p.stackState.replaceFromTop(i, mopTmp.copy()); 350 if (generated) { 351 if (DBG_STACK || BC2IR.DBG_SELECTED) { 352 db("\t...forced to regenerate " + p + " (" + p.block + ") because of this"); 353 } 354 markBlockForRegeneration(p); 355 generated = false; 356 p.block.deleteOut(); 357 if (DBG_CFG || BC2IR.DBG_SELECTED) db("Deleted all out edges of " + p.block); 358 } 359 mop = mopTmp; 360 } 361 if (sop.isConstant()) { 362 // Insert move instruction into block. 363 RegisterOperand sopTmp = gc.getTemps().makeTemp(sop); 364 if (DBG_STACK || BC2IR.DBG_SELECTED) db("incoming stack has constant operand " + sop); 365 injectMove(block, sopTmp, sop); 366 sop = sopTmp.copyRO(); 367 } 368 } 369 370 // sop and mop are RegisterOperands (either originally or because 371 // we forced them to be above due to incompatible constants. 372 RegisterOperand rsop = sop.asRegister(); 373 RegisterOperand rmop = mop.asRegister(); 374 if (rmop.getRegister() != rsop.getRegister()) { 375 // must insert move at end of block to get register #s to match 376 RegisterOperand temp = rsop.copyRO(); 377 temp.setRegister(rmop.getRegister()); 378 injectMove(block, temp, rsop.copyRO()); 379 } 380 Operand meet = Operand.meet(rmop, rsop, rmop.getRegister()); 381 if (DBG_STACK || BC2IR.DBG_SELECTED) db("Meet of " + rmop + " and " + rsop + " is " + meet); 382 if (meet != rmop) { 383 if (generated) { 384 if (DBG_STACK || BC2IR.DBG_SELECTED) { 385 db("\t...forced to regenerate " + p + " (" + p.block + ") because of this"); 386 } 387 markBlockForRegeneration(p); 388 generated = false; 389 p.block.deleteOut(); 390 if (DBG_CFG || BC2IR.DBG_SELECTED) db("Deleted all out edges of " + p.block); 391 } 392 p.stackState.replaceFromTop(i, meet); 393 } 394 } 395 } 396 } 397 398 private void injectMove(BasicBlock block, RegisterOperand res, Operand val) { 399 Instruction move = Move.create(IRTools.getMoveOp(res.getType()), res, val); 400 move.bcIndex = RECTIFY_BCI; 401 move.position = gc.getInlineSequence(); 402 block.appendInstructionRespectingTerminalBranch(move); 403 if (DBG_STACK || BC2IR.DBG_SELECTED) { 404 db("Inserted " + move + " into " + block); 405 } 406 } 407 408 /** 409 * Rectify the given local variable state with the local variable state 410 * stored in the given BBLE. 411 * 412 * @param localState local variable state to rectify 413 * @param p target BBLE to rectify state to 414 */ 415 void rectifyLocals(Operand[] localState, BasicBlockLE p) { 416 if (!p.isLocalKnown()) { 417 if (DBG_LOCAL || BC2IR.DBG_SELECTED) { 418 db("rectifying with heretofore unknown locals, changing to save"); 419 } 420 p.copyIntoLocalState(localState); 421 return; 422 } 423 if (DBG_LOCAL || BC2IR.DBG_SELECTED) db("rectifying current local state with " + p); 424 boolean generated = p.isGenerated(); 425 Operand[] incomingState = localState; 426 Operand[] presentState = p.localState; 427 if (VM.VerifyAssertions) { 428 VM._assert(incomingState.length == presentState.length); 429 } 430 for (int i = 0, n = incomingState.length; i < n; ++i) { 431 Operand pOP = presentState[i]; 432 Operand iOP = incomingState[i]; 433 if (pOP == iOP) { 434 if (DBG_LOCAL || BC2IR.DBG_SELECTED) { 435 db("local states have the exact same operand " + pOP + " for local " + i); 436 } 437 } else { 438 boolean untyped = (pOP == null || pOP == BC2IR.DUMMY || pOP instanceof ReturnAddressOperand); 439 Operand mOP = Operand.meet(pOP, iOP, untyped ? null : gc.localReg(i, pOP.getType())); 440 if (DBG_LOCAL || BC2IR.DBG_SELECTED) db("Meet of " + pOP + " and " + iOP + " is " + mOP); 441 if (mOP != pOP) { 442 if (generated) { 443 if (DBG_LOCAL || BC2IR.DBG_SELECTED) { 444 db("\t...forced to regenerate " + p + " (" + p.block + ") because of this"); 445 } 446 markBlockForRegeneration(p); 447 generated = false; 448 p.block.deleteOut(); 449 if (DBG_CFG || BC2IR.DBG_SELECTED) db("Deleted all out edges of " + p.block); 450 } 451 presentState[i] = mOP; 452 } 453 } 454 } 455 } 456 457 /** 458 * Do a final pass over the generated basic blocks to create 459 * the initial code ordering. All blocks generated for the method 460 * will be inserted after gc.prologue. 461 * <p> 462 * NOTE: Only some CFG edges are created here..... 463 * we're mainly just patching together a code linearization. 464 * 465 * @param inlinedSomething was a normal method (i.e. non-magic) inlined? 466 */ 467 void finalPass(boolean inlinedSomething) { 468 BBSet.TreeEnumerator e = TreeEnumerator.enumFromRoot(root); 469 BasicBlock cop = gc.getPrologue(); 470 BasicBlockLE curr = getEntry(); 471 BasicBlockLE next = null; 472 top: 473 while (true) { 474 // Step 0: If curr is the first block in a catch block, 475 // inject synthetic entry block too. 476 if (curr instanceof HandlerBlockLE) { 477 // tell our caller that we actually put a handler in the final CFG. 478 gc.markExceptionHandlersAsGenerated(); 479 HandlerBlockLE hcurr = (HandlerBlockLE) curr; 480 if (DBG_FLATTEN) { 481 db("injecting handler entry block " + hcurr.entryBlock + " before " + hcurr); 482 } 483 gc.getCfg().insertAfterInCodeOrder(cop, hcurr.entryBlock); 484 cop = hcurr.entryBlock; 485 } 486 // Step 1: Insert curr in the code order (after cop, updating cop). 487 if (DBG_FLATTEN) db("flattening: " + curr + " (" + curr.block + ")"); 488 curr.setInCodeOrder(); 489 gc.getCfg().insertAfterInCodeOrder(cop, curr.block); 490 cop = curr.block; 491 if (DBG_FLATTEN) { 492 db("Current Code order for " + gc.getMethod() + "\n"); 493 for (BasicBlock bb = gc.getPrologue(); bb != null; bb = (BasicBlock) bb.getNext()) { 494 VM.sysWrite(bb + "\n"); 495 } 496 } 497 // Step 1.1 Sometimes (rarely) there will be an inscope 498 // exception handler that wasn't actually generated. If this happens, 499 // make a new, filtered EHBBB to avoid later confusion. 500 if (curr.handlers != null) { 501 int notGenerated = 0; 502 for (HandlerBlockLE handler : curr.handlers) { 503 if (!handler.isGenerated()) { 504 if (DBG_EX || DBG_FLATTEN) { 505 db("Will remove unreachable handler " + handler + " from " + curr); 506 } 507 notGenerated++; 508 } 509 } 510 if (notGenerated > 0) { 511 if (notGenerated == curr.handlers.length) { 512 if (DBG_EX || DBG_FLATTEN) { 513 db("No (local) handlers were actually reachable for " + curr + "; setting to caller"); 514 } 515 curr.block.exceptionHandlers = curr.block.exceptionHandlers.getCaller(); 516 } else { 517 ExceptionHandlerBasicBlock[] nlh = 518 new ExceptionHandlerBasicBlock[curr.handlers.length - notGenerated]; 519 for (int i = 0, j = 0; i < curr.handlers.length; i++) { 520 if (curr.handlers[i].isGenerated()) { 521 nlh[j++] = curr.handlers[i].entryBlock; 522 } else { 523 if (VM.VerifyAssertions) { 524 VM._assert(curr.handlers[i].entryBlock.hasZeroIn(), "Non-generated handler with CFG edges"); 525 } 526 } 527 } 528 curr.block.exceptionHandlers = 529 new ExceptionHandlerBasicBlockBag(nlh, curr.block.exceptionHandlers.getCaller()); 530 } 531 } 532 } 533 // Step 2: Identify the next basic block to add to the code order. 534 // curr wants to fallthrough to an inlined method. 535 // Inject the entire inlined CFG in the code order. 536 // There's some fairly complicated coordination between this code, 537 // GenerationContext, and maybeInlineMethod. Sorry, but you'll 538 // have to take a close look at all of these to see how it 539 // all fits together....--dave 540 if (curr.fallThrough != null && curr.fallThrough instanceof InliningBlockLE) { 541 InliningBlockLE icurr = (InliningBlockLE) curr.fallThrough; 542 BasicBlock forw = cop.nextBasicBlockInCodeOrder(); 543 BasicBlock calleeEntry = icurr.gc.getCfg().firstInCodeOrder(); 544 BasicBlock calleeExit = icurr.gc.getCfg().lastInCodeOrder(); 545 gc.getCfg().breakCodeOrder(cop, forw); 546 gc.getCfg().linkInCodeOrder(cop, icurr.gc.getCfg().firstInCodeOrder()); 547 gc.getCfg().linkInCodeOrder(icurr.gc.getCfg().lastInCodeOrder(), forw); 548 if (DBG_CFG || BC2IR.DBG_SELECTED) { 549 db("Added CFG edge from " + cop + " to " + calleeEntry); 550 } 551 if (icurr.epilogueBBLE != null) { 552 if (DBG_FLATTEN) { 553 db("injected " + icurr + " between " + curr + " and " + icurr.epilogueBBLE.fallThrough); 554 } 555 if (VM.VerifyAssertions) { 556 VM._assert(icurr.epilogueBBLE.block == icurr.gc.getCfg().lastInCodeOrder()); 557 } 558 curr = icurr.epilogueBBLE; 559 cop = curr.block; 560 } else { 561 if (DBG_FLATTEN) db("injected " + icurr + " after " + curr); 562 curr = icurr; 563 cop = calleeExit; 564 } 565 } 566 next = curr.fallThrough; 567 if (DBG_FLATTEN && next == null) { 568 db(curr + " has no fallthrough case, getting next block"); 569 } 570 if (next != null) { 571 if (DBG_CFG || BC2IR.DBG_SELECTED) { 572 db("Added CFG edge from " + curr.block + " to " + next.block); 573 } 574 if (next.isInCodeOrder()) { 575 if (DBG_FLATTEN) { 576 db("fallthrough " + next + " is already flattened, adding goto"); 577 } 578 curr.block.appendInstruction(next.block.makeGOTO()); 579 // set next to null to indicate no "real" fall through 580 next = null; 581 } 582 } 583 if (next == null) { 584 // Can't process fallthroughblock, so get next BBLE from enumeration 585 while (true) { 586 if (!e.hasMoreElements()) { 587 // all done. 588 if (DBG_FLATTEN) db("no more blocks! all done"); 589 break top; 590 } 591 next = e.next(); 592 if (DBG_FLATTEN) db("looking at " + next); 593 if (!next.isGenerated()) { 594 if (DBG_FLATTEN) db("block " + next + " was not generated"); 595 continue; 596 } 597 if (!next.isInCodeOrder()) { 598 break; 599 } 600 } 601 if (DBG_FLATTEN) db("found unflattened block: " + next); 602 } 603 curr = next; 604 } 605 // If the epilogue was unreachable, remove it from the code order and cfg 606 // and set gc.epilogue to null. 607 boolean removedSomethingFromCodeOrdering = inlinedSomething; 608 if (gc.getEpilogue().hasZeroIn()) { 609 if (DBG_FLATTEN || DBG_CFG) { 610 db("Deleting unreachable epilogue " + gc.getEpilogue()); 611 } 612 gc.getCfg().removeFromCodeOrder(gc.getEpilogue()); 613 removedSomethingFromCodeOrdering = true; 614 615 // remove the node from the graph AND adjust its edge info 616 gc.getEpilogue().remove(); 617 gc.getEpilogue().deleteIn(); 618 gc.getEpilogue().deleteOut(); 619 if (VM.VerifyAssertions) VM._assert(gc.getEpilogue().hasZeroOut()); 620 gc.setEpilogue(null); 621 } 622 // if gc has an unlockAndRethrow block that was not used, then remove it 623 if (gc.getUnlockAndRethrow() != null && gc.getUnlockAndRethrow().hasZeroIn()) { 624 gc.getCfg().removeFromCFGAndCodeOrder(gc.getUnlockAndRethrow()); 625 removedSomethingFromCodeOrdering = true; 626 gc.getEnclosingHandlers().remove(gc.getUnlockAndRethrow()); 627 } 628 // if we removed a basic block then we should compact the node numbering 629 if (removedSomethingFromCodeOrdering) { 630 gc.getCfg().compactNodeNumbering(); 631 } 632 633 if (DBG_FLATTEN) { 634 db("Current Code order for " + gc.getMethod() + "\n"); 635 for (BasicBlock bb = gc.getPrologue(); bb != null; bb = (BasicBlock) bb.getNext()) { 636 bb.printExtended(); 637 } 638 } 639 if (DBG_FLATTEN) { 640 db("Final CFG for " + gc.getMethod() + "\n"); 641 gc.getCfg().printDepthFirst(); 642 } 643 } 644 645 ////////////////////////////////////////// 646 // Gory implementation details of BBSet // 647 ////////////////////////////////////////// 648 649 /** 650 * Print a debug string to the sysWrite stream. 651 * @param val string to print 652 */ 653 private void db(String val) { 654 VM.sysWrite("IRGEN " + bcodes.getDeclaringClass() + "." + gc.getMethod().getName() + ":" + val + "\n"); 655 } 656 657 /** 658 * Initializes the global exception handler arrays for the method. 659 */ 660 private void parseExceptionTables() { 661 ExceptionHandlerMap eMap = gc.getMethod().getExceptionHandlerMap(); 662 if (DBG_EX) db("\texception handlers for " + gc.getMethod() + ": " + eMap); 663 if (eMap == null) return; // method has no exception handling ranges. 664 startPCs = eMap.getStartPC(); 665 endPCs = eMap.getEndPC(); 666 handlerPCs = eMap.getHandlerPC(); 667 int numExceptionHandlers = startPCs.length; 668 exceptionTypes = new TypeOperand[numExceptionHandlers]; 669 for (int i = 0; i < numExceptionHandlers; i++) { 670 exceptionTypes[i] = new TypeOperand(eMap.getExceptionType(i)); 671 if (DBG_EX) db("\t\t[" + startPCs[i] + "," + endPCs[i] + "] " + eMap.getExceptionType(i)); 672 } 673 } 674 675 /** 676 * Initialize bble's handlers array based on startPCs/endPCs. 677 * In the process, new HandlerBlockLE's may be created 678 * (by the call to getOrCreateBlock). <p> 679 * PRECONDITION: bble.low and bble.max have already been correctly 680 * set to reflect the invariant that a basic block is in exactly one 681 * "handler range."<p> 682 * Also initializes bble.block.exceptionHandlers. 683 * 684 * @param bble the block whose handlers are to be initialized 685 * @param simLocals local variables 686 */ 687 private void initializeExceptionHandlers(BasicBlockLE bble, Operand[] simLocals) { 688 if (startPCs != null) { 689 HashSet<TypeReference> caughtTypes = new HashSet<TypeReference>(); 690 for (int i = 0; i < startPCs.length; i++) { 691 TypeReference caughtType = exceptionTypes[i].getTypeRef(); 692 if (bble.low >= startPCs[i] && bble.max <= endPCs[i] && !caughtTypes.contains(caughtType)) { 693 // bble's basic block is contained within this handler's range. 694 HandlerBlockLE eh = (HandlerBlockLE) getOrCreateBlock(handlerPCs[i], bble, null, simLocals); 695 if (DBG_EX) db("Adding handler " + eh + " to " + bble); 696 caughtTypes.add(caughtType); 697 bble.addHandler(eh); 698 } 699 } 700 } 701 if (bble.handlers != null) { 702 ExceptionHandlerBasicBlock[] ehbbs = new ExceptionHandlerBasicBlock[bble.handlers.length]; 703 for (int i = 0; i < bble.handlers.length; i++) { 704 ehbbs[i] = bble.handlers[i].entryBlock; 705 } 706 bble.block.exceptionHandlers = new ExceptionHandlerBasicBlockBag(ehbbs, gc.getEnclosingHandlers()); 707 } else { 708 bble.block.exceptionHandlers = gc.getEnclosingHandlers(); 709 } 710 } 711 712 /** 713 * Given a starting bytecode index, find the greatest bcIndex that 714 * is still has the same inscope exception handlers. 715 * @param bcIndex the start bytecode index 716 * @return greatest bytecode index with the same in scope exception 717 * handlers 718 */ 719 private int exceptionEndRange(int bcIndex) { 720 int max = bcodes.length(); 721 if (startPCs != null) { 722 for (int spc : startPCs) { 723 if (bcIndex < spc && max > spc) { 724 max = spc; 725 } 726 } 727 for (int epc : endPCs) { 728 if (bcIndex < epc && max > epc) { 729 max = epc; 730 } 731 } 732 } 733 return max; 734 } 735 736 /** 737 * We specialize basic blocks with respect to the return addresses 738 * they have on their expression stack and/or in their local variables 739 * on entry to the block. This has the effect of inlining the 740 * subroutine body at all of the JSR sites that invoke it. 741 * This is the key routine: it determines whether or not the 742 * argument simState (stack and locals) contains compatible 743 * return addresses as the candidate BasicBlockLE. 744 * <p> 745 * The main motivation for inlining away all JSR's is that it eliminates 746 * the "JSR problem" for type accurate GC. It is also simpler to 747 * implement and arguably results in more efficient generated code 748 * (assuming that we don't get horrific code bloat). 749 * To deal with the code bloat, we detect excessive code duplication and 750 * stop IR generation (bail out to the baseline compiler). 751 * 752 * @param simStack The expression stack to match 753 * @param simLocals The local variables to match 754 * @param candBBLE The candidate BaseicBlockLE 755 * @return {@code true} if and only if a matching JSR context (see above) 756 * was found 757 */ 758 private boolean matchingJSRcontext(OperandStack simStack, Operand[] simLocals, BasicBlockLE candBBLE) { 759 if (DBG_INLINE_JSR) { 760 db("Matching JSR context of argument stack/locals against " + candBBLE); 761 } 762 763 int numRA = 0; 764 if (simStack != null && candBBLE.isStackKnown()) { 765 for (int i = simStack.getSize() - 1; i >= 0; i--) { 766 Operand op = simStack.getFromTop(i); 767 if (op instanceof ReturnAddressOperand) { 768 if (numRA++ > MAX_RETURN_ADDRESSES) { 769 throw new OperationNotImplementedException("Too many subroutines"); 770 } 771 if (DBG_INLINE_JSR) db("simStack operand " + i + " is " + op); 772 Operand cop = candBBLE.stackState.getFromTop(i); 773 if (!Operand.conservativelyApproximates(cop, op)) { 774 if (DBG_INLINE_JSR) db("Not Matching: " + cop + " and " + op); 775 return false; 776 } else { 777 if (DBG_INLINE_JSR) db("operand " + cop + " is compatible with " + op); 778 } 779 } 780 } 781 } 782 783 if (simLocals != null && candBBLE.isLocalKnown()) { 784 for (int i = 0; i < simLocals.length; i++) { 785 Operand op = simLocals[i]; 786 if (op instanceof ReturnAddressOperand) { 787 if (numRA++ > MAX_RETURN_ADDRESSES) { 788 throw new OperationNotImplementedException("Too many subroutines"); 789 } 790 if (DBG_INLINE_JSR) db("simLocal " + i + " is " + op); 791 Operand cop = candBBLE.localState[i]; 792 if (!Operand.conservativelyApproximates(cop, op)) { 793 if (DBG_INLINE_JSR) db("Not Matching: " + cop + " and " + op); 794 return false; 795 } else { 796 if (DBG_INLINE_JSR) db("operand " + cop + " is compatible with " + op); 797 } 798 } 799 } 800 } 801 802 if (DBG_INLINE_JSR) db("Found " + candBBLE + " to be compatible"); 803 return true; 804 } 805 806 /** 807 * Get or create a block at the specified target. 808 * If simStack is non-{@code null}, rectifies stack state with target stack state. 809 * If simLocals is non-{@code null}, rectifies local state with target local state. 810 * Any instructions needed to rectify stack/local state are appended to 811 * from. 812 * As blocks are created, they are added to the red/black tree below x. 813 * 814 * @param x starting node for search. 815 * @param shouldCreate should we create the block if we run off the tree? 816 * @param target target index 817 * @param from the block from which control is being transfered 818 * and to which rectification instructions are added. 819 * @param simStack stack state to rectify, or {@code null} 820 * @param simLocals local state to rectify, or {@code null} 821 * @return a new block, never {@code null} 822 */ 823 private BasicBlockLE getOrCreateBlock(BasicBlockLE x, boolean shouldCreate, int target, BasicBlockLE from, 824 OperandStack simStack, Operand[] simLocals) { 825 if (target < x.low) { 826 if (x.left == null) { 827 return condCreateAndInit(x, shouldCreate, target, from, simStack, simLocals, true); 828 } else { 829 if (DBG_BBSET) db("following left branch from " + x + " to " + x.left); 830 return getOrCreateBlock(x.left, shouldCreate, target, from, simStack, simLocals); 831 } 832 } else if (target > x.low) { 833 if ((x.low < target) && (target <= x.high)) { 834 // the target points to the middle of x; mark x for regen 835 if (DBG_BBSET) db("target points to middle of " + x); 836 markBlockForRegeneration(x); 837 x.high = x.low; 838 x.block.deleteOut(); 839 if (DBG_CFG || BC2IR.DBG_SELECTED) db("Deleted all out edges of " + x.block); 840 } 841 if (x.right == null) { 842 return condCreateAndInit(x, shouldCreate, target, from, simStack, simLocals, false); 843 } else { 844 if (DBG_BBSET) db("following right branch from " + x + " to " + x.right); 845 return getOrCreateBlock(x.right, shouldCreate, target, from, simStack, simLocals); 846 } 847 } else { 848 // found a basic block at the target bytecode index. 849 if (noJSR || matchingJSRcontext(simStack, simLocals, x)) { 850 if (DBG_BBSET) db("found block " + x + " (" + x.block + ")"); 851 if (simStack != null) rectifyStacks(from.block, simStack, x); 852 if (simLocals != null) rectifyLocals(simLocals, x); 853 return x; 854 } 855 if (DBG_BBSET) db("found block " + x + ", but JSR context didn't match"); 856 if (x.left == null) { 857 if (x.right == null) { 858 return condCreateAndInit(x, shouldCreate, target, from, simStack, simLocals, true); 859 } else { 860 if (DBG_BBSET) { 861 db(x + " has only right child, continuing down that branch"); 862 } 863 return getOrCreateBlock(x.right, shouldCreate, target, from, simStack, simLocals); 864 } 865 } else { 866 if (x.right == null) { 867 if (DBG_BBSET) { 868 db(x + " has only left child, continuing down that branch"); 869 } 870 return getOrCreateBlock(x.left, shouldCreate, target, from, simStack, simLocals); 871 } else { 872 if (DBG_BBSET) { 873 db(x + " has two children, searching left branch first"); 874 } 875 BasicBlockLE bble = getOrCreateBlock(x.left, false, target, from, simStack, simLocals); 876 if (bble != null) { 877 return bble; 878 } else { 879 if (DBG_BBSET) { 880 db("didn't find " + target + " on left branch, continuing down right branch"); 881 } 882 return getOrCreateBlock(x.right, shouldCreate, target, from, simStack, simLocals); 883 } 884 } 885 } 886 } 887 } 888 889 /** 890 * Conditionally create a block at the specified target as a child of x. 891 * If simStack is non-{@code null}, rectifies stack state with target stack state. 892 * If simLocals is non-{@code null}, rectifies local state with target local state. 893 * Any instructions needed to rectify stack/local state are appended to 894 * from. 895 * 896 * @param x starting node for search. 897 * @param shouldCreate should we create the block if we run off the tree? 898 * @param target target index 899 * @param from the block from which control is being transfered 900 * and to which rectification instructions are added. 901 * @param simStack stack state to rectify, or {@code null} 902 * @param simLocals local state to rectify, or {@code null} 903 * @param left are we creating a left child of parent? 904 * @return the newly created block, or {@code null} if !shouldCreate 905 */ 906 private BasicBlockLE condCreateAndInit(BasicBlockLE x, boolean shouldCreate, int target, BasicBlockLE from, 907 OperandStack simStack, Operand[] simLocals, boolean left) { 908 BasicBlockLE bble = null; 909 if (shouldCreate) { 910 bble = _createBBLE(target, simLocals, x, left); 911 if (simStack != null) { 912 rectifyStacks(from.block, simStack, bble); 913 } 914 if (simLocals != null) { 915 bble.copyIntoLocalState(simLocals); 916 } 917 } 918 return bble; 919 } 920 921 /** 922 * Allocate a new BBLE at the given bcIndex. 923 * If bcIndex is the start of an handler block, 924 * then a HandlerBlockLE is created. 925 * After the BBLE is created, its handlers data structure is initialized 926 * (which may cause other blocks to be created). 927 * @param bcIndex the bytecode index at which the block should be created. 928 * @param simLocals the localState to pass (via initializeExceptionHandler)to 929 * to getOrCreateBlock if we need to create BBLEs for 930 * exception handlers. This is only actually used if 931 * !noJSR. We don't need the expression stack, since 932 * the only thing on the expression stack on entry to 933 * a handler block is the exception object (and thus 934 * we can skip scanning the expression stack for 935 * return addresses when creating a handler block). 936 * @param parent parent in Red/Black tree 937 * @param left are we creating a left child of parent? 938 * @return a new BBLE 939 */ 940 private BasicBlockLE _createBBLE(int bcIndex, Operand[] simLocals, BasicBlockLE parent, boolean left) { 941 BasicBlockLE newBBLE = null; 942 if (handlerPCs != null) { 943 for (int i = 0; i < handlerPCs.length; i++) { 944 if (handlerPCs[i] == bcIndex) { 945 if (newBBLE == null) { 946 newBBLE = 947 new HandlerBlockLE(bcIndex, 948 gc.getInlineSequence(), 949 exceptionTypes[i], 950 gc.getTemps(), 951 gc.getMethod().getOperandWords(), 952 gc.getCfg()); 953 ((HandlerBlockLE) newBBLE).entryBlock.firstRealInstruction(). 954 position = gc.getInlineSequence(); 955 } else { 956 ((HandlerBlockLE) newBBLE).addCaughtException(exceptionTypes[i]); 957 } 958 } 959 } 960 } 961 if (newBBLE == null) { 962 newBBLE = new BasicBlockLE(bcIndex, gc.getInlineSequence(), gc.getCfg()); 963 } 964 965 // Set newBBLE.max to encode exception ranges 966 newBBLE.max = exceptionEndRange(bcIndex); 967 968 if (DBG_BBSET) db("Created " + newBBLE); 969 970 // Now, insert newBBLE into our backing Red/Black tree before we call 971 // initializeExceptionHandlers. 972 // We must do it in this order because initExHand may in turn call 973 // _createBBLE to create new handler blocks, and our tree must contain 974 // newBBLE before we can correctly insert another block. 975 treeInsert(parent, newBBLE, left); 976 977 initializeExceptionHandlers(newBBLE, simLocals); 978 return newBBLE; 979 } 980 981 /** 982 * Returns the basic block's sucessor in bytecode sequence. 983 * 984 * @param x basic block at which to start the search for a higher block 985 * @param value the contents of x.low (makes tail call elim work better 986 * if we avoid the obvious 1 argument wrapper function) 987 * @return {@code null} if x is the block with the highest bytecode index, 988 * the block with the next-higher bytecode index otherwise. 989 */ 990 private BasicBlockLE getSuccessor(BasicBlockLE x, int value) { 991 if (x.right != null) return minimumBB(x.right, value); 992 BasicBlockLE y = x.parent; 993 while ((y != null) && (x == y.right)) { 994 x = y; 995 y = x.parent; 996 } 997 // at this point either x is the root, or x is the left child of y 998 if ((y == null) || (y.low != value)) return y; 999 return getSuccessor(y, value); 1000 } 1001 1002 private BasicBlockLE minimumBB(BasicBlockLE x, int value) { 1003 if (x.left != null) return minimumBB(x.left, value); 1004 if (value == x.low) return getSuccessor(x, value); 1005 return x; 1006 } 1007 1008 /** 1009 * Insert <code>newBBLE</code> as a child of parent in our Red/Black tree. 1010 * @param parent the parent node 1011 * @param newBBLE the new child node 1012 * @param left is the child the left or right child of parent? 1013 */ 1014 private void treeInsert(BasicBlockLE parent, BasicBlockLE newBBLE, boolean left) { 1015 if (parent == null) { 1016 if (VM.VerifyAssertions) VM._assert(root == null); 1017 root = newBBLE; 1018 root.setBlack(); 1019 if (DBG_BBSET) db("inserted " + newBBLE + " as root of tree"); 1020 } else { 1021 if (left) { 1022 parent.left = newBBLE; 1023 } else { 1024 parent.right = newBBLE; 1025 } 1026 newBBLE.parent = parent; 1027 if (DBG_BBSET) { 1028 db("inserted new block " + newBBLE + " as " + (left ? "left" : "right") + " child of " + parent); 1029 } 1030 fixupBBSet(newBBLE); 1031 } 1032 } 1033 1034 /** 1035 * Performs tree fixup (restore Red/Black invariants) after adding a 1036 * new node to the tree. 1037 * @param x node that was added. 1038 */ 1039 private void fixupBBSet(BasicBlockLE x) { 1040 if (DBG_BBSET) db("fixing up tree after inserting " + x); 1041 x.setRed(); 1042 while (x != root) { 1043 BasicBlockLE xp = x.parent; 1044 if (xp.isBlack()) { 1045 break; 1046 } 1047 if (DBG_BBSET) db(x + " and its parent " + xp + " are both red"); 1048 BasicBlockLE xpp = xp.parent; 1049 if (DBG_BBSET) db(xp + "'s parent is " + xpp); 1050 if (xp == xpp.left) { 1051 BasicBlockLE y = xpp.right; 1052 if ((y != null) && y.isRed()) { 1053 xp.setBlack(); 1054 y.setBlack(); 1055 xpp.setRed(); 1056 x = xpp; 1057 } else { 1058 if (x == xp.right) { 1059 x = xp; 1060 leftRotateBBSet(xp); 1061 xp = x.parent; 1062 xpp = xp.parent; 1063 } 1064 xp.setBlack(); 1065 xpp.setRed(); 1066 rightRotateBBSet(xpp); 1067 } 1068 } else { 1069 BasicBlockLE y = xpp.left; 1070 if ((y != null) && y.isRed()) { 1071 xp.setBlack(); 1072 y.setBlack(); 1073 xpp.setRed(); 1074 x = xpp; 1075 } else { 1076 if (x == xp.left) { 1077 x = xp; 1078 rightRotateBBSet(xp); 1079 xp = x.parent; 1080 xpp = xp.parent; 1081 } 1082 xp.setBlack(); 1083 xpp.setRed(); 1084 leftRotateBBSet(xpp); 1085 } 1086 } 1087 } 1088 root.setBlack(); 1089 // verifyTree(); 1090 } 1091 1092 private void leftRotateBBSet(BasicBlockLE x) { 1093 if (DBG_BBSET) db("performing left tree rotation"); 1094 BasicBlockLE y = x.right; 1095 BasicBlockLE yl = y.left; 1096 x.right = yl; 1097 if (yl != null) { 1098 yl.parent = x; 1099 } 1100 BasicBlockLE xp = x.parent; 1101 y.parent = xp; 1102 if (xp == null) { 1103 root = y; 1104 } else if (x == xp.left) { 1105 xp.left = y; 1106 } else { 1107 xp.right = y; 1108 } 1109 y.left = x; 1110 x.parent = y; 1111 } 1112 1113 private void rightRotateBBSet(BasicBlockLE x) { 1114 if (DBG_BBSET) db("performing right tree rotation"); 1115 BasicBlockLE y = x.left; 1116 BasicBlockLE yr = y.right; 1117 x.left = yr; 1118 if (yr != null) { 1119 yr.parent = x; 1120 } 1121 BasicBlockLE xp = x.parent; 1122 y.parent = xp; 1123 if (xp == null) { 1124 root = y; 1125 } else if (x == xp.right) { 1126 xp.right = y; 1127 } else { 1128 xp.left = y; 1129 } 1130 y.right = x; 1131 x.parent = y; 1132 } 1133 1134 @SuppressWarnings("unused") 1135 // here for debugging 1136 private void verifyTree() { 1137 if (VM.VerifyAssertions) { 1138 VM._assert(root.isBlack()); 1139 verifyTree(root, -1, bcodes.length()); 1140 countBlack(root); 1141 } 1142 } 1143 1144 private void verifyTree(BasicBlockLE node, int min, int max) { 1145 if (VM.VerifyAssertions) { 1146 VM._assert(node.low >= min); 1147 VM._assert(node.low <= max); 1148 if (node.left != null) { 1149 VM._assert(node.isBlack() || node.left.isBlack()); 1150 VM._assert(node.left.parent == node); 1151 verifyTree(node.left, min, node.low); 1152 } 1153 if (node.right != null) { 1154 VM._assert(node.isBlack() || node.right.isBlack()); 1155 VM._assert(node.right.parent == node); 1156 verifyTree(node.right, node.low, max); 1157 } 1158 } 1159 } 1160 1161 private int countBlack(BasicBlockLE node) { 1162 if (node == null) return 1; 1163 int left = countBlack(node.left); 1164 int right = countBlack(node.right); 1165 if (VM.VerifyAssertions) VM._assert(left == right); 1166 if (node.isBlack()) { 1167 left++; 1168 } 1169 return left; 1170 } 1171 1172 private static final class TreeEnumerator implements Enumeration<BasicBlockLE> { 1173 BasicBlockLE node; 1174 1175 static BBSet.TreeEnumerator enumFromRoot(BasicBlockLE root) { 1176 if (root.left != null) { 1177 do { 1178 root = root.left; 1179 } while (root.left != null); 1180 } 1181 return new TreeEnumerator(root); 1182 } 1183 1184 static BBSet.TreeEnumerator enumFromNode(BasicBlockLE node) { 1185 return new TreeEnumerator(node); 1186 } 1187 1188 private TreeEnumerator(BasicBlockLE node) { 1189 this.node = node; 1190 } 1191 1192 @Override 1193 public boolean hasMoreElements() { 1194 return (node != null); 1195 } 1196 1197 public BasicBlockLE next() { 1198 BasicBlockLE retVal = node; 1199 if (retVal == null) { 1200 throw new NoSuchElementException(); 1201 } 1202 if (retVal.right != null) { 1203 node = retVal.right; 1204 while (node.left != null) { 1205 node = node.left; 1206 } 1207 } else { 1208 BasicBlockLE x = retVal; 1209 node = x.parent; 1210 while ((node != null) && (node.right == x)) { 1211 x = node; 1212 node = x.parent; 1213 } 1214 } 1215 return retVal; 1216 } 1217 1218 @Override 1219 public BasicBlockLE nextElement() { 1220 return next(); 1221 } 1222 } 1223}