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