Home | History | Annotate | Download | only in code
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dx.cf.code;
     18 
     19 import com.android.dx.rop.code.*;
     20 import com.android.dx.rop.cst.CstInteger;
     21 import com.android.dx.rop.cst.CstType;
     22 import com.android.dx.rop.type.Prototype;
     23 import com.android.dx.rop.type.StdTypeList;
     24 import com.android.dx.rop.type.Type;
     25 import com.android.dx.rop.type.TypeList;
     26 import com.android.dx.util.Bits;
     27 import com.android.dx.util.Hex;
     28 import com.android.dx.util.IntList;
     29 
     30 import java.util.ArrayList;
     31 import java.util.BitSet;
     32 import java.util.HashMap;
     33 
     34 /**
     35  * Utility that converts a basic block list into a list of register-oriented
     36  * blocks.
     37  */
     38 public final class Ropper {
     39     /** label offset for the parameter assignment block */
     40     private static final int PARAM_ASSIGNMENT = -1;
     41 
     42     /** label offset for the return block */
     43     private static final int RETURN = -2;
     44 
     45     /** label offset for the synchronized method final return block */
     46     private static final int SYNCH_RETURN = -3;
     47 
     48     /** label offset for the first synchronized method setup block */
     49     private static final int SYNCH_SETUP_1 = -4;
     50 
     51     /** label offset for the second synchronized method setup block */
     52     private static final int SYNCH_SETUP_2 = -5;
     53 
     54     /**
     55      * label offset for the first synchronized method exception
     56      * handler block
     57      */
     58     private static final int SYNCH_CATCH_1 = -6;
     59 
     60     /**
     61      * label offset for the second synchronized method exception
     62      * handler block
     63      */
     64     private static final int SYNCH_CATCH_2 = -7;
     65 
     66     /** number of special label offsets */
     67     private static final int SPECIAL_LABEL_COUNT = 7;
     68 
     69     /** {@code non-null;} method being converted */
     70     private final ConcreteMethod method;
     71 
     72     /** {@code non-null;} original block list */
     73     private final ByteBlockList blocks;
     74 
     75     /** max locals of the method */
     76     private final int maxLocals;
     77 
     78     /** max label (exclusive) of any original bytecode block */
     79     private final int maxLabel;
     80 
     81     /** {@code non-null;} simulation machine to use */
     82     private final RopperMachine machine;
     83 
     84     /** {@code non-null;} simulator to use */
     85     private final Simulator sim;
     86 
     87     /**
     88      * {@code non-null;} sparse array mapping block labels to initial frame
     89      * contents, if known
     90      */
     91     private final Frame[] startFrames;
     92 
     93     /** {@code non-null;} output block list in-progress */
     94     private final ArrayList<BasicBlock> result;
     95 
     96     /**
     97      * {@code non-null;} list of subroutine-nest labels
     98      * (See {@link Frame#getSubroutines} associated with each result block.
     99      * Parallel to {@link Ropper#result}.
    100      */
    101     private final ArrayList<IntList> resultSubroutines;
    102 
    103     /**
    104      * {@code non-null;} for each block (by label) that is used as an exception
    105      * handler, the type of exception it catches
    106      */
    107     private final Type[] catchTypes;
    108 
    109     /**
    110      * whether an exception-handler block for a synchronized method was
    111      * ever required
    112      */
    113     private boolean synchNeedsExceptionHandler;
    114 
    115     /**
    116      * {@code non-null;} list of subroutines indexed by label of start
    117      * address */
    118     private final Subroutine[] subroutines;
    119 
    120     /** true if {@code subroutines} is non-empty */
    121     private boolean hasSubroutines;
    122 
    123     /**
    124      * Keeps track of subroutines that exist in java form and are inlined in
    125      * Rop form.
    126      */
    127     private class Subroutine {
    128         /** list of all blocks that jsr to this subroutine */
    129         private BitSet callerBlocks;
    130         /** List of all blocks that return from this subroutine */
    131         private BitSet retBlocks;
    132         /** first block in this subroutine */
    133         private int startBlock;
    134 
    135         /**
    136          * Constructs instance.
    137          *
    138          * @param startBlock First block of the subroutine.
    139          */
    140         Subroutine(int startBlock) {
    141             this.startBlock = startBlock;
    142             retBlocks = new BitSet(maxLabel);
    143             callerBlocks = new BitSet(maxLabel);
    144             hasSubroutines = true;
    145         }
    146 
    147         /**
    148          * Constructs instance.
    149          *
    150          * @param startBlock First block of the subroutine.
    151          * @param retBlock one of the ret blocks (final blocks) of this
    152          * subroutine.
    153          */
    154         Subroutine(int startBlock, int retBlock) {
    155             this(startBlock);
    156             addRetBlock(retBlock);
    157         }
    158 
    159         /**
    160          * @return {@code >= 0;} the label of the subroutine's start block.
    161          */
    162         int getStartBlock() {
    163             return startBlock;
    164         }
    165 
    166         /**
    167          * Adds a label to the list of ret blocks (final blocks) for this
    168          * subroutine.
    169          *
    170          * @param retBlock ret block label
    171          */
    172         void addRetBlock(int retBlock) {
    173             retBlocks.set(retBlock);
    174         }
    175 
    176         /**
    177          * Adds a label to the list of caller blocks for this subroutine.
    178          *
    179          * @param label a block that invokes this subroutine.
    180          */
    181         void addCallerBlock(int label) {
    182             callerBlocks.set(label);
    183         }
    184 
    185         /**
    186          * Generates a list of subroutine successors. Note: successor blocks
    187          * could be listed more than once. This is ok, because this successor
    188          * list (and the block it's associated with) will be copied and inlined
    189          * before we leave the ropper. Redundent successors will result in
    190          * redundent (no-op) merges.
    191          *
    192          * @return all currently known successors
    193          * (return destinations) for that subroutine
    194          */
    195         IntList getSuccessors() {
    196             IntList successors = new IntList(callerBlocks.size());
    197 
    198             /*
    199              * For each subroutine caller, get it's target. If the
    200              * target is us, add the ret target (subroutine successor)
    201              * to our list
    202              */
    203 
    204             for (int label = callerBlocks.nextSetBit(0); label >= 0;
    205                  label = callerBlocks.nextSetBit(label+1)) {
    206                 BasicBlock subCaller = labelToBlock(label);
    207                 successors.add(subCaller.getSuccessors().get(0));
    208             }
    209 
    210             successors.setImmutable();
    211 
    212             return successors;
    213         }
    214 
    215         /**
    216          * Merges the specified frame into this subroutine's successors,
    217          * setting {@code workSet} as appropriate. To be called with
    218          * the frame of a subroutine ret block.
    219          *
    220          * @param frame {@code non-null;} frame from ret block to merge
    221          * @param workSet {@code non-null;} workset to update
    222          */
    223         void mergeToSuccessors(Frame frame, int[] workSet) {
    224             for (int label = callerBlocks.nextSetBit(0); label >= 0;
    225                  label = callerBlocks.nextSetBit(label+1)) {
    226                 BasicBlock subCaller = labelToBlock(label);
    227                 int succLabel = subCaller.getSuccessors().get(0);
    228 
    229                 Frame subFrame = frame.subFrameForLabel(startBlock, label);
    230 
    231                 if (subFrame != null) {
    232                     mergeAndWorkAsNecessary(succLabel, -1, null,
    233                             subFrame, workSet);
    234                 } else {
    235                     Bits.set(workSet, label);
    236                 }
    237             }
    238         }
    239     }
    240 
    241     /**
    242      * Converts a {@link ConcreteMethod} to a {@link RopMethod}.
    243      *
    244      * @param method {@code non-null;} method to convert
    245      * @param advice {@code non-null;} translation advice to use
    246      * @return {@code non-null;} the converted instance
    247      */
    248     public static RopMethod convert(ConcreteMethod method,
    249             TranslationAdvice advice) {
    250         try {
    251             Ropper r = new Ropper(method, advice);
    252             r.doit();
    253             return r.getRopMethod();
    254         } catch (SimException ex) {
    255             ex.addContext("...while working on method " +
    256                           method.getNat().toHuman());
    257             throw ex;
    258         }
    259     }
    260 
    261     /**
    262      * Constructs an instance. This class is not publicly instantiable; use
    263      * {@link #convert}.
    264      *
    265      * @param method {@code non-null;} method to convert
    266      * @param advice {@code non-null;} translation advice to use
    267      */
    268     private Ropper(ConcreteMethod method, TranslationAdvice advice) {
    269         if (method == null) {
    270             throw new NullPointerException("method == null");
    271         }
    272 
    273         if (advice == null) {
    274             throw new NullPointerException("advice == null");
    275         }
    276 
    277         this.method = method;
    278         this.blocks = BasicBlocker.identifyBlocks(method);
    279         this.maxLabel = blocks.getMaxLabel();
    280         this.maxLocals = method.getMaxLocals();
    281         this.machine = new RopperMachine(this, method, advice);
    282         this.sim = new Simulator(machine, method);
    283         this.startFrames = new Frame[maxLabel];
    284         this.subroutines = new Subroutine[maxLabel];
    285 
    286         /*
    287          * The "* 2 + 10" below is to conservatively believe that every
    288          * block is an exception handler target and should also
    289          * take care of enough other possible extra overhead such that
    290          * the underlying array is unlikely to need resizing.
    291          */
    292         this.result = new ArrayList<BasicBlock>(blocks.size() * 2 + 10);
    293         this.resultSubroutines =
    294             new ArrayList<IntList>(blocks.size() * 2 + 10);
    295 
    296         this.catchTypes = new Type[maxLabel];
    297         this.synchNeedsExceptionHandler = false;
    298 
    299         /*
    300          * Set up the first stack frame with the right limits, but leave it
    301          * empty here (to be filled in outside of the constructor).
    302          */
    303         startFrames[0] = new Frame(maxLocals, method.getMaxStack());
    304     }
    305 
    306     /**
    307      * Gets the first (lowest) register number to use as the temporary
    308      * area when unwinding stack manipulation ops.
    309      *
    310      * @return {@code >= 0;} the first register to use
    311      */
    312     /*package*/ int getFirstTempStackReg() {
    313         /*
    314          * We use the register that is just past the deepest possible
    315          * stack element, plus one if the method is synchronized to
    316          * avoid overlapping with the synch register. We don't need to
    317          * do anything else special at this level, since later passes
    318          * will merely notice the highest register used by explicit
    319          * inspection.
    320          */
    321         int regCount = getNormalRegCount();
    322         return isSynchronized() ? regCount + 1 : regCount;
    323     }
    324 
    325     /**
    326      * Gets the label for the exception handler setup block corresponding
    327      * to the given label.
    328      *
    329      * @param label {@code >= 0;} the original label
    330      * @return {@code >= 0;} the corresponding exception handler setup label
    331      */
    332     private int getExceptionSetupLabel(int label) {
    333         return maxLabel + label;
    334     }
    335 
    336     /**
    337      * Gets the label for the given special-purpose block. The given label
    338      * should be one of the static constants defined by this class.
    339      *
    340      * @param label {@code < 0;} the special label constant
    341      * @return {@code >= 0;} the actual label value to use
    342      */
    343     private int getSpecialLabel(int label) {
    344         /*
    345          * The label is bitwise-complemented so that mistakes where
    346          * LABEL is used instead of getSpecialLabel(LABEL) cause a
    347          * failure at block construction time, since negative labels
    348          * are illegal. We multiply maxLabel by 2 since 0..maxLabel
    349          * (exclusive) are the original blocks and
    350          * maxLabel..(maxLabel*2) are reserved for exception handler
    351          * setup blocks (see getExceptionSetupLabel(), above).
    352          */
    353         return (maxLabel * 2) + ~label;
    354     }
    355 
    356     /**
    357      * Gets the minimum label for unreserved use.
    358      *
    359      * @return {@code >= 0;} the minimum label
    360      */
    361     private int getMinimumUnreservedLabel() {
    362         /*
    363          * The labels below ((maxLabel * 2) + SPECIAL_LABEL_COUNT) are
    364          * reserved for particular uses.
    365          */
    366 
    367         return (maxLabel * 2) + SPECIAL_LABEL_COUNT;
    368     }
    369 
    370     /**
    371      * Gets an arbitrary unreserved and available label.
    372      *
    373      * @return {@code >= 0;} the label
    374      */
    375     private int getAvailableLabel() {
    376         int candidate = getMinimumUnreservedLabel();
    377 
    378         for (BasicBlock bb : result) {
    379             int label = bb.getLabel();
    380             if (label >= candidate) {
    381                 candidate = label + 1;
    382             }
    383         }
    384 
    385         return candidate;
    386     }
    387 
    388     /**
    389      * Gets whether the method being translated is synchronized.
    390      *
    391      * @return whether the method being translated is synchronized
    392      */
    393     private boolean isSynchronized() {
    394         int accessFlags = method.getAccessFlags();
    395         return (accessFlags & AccessFlags.ACC_SYNCHRONIZED) != 0;
    396     }
    397 
    398     /**
    399      * Gets whether the method being translated is static.
    400      *
    401      * @return whether the method being translated is static
    402      */
    403     private boolean isStatic() {
    404         int accessFlags = method.getAccessFlags();
    405         return (accessFlags & AccessFlags.ACC_STATIC) != 0;
    406     }
    407 
    408     /**
    409      * Gets the total number of registers used for "normal" purposes (i.e.,
    410      * for the straightforward translation from the original Java).
    411      *
    412      * @return {@code >= 0;} the total number of registers used
    413      */
    414     private int getNormalRegCount() {
    415         return maxLocals + method.getMaxStack();
    416     }
    417 
    418     /**
    419      * Gets the register spec to use to hold the object to synchronize on,
    420      * for a synchronized method.
    421      *
    422      * @return {@code non-null;} the register spec
    423      */
    424     private RegisterSpec getSynchReg() {
    425         /*
    426          * We use the register that is just past the deepest possible
    427          * stack element, with a minimum of v1 since v0 is what's
    428          * always used to hold the caught exception when unwinding. We
    429          * don't need to do anything else special at this level, since
    430          * later passes will merely notice the highest register used
    431          * by explicit inspection.
    432          */
    433         int reg = getNormalRegCount();
    434         return RegisterSpec.make((reg < 1) ? 1 : reg, Type.OBJECT);
    435     }
    436 
    437     /**
    438      * Searches {@link #result} for a block with the given label. Returns its
    439      * index if found, or returns {@code -1} if there is no such block.
    440      *
    441      * @param label the label to look for
    442      * @return {@code >= -1;} the index for the block with the given label or
    443      * {@code -1} if there is no such block
    444      */
    445     private int labelToResultIndex(int label) {
    446         int sz = result.size();
    447         for (int i = 0; i < sz; i++) {
    448             BasicBlock one = result.get(i);
    449             if (one.getLabel() == label) {
    450                 return i;
    451             }
    452         }
    453 
    454         return -1;
    455     }
    456 
    457     /**
    458      * Searches {@link #result} for a block with the given label. Returns it if
    459      * found, or throws an exception if there is no such block.
    460      *
    461      * @param label the label to look for
    462      * @return {@code non-null;} the block with the given label
    463      */
    464     private BasicBlock labelToBlock(int label) {
    465         int idx = labelToResultIndex(label);
    466 
    467         if (idx < 0) {
    468             throw new IllegalArgumentException("no such label " +
    469                     Hex.u2(label));
    470         }
    471 
    472         return result.get(idx);
    473     }
    474 
    475     /**
    476      * Adds a block to the output result.
    477      *
    478      * @param block {@code non-null;} the block to add
    479      * @param subroutines {@code non-null;} subroutine label list
    480      * as described in {@link Frame#getSubroutines}
    481      */
    482     private void addBlock(BasicBlock block, IntList subroutines) {
    483         if (block == null) {
    484             throw new NullPointerException("block == null");
    485         }
    486 
    487         result.add(block);
    488         subroutines.throwIfMutable();
    489         resultSubroutines.add(subroutines);
    490     }
    491 
    492     /**
    493      * Adds or replace a block in the output result. If this is a
    494      * replacement, then any extra blocks that got added with the
    495      * original get removed as a result of calling this method.
    496      *
    497      * @param block {@code non-null;} the block to add or replace
    498      * @param subroutines {@code non-null;} subroutine label list
    499      * as described in {@link Frame#getSubroutines}
    500      * @return {@code true} if the block was replaced or
    501      * {@code false} if it was added for the first time
    502      */
    503     private boolean addOrReplaceBlock(BasicBlock block, IntList subroutines) {
    504         if (block == null) {
    505             throw new NullPointerException("block == null");
    506         }
    507 
    508         int idx = labelToResultIndex(block.getLabel());
    509         boolean ret;
    510 
    511         if (idx < 0) {
    512             ret = false;
    513         } else {
    514             /*
    515              * We are replacing a pre-existing block, so find any
    516              * blocks that got added as part of the original and
    517              * remove those too. Such blocks are (possibly indirect)
    518              * successors of this block which are out of the range of
    519              * normally-translated blocks.
    520              */
    521             removeBlockAndSpecialSuccessors(idx);
    522             ret = true;
    523         }
    524 
    525         result.add(block);
    526         subroutines.throwIfMutable();
    527         resultSubroutines.add(subroutines);
    528         return ret;
    529     }
    530 
    531     /**
    532      * Adds or replaces a block in the output result. Do not delete
    533      * any successors.
    534      *
    535      * @param block {@code non-null;} the block to add or replace
    536      * @param subroutines {@code non-null;} subroutine label list
    537      * as described in {@link Frame#getSubroutines}
    538      * @return {@code true} if the block was replaced or
    539      * {@code false} if it was added for the first time
    540      */
    541     private boolean addOrReplaceBlockNoDelete(BasicBlock block,
    542             IntList subroutines) {
    543         if (block == null) {
    544             throw new NullPointerException("block == null");
    545         }
    546 
    547         int idx = labelToResultIndex(block.getLabel());
    548         boolean ret;
    549 
    550         if (idx < 0) {
    551             ret = false;
    552         } else {
    553             result.remove(idx);
    554             resultSubroutines.remove(idx);
    555             ret = true;
    556         }
    557 
    558         result.add(block);
    559         subroutines.throwIfMutable();
    560         resultSubroutines.add(subroutines);
    561         return ret;
    562     }
    563 
    564     /**
    565      * Helper for {@link #addOrReplaceBlock} which recursively removes
    566      * the given block and all blocks that are (direct and indirect)
    567      * successors of it whose labels indicate that they are not in the
    568      * normally-translated range.
    569      *
    570      * @param idx {@code non-null;} block to remove (etc.)
    571      */
    572     private void removeBlockAndSpecialSuccessors(int idx) {
    573         int minLabel = getMinimumUnreservedLabel();
    574         BasicBlock block = result.get(idx);
    575         IntList successors = block.getSuccessors();
    576         int sz = successors.size();
    577 
    578         result.remove(idx);
    579         resultSubroutines.remove(idx);
    580 
    581         for (int i = 0; i < sz; i++) {
    582             int label = successors.get(i);
    583             if (label >= minLabel) {
    584                 idx = labelToResultIndex(label);
    585                 if (idx < 0) {
    586                     throw new RuntimeException("Invalid label "
    587                             + Hex.u2(label));
    588                 }
    589                 removeBlockAndSpecialSuccessors(idx);
    590             }
    591         }
    592     }
    593 
    594     /**
    595      * Extracts the resulting {@link RopMethod} from the instance.
    596      *
    597      * @return {@code non-null;} the method object
    598      */
    599     private RopMethod getRopMethod() {
    600 
    601         // Construct the final list of blocks.
    602 
    603         int sz = result.size();
    604         BasicBlockList bbl = new BasicBlockList(sz);
    605         for (int i = 0; i < sz; i++) {
    606             bbl.set(i, result.get(i));
    607         }
    608         bbl.setImmutable();
    609 
    610         // Construct the method object to wrap it all up.
    611 
    612         /*
    613          * Note: The parameter assignment block is always the first
    614          * that should be executed, hence the second argument to the
    615          * constructor.
    616          */
    617         return new RopMethod(bbl, getSpecialLabel(PARAM_ASSIGNMENT));
    618     }
    619 
    620     /**
    621      * Does the conversion.
    622      */
    623     private void doit() {
    624         int[] workSet = Bits.makeBitSet(maxLabel);
    625 
    626         Bits.set(workSet, 0);
    627         addSetupBlocks();
    628         setFirstFrame();
    629 
    630         for (;;) {
    631             int offset = Bits.findFirst(workSet, 0);
    632             if (offset < 0) {
    633                 break;
    634             }
    635             Bits.clear(workSet, offset);
    636             ByteBlock block = blocks.labelToBlock(offset);
    637             Frame frame = startFrames[offset];
    638             try {
    639                 processBlock(block, frame, workSet);
    640             } catch (SimException ex) {
    641                 ex.addContext("...while working on block " + Hex.u2(offset));
    642                 throw ex;
    643             }
    644         }
    645 
    646         addReturnBlock();
    647         addSynchExceptionHandlerBlock();
    648         addExceptionSetupBlocks();
    649 
    650         if (hasSubroutines) {
    651             // Subroutines are very rare, so skip this step if it's n/a
    652             inlineSubroutines();
    653         }
    654     }
    655 
    656     /**
    657      * Sets up the first frame to contain all the incoming parameters in
    658      * locals.
    659      */
    660     private void setFirstFrame() {
    661         Prototype desc = method.getEffectiveDescriptor();
    662         startFrames[0].initializeWithParameters(desc.getParameterTypes());
    663         startFrames[0].setImmutable();
    664     }
    665 
    666     /**
    667      * Processes the given block.
    668      *
    669      * @param block {@code non-null;} block to process
    670      * @param frame {@code non-null;} start frame for the block
    671      * @param workSet {@code non-null;} bits representing work to do,
    672      * which this method may add to
    673      */
    674     private void processBlock(ByteBlock block, Frame frame, int[] workSet) {
    675         // Prepare the list of caught exceptions for this block.
    676         ByteCatchList catches = block.getCatches();
    677         machine.startBlock(catches.toRopCatchList());
    678 
    679         /*
    680          * Using a copy of the given frame, simulate each instruction,
    681          * calling into machine for each.
    682          */
    683         frame = frame.copy();
    684         sim.simulate(block, frame);
    685         frame.setImmutable();
    686 
    687         int extraBlockCount = machine.getExtraBlockCount();
    688         ArrayList<Insn> insns = machine.getInsns();
    689         int insnSz = insns.size();
    690 
    691         /*
    692          * Merge the frame into each possible non-exceptional
    693          * successor.
    694          */
    695 
    696         int catchSz = catches.size();
    697         IntList successors = block.getSuccessors();
    698 
    699         int startSuccessorIndex;
    700 
    701         Subroutine calledSubroutine = null;
    702         if (machine.hasJsr()) {
    703             /*
    704              * If this frame ends in a JSR, only merge our frame with
    705              * the subroutine start, not the subroutine's return target.
    706              */
    707             startSuccessorIndex = 1;
    708 
    709             int subroutineLabel = successors.get(1);
    710 
    711             if (subroutines[subroutineLabel] == null) {
    712                 subroutines[subroutineLabel] =
    713                     new Subroutine (subroutineLabel);
    714             }
    715 
    716             subroutines[subroutineLabel].addCallerBlock(block.getLabel());
    717 
    718             calledSubroutine = subroutines[subroutineLabel];
    719         } else if (machine.hasRet()) {
    720             /*
    721              * This block ends in a ret, which means it's the final block
    722              * in some subroutine. Ultimately, this block will be copied
    723              * and inlined for each call and then disposed of.
    724              */
    725 
    726             ReturnAddress ra = machine.getReturnAddress();
    727             int subroutineLabel = ra.getSubroutineAddress();
    728 
    729             if (subroutines[subroutineLabel] == null) {
    730                 subroutines[subroutineLabel]
    731                         = new Subroutine (subroutineLabel, block.getLabel());
    732             } else {
    733                 subroutines[subroutineLabel].addRetBlock(block.getLabel());
    734             }
    735 
    736             successors = subroutines[subroutineLabel].getSuccessors();
    737             subroutines[subroutineLabel]
    738                     .mergeToSuccessors(frame, workSet);
    739             // Skip processing below since we just did it.
    740             startSuccessorIndex = successors.size();
    741         } else if (machine.wereCatchesUsed()) {
    742             /*
    743              * If there are catches, then the first successors
    744              * (which will either be all of them or all but the last one)
    745              * are catch targets.
    746              */
    747             startSuccessorIndex = catchSz;
    748         } else {
    749             startSuccessorIndex = 0;
    750         }
    751 
    752         int succSz = successors.size();
    753         for (int i = startSuccessorIndex; i < succSz;
    754              i++) {
    755             int succ = successors.get(i);
    756             try {
    757                 mergeAndWorkAsNecessary(succ, block.getLabel(),
    758                         calledSubroutine, frame, workSet);
    759             } catch (SimException ex) {
    760                 ex.addContext("...while merging to block " + Hex.u2(succ));
    761                 throw ex;
    762             }
    763         }
    764 
    765         if ((succSz == 0) && machine.returns()) {
    766             /*
    767              * The block originally contained a return, but it has
    768              * been made to instead end with a goto, and we need to
    769              * tell it at this point that its sole successor is the
    770              * return block. This has to happen after the merge loop
    771              * above, since, at this point, the return block doesn't
    772              * actually exist; it gets synthesized at the end of
    773              * processing the original blocks.
    774              */
    775             successors = IntList.makeImmutable(getSpecialLabel(RETURN));
    776             succSz = 1;
    777         }
    778 
    779         int primarySucc;
    780 
    781         if (succSz == 0) {
    782             primarySucc = -1;
    783         } else {
    784             primarySucc = machine.getPrimarySuccessorIndex();
    785             if (primarySucc >= 0) {
    786                 primarySucc = successors.get(primarySucc);
    787             }
    788         }
    789 
    790         /*
    791          * This variable is true only when the method is synchronized and
    792          * the block being processed can possibly throw an exception.
    793          */
    794         boolean synch = isSynchronized() && machine.canThrow();
    795 
    796         if (synch || (catchSz != 0)) {
    797             /*
    798              * Deal with exception handlers: Merge an exception-catch
    799              * frame into each possible exception handler, and
    800              * construct a new set of successors to point at the
    801              * exception handler setup blocks (which get synthesized
    802              * at the very end of processing).
    803              */
    804             boolean catchesAny = false;
    805             IntList newSucc = new IntList(succSz);
    806             for (int i = 0; i < catchSz; i++) {
    807                 ByteCatchList.Item one = catches.get(i);
    808                 CstType exceptionClass = one.getExceptionClass();
    809                 int targ = one.getHandlerPc();
    810 
    811                 catchesAny |= (exceptionClass == CstType.OBJECT);
    812 
    813                 Frame f = frame.makeExceptionHandlerStartFrame(exceptionClass);
    814 
    815                 try {
    816                     mergeAndWorkAsNecessary(targ, block.getLabel(),
    817                             null, f, workSet);
    818                 } catch (SimException ex) {
    819                     ex.addContext("...while merging exception to block " +
    820                                   Hex.u2(targ));
    821                     throw ex;
    822                 }
    823 
    824                 /*
    825                  * Set up the exception handler type, by setting it if
    826                  * the given handler has yet to be encountered, or by
    827                  * conservatively unioning if it has.
    828                  */
    829                 Type already = catchTypes[targ];
    830                 if (already == null) {
    831                     catchTypes[targ] = exceptionClass.getClassType();
    832                 } else if (already != exceptionClass.getClassType()) {
    833                     catchTypes[targ] = Type.OBJECT;
    834                 }
    835 
    836                 /*
    837                  * The synthesized exception setup block will have the
    838                  * label getExceptionSetupLabel(targ).
    839                  */
    840                 newSucc.add(getExceptionSetupLabel(targ));
    841             }
    842 
    843             if (synch && !catchesAny) {
    844                 /*
    845                  * The method is synchronized and this block doesn't
    846                  * already have a catch-all handler, so add one to the
    847                  * end, both in the successors and in the throwing
    848                  * instruction(s) at the end of the block (which is where
    849                  * the caught classes live).
    850                  */
    851                 newSucc.add(getSpecialLabel(SYNCH_CATCH_1));
    852                 synchNeedsExceptionHandler = true;
    853 
    854                 for (int i = insnSz - extraBlockCount - 1; i < insnSz; i++) {
    855                     Insn insn = insns.get(i);
    856                     if (insn.canThrow()) {
    857                         insn = insn.withAddedCatch(Type.OBJECT);
    858                         insns.set(i, insn);
    859                     }
    860                 }
    861             }
    862 
    863             if (primarySucc >= 0) {
    864                 newSucc.add(primarySucc);
    865             }
    866 
    867             newSucc.setImmutable();
    868             successors = newSucc;
    869         }
    870 
    871         // Construct the final resulting block(s), and store it (them).
    872 
    873         int primarySuccListIndex = successors.indexOf(primarySucc);
    874 
    875         /*
    876          * If there are any extra blocks, work backwards through the
    877          * list of instructions, adding single-instruction blocks, and
    878          * resetting the successors variables as appropriate.
    879          */
    880         for (/*extraBlockCount*/; extraBlockCount > 0; extraBlockCount--) {
    881             /*
    882              * Some of the blocks that the RopperMachine wants added
    883              * are for move-result insns, and these need goto insns as well.
    884              */
    885             Insn extraInsn = insns.get(--insnSz);
    886             boolean needsGoto
    887                     = extraInsn.getOpcode().getBranchingness()
    888                         == Rop.BRANCH_NONE;
    889             InsnList il = new InsnList(needsGoto ? 2 : 1);
    890             IntList extraBlockSuccessors = successors;
    891 
    892             il.set(0, extraInsn);
    893 
    894             if (needsGoto) {
    895                 il.set(1, new PlainInsn(Rops.GOTO,
    896                         extraInsn.getPosition(), null,
    897                         RegisterSpecList.EMPTY));
    898                 /*
    899                  * Obviously, this block won't be throwing an exception
    900                  * so it should only have one successor.
    901                  */
    902                 extraBlockSuccessors = IntList.makeImmutable(primarySucc);
    903             }
    904             il.setImmutable();
    905 
    906             int label = getAvailableLabel();
    907             BasicBlock bb = new BasicBlock(label, il, extraBlockSuccessors,
    908                     primarySucc);
    909             // All of these extra blocks will be in the same subroutine
    910             addBlock(bb, frame.getSubroutines());
    911 
    912             successors = successors.mutableCopy();
    913             successors.set(primarySuccListIndex, label);
    914             successors.setImmutable();
    915             primarySucc = label;
    916         }
    917 
    918         Insn lastInsn = (insnSz == 0) ? null : insns.get(insnSz - 1);
    919 
    920         /*
    921          * Add a goto to the end of the block if it doesn't already
    922          * end with a branch, to maintain the invariant that all
    923          * blocks end with a branch of some sort or other. Note that
    924          * it is possible for there to be blocks for which no
    925          * instructions were ever output (e.g., only consist of pop*
    926          * in the original Java bytecode).
    927          */
    928         if ((lastInsn == null) ||
    929             (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE)) {
    930             SourcePosition pos = (lastInsn == null) ? SourcePosition.NO_INFO :
    931                 lastInsn.getPosition();
    932             insns.add(new PlainInsn(Rops.GOTO, pos, null,
    933                                     RegisterSpecList.EMPTY));
    934             insnSz++;
    935         }
    936 
    937         /*
    938          * Construct a block for the remaining instructions (which in
    939          * the usual case is all of them).
    940          */
    941 
    942         InsnList il = new InsnList(insnSz);
    943         for (int i = 0; i < insnSz; i++) {
    944             il.set(i, insns.get(i));
    945         }
    946         il.setImmutable();
    947 
    948         BasicBlock bb =
    949             new BasicBlock(block.getLabel(), il, successors, primarySucc);
    950         addOrReplaceBlock(bb, frame.getSubroutines());
    951     }
    952 
    953     /**
    954      * Helper for {@link #processBlock}, which merges frames and
    955      * adds to the work set, as necessary.
    956      *
    957      * @param label {@code >= 0;} label to work on
    958      * @param pred  predecessor label; must be {@code >= 0} when
    959      * {@code label} is a subroutine start block and calledSubroutine
    960      * is non-null. Otherwise, may be -1.
    961      * @param calledSubroutine {@code null-ok;} a Subroutine instance if
    962      * {@code label} is the first block in a subroutine.
    963      * @param frame {@code non-null;} new frame for the labelled block
    964      * @param workSet {@code non-null;} bits representing work to do,
    965      * which this method may add to
    966      */
    967     private void mergeAndWorkAsNecessary(int label, int pred,
    968             Subroutine calledSubroutine, Frame frame, int[] workSet) {
    969         Frame existing = startFrames[label];
    970         Frame merged;
    971 
    972         if (existing != null) {
    973             /*
    974              * Some other block also continues at this label. Merge
    975              * the frames, and re-set the bit in the work set if there
    976              * was a change.
    977              */
    978             if (calledSubroutine != null) {
    979                 merged = existing.mergeWithSubroutineCaller(frame,
    980                         calledSubroutine.getStartBlock(), pred);
    981             } else {
    982                 merged = existing.mergeWith(frame);
    983             }
    984             if (merged != existing) {
    985                 startFrames[label] = merged;
    986                 Bits.set(workSet, label);
    987             }
    988         } else {
    989             // This is the first time this label has been encountered.
    990             if (calledSubroutine != null) {
    991                 startFrames[label]
    992                         = frame.makeNewSubroutineStartFrame(label, pred);
    993             } else {
    994                 startFrames[label] = frame;
    995             }
    996             Bits.set(workSet, label);
    997         }
    998     }
    999 
   1000     /**
   1001      * Constructs and adds the blocks that perform setup for the rest of
   1002      * the method. This includes a first block which merely contains
   1003      * assignments from parameters to the same-numbered registers and
   1004      * a possible second block which deals with synchronization.
   1005      */
   1006     private void addSetupBlocks() {
   1007         LocalVariableList localVariables = method.getLocalVariables();
   1008         SourcePosition pos = method.makeSourcePosistion(0);
   1009         Prototype desc = method.getEffectiveDescriptor();
   1010         StdTypeList params = desc.getParameterTypes();
   1011         int sz = params.size();
   1012         InsnList insns = new InsnList(sz + 1);
   1013         int at = 0;
   1014 
   1015         for (int i = 0; i < sz; i++) {
   1016             Type one = params.get(i);
   1017             LocalVariableList.Item local =
   1018                 localVariables.pcAndIndexToLocal(0, at);
   1019             RegisterSpec result = (local == null) ?
   1020                 RegisterSpec.make(at, one) :
   1021                 RegisterSpec.makeLocalOptional(at, one, local.getLocalItem());
   1022 
   1023             Insn insn = new PlainCstInsn(Rops.opMoveParam(one), pos, result,
   1024                                          RegisterSpecList.EMPTY,
   1025                                          CstInteger.make(at));
   1026             insns.set(i, insn);
   1027             at += one.getCategory();
   1028         }
   1029 
   1030         insns.set(sz, new PlainInsn(Rops.GOTO, pos, null,
   1031                                     RegisterSpecList.EMPTY));
   1032         insns.setImmutable();
   1033 
   1034         boolean synch = isSynchronized();
   1035         int label = synch ? getSpecialLabel(SYNCH_SETUP_1) : 0;
   1036         BasicBlock bb =
   1037             new BasicBlock(getSpecialLabel(PARAM_ASSIGNMENT), insns,
   1038                            IntList.makeImmutable(label), label);
   1039         addBlock(bb, IntList.EMPTY);
   1040 
   1041         if (synch) {
   1042             RegisterSpec synchReg = getSynchReg();
   1043             Insn insn;
   1044             if (isStatic()) {
   1045                 insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
   1046                                            RegisterSpecList.EMPTY,
   1047                                            StdTypeList.EMPTY,
   1048                                            method.getDefiningClass());
   1049                 insns = new InsnList(1);
   1050                 insns.set(0, insn);
   1051             } else {
   1052                 insns = new InsnList(2);
   1053                 insn = new PlainCstInsn(Rops.MOVE_PARAM_OBJECT, pos,
   1054                                         synchReg, RegisterSpecList.EMPTY,
   1055                                         CstInteger.VALUE_0);
   1056                 insns.set(0, insn);
   1057                 insns.set(1, new PlainInsn(Rops.GOTO, pos, null,
   1058                                            RegisterSpecList.EMPTY));
   1059             }
   1060 
   1061             int label2 = getSpecialLabel(SYNCH_SETUP_2);
   1062             insns.setImmutable();
   1063             bb = new BasicBlock(label, insns,
   1064                                 IntList.makeImmutable(label2), label2);
   1065             addBlock(bb, IntList.EMPTY);
   1066 
   1067             insns = new InsnList(isStatic() ? 2 : 1);
   1068 
   1069             if (isStatic()) {
   1070                 insns.set(0, new PlainInsn(Rops.opMoveResultPseudo(synchReg),
   1071                         pos, synchReg, RegisterSpecList.EMPTY));
   1072             }
   1073 
   1074             insn = new ThrowingInsn(Rops.MONITOR_ENTER, pos,
   1075                                     RegisterSpecList.make(synchReg),
   1076                                     StdTypeList.EMPTY);
   1077             insns.set(isStatic() ? 1 :0, insn);
   1078             insns.setImmutable();
   1079             bb = new BasicBlock(label2, insns, IntList.makeImmutable(0), 0);
   1080             addBlock(bb, IntList.EMPTY);
   1081         }
   1082     }
   1083 
   1084     /**
   1085      * Constructs and adds the return block, if necessary. The return
   1086      * block merely contains an appropriate {@code return}
   1087      * instruction.
   1088      */
   1089     private void addReturnBlock() {
   1090         Rop returnOp = machine.getReturnOp();
   1091 
   1092         if (returnOp == null) {
   1093             /*
   1094              * The method being converted never returns normally, so there's
   1095              * no need for a return block.
   1096              */
   1097             return;
   1098         }
   1099 
   1100         SourcePosition returnPos = machine.getReturnPosition();
   1101         int label = getSpecialLabel(RETURN);
   1102 
   1103         if (isSynchronized()) {
   1104             InsnList insns = new InsnList(1);
   1105             Insn insn = new ThrowingInsn(Rops.MONITOR_EXIT, returnPos,
   1106                                          RegisterSpecList.make(getSynchReg()),
   1107                                          StdTypeList.EMPTY);
   1108             insns.set(0, insn);
   1109             insns.setImmutable();
   1110 
   1111             int nextLabel = getSpecialLabel(SYNCH_RETURN);
   1112             BasicBlock bb =
   1113                 new BasicBlock(label, insns,
   1114                                IntList.makeImmutable(nextLabel), nextLabel);
   1115             addBlock(bb, IntList.EMPTY);
   1116 
   1117             label = nextLabel;
   1118         }
   1119 
   1120         InsnList insns = new InsnList(1);
   1121         TypeList sourceTypes = returnOp.getSources();
   1122         RegisterSpecList sources;
   1123 
   1124         if (sourceTypes.size() == 0) {
   1125             sources = RegisterSpecList.EMPTY;
   1126         } else {
   1127             RegisterSpec source = RegisterSpec.make(0, sourceTypes.getType(0));
   1128             sources = RegisterSpecList.make(source);
   1129         }
   1130 
   1131         Insn insn = new PlainInsn(returnOp, returnPos, null, sources);
   1132         insns.set(0, insn);
   1133         insns.setImmutable();
   1134 
   1135         BasicBlock bb = new BasicBlock(label, insns, IntList.EMPTY, -1);
   1136         addBlock(bb, IntList.EMPTY);
   1137     }
   1138 
   1139     /**
   1140      * Constructs and adds, if necessary, the catch-all exception handler
   1141      * block to deal with unwinding the lock taken on entry to a synchronized
   1142      * method.
   1143      */
   1144     private void addSynchExceptionHandlerBlock() {
   1145         if (!synchNeedsExceptionHandler) {
   1146             /*
   1147              * The method being converted either isn't synchronized or
   1148              * can't possibly throw exceptions in its main body, so
   1149              * there's no need for a synchronized method exception
   1150              * handler.
   1151              */
   1152             return;
   1153         }
   1154 
   1155         SourcePosition pos = method.makeSourcePosistion(0);
   1156         RegisterSpec exReg = RegisterSpec.make(0, Type.THROWABLE);
   1157         BasicBlock bb;
   1158         Insn insn;
   1159 
   1160         InsnList insns = new InsnList(2);
   1161         insn = new PlainInsn(Rops.opMoveException(Type.THROWABLE), pos,
   1162                              exReg, RegisterSpecList.EMPTY);
   1163         insns.set(0, insn);
   1164         insn = new ThrowingInsn(Rops.MONITOR_EXIT, pos,
   1165                                 RegisterSpecList.make(getSynchReg()),
   1166                                 StdTypeList.EMPTY);
   1167         insns.set(1, insn);
   1168         insns.setImmutable();
   1169 
   1170         int label2 = getSpecialLabel(SYNCH_CATCH_2);
   1171         bb = new BasicBlock(getSpecialLabel(SYNCH_CATCH_1), insns,
   1172                             IntList.makeImmutable(label2), label2);
   1173         addBlock(bb, IntList.EMPTY);
   1174 
   1175         insns = new InsnList(1);
   1176         insn = new ThrowingInsn(Rops.THROW, pos,
   1177                                 RegisterSpecList.make(exReg),
   1178                                 StdTypeList.EMPTY);
   1179         insns.set(0, insn);
   1180         insns.setImmutable();
   1181 
   1182         bb = new BasicBlock(label2, insns, IntList.EMPTY, -1);
   1183         addBlock(bb, IntList.EMPTY);
   1184     }
   1185 
   1186     /**
   1187      * Creates the exception handler setup blocks. "maxLocals"
   1188      * below is because that's the register number corresponding
   1189      * to the sole element on a one-deep stack (which is the
   1190      * situation at the start of an exception handler block).
   1191      */
   1192     private void addExceptionSetupBlocks() {
   1193 
   1194         int len = catchTypes.length;
   1195         for (int i = 0; i < len; i++) {
   1196             Type one = catchTypes[i];
   1197             if (one != null) {
   1198                 Insn proto = labelToBlock(i).getFirstInsn();
   1199                 SourcePosition pos = proto.getPosition();
   1200                 InsnList il = new InsnList(2);
   1201 
   1202                 Insn insn = new PlainInsn(Rops.opMoveException(one),
   1203                                           pos,
   1204                                           RegisterSpec.make(maxLocals, one),
   1205                                           RegisterSpecList.EMPTY);
   1206                 il.set(0, insn);
   1207 
   1208                 insn = new PlainInsn(Rops.GOTO, pos, null,
   1209                                      RegisterSpecList.EMPTY);
   1210                 il.set(1, insn);
   1211                 il.setImmutable();
   1212 
   1213                 BasicBlock bb = new BasicBlock(getExceptionSetupLabel(i),
   1214                                                il,
   1215                                                IntList.makeImmutable(i),
   1216                                                i);
   1217                 addBlock(bb, startFrames[i].getSubroutines());
   1218             }
   1219         }
   1220     }
   1221 
   1222     /**
   1223      * Checks to see if the basic block is a subroutine caller block.
   1224      *
   1225      * @param bb {@code non-null;} the basic block in question
   1226      * @return true if this block calls a subroutine
   1227      */
   1228     private boolean isSubroutineCaller(BasicBlock bb) {
   1229         IntList successors = bb.getSuccessors();
   1230         if (successors.size() < 2) return false;
   1231 
   1232         int subLabel = successors.get(1);
   1233 
   1234         return (subLabel < subroutines.length)
   1235                 && (subroutines[subLabel] != null);
   1236     }
   1237 
   1238     /**
   1239      * Inlines any subroutine calls.
   1240      */
   1241     private void inlineSubroutines() {
   1242         final IntList reachableSubroutineCallerLabels = new IntList(4);
   1243 
   1244         /*
   1245          * Compile a list of all subroutine calls reachable
   1246          * through the normal (non-subroutine) flow.  We do this first, since
   1247          * we'll be affecting the call flow as we go.
   1248          *
   1249          * Start at label 0 --  the param assignment block has nothing for us
   1250          */
   1251         forEachNonSubBlockDepthFirst(0, new BasicBlock.Visitor() {
   1252             public void visitBlock(BasicBlock b) {
   1253                 if (isSubroutineCaller(b)) {
   1254                     reachableSubroutineCallerLabels.add(b.getLabel());
   1255                 }
   1256             }
   1257         });
   1258 
   1259         /*
   1260          * Convert the resultSubroutines list, indexed by block index,
   1261          * to a label-to-subroutines mapping used by the inliner.
   1262          */
   1263         int largestAllocedLabel = getAvailableLabel();
   1264         ArrayList<IntList> labelToSubroutines
   1265                 = new ArrayList<IntList>(largestAllocedLabel);
   1266         for (int i = 0; i < largestAllocedLabel; i++) {
   1267             labelToSubroutines.add(null);
   1268         }
   1269 
   1270         for (int i = 0; i < result.size(); i++) {
   1271             BasicBlock b = result.get(i);
   1272             if (b == null) {
   1273                 continue;
   1274             }
   1275             IntList subroutineList = resultSubroutines.get(i);
   1276             labelToSubroutines.set(b.getLabel(), subroutineList);
   1277         }
   1278 
   1279         /*
   1280          * Inline all reachable subroutines.
   1281          * Inner subroutines will be inlined as they are encountered.
   1282          */
   1283         int sz = reachableSubroutineCallerLabels.size();
   1284         for (int i = 0 ; i < sz ; i++) {
   1285             int label = reachableSubroutineCallerLabels.get(i);
   1286             new SubroutineInliner(
   1287                     new LabelAllocator(getAvailableLabel()),
   1288                     labelToSubroutines)
   1289                     .inlineSubroutineCalledFrom(labelToBlock(label));
   1290         }
   1291 
   1292         // Now find the blocks that aren't reachable and remove them
   1293         deleteUnreachableBlocks();
   1294     }
   1295 
   1296     /**
   1297      * Deletes all blocks that cannot be reached. This is run to delete
   1298      * original subroutine blocks after subroutine inlining.
   1299      */
   1300     private void deleteUnreachableBlocks() {
   1301         final IntList reachableLabels = new IntList(result.size());
   1302 
   1303         // subroutine inlining is done now and we won't update this list here
   1304         resultSubroutines.clear();
   1305 
   1306         forEachNonSubBlockDepthFirst(getSpecialLabel(PARAM_ASSIGNMENT),
   1307                 new BasicBlock.Visitor() {
   1308 
   1309             public void visitBlock(BasicBlock b) {
   1310                 reachableLabels.add(b.getLabel());
   1311             }
   1312         });
   1313 
   1314         reachableLabels.sort();
   1315 
   1316         for (int i = result.size() - 1 ; i >= 0 ; i--) {
   1317             if (reachableLabels.indexOf(result.get(i).getLabel()) < 0) {
   1318                 result.remove(i);
   1319                 // unnecessary here really, since subroutine inlining is done
   1320                 //resultSubroutines.remove(i);
   1321             }
   1322         }
   1323     }
   1324 
   1325     /**
   1326      * Allocates labels, without requiring previously allocated labels
   1327      * to have been added to the blocks list.
   1328      */
   1329     private static class LabelAllocator {
   1330         int nextAvailableLabel;
   1331 
   1332         /**
   1333          * @param startLabel available label to start allocating from
   1334          */
   1335         LabelAllocator(int startLabel) {
   1336             nextAvailableLabel = startLabel;
   1337         }
   1338 
   1339         /**
   1340          * @return next available label
   1341          */
   1342         int getNextLabel() {
   1343             return nextAvailableLabel++;
   1344         }
   1345     }
   1346 
   1347     /**
   1348      * Inlines a subroutine. Start by calling
   1349      * {@link #inlineSubroutineCalledFrom}.
   1350      */
   1351     private class SubroutineInliner {
   1352         /**
   1353          * maps original label to the label that will be used by the
   1354          * inlined version
   1355          */
   1356         private final HashMap<Integer, Integer> origLabelToCopiedLabel;
   1357 
   1358         /** set of original labels that need to be copied */
   1359         private final BitSet workList;
   1360 
   1361         /** the label of the original start block for this subroutine */
   1362         private int subroutineStart;
   1363 
   1364         /** the label of the ultimate return block */
   1365         private int subroutineSuccessor;
   1366 
   1367         /** used for generating new labels for copied blocks */
   1368         private final LabelAllocator labelAllocator;
   1369 
   1370         /**
   1371          * A mapping, indexed by label, to subroutine nesting list.
   1372          * The subroutine nest list is as returned by
   1373          * {@link Frame#getSubroutines}.
   1374          */
   1375         private final ArrayList<IntList> labelToSubroutines;
   1376 
   1377         SubroutineInliner(final LabelAllocator labelAllocator,
   1378                 ArrayList<IntList> labelToSubroutines) {
   1379             origLabelToCopiedLabel = new HashMap<Integer, Integer>();
   1380 
   1381             workList = new BitSet(maxLabel);
   1382 
   1383             this.labelAllocator = labelAllocator;
   1384             this.labelToSubroutines = labelToSubroutines;
   1385         }
   1386 
   1387         /**
   1388          * Inlines a subroutine.
   1389          *
   1390          * @param b block where {@code jsr} occurred in the original bytecode
   1391          */
   1392         void inlineSubroutineCalledFrom(final BasicBlock b) {
   1393             /*
   1394              * The 0th successor of a subroutine caller block is where
   1395              * the subroutine should return to. The 1st successor is
   1396              * the start block of the subroutine.
   1397              */
   1398             subroutineSuccessor = b.getSuccessors().get(0);
   1399             subroutineStart = b.getSuccessors().get(1);
   1400 
   1401             /*
   1402              * This allocates an initial label and adds the first
   1403              * block to the worklist.
   1404              */
   1405             int newSubStartLabel = mapOrAllocateLabel(subroutineStart);
   1406 
   1407             for (int label = workList.nextSetBit(0); label >= 0;
   1408                  label = workList.nextSetBit(0)) {
   1409                 workList.clear(label);
   1410                 int newLabel = origLabelToCopiedLabel.get(label);
   1411 
   1412                 copyBlock(label, newLabel);
   1413 
   1414                 if (isSubroutineCaller(labelToBlock(label))) {
   1415                     new SubroutineInliner(labelAllocator, labelToSubroutines)
   1416                         .inlineSubroutineCalledFrom(labelToBlock(newLabel));
   1417                 }
   1418             }
   1419 
   1420             /*
   1421              * Replace the original caller block, since we now have a
   1422              * new successor
   1423              */
   1424 
   1425             addOrReplaceBlockNoDelete(
   1426                 new BasicBlock(b.getLabel(), b.getInsns(),
   1427                     IntList.makeImmutable (newSubStartLabel),
   1428                             newSubStartLabel),
   1429                 labelToSubroutines.get(b.getLabel()));
   1430         }
   1431 
   1432         /**
   1433          * Copies a basic block, mapping its successors along the way.
   1434          *
   1435          * @param origLabel original block label
   1436          * @param newLabel label that the new block should have
   1437          */
   1438         private void copyBlock(int origLabel, int newLabel) {
   1439 
   1440             BasicBlock origBlock = labelToBlock(origLabel);
   1441 
   1442             final IntList origSuccessors = origBlock.getSuccessors();
   1443             IntList successors;
   1444             int primarySuccessor = -1;
   1445             Subroutine subroutine;
   1446 
   1447             if (isSubroutineCaller(origBlock)) {
   1448                 /*
   1449                  * A subroutine call inside a subroutine call.
   1450                  * Set up so we can recurse. The caller block should have
   1451                  * it's first successor be a copied block that will be
   1452                  * the subroutine's return point. It's second successor will
   1453                  * be copied when we recurse, and remains as the original
   1454                  * label of the start of the inner subroutine.
   1455                  */
   1456 
   1457                 successors = IntList.makeImmutable(
   1458                         mapOrAllocateLabel(origSuccessors.get(0)),
   1459                         origSuccessors.get(1));
   1460                 // primary successor will be set when this block is replaced
   1461             } else if (null
   1462                     != (subroutine = subroutineFromRetBlock(origLabel))) {
   1463                 /*
   1464                  * this is a ret block -- its successor
   1465                  * should be subroutineSuccessor
   1466                  */
   1467 
   1468                 // Sanity check
   1469                 if (subroutine.startBlock != subroutineStart) {
   1470                     throw new RuntimeException (
   1471                             "ret instruction returns to label "
   1472                             + Hex.u2 (subroutine.startBlock)
   1473                             + " expected: " + Hex.u2(subroutineStart));
   1474                 }
   1475 
   1476                 successors = IntList.makeImmutable(subroutineSuccessor);
   1477                 primarySuccessor = subroutineSuccessor;
   1478             } else {
   1479                 // Map all the successor labels
   1480 
   1481                 int origPrimary = origBlock.getPrimarySuccessor();
   1482                 int sz = origSuccessors.size();
   1483 
   1484                 successors = new IntList(sz);
   1485 
   1486                 for (int i = 0 ; i < sz ; i++) {
   1487                     int origSuccLabel = origSuccessors.get(i);
   1488                     int newSuccLabel =  mapOrAllocateLabel(origSuccLabel);
   1489 
   1490                     successors.add(newSuccLabel);
   1491 
   1492                     if (origPrimary == origSuccLabel) {
   1493                         primarySuccessor = newSuccLabel;
   1494                     }
   1495                 }
   1496 
   1497                 successors.setImmutable();
   1498             }
   1499 
   1500             addBlock (
   1501                 new BasicBlock(newLabel,
   1502                     filterMoveReturnAddressInsns(origBlock.getInsns()),
   1503                     successors, primarySuccessor),
   1504                     labelToSubroutines.get(newLabel));
   1505         }
   1506 
   1507         /**
   1508          * Checks to see if a specified label is involved in a specified
   1509          * subroutine.
   1510          *
   1511          * @param label {@code >= 0;} a basic block label
   1512          * @param subroutineStart {@code >= 0;} a subroutine as identified
   1513          * by the label of its start block
   1514          * @return true if the block is dominated by the subroutine call
   1515          */
   1516         private boolean involvedInSubroutine(int label, int subroutineStart) {
   1517             IntList subroutinesList = labelToSubroutines.get(label);
   1518             return (subroutinesList != null && subroutinesList.size() > 0
   1519                     && subroutinesList.top() == subroutineStart);
   1520         }
   1521 
   1522         /**
   1523          * Maps the label of a pre-copied block to the label of the inlined
   1524          * block, allocating a new label and adding it to the worklist
   1525          * if necessary.  If the origLabel is a "special" label, it
   1526          * is returned exactly and not scheduled for duplication: copying
   1527          * never proceeds past a special label, which likely is the function
   1528          * return block or an immediate predecessor.
   1529          *
   1530          * @param origLabel label of original, pre-copied block
   1531          * @return label for new, inlined block
   1532          */
   1533         private int mapOrAllocateLabel(int origLabel) {
   1534             int resultLabel;
   1535             Integer mappedLabel = origLabelToCopiedLabel.get(origLabel);
   1536 
   1537             if (mappedLabel != null) {
   1538                 resultLabel = mappedLabel;
   1539             } else if (!involvedInSubroutine(origLabel,subroutineStart)) {
   1540                 /*
   1541                  * A subroutine has ended by some means other than a "ret"
   1542                  * (which really means a throw caught later).
   1543                  */
   1544                 resultLabel = origLabel;
   1545             } else {
   1546                 resultLabel = labelAllocator.getNextLabel();
   1547                 workList.set(origLabel);
   1548                 origLabelToCopiedLabel.put(origLabel, resultLabel);
   1549 
   1550                 // The new label has the same frame as the original label
   1551                 while (labelToSubroutines.size() <= resultLabel) {
   1552                     labelToSubroutines.add(null);
   1553                 }
   1554                 labelToSubroutines.set(resultLabel,
   1555                         labelToSubroutines.get(origLabel));
   1556             }
   1557 
   1558             return resultLabel;
   1559         }
   1560     }
   1561 
   1562     /**
   1563      * Finds a {@code Subroutine} that is returned from by a {@code ret} in
   1564      * a given block.
   1565      *
   1566      * @param label A block that originally contained a {@code ret} instruction
   1567      * @return {@code null-ok;} found subroutine or {@code null} if none
   1568      * was found
   1569      */
   1570     private Subroutine subroutineFromRetBlock(int label) {
   1571         for (int i = subroutines.length - 1 ; i >= 0 ; i--) {
   1572             if (subroutines[i] != null) {
   1573                 Subroutine subroutine = subroutines[i];
   1574 
   1575                 if (subroutine.retBlocks.get(label)) {
   1576                     return subroutine;
   1577                 }
   1578             }
   1579         }
   1580 
   1581         return null;
   1582     }
   1583 
   1584 
   1585     /**
   1586      * Removes all {@code move-return-address} instructions, returning a new
   1587      * {@code InsnList} if necessary. The {@code move-return-address}
   1588      * insns are dead code after subroutines have been inlined.
   1589      *
   1590      * @param insns {@code InsnList} that may contain
   1591      * {@code move-return-address} insns
   1592      * @return {@code InsnList} with {@code move-return-address} removed
   1593      */
   1594     private InsnList filterMoveReturnAddressInsns(InsnList insns) {
   1595         int sz;
   1596         int newSz = 0;
   1597 
   1598         // First see if we need to filter, and if so what the new size will be
   1599         sz = insns.size();
   1600         for (int i = 0; i < sz; i++) {
   1601             if (insns.get(i).getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
   1602                 newSz++;
   1603             }
   1604         }
   1605 
   1606         if (newSz == sz) {
   1607             return insns;
   1608         }
   1609 
   1610         // Make a new list without the MOVE_RETURN_ADDRESS insns
   1611         InsnList newInsns = new InsnList(newSz);
   1612 
   1613         int newIndex = 0;
   1614         for (int i = 0; i < sz; i++) {
   1615             Insn insn = insns.get(i);
   1616             if (insn.getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
   1617                 newInsns.set(newIndex++, insn);
   1618             }
   1619         }
   1620 
   1621         newInsns.setImmutable();
   1622         return newInsns;
   1623     }
   1624 
   1625     /**
   1626      * Visits each non-subroutine block once in depth-first successor order.
   1627      *
   1628      * @param firstLabel label of start block
   1629      * @param v callback interface
   1630      */
   1631     private void forEachNonSubBlockDepthFirst(int firstLabel,
   1632             BasicBlock.Visitor v) {
   1633         forEachNonSubBlockDepthFirst0(labelToBlock(firstLabel),
   1634                 v, new BitSet(maxLabel));
   1635     }
   1636 
   1637     /**
   1638      * Visits each block once in depth-first successor order, ignoring
   1639      * {@code jsr} targets. Worker for {@link #forEachNonSubBlockDepthFirst}.
   1640      *
   1641      * @param next next block to visit
   1642      * @param v callback interface
   1643      * @param visited set of blocks already visited
   1644      */
   1645     private void forEachNonSubBlockDepthFirst0(
   1646             BasicBlock next, BasicBlock.Visitor v, BitSet visited) {
   1647         v.visitBlock(next);
   1648         visited.set(next.getLabel());
   1649 
   1650         IntList successors = next.getSuccessors();
   1651         int sz = successors.size();
   1652 
   1653         for (int i = 0; i < sz; i++) {
   1654             int succ = successors.get(i);
   1655 
   1656             if (visited.get(succ)) {
   1657                 continue;
   1658             }
   1659 
   1660             if (isSubroutineCaller(next) && i > 0) {
   1661                 // ignore jsr targets
   1662                 continue;
   1663             }
   1664 
   1665             /*
   1666              * Ignore missing labels: they're successors of
   1667              * subroutines that never invoke a ret.
   1668              */
   1669             int idx = labelToResultIndex(succ);
   1670             if (idx >= 0) {
   1671                 forEachNonSubBlockDepthFirst0(result.get(idx), v, visited);
   1672             }
   1673         }
   1674     }
   1675 }
   1676