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