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.dex.code;
18
19import com.android.dx.dex.DexOptions;
20import com.android.dx.io.Opcodes;
21import com.android.dx.rop.code.BasicBlock;
22import com.android.dx.rop.code.BasicBlockList;
23import com.android.dx.rop.code.FillArrayDataInsn;
24import com.android.dx.rop.code.Insn;
25import com.android.dx.rop.code.InvokePolymorphicInsn;
26import com.android.dx.rop.code.LocalVariableInfo;
27import com.android.dx.rop.code.PlainCstInsn;
28import com.android.dx.rop.code.PlainInsn;
29import com.android.dx.rop.code.RegOps;
30import com.android.dx.rop.code.RegisterSpec;
31import com.android.dx.rop.code.RegisterSpecList;
32import com.android.dx.rop.code.RegisterSpecSet;
33import com.android.dx.rop.code.Rop;
34import com.android.dx.rop.code.RopMethod;
35import com.android.dx.rop.code.SourcePosition;
36import com.android.dx.rop.code.SwitchInsn;
37import com.android.dx.rop.code.ThrowingCstInsn;
38import com.android.dx.rop.code.ThrowingInsn;
39import com.android.dx.rop.cst.Constant;
40import com.android.dx.rop.cst.CstInteger;
41import com.android.dx.util.Bits;
42import com.android.dx.util.IntList;
43import java.util.ArrayList;
44
45/**
46 * Translator from {@link RopMethod} to {@link DalvCode}. The {@link
47 * #translate} method is the thing to call on this class.
48 */
49public final class RopTranslator {
50    /** {@code non-null;} options for dex output */
51    private final DexOptions dexOptions;
52
53    /** {@code non-null;} method to translate */
54    private final RopMethod method;
55
56    /**
57     * how much position info to preserve; one of the static
58     * constants in {@link PositionList}
59     */
60    private final int positionInfo;
61
62    /** {@code null-ok;} local variable info to use */
63    private final LocalVariableInfo locals;
64
65    /** {@code non-null;} container for all the address objects for the method */
66    private final BlockAddresses addresses;
67
68    /** {@code non-null;} list of output instructions in-progress */
69    private final OutputCollector output;
70
71    /** {@code non-null;} visitor to use during translation */
72    private final TranslationVisitor translationVisitor;
73
74    /** {@code >= 0;} register count for the method */
75    private final int regCount;
76
77    /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */
78    private int[] order;
79
80    /** size, in register units, of all the parameters to this method */
81    private final int paramSize;
82
83    /**
84     * true if the parameters to this method happen to be in proper order
85     * at the end of the frame (as the optimizer emits them)
86     */
87    private final boolean paramsAreInOrder;
88
89    /**
90     * Translates a {@link RopMethod}. This may modify the given
91     * input.
92     *
93     * @param method {@code non-null;} the original method
94     * @param positionInfo how much position info to preserve; one of the
95     * static constants in {@link PositionList}
96     * @param locals {@code null-ok;} local variable information to use
97     * @param paramSize size, in register units, of all the parameters to
98     * this method
99     * @param dexOptions {@code non-null;} options for dex output
100     * @return {@code non-null;} the translated version
101     */
102    public static DalvCode translate(RopMethod method, int positionInfo,
103            LocalVariableInfo locals, int paramSize, DexOptions dexOptions) {
104        RopTranslator translator =
105            new RopTranslator(method, positionInfo, locals, paramSize, dexOptions);
106        return translator.translateAndGetResult();
107    }
108
109    /**
110     * Constructs an instance. This method is private. Use {@link #translate}.
111     *
112     * @param method {@code non-null;} the original method
113     * @param positionInfo how much position info to preserve; one of the
114     * static constants in {@link PositionList}
115     * @param locals {@code null-ok;} local variable information to use
116     * @param paramSize size, in register units, of all the parameters to
117     * this method
118     * @param dexOptions {@code non-null;} options for dex output
119     */
120    private RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals,
121            int paramSize, DexOptions dexOptions) {
122        this.dexOptions = dexOptions;
123        this.method = method;
124        this.positionInfo = positionInfo;
125        this.locals = locals;
126        this.addresses = new BlockAddresses(method);
127        this.paramSize = paramSize;
128        this.order = null;
129        this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize);
130
131        BasicBlockList blocks = method.getBlocks();
132        int bsz = blocks.size();
133
134        /*
135         * Max possible instructions includes three code address
136         * objects per basic block (to the first and last instruction,
137         * and just past the end of the block), and the possibility of
138         * an extra goto at the end of each basic block.
139         */
140        int maxInsns = (bsz * 3) + blocks.getInstructionCount();
141
142        if (locals != null) {
143            /*
144             * If we're tracking locals, then there's could be another
145             * extra instruction per block (for the locals state at the
146             * start of the block) as well as one for each interblock
147             * local introduction.
148             */
149            maxInsns += bsz + locals.getAssignmentCount();
150        }
151
152        /*
153         * If params are not in order, we will need register space
154         * for them before this is all over...
155         */
156        this.regCount = blocks.getRegCount()
157                + (paramsAreInOrder ? 0 : this.paramSize);
158
159        this.output = new OutputCollector(dexOptions, maxInsns, bsz * 3, regCount, paramSize);
160
161        if (locals != null) {
162            this.translationVisitor =
163                new LocalVariableAwareTranslationVisitor(output, locals);
164        } else {
165            this.translationVisitor = new TranslationVisitor(output);
166        }
167    }
168
169    /**
170     * Checks to see if the move-param instructions that occur in this
171     * method happen to slot the params in an order at the top of the
172     * stack frame that matches dalvik's calling conventions. This will
173     * alway result in "true" for methods that have run through the
174     * SSA optimizer.
175     *
176     * @param paramSize size, in register units, of all the parameters
177     * to this method
178     */
179    private static boolean calculateParamsAreInOrder(RopMethod method,
180            final int paramSize) {
181        final boolean[] paramsAreInOrder = { true };
182        final int initialRegCount = method.getBlocks().getRegCount();
183
184        /*
185         * We almost could just check the first block here, but the
186         * {@code cf} layer will put in a second move-param in a
187         * subsequent block in the case of synchronized methods.
188         */
189        method.getBlocks().forEachInsn(new Insn.BaseVisitor() {
190            @Override
191            public void visitPlainCstInsn(PlainCstInsn insn) {
192                if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) {
193                    int param =
194                        ((CstInteger) insn.getConstant()).getValue();
195
196                    paramsAreInOrder[0] = paramsAreInOrder[0]
197                            && ((initialRegCount - paramSize + param)
198                                == insn.getResult().getReg());
199                }
200            }
201        });
202
203        return paramsAreInOrder[0];
204    }
205
206    /**
207     * Does the translation and returns the result.
208     *
209     * @return {@code non-null;} the result
210     */
211    private DalvCode translateAndGetResult() {
212        pickOrder();
213        outputInstructions();
214
215        StdCatchBuilder catches =
216            new StdCatchBuilder(method, order, addresses);
217
218        return new DalvCode(positionInfo, output.getFinisher(), catches);
219    }
220
221    /**
222     * Performs initial creation of output instructions based on the
223     * original blocks.
224     */
225    private void outputInstructions() {
226        BasicBlockList blocks = method.getBlocks();
227        int[] order = this.order;
228        int len = order.length;
229
230        // Process the blocks in output order.
231        for (int i = 0; i < len; i++) {
232            int nextI = i + 1;
233            int nextLabel = (nextI == order.length) ? -1 : order[nextI];
234            outputBlock(blocks.labelToBlock(order[i]), nextLabel);
235        }
236    }
237
238    /**
239     * Helper for {@link #outputInstructions}, which does the processing
240     * and output of one block.
241     *
242     * @param block {@code non-null;} the block to process and output
243     * @param nextLabel {@code >= -1;} the next block that will be processed, or
244     * {@code -1} if there is no next block
245     */
246    private void outputBlock(BasicBlock block, int nextLabel) {
247        // Append the code address for this block.
248        CodeAddress startAddress = addresses.getStart(block);
249        output.add(startAddress);
250
251        // Append the local variable state for the block.
252        if (locals != null) {
253            RegisterSpecSet starts = locals.getStarts(block);
254            output.add(new LocalSnapshot(startAddress.getPosition(),
255                                         starts));
256        }
257
258        /*
259         * Choose and append an output instruction for each original
260         * instruction.
261         */
262        translationVisitor.setBlock(block, addresses.getLast(block));
263        block.getInsns().forEach(translationVisitor);
264
265        // Insert the block end code address.
266        output.add(addresses.getEnd(block));
267
268        // Set up for end-of-block activities.
269
270        int succ = block.getPrimarySuccessor();
271        Insn lastInsn = block.getLastInsn();
272
273        /*
274         * Check for (and possibly correct for) a non-optimal choice of
275         * which block will get output next.
276         */
277
278        if ((succ >= 0) && (succ != nextLabel)) {
279            /*
280             * The block has a "primary successor" and that primary
281             * successor isn't the next block to be output.
282             */
283            Rop lastRop = lastInsn.getOpcode();
284            if ((lastRop.getBranchingness() == Rop.BRANCH_IF) &&
285                    (block.getSecondarySuccessor() == nextLabel)) {
286                /*
287                 * The block ends with an "if" of some sort, and its
288                 * secondary successor (the "then") is in fact the
289                 * next block to output. So, reverse the sense of
290                 * the test, so that we can just emit the next block
291                 * without an interstitial goto.
292                 */
293                output.reverseBranch(1, addresses.getStart(succ));
294            } else {
295                /*
296                 * Our only recourse is to add a goto here to get the
297                 * flow to be correct.
298                 */
299                TargetInsn insn =
300                    new TargetInsn(Dops.GOTO, lastInsn.getPosition(),
301                            RegisterSpecList.EMPTY,
302                            addresses.getStart(succ));
303                output.add(insn);
304            }
305        }
306    }
307
308    /**
309     * Picks an order for the blocks by doing "trace" analysis.
310     */
311    private void pickOrder() {
312        BasicBlockList blocks = method.getBlocks();
313        int sz = blocks.size();
314        int maxLabel = blocks.getMaxLabel();
315        int[] workSet = Bits.makeBitSet(maxLabel);
316        int[] tracebackSet = Bits.makeBitSet(maxLabel);
317
318        for (int i = 0; i < sz; i++) {
319            BasicBlock one = blocks.get(i);
320            Bits.set(workSet, one.getLabel());
321        }
322
323        int[] order = new int[sz];
324        int at = 0;
325
326        /*
327         * Starting with the designated "first label" (that is, the
328         * first block of the method), add that label to the order,
329         * and then pick its first as-yet unordered successor to
330         * immediately follow it, giving top priority to the primary
331         * (aka default) successor (if any). Keep following successors
332         * until the trace runs out of possibilities. Then, continue
333         * by finding an unordered chain containing the first as-yet
334         * unordered block, and adding it to the order, and so on.
335         */
336        for (int label = method.getFirstLabel();
337             label != -1;
338             label = Bits.findFirst(workSet, 0)) {
339
340            /*
341             * Attempt to trace backward from the chosen block to an
342             * as-yet unordered predecessor which lists the chosen
343             * block as its primary successor, and so on, until we
344             * fail to find such an unordered predecessor. Start the
345             * trace with that block. Note that the first block in the
346             * method has no predecessors, so in that case this loop
347             * will simply terminate with zero iterations and without
348             * picking a new starter block.
349             */
350            traceBack:
351            for (;;) {
352                IntList preds = method.labelToPredecessors(label);
353                int psz = preds.size();
354
355                for (int i = 0; i < psz; i++) {
356                    int predLabel = preds.get(i);
357
358                    if (Bits.get(tracebackSet, predLabel)) {
359                        /*
360                         * We found a predecessor loop; stop tracing back
361                         * from here.
362                         */
363                        break;
364                    }
365
366                    if (!Bits.get(workSet, predLabel)) {
367                        // This one's already ordered.
368                        continue;
369                    }
370
371                    BasicBlock pred = blocks.labelToBlock(predLabel);
372                    if (pred.getPrimarySuccessor() == label) {
373                        // Found one!
374                        label = predLabel;
375                        Bits.set(tracebackSet, label);
376                        continue traceBack;
377                    }
378                }
379
380                // Failed to find a better block to start the trace.
381                break;
382            }
383
384            /*
385             * Trace a path from the chosen block to one of its
386             * unordered successors (hopefully the primary), and so
387             * on, until we run out of unordered successors.
388             */
389            while (label != -1) {
390                Bits.clear(workSet, label);
391                Bits.clear(tracebackSet, label);
392                order[at] = label;
393                at++;
394
395                BasicBlock one = blocks.labelToBlock(label);
396                BasicBlock preferredBlock = blocks.preferredSuccessorOf(one);
397
398                if (preferredBlock == null) {
399                    break;
400                }
401
402                int preferred = preferredBlock.getLabel();
403                int primary = one.getPrimarySuccessor();
404
405                if (Bits.get(workSet, preferred)) {
406                    /*
407                     * Order the current block's preferred successor
408                     * next, as it has yet to be scheduled.
409                     */
410                    label = preferred;
411                } else if ((primary != preferred) && (primary >= 0)
412                        && Bits.get(workSet, primary)) {
413                    /*
414                     * The primary is available, so use that.
415                     */
416                    label = primary;
417                } else {
418                    /*
419                     * There's no obvious candidate, so pick the first
420                     * one that's available, if any.
421                     */
422                    IntList successors = one.getSuccessors();
423                    int ssz = successors.size();
424                    label = -1;
425                    for (int i = 0; i < ssz; i++) {
426                        int candidate = successors.get(i);
427                        if (Bits.get(workSet, candidate)) {
428                            label = candidate;
429                            break;
430                        }
431                    }
432                }
433            }
434        }
435
436        if (at != sz) {
437            // There was a duplicate block label.
438            throw new RuntimeException("shouldn't happen");
439        }
440
441        this.order = order;
442    }
443
444    /**
445     * Gets the complete register list (result and sources) out of a
446     * given rop instruction. For insns that are commutative, have
447     * two register sources, and have a source equal to the result,
448     * place that source first.
449     *
450     * @param insn {@code non-null;} instruction in question
451     * @return {@code non-null;} the instruction's complete register list
452     */
453    private static RegisterSpecList getRegs(Insn insn) {
454        return getRegs(insn, insn.getResult());
455    }
456
457    /**
458     * Gets the complete register list (result and sources) out of a
459     * given rop instruction. For insns that are commutative, have
460     * two register sources, and have a source equal to the result,
461     * place that source first.
462     *
463     * @param insn {@code non-null;} instruction in question
464     * @param resultReg {@code null-ok;} the real result to use (ignore the insn's)
465     * @return {@code non-null;} the instruction's complete register list
466     */
467    private static RegisterSpecList getRegs(Insn insn,
468            RegisterSpec resultReg) {
469        RegisterSpecList regs = insn.getSources();
470
471        if (insn.getOpcode().isCommutative()
472                && (regs.size() == 2)
473                && (resultReg.getReg() == regs.get(1).getReg())) {
474
475            /*
476             * For commutative ops which have two register sources,
477             * if the second source is the same register as the result,
478             * swap the sources so that an opcode of form 12x can be selected
479             * instead of one of form 23x
480             */
481
482            regs = RegisterSpecList.make(regs.get(1), regs.get(0));
483        }
484
485        if (resultReg == null) {
486            return regs;
487        }
488
489        return regs.withFirst(resultReg);
490    }
491
492    /**
493     * Instruction visitor class for doing the instruction translation per se.
494     */
495    private class TranslationVisitor implements Insn.Visitor {
496        /** {@code non-null;} list of output instructions in-progress */
497        private final OutputCollector output;
498
499        /** {@code non-null;} basic block being worked on */
500        private BasicBlock block;
501
502        /**
503         * {@code null-ok;} code address for the salient last instruction of the
504         * block (used before switches and throwing instructions)
505         */
506        private CodeAddress lastAddress;
507
508        /**
509         * Constructs an instance.
510         *
511         * @param output {@code non-null;} destination for instruction output
512         */
513        public TranslationVisitor(OutputCollector output) {
514            this.output = output;
515        }
516
517        /**
518         * Sets the block currently being worked on.
519         *
520         * @param block {@code non-null;} the block
521         * @param lastAddress {@code non-null;} code address for the salient
522         * last instruction of the block
523         */
524        public void setBlock(BasicBlock block, CodeAddress lastAddress) {
525            this.block = block;
526            this.lastAddress = lastAddress;
527        }
528
529        /** {@inheritDoc} */
530        @Override
531        public void visitPlainInsn(PlainInsn insn) {
532            Rop rop = insn.getOpcode();
533            if (rop.getOpcode() == RegOps.MARK_LOCAL) {
534                /*
535                 * Ignore these. They're dealt with by
536                 * the LocalVariableAwareTranslationVisitor
537                 */
538                return;
539            }
540            if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
541                // These get skipped
542                return;
543            }
544
545            SourcePosition pos = insn.getPosition();
546            Dop opcode = RopToDop.dopFor(insn);
547            DalvInsn di;
548
549            switch (rop.getBranchingness()) {
550                case Rop.BRANCH_NONE:
551                case Rop.BRANCH_RETURN:
552                case Rop.BRANCH_THROW: {
553                    di = new SimpleInsn(opcode, pos, getRegs(insn));
554                    break;
555                }
556                case Rop.BRANCH_GOTO: {
557                    /*
558                     * Code in the main translation loop will emit a
559                     * goto if necessary (if the branch isn't to the
560                     * immediately subsequent block).
561                     */
562                    return;
563                }
564                case Rop.BRANCH_IF: {
565                    int target = block.getSuccessors().get(1);
566                    di = new TargetInsn(opcode, pos, getRegs(insn),
567                                        addresses.getStart(target));
568                    break;
569                }
570                default: {
571                    throw new RuntimeException("shouldn't happen");
572                }
573            }
574
575            addOutput(di);
576        }
577
578        /** {@inheritDoc} */
579        @Override
580        public void visitPlainCstInsn(PlainCstInsn insn) {
581            SourcePosition pos = insn.getPosition();
582            Dop opcode = RopToDop.dopFor(insn);
583            Rop rop = insn.getOpcode();
584            int ropOpcode = rop.getOpcode();
585            DalvInsn di;
586
587            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
588                throw new RuntimeException("shouldn't happen");
589            }
590
591            if (ropOpcode == RegOps.MOVE_PARAM) {
592                if (!paramsAreInOrder) {
593                    /*
594                     * Parameters are not in order at the top of the reg space.
595                     * We need to add moves.
596                     */
597
598                    RegisterSpec dest = insn.getResult();
599                    int param =
600                        ((CstInteger) insn.getConstant()).getValue();
601                    RegisterSpec source =
602                        RegisterSpec.make(regCount - paramSize + param,
603                                dest.getType());
604                    di = new SimpleInsn(opcode, pos,
605                                        RegisterSpecList.make(dest, source));
606                    addOutput(di);
607                }
608            } else {
609                // No moves required for the parameters
610                RegisterSpecList regs = getRegs(insn);
611                di = new CstInsn(opcode, pos, regs, insn.getConstant());
612                addOutput(di);
613            }
614        }
615
616        /** {@inheritDoc} */
617        @Override
618        public void visitSwitchInsn(SwitchInsn insn) {
619            SourcePosition pos = insn.getPosition();
620            IntList cases = insn.getCases();
621            IntList successors = block.getSuccessors();
622            int casesSz = cases.size();
623            int succSz = successors.size();
624            int primarySuccessor = block.getPrimarySuccessor();
625
626            /*
627             * Check the assumptions that the number of cases is one
628             * less than the number of successors and that the last
629             * successor in the list is the primary (in this case, the
630             * default). This test is here to guard against forgetting
631             * to change this code if the way switch instructions are
632             * constructed also gets changed.
633             */
634            if ((casesSz != (succSz - 1)) ||
635                (primarySuccessor != successors.get(casesSz))) {
636                throw new RuntimeException("shouldn't happen");
637            }
638
639            CodeAddress[] switchTargets = new CodeAddress[casesSz];
640
641            for (int i = 0; i < casesSz; i++) {
642                int label = successors.get(i);
643                switchTargets[i] = addresses.getStart(label);
644            }
645
646            CodeAddress dataAddress = new CodeAddress(pos);
647            // make a new address that binds closely to the switch instruction
648            CodeAddress switchAddress =
649                new CodeAddress(lastAddress.getPosition(), true);
650            SwitchData dataInsn =
651                new SwitchData(pos, switchAddress, cases, switchTargets);
652            Dop opcode = dataInsn.isPacked() ?
653                Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
654            TargetInsn switchInsn =
655                new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
656
657            addOutput(switchAddress);
658            addOutput(switchInsn);
659
660            addOutputSuffix(new OddSpacer(pos));
661            addOutputSuffix(dataAddress);
662            addOutputSuffix(dataInsn);
663        }
664
665        /**
666         * Looks forward to the current block's primary successor, returning
667         * the RegisterSpec of the result of the move-result-pseudo at the
668         * top of that block or null if none.
669         *
670         * @return {@code null-ok;} result of move-result-pseudo at the beginning of
671         * primary successor
672         */
673        private RegisterSpec getNextMoveResultPseudo()
674        {
675            int label = block.getPrimarySuccessor();
676
677            if (label < 0) {
678                return null;
679            }
680
681            Insn insn
682                    = method.getBlocks().labelToBlock(label).getInsns().get(0);
683
684            if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) {
685                return null;
686            } else {
687                return insn.getResult();
688            }
689        }
690
691        /** {@inheritDoc} */
692        @Override
693        public void visitInvokePolymorphicInsn(InvokePolymorphicInsn insn) {
694            SourcePosition pos = insn.getPosition();
695            Dop opcode = RopToDop.dopFor(insn);
696            Rop rop = insn.getOpcode();
697
698            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
699                throw new RuntimeException("Expected BRANCH_THROW got " + rop.getBranchingness());
700            } else if (!rop.isCallLike()) {
701                throw new RuntimeException("Expected call-like operation");
702            }
703
704            addOutput(lastAddress);
705
706            RegisterSpecList regs = insn.getSources();
707            Constant[] constants = new Constant[] {
708                insn.getPolymorphicMethod(),
709                insn.getCallSiteProto()
710                };
711            DalvInsn di = new MultiCstInsn(opcode, pos, regs, constants);
712
713            addOutput(di);
714        }
715
716        /** {@inheritDoc} */
717        @Override
718        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
719            SourcePosition pos = insn.getPosition();
720            Dop opcode = RopToDop.dopFor(insn);
721            Rop rop = insn.getOpcode();
722            Constant cst = insn.getConstant();
723
724            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
725                throw new RuntimeException("Expected BRANCH_THROW got " + rop.getBranchingness());
726            }
727
728            addOutput(lastAddress);
729
730            if (rop.isCallLike()) {
731                RegisterSpecList regs = insn.getSources();
732                DalvInsn di = new CstInsn(opcode, pos, regs, cst);
733
734                addOutput(di);
735            } else {
736                RegisterSpec realResult = getNextMoveResultPseudo();
737
738                RegisterSpecList regs = getRegs(insn, realResult);
739                DalvInsn di;
740
741                boolean hasResult = opcode.hasResult()
742                        || (rop.getOpcode() == RegOps.CHECK_CAST);
743
744                if (hasResult != (realResult != null)) {
745                    throw new RuntimeException(
746                            "Insn with result/move-result-pseudo mismatch " +
747                            insn);
748                }
749
750                if ((rop.getOpcode() == RegOps.NEW_ARRAY) &&
751                    (opcode.getOpcode() != Opcodes.NEW_ARRAY)) {
752                    /*
753                     * It's a type-specific new-array-<primitive>, and
754                     * so it should be turned into a SimpleInsn (no
755                     * constant ref as it's implicit).
756                     */
757                    di = new SimpleInsn(opcode, pos, regs);
758                } else {
759                    /*
760                     * This is the general case for constant-bearing
761                     * instructions.
762                     */
763                    di = new CstInsn(opcode, pos, regs, cst);
764                }
765
766                addOutput(di);
767            }
768        }
769
770        /** {@inheritDoc} */
771        @Override
772        public void visitThrowingInsn(ThrowingInsn insn) {
773            SourcePosition pos = insn.getPosition();
774            Dop opcode = RopToDop.dopFor(insn);
775            Rop rop = insn.getOpcode();
776            RegisterSpec realResult;
777
778            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
779                throw new RuntimeException("shouldn't happen");
780            }
781
782            realResult = getNextMoveResultPseudo();
783
784            if (opcode.hasResult() != (realResult != null)) {
785                throw new RuntimeException(
786                        "Insn with result/move-result-pseudo mismatch" + insn);
787            }
788
789            addOutput(lastAddress);
790
791            DalvInsn di = new SimpleInsn(opcode, pos,
792                    getRegs(insn, realResult));
793
794            addOutput(di);
795        }
796
797        /** {@inheritDoc} */
798        @Override
799        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
800            SourcePosition pos = insn.getPosition();
801            Constant cst = insn.getConstant();
802            ArrayList<Constant> values = insn.getInitValues();
803            Rop rop = insn.getOpcode();
804
805            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
806                throw new RuntimeException("shouldn't happen");
807            }
808            CodeAddress dataAddress = new CodeAddress(pos);
809            ArrayData dataInsn =
810                new ArrayData(pos, lastAddress, values, cst);
811
812            TargetInsn fillArrayDataInsn =
813                new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn),
814                        dataAddress);
815
816            addOutput(lastAddress);
817            addOutput(fillArrayDataInsn);
818
819            addOutputSuffix(new OddSpacer(pos));
820            addOutputSuffix(dataAddress);
821            addOutputSuffix(dataInsn);
822        }
823
824        /**
825         * Adds to the output.
826         *
827         * @param insn {@code non-null;} instruction to add
828         */
829        protected void addOutput(DalvInsn insn) {
830            output.add(insn);
831        }
832
833        /**
834         * Adds to the output suffix.
835         *
836         * @param insn {@code non-null;} instruction to add
837         */
838        protected void addOutputSuffix(DalvInsn insn) {
839            output.addSuffix(insn);
840        }
841    }
842
843    /**
844     * Instruction visitor class for doing instruction translation with
845     * local variable tracking
846     */
847    private class LocalVariableAwareTranslationVisitor
848            extends TranslationVisitor {
849        /** {@code non-null;} local variable info */
850        private final LocalVariableInfo locals;
851
852        /**
853         * Constructs an instance.
854         *
855         * @param output {@code non-null;} destination for instruction output
856         * @param locals {@code non-null;} the local variable info
857         */
858        public LocalVariableAwareTranslationVisitor(OutputCollector output,
859                                                    LocalVariableInfo locals) {
860            super(output);
861            this.locals = locals;
862        }
863
864        /** {@inheritDoc} */
865        @Override
866        public void visitPlainInsn(PlainInsn insn) {
867            super.visitPlainInsn(insn);
868            addIntroductionIfNecessary(insn);
869        }
870
871        /** {@inheritDoc} */
872        @Override
873        public void visitPlainCstInsn(PlainCstInsn insn) {
874            super.visitPlainCstInsn(insn);
875            addIntroductionIfNecessary(insn);
876        }
877
878        /** {@inheritDoc} */
879        @Override
880        public void visitSwitchInsn(SwitchInsn insn) {
881            super.visitSwitchInsn(insn);
882            addIntroductionIfNecessary(insn);
883        }
884
885        /** {@inheritDoc} */
886        @Override
887        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
888            super.visitThrowingCstInsn(insn);
889            addIntroductionIfNecessary(insn);
890        }
891
892        /** {@inheritDoc} */
893        @Override
894        public void visitThrowingInsn(ThrowingInsn insn) {
895            super.visitThrowingInsn(insn);
896            addIntroductionIfNecessary(insn);
897        }
898
899        /**
900         * Adds a {@link LocalStart} to the output if the given
901         * instruction in fact introduces a local variable.
902         *
903         * @param insn {@code non-null;} instruction in question
904         */
905        public void addIntroductionIfNecessary(Insn insn) {
906            RegisterSpec spec = locals.getAssignment(insn);
907
908            if (spec != null) {
909                addOutput(new LocalStart(insn.getPosition(), spec));
910            }
911        }
912    }
913}
914