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