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.LocalVariableInfo;
26import com.android.dx.rop.code.PlainCstInsn;
27import com.android.dx.rop.code.PlainInsn;
28import com.android.dx.rop.code.RegOps;
29import com.android.dx.rop.code.RegisterSpec;
30import com.android.dx.rop.code.RegisterSpecList;
31import com.android.dx.rop.code.RegisterSpecSet;
32import com.android.dx.rop.code.Rop;
33import com.android.dx.rop.code.RopMethod;
34import com.android.dx.rop.code.SourcePosition;
35import com.android.dx.rop.code.SwitchInsn;
36import com.android.dx.rop.code.ThrowingCstInsn;
37import com.android.dx.rop.code.ThrowingInsn;
38import com.android.dx.rop.cst.Constant;
39import com.android.dx.rop.cst.CstInteger;
40import com.android.dx.util.Bits;
41import com.android.dx.util.IntList;
42
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 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);
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        public void visitPlainInsn(PlainInsn insn) {
531            Rop rop = insn.getOpcode();
532            if (rop.getOpcode() == RegOps.MARK_LOCAL) {
533                /*
534                 * Ignore these. They're dealt with by
535                 * the LocalVariableAwareTranslationVisitor
536                 */
537                return;
538            }
539            if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
540                // These get skipped
541                return;
542            }
543
544            SourcePosition pos = insn.getPosition();
545            Dop opcode = RopToDop.dopFor(insn);
546            DalvInsn di;
547
548            switch (rop.getBranchingness()) {
549                case Rop.BRANCH_NONE:
550                case Rop.BRANCH_RETURN:
551                case Rop.BRANCH_THROW: {
552                    di = new SimpleInsn(opcode, pos, getRegs(insn));
553                    break;
554                }
555                case Rop.BRANCH_GOTO: {
556                    /*
557                     * Code in the main translation loop will emit a
558                     * goto if necessary (if the branch isn't to the
559                     * immediately subsequent block).
560                     */
561                    return;
562                }
563                case Rop.BRANCH_IF: {
564                    int target = block.getSuccessors().get(1);
565                    di = new TargetInsn(opcode, pos, getRegs(insn),
566                                        addresses.getStart(target));
567                    break;
568                }
569                default: {
570                    throw new RuntimeException("shouldn't happen");
571                }
572            }
573
574            addOutput(di);
575        }
576
577        /** {@inheritDoc} */
578        public void visitPlainCstInsn(PlainCstInsn insn) {
579            SourcePosition pos = insn.getPosition();
580            Dop opcode = RopToDop.dopFor(insn);
581            Rop rop = insn.getOpcode();
582            int ropOpcode = rop.getOpcode();
583            DalvInsn di;
584
585            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
586                throw new RuntimeException("shouldn't happen");
587            }
588
589            if (ropOpcode == RegOps.MOVE_PARAM) {
590                if (!paramsAreInOrder) {
591                    /*
592                     * Parameters are not in order at the top of the reg space.
593                     * We need to add moves.
594                     */
595
596                    RegisterSpec dest = insn.getResult();
597                    int param =
598                        ((CstInteger) insn.getConstant()).getValue();
599                    RegisterSpec source =
600                        RegisterSpec.make(regCount - paramSize + param,
601                                dest.getType());
602                    di = new SimpleInsn(opcode, pos,
603                                        RegisterSpecList.make(dest, source));
604                    addOutput(di);
605                }
606            } else {
607                // No moves required for the parameters
608                RegisterSpecList regs = getRegs(insn);
609                di = new CstInsn(opcode, pos, regs, insn.getConstant());
610                addOutput(di);
611            }
612        }
613
614        /** {@inheritDoc} */
615        public void visitSwitchInsn(SwitchInsn insn) {
616            SourcePosition pos = insn.getPosition();
617            IntList cases = insn.getCases();
618            IntList successors = block.getSuccessors();
619            int casesSz = cases.size();
620            int succSz = successors.size();
621            int primarySuccessor = block.getPrimarySuccessor();
622
623            /*
624             * Check the assumptions that the number of cases is one
625             * less than the number of successors and that the last
626             * successor in the list is the primary (in this case, the
627             * default). This test is here to guard against forgetting
628             * to change this code if the way switch instructions are
629             * constructed also gets changed.
630             */
631            if ((casesSz != (succSz - 1)) ||
632                (primarySuccessor != successors.get(casesSz))) {
633                throw new RuntimeException("shouldn't happen");
634            }
635
636            CodeAddress[] switchTargets = new CodeAddress[casesSz];
637
638            for (int i = 0; i < casesSz; i++) {
639                int label = successors.get(i);
640                switchTargets[i] = addresses.getStart(label);
641            }
642
643            CodeAddress dataAddress = new CodeAddress(pos);
644            SwitchData dataInsn =
645                new SwitchData(pos, lastAddress, cases, switchTargets);
646            Dop opcode = dataInsn.isPacked() ?
647                Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
648            TargetInsn switchInsn =
649                new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
650
651            addOutput(lastAddress);
652            addOutput(switchInsn);
653
654            addOutputSuffix(new OddSpacer(pos));
655            addOutputSuffix(dataAddress);
656            addOutputSuffix(dataInsn);
657        }
658
659        /**
660         * Looks forward to the current block's primary successor, returning
661         * the RegisterSpec of the result of the move-result-pseudo at the
662         * top of that block or null if none.
663         *
664         * @return {@code null-ok;} result of move-result-pseudo at the beginning of
665         * primary successor
666         */
667        private RegisterSpec getNextMoveResultPseudo()
668        {
669            int label = block.getPrimarySuccessor();
670
671            if (label < 0) {
672                return null;
673            }
674
675            Insn insn
676                    = method.getBlocks().labelToBlock(label).getInsns().get(0);
677
678            if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) {
679                return null;
680            } else {
681                return insn.getResult();
682            }
683        }
684
685        /** {@inheritDoc} */
686        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
687            SourcePosition pos = insn.getPosition();
688            Dop opcode = RopToDop.dopFor(insn);
689            Rop rop = insn.getOpcode();
690            Constant cst = insn.getConstant();
691
692            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
693                throw new RuntimeException("shouldn't happen");
694            }
695
696            addOutput(lastAddress);
697
698            if (rop.isCallLike()) {
699                RegisterSpecList regs = insn.getSources();
700                DalvInsn di = new CstInsn(opcode, pos, regs, cst);
701
702                addOutput(di);
703            } else {
704                RegisterSpec realResult = getNextMoveResultPseudo();
705
706                RegisterSpecList regs = getRegs(insn, realResult);
707                DalvInsn di;
708
709                boolean hasResult = opcode.hasResult()
710                        || (rop.getOpcode() == RegOps.CHECK_CAST);
711
712                if (hasResult != (realResult != null)) {
713                    throw new RuntimeException(
714                            "Insn with result/move-result-pseudo mismatch " +
715                            insn);
716                }
717
718                if ((rop.getOpcode() == RegOps.NEW_ARRAY) &&
719                    (opcode.getOpcode() != Opcodes.NEW_ARRAY)) {
720                    /*
721                     * It's a type-specific new-array-<primitive>, and
722                     * so it should be turned into a SimpleInsn (no
723                     * constant ref as it's implicit).
724                     */
725                    di = new SimpleInsn(opcode, pos, regs);
726                } else {
727                    /*
728                     * This is the general case for constant-bearing
729                     * instructions.
730                     */
731                    di = new CstInsn(opcode, pos, regs, cst);
732                }
733
734                addOutput(di);
735            }
736        }
737
738        /** {@inheritDoc} */
739        public void visitThrowingInsn(ThrowingInsn insn) {
740            SourcePosition pos = insn.getPosition();
741            Dop opcode = RopToDop.dopFor(insn);
742            Rop rop = insn.getOpcode();
743            RegisterSpec realResult;
744
745            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
746                throw new RuntimeException("shouldn't happen");
747            }
748
749            realResult = getNextMoveResultPseudo();
750
751            if (opcode.hasResult() != (realResult != null)) {
752                throw new RuntimeException(
753                        "Insn with result/move-result-pseudo mismatch" + insn);
754            }
755
756            addOutput(lastAddress);
757
758            DalvInsn di = new SimpleInsn(opcode, pos,
759                    getRegs(insn, realResult));
760
761            addOutput(di);
762        }
763
764        /** {@inheritDoc} */
765        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
766            SourcePosition pos = insn.getPosition();
767            Constant cst = insn.getConstant();
768            ArrayList<Constant> values = insn.getInitValues();
769            Rop rop = insn.getOpcode();
770
771            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
772                throw new RuntimeException("shouldn't happen");
773            }
774            CodeAddress dataAddress = new CodeAddress(pos);
775            ArrayData dataInsn =
776                new ArrayData(pos, lastAddress, values, cst);
777
778            TargetInsn fillArrayDataInsn =
779                new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn),
780                        dataAddress);
781
782            addOutput(lastAddress);
783            addOutput(fillArrayDataInsn);
784
785            addOutputSuffix(new OddSpacer(pos));
786            addOutputSuffix(dataAddress);
787            addOutputSuffix(dataInsn);
788        }
789
790        /**
791         * Adds to the output.
792         *
793         * @param insn {@code non-null;} instruction to add
794         */
795        protected void addOutput(DalvInsn insn) {
796            output.add(insn);
797        }
798
799        /**
800         * Adds to the output suffix.
801         *
802         * @param insn {@code non-null;} instruction to add
803         */
804        protected void addOutputSuffix(DalvInsn insn) {
805            output.addSuffix(insn);
806        }
807    }
808
809    /**
810     * Instruction visitor class for doing instruction translation with
811     * local variable tracking
812     */
813    private class LocalVariableAwareTranslationVisitor
814            extends TranslationVisitor {
815        /** {@code non-null;} local variable info */
816        private LocalVariableInfo locals;
817
818        /**
819         * Constructs an instance.
820         *
821         * @param output {@code non-null;} destination for instruction output
822         * @param locals {@code non-null;} the local variable info
823         */
824        public LocalVariableAwareTranslationVisitor(OutputCollector output,
825                                                    LocalVariableInfo locals) {
826            super(output);
827            this.locals = locals;
828        }
829
830        /** {@inheritDoc} */
831        @Override
832        public void visitPlainInsn(PlainInsn insn) {
833            super.visitPlainInsn(insn);
834            addIntroductionIfNecessary(insn);
835        }
836
837        /** {@inheritDoc} */
838        @Override
839        public void visitPlainCstInsn(PlainCstInsn insn) {
840            super.visitPlainCstInsn(insn);
841            addIntroductionIfNecessary(insn);
842        }
843
844        /** {@inheritDoc} */
845        @Override
846        public void visitSwitchInsn(SwitchInsn insn) {
847            super.visitSwitchInsn(insn);
848            addIntroductionIfNecessary(insn);
849        }
850
851        /** {@inheritDoc} */
852        @Override
853        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
854            super.visitThrowingCstInsn(insn);
855            addIntroductionIfNecessary(insn);
856        }
857
858        /** {@inheritDoc} */
859        @Override
860        public void visitThrowingInsn(ThrowingInsn insn) {
861            super.visitThrowingInsn(insn);
862            addIntroductionIfNecessary(insn);
863        }
864
865        /**
866         * Adds a {@link LocalStart} to the output if the given
867         * instruction in fact introduces a local variable.
868         *
869         * @param insn {@code non-null;} instruction in question
870         */
871        public void addIntroductionIfNecessary(Insn insn) {
872            RegisterSpec spec = locals.getAssignment(insn);
873
874            if (spec != null) {
875                addOutput(new LocalStart(insn.getPosition(), spec));
876            }
877        }
878    }
879}
880