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