Ropper.java revision c51439a513d4cc3c2be4a7cce7b3e9ae480fd5c2
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
17package com.android.dx.cf.code;
18
19import com.android.dx.rop.code.*;
20import com.android.dx.rop.cst.CstInteger;
21import com.android.dx.rop.cst.CstType;
22import com.android.dx.rop.type.Prototype;
23import com.android.dx.rop.type.StdTypeList;
24import com.android.dx.rop.type.Type;
25import com.android.dx.rop.type.TypeList;
26import com.android.dx.util.Bits;
27import com.android.dx.util.Hex;
28import com.android.dx.util.IntList;
29
30import java.util.ArrayList;
31import java.util.BitSet;
32import java.util.HashMap;
33
34/**
35 * Utility that converts a basic block list into a list of register-oriented
36 * blocks.
37 */
38public 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.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