• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dexgen.dex.code;
18 
19 import com.android.dexgen.rop.code.BasicBlock;
20 import com.android.dexgen.rop.code.BasicBlockList;
21 import com.android.dexgen.rop.code.FillArrayDataInsn;
22 import com.android.dexgen.rop.code.Insn;
23 import com.android.dexgen.rop.code.LocalVariableInfo;
24 import com.android.dexgen.rop.code.PlainCstInsn;
25 import com.android.dexgen.rop.code.PlainInsn;
26 import com.android.dexgen.rop.code.RegOps;
27 import com.android.dexgen.rop.code.RegisterSpec;
28 import com.android.dexgen.rop.code.RegisterSpecList;
29 import com.android.dexgen.rop.code.RegisterSpecSet;
30 import com.android.dexgen.rop.code.Rop;
31 import com.android.dexgen.rop.code.RopMethod;
32 import com.android.dexgen.rop.code.SourcePosition;
33 import com.android.dexgen.rop.code.SwitchInsn;
34 import com.android.dexgen.rop.code.ThrowingCstInsn;
35 import com.android.dexgen.rop.code.ThrowingInsn;
36 import com.android.dexgen.rop.cst.Constant;
37 import com.android.dexgen.rop.cst.CstInteger;
38 import com.android.dexgen.rop.type.Type;
39 import com.android.dexgen.util.Bits;
40 import com.android.dexgen.util.IntList;
41 
42 import 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  */
48 public 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      */
translate(RopMethod method, int positionInfo, LocalVariableInfo locals, int paramSize)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      */
RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals, int paramSize)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      */
calculateParamsAreInOrder(RopMethod method, final int paramSize)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      */
translateAndGetResult()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      */
outputInstructions()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      */
outputBlock(BasicBlock block, int nextLabel)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      */
pickOrder()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      */
getRegs(Insn insn)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      */
getRegs(Insn insn, RegisterSpec resultReg)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          */
TranslationVisitor(OutputCollector output)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          */
setBlock(BasicBlock block, CodeAddress lastAddress)517         public void setBlock(BasicBlock block, CodeAddress lastAddress) {
518             this.block = block;
519             this.lastAddress = lastAddress;
520         }
521 
522         /** {@inheritDoc} */
visitPlainInsn(PlainInsn insn)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} */
visitPlainCstInsn(PlainCstInsn insn)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} */
visitSwitchInsn(SwitchInsn insn)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          */
getNextMoveResultPseudo()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} */
visitThrowingCstInsn(ThrowingCstInsn insn)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} */
visitThrowingInsn(ThrowingInsn insn)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} */
visitFillArrayDataInsn(FillArrayDataInsn insn)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          */
addOutput(DalvInsn insn)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          */
addOutputSuffix(DalvInsn insn)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          */
LocalVariableAwareTranslationVisitor(OutputCollector output, LocalVariableInfo locals)817         public LocalVariableAwareTranslationVisitor(OutputCollector output,
818                                                     LocalVariableInfo locals) {
819             super(output);
820             this.locals = locals;
821         }
822 
823         /** {@inheritDoc} */
824         @Override
visitPlainInsn(PlainInsn insn)825         public void visitPlainInsn(PlainInsn insn) {
826             super.visitPlainInsn(insn);
827             addIntroductionIfNecessary(insn);
828         }
829 
830         /** {@inheritDoc} */
831         @Override
visitPlainCstInsn(PlainCstInsn insn)832         public void visitPlainCstInsn(PlainCstInsn insn) {
833             super.visitPlainCstInsn(insn);
834             addIntroductionIfNecessary(insn);
835         }
836 
837         /** {@inheritDoc} */
838         @Override
visitSwitchInsn(SwitchInsn insn)839         public void visitSwitchInsn(SwitchInsn insn) {
840             super.visitSwitchInsn(insn);
841             addIntroductionIfNecessary(insn);
842         }
843 
844         /** {@inheritDoc} */
845         @Override
visitThrowingCstInsn(ThrowingCstInsn insn)846         public void visitThrowingCstInsn(ThrowingCstInsn insn) {
847             super.visitThrowingCstInsn(insn);
848             addIntroductionIfNecessary(insn);
849         }
850 
851         /** {@inheritDoc} */
852         @Override
visitThrowingInsn(ThrowingInsn insn)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          */
addIntroductionIfNecessary(Insn insn)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