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