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