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