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