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}