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.rop.code.RegisterSpec;
     20 import com.android.dx.rop.code.RegisterSpecList;
     21 import com.android.dx.rop.code.SourcePosition;
     22 import com.android.dx.util.AnnotatedOutput;
     23 import com.android.dx.util.Hex;
     24 import com.android.dx.util.TwoColumnOutput;
     25 import java.util.BitSet;
     26 
     27 /**
     28  * Base class for Dalvik instructions.
     29  */
     30 public abstract class DalvInsn {
     31     /**
     32      * the actual output address of this instance, if known, or
     33      * {@code -1} if not
     34      */
     35     private int address;
     36 
     37     /** the opcode; one of the constants from {@link Dops} */
     38     private final Dop opcode;
     39 
     40     /** {@code non-null;} source position */
     41     private final SourcePosition position;
     42 
     43     /** {@code non-null;} list of register arguments */
     44     private final RegisterSpecList registers;
     45 
     46     /**
     47      * Makes a move instruction, appropriate and ideal for the given arguments.
     48      *
     49      * @param position {@code non-null;} source position information
     50      * @param dest {@code non-null;} destination register
     51      * @param src {@code non-null;} source register
     52      * @return {@code non-null;} an appropriately-constructed instance
     53      */
     54     public static SimpleInsn makeMove(SourcePosition position,
     55             RegisterSpec dest, RegisterSpec src) {
     56         boolean category1 = dest.getCategory() == 1;
     57         boolean reference = dest.getType().isReference();
     58         int destReg = dest.getReg();
     59         int srcReg = src.getReg();
     60         Dop opcode;
     61 
     62         if ((srcReg | destReg) < 16) {
     63             opcode = reference ? Dops.MOVE_OBJECT :
     64                 (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
     65         } else if (destReg < 256) {
     66             opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
     67                 (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
     68         } else {
     69             opcode = reference ? Dops.MOVE_OBJECT_16 :
     70                 (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
     71         }
     72 
     73         return new SimpleInsn(opcode, position,
     74                               RegisterSpecList.make(dest, src));
     75     }
     76 
     77     /**
     78      * Constructs an instance. The output address of this instance is initially
     79      * unknown ({@code -1}).
     80      *
     81      * <p><b>Note:</b> In the unlikely event that an instruction takes
     82      * absolutely no registers (e.g., a {@code nop} or a
     83      * no-argument no-result static method call), then the given
     84      * register list may be passed as {@link
     85      * RegisterSpecList#EMPTY}.</p>
     86      *
     87      * @param opcode the opcode; one of the constants from {@link Dops}
     88      * @param position {@code non-null;} source position
     89      * @param registers {@code non-null;} register list, including a
     90      * result register if appropriate (that is, registers may be either
     91      * ins and outs)
     92      */
     93     public DalvInsn(Dop opcode, SourcePosition position,
     94                     RegisterSpecList registers) {
     95         if (opcode == null) {
     96             throw new NullPointerException("opcode == null");
     97         }
     98 
     99         if (position == null) {
    100             throw new NullPointerException("position == null");
    101         }
    102 
    103         if (registers == null) {
    104             throw new NullPointerException("registers == null");
    105         }
    106 
    107         this.address = -1;
    108         this.opcode = opcode;
    109         this.position = position;
    110         this.registers = registers;
    111     }
    112 
    113     /** {@inheritDoc} */
    114     @Override
    115     public final String toString() {
    116         StringBuffer sb = new StringBuffer(100);
    117 
    118         sb.append(identifierString());
    119         sb.append(' ');
    120         sb.append(position);
    121 
    122         sb.append(": ");
    123         sb.append(opcode.getName());
    124 
    125         boolean needComma = false;
    126         if (registers.size() != 0) {
    127             sb.append(registers.toHuman(" ", ", ", null));
    128             needComma = true;
    129         }
    130 
    131         String extra = argString();
    132         if (extra != null) {
    133             if (needComma) {
    134                 sb.append(',');
    135             }
    136             sb.append(' ');
    137             sb.append(extra);
    138         }
    139 
    140         return sb.toString();
    141     }
    142 
    143     /**
    144      * Gets whether the address of this instruction is known.
    145      *
    146      * @see #getAddress
    147      * @see #setAddress
    148      */
    149     public final boolean hasAddress() {
    150         return (address >= 0);
    151     }
    152 
    153     /**
    154      * Gets the output address of this instruction, if it is known. This throws
    155      * a {@code RuntimeException} if it has not yet been set.
    156      *
    157      * @see #setAddress
    158      *
    159      * @return {@code >= 0;} the output address
    160      */
    161     public final int getAddress() {
    162         if (address < 0) {
    163             throw new RuntimeException("address not yet known");
    164         }
    165 
    166         return address;
    167     }
    168 
    169     /**
    170      * Gets the opcode.
    171      *
    172      * @return {@code non-null;} the opcode
    173      */
    174     public final Dop getOpcode() {
    175         return opcode;
    176     }
    177 
    178     /**
    179      * Gets the source position.
    180      *
    181      * @return {@code non-null;} the source position
    182      */
    183     public final SourcePosition getPosition() {
    184         return position;
    185     }
    186 
    187     /**
    188      * Gets the register list for this instruction.
    189      *
    190      * @return {@code non-null;} the registers
    191      */
    192     public final RegisterSpecList getRegisters() {
    193         return registers;
    194     }
    195 
    196     /**
    197      * Returns whether this instance's opcode uses a result register.
    198      * This method is a convenient shorthand for
    199      * {@code getOpcode().hasResult()}.
    200      *
    201      * @return {@code true} iff this opcode uses a result register
    202      */
    203     public final boolean hasResult() {
    204         return opcode.hasResult();
    205     }
    206 
    207     /**
    208      * Gets the minimum distinct registers required for this instruction.
    209      * Uses the given BitSet to determine which registers require
    210      * replacement, and ignores registers that are already compatible.
    211      * This assumes that the result (if any) can share registers with the
    212      * sources (if any), that each source register is unique, and that
    213      * (to be explicit here) category-2 values take up two consecutive
    214      * registers.
    215      *
    216      * @param compatRegs {@code non-null;} set of compatible registers
    217      * @return {@code >= 0;} the minimum distinct register requirement
    218      */
    219     public final int getMinimumRegisterRequirement(BitSet compatRegs) {
    220         boolean hasResult = hasResult();
    221         int regSz = registers.size();
    222         int resultRequirement = 0;
    223         int sourceRequirement = 0;
    224 
    225         if (hasResult && !compatRegs.get(0)) {
    226             resultRequirement = registers.get(0).getCategory();
    227         }
    228 
    229         for (int i = hasResult ? 1 : 0; i < regSz; i++) {
    230             if (!compatRegs.get(i)) {
    231                 sourceRequirement += registers.get(i).getCategory();
    232             }
    233         }
    234 
    235         return Math.max(sourceRequirement, resultRequirement);
    236     }
    237 
    238     /**
    239      * Gets the instruction that is equivalent to this one, except that
    240      * it uses sequential registers starting at {@code 0} (storing
    241      * the result, if any, in register {@code 0} as well).
    242      *
    243      * @return {@code non-null;} the replacement
    244      */
    245     public DalvInsn getLowRegVersion() {
    246         RegisterSpecList regs =
    247             registers.withExpandedRegisters(0, hasResult(), null);
    248         return withRegisters(regs);
    249     }
    250 
    251     /**
    252      * Gets the instruction prefix required, if any, to use in an expanded
    253      * version of this instance. Will not generate moves for registers
    254      * marked compatible to the format by the given BitSet.
    255      *
    256      * @see #expandedVersion
    257      *
    258      * @param compatRegs {@code non-null;} set of compatible registers
    259      * @return {@code null-ok;} the prefix, if any
    260      */
    261     public DalvInsn expandedPrefix(BitSet compatRegs) {
    262         RegisterSpecList regs = registers;
    263         boolean firstBit = compatRegs.get(0);
    264 
    265         if (hasResult()) compatRegs.set(0);
    266 
    267         regs = regs.subset(compatRegs);
    268 
    269         if (hasResult()) compatRegs.set(0, firstBit);
    270 
    271         if (regs.size() == 0) return null;
    272 
    273         return new HighRegisterPrefix(position, regs);
    274     }
    275 
    276     /**
    277      * Gets the instruction suffix required, if any, to use in an expanded
    278      * version of this instance. Will not generate a move for a register
    279      * marked compatible to the format by the given BitSet.
    280      *
    281      * @see #expandedVersion
    282      *
    283      * @param compatRegs {@code non-null;} set of compatible registers
    284      * @return {@code null-ok;} the suffix, if any
    285      */
    286     public DalvInsn expandedSuffix(BitSet compatRegs) {
    287         if (hasResult() && !compatRegs.get(0)) {
    288             RegisterSpec r = registers.get(0);
    289             return makeMove(position, r, r.withReg(0));
    290         } else {
    291             return null;
    292         }
    293     }
    294 
    295     /**
    296      * Gets the instruction that is equivalent to this one, except that
    297      * it replaces incompatible registers with sequential registers
    298      * starting at {@code 0} (storing the result, if any, in register
    299      * {@code 0} as well). The sequence of instructions from
    300      * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null)
    301      * surrounding the result of a call to this method are the expanded
    302      * transformation of this instance, and it is guaranteed that the
    303      * number of low registers used will be the number returned by
    304      * {@link #getMinimumRegisterRequirement}.
    305      *
    306      * @param compatRegs {@code non-null;} set of compatible registers
    307      * @return {@code non-null;} the replacement
    308      */
    309     public DalvInsn expandedVersion(BitSet compatRegs) {
    310         RegisterSpecList regs =
    311             registers.withExpandedRegisters(0, hasResult(), compatRegs);
    312         return withRegisters(regs);
    313     }
    314 
    315     /**
    316      * Gets the short identifier for this instruction. This is its
    317      * address, if assigned, or its identity hashcode if not.
    318      *
    319      * @return {@code non-null;} the identifier
    320      */
    321     public final String identifierString() {
    322         if (address != -1) {
    323             return String.format("%04x", address);
    324         }
    325 
    326         return Hex.u4(System.identityHashCode(this));
    327     }
    328 
    329     /**
    330      * Returns the string form of this instance suitable for inclusion in
    331      * a human-oriented listing dump. This method will return {@code null}
    332      * if this instance should not appear in a listing.
    333      *
    334      * @param prefix {@code non-null;} prefix before the address; each follow-on
    335      * line will be indented to match as well
    336      * @param width {@code >= 0;} the width of the output or {@code 0} for
    337      * unlimited width
    338      * @param noteIndices whether to include an explicit notation of
    339      * constant pool indices
    340      * @return {@code null-ok;} the string form or {@code null} if this
    341      * instance should not appear in a listing
    342      */
    343     public final String listingString(String prefix, int width,
    344             boolean noteIndices) {
    345         String insnPerSe = listingString0(noteIndices);
    346 
    347         if (insnPerSe == null) {
    348             return null;
    349         }
    350 
    351         String addr = prefix + identifierString() + ": ";
    352         int w1 = addr.length();
    353         int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
    354 
    355         return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
    356     }
    357 
    358     /**
    359      * Sets the output address.
    360      *
    361      * @param address {@code >= 0;} the output address
    362      */
    363     public final void setAddress(int address) {
    364         if (address < 0) {
    365             throw new IllegalArgumentException("address < 0");
    366         }
    367 
    368         this.address = address;
    369     }
    370 
    371     /**
    372      * Gets the address immediately after this instance. This is only
    373      * calculable if this instance's address is known, and it is equal
    374      * to the address plus the length of the instruction format of this
    375      * instance's opcode.
    376      *
    377      * @return {@code >= 0;} the next address
    378      */
    379     public final int getNextAddress() {
    380         return getAddress() + codeSize();
    381     }
    382 
    383     /**
    384      * Gets the size of this instruction, in 16-bit code units.
    385      *
    386      * @return {@code >= 0;} the code size of this instruction
    387      */
    388     public abstract int codeSize();
    389 
    390     /**
    391      * Writes this instance to the given output. This method should
    392      * never annotate the output.
    393      *
    394      * @param out {@code non-null;} where to write to
    395      */
    396     public abstract void writeTo(AnnotatedOutput out);
    397 
    398     /**
    399      * Returns an instance that is just like this one, except that its
    400      * opcode is replaced by the one given, and its address is reset.
    401      *
    402      * @param opcode {@code non-null;} the new opcode
    403      * @return {@code non-null;} an appropriately-constructed instance
    404      */
    405     public abstract DalvInsn withOpcode(Dop opcode);
    406 
    407     /**
    408      * Returns an instance that is just like this one, except that all
    409      * register references have been offset by the given delta, and its
    410      * address is reset.
    411      *
    412      * @param delta the amount to offset register references by
    413      * @return {@code non-null;} an appropriately-constructed instance
    414      */
    415     public abstract DalvInsn withRegisterOffset(int delta);
    416 
    417     /**
    418      * Returns an instance that is just like this one, except that the
    419      * register list is replaced by the given one, and its address is
    420      * reset.
    421      *
    422      * @param registers {@code non-null;} new register list
    423      * @return {@code non-null;} an appropriately-constructed instance
    424      */
    425     public abstract DalvInsn withRegisters(RegisterSpecList registers);
    426 
    427     /**
    428      * Gets the string form for any arguments to this instance. Subclasses
    429      * must override this.
    430      *
    431      * @return {@code null-ok;} the string version of any arguments or
    432      * {@code null} if there are none
    433      */
    434     protected abstract String argString();
    435 
    436     /**
    437      * Helper for {@link #listingString}, which returns the string
    438      * form of this instance suitable for inclusion in a
    439      * human-oriented listing dump, not including the instruction
    440      * address and without respect for any output formatting. This
    441      * method should return {@code null} if this instance should
    442      * not appear in a listing.
    443      *
    444      * @param noteIndices whether to include an explicit notation of
    445      * constant pool indices
    446      * @return {@code null-ok;} the listing string
    447      */
    448     protected abstract String listingString0(boolean noteIndices);
    449 }
    450