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