blob: 49a2f30f1cd155aa478db13aabff35e80e0c0be0 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dx.dex.code;
import com.android.dx.rop.code.RegisterSpec;
import com.android.dx.rop.code.RegisterSpecList;
import com.android.dx.rop.code.SourcePosition;
import com.android.dx.ssa.RegisterMapper;
import com.android.dx.util.AnnotatedOutput;
import com.android.dx.util.Hex;
import com.android.dx.util.TwoColumnOutput;
import java.util.BitSet;
/**
* Base class for Dalvik instructions.
*/
public abstract class DalvInsn {
/**
* the actual output address of this instance, if known, or
* {@code -1} if not
*/
private int address;
/** the opcode; one of the constants from {@link Dops} */
private final Dop opcode;
/** {@code non-null;} source position */
private final SourcePosition position;
/** {@code non-null;} list of register arguments */
private final RegisterSpecList registers;
/**
* Makes a move instruction, appropriate and ideal for the given arguments.
*
* @param position {@code non-null;} source position information
* @param dest {@code non-null;} destination register
* @param src {@code non-null;} source register
* @return {@code non-null;} an appropriately-constructed instance
*/
public static SimpleInsn makeMove(SourcePosition position,
RegisterSpec dest, RegisterSpec src) {
boolean category1 = dest.getCategory() == 1;
boolean reference = dest.getType().isReference();
int destReg = dest.getReg();
int srcReg = src.getReg();
Dop opcode;
if ((srcReg | destReg) < 16) {
opcode = reference ? Dops.MOVE_OBJECT :
(category1 ? Dops.MOVE : Dops.MOVE_WIDE);
} else if (destReg < 256) {
opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
(category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
} else {
opcode = reference ? Dops.MOVE_OBJECT_16 :
(category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
}
return new SimpleInsn(opcode, position,
RegisterSpecList.make(dest, src));
}
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* <p><b>Note:</b> In the unlikely event that an instruction takes
* absolutely no registers (e.g., a {@code nop} or a
* no-argument no-result static method call), then the given
* register list may be passed as {@link
* RegisterSpecList#EMPTY}.</p>
*
* @param opcode the opcode; one of the constants from {@link Dops}
* @param position {@code non-null;} source position
* @param registers {@code non-null;} register list, including a
* result register if appropriate (that is, registers may be either
* ins and outs)
*/
public DalvInsn(Dop opcode, SourcePosition position,
RegisterSpecList registers) {
if (opcode == null) {
throw new NullPointerException("opcode == null");
}
if (position == null) {
throw new NullPointerException("position == null");
}
if (registers == null) {
throw new NullPointerException("registers == null");
}
this.address = -1;
this.opcode = opcode;
this.position = position;
this.registers = registers;
}
/** {@inheritDoc} */
@Override
public final String toString() {
StringBuffer sb = new StringBuffer(100);
sb.append(identifierString());
sb.append(' ');
sb.append(position);
sb.append(": ");
sb.append(opcode.getName());
boolean needComma = false;
if (registers.size() != 0) {
sb.append(registers.toHuman(" ", ", ", null));
needComma = true;
}
String extra = argString();
if (extra != null) {
if (needComma) {
sb.append(',');
}
sb.append(' ');
sb.append(extra);
}
return sb.toString();
}
/**
* Gets whether the address of this instruction is known.
*
* @see #getAddress
* @see #setAddress
*/
public final boolean hasAddress() {
return (address >= 0);
}
/**
* Gets the output address of this instruction, if it is known. This throws
* a {@code RuntimeException} if it has not yet been set.
*
* @see #setAddress
*
* @return {@code >= 0;} the output address
*/
public final int getAddress() {
if (address < 0) {
throw new RuntimeException("address not yet known");
}
return address;
}
/**
* Gets the opcode.
*
* @return {@code non-null;} the opcode
*/
public final Dop getOpcode() {
return opcode;
}
/**
* Gets the source position.
*
* @return {@code non-null;} the source position
*/
public final SourcePosition getPosition() {
return position;
}
/**
* Gets the register list for this instruction.
*
* @return {@code non-null;} the registers
*/
public final RegisterSpecList getRegisters() {
return registers;
}
/**
* Returns whether this instance's opcode uses a result register.
* This method is a convenient shorthand for
* {@code getOpcode().hasResult()}.
*
* @return {@code true} iff this opcode uses a result register
*/
public final boolean hasResult() {
return opcode.hasResult();
}
/**
* Gets the minimum distinct registers required for this instruction.
* Uses the given BitSet to determine which registers require
* replacement, and ignores registers that are already compatible.
* This assumes that the result (if any) can share registers with the
* sources (if any), that each source register is unique, and that
* (to be explicit here) category-2 values take up two consecutive
* registers.
*
* @param compatRegs {@code non-null;} set of compatible registers
* @return {@code >= 0;} the minimum distinct register requirement
*/
public final int getMinimumRegisterRequirement(BitSet compatRegs) {
boolean hasResult = hasResult();
int regSz = registers.size();
int resultRequirement = 0;
int sourceRequirement = 0;
if (hasResult && !compatRegs.get(0)) {
resultRequirement = registers.get(0).getCategory();
}
for (int i = hasResult ? 1 : 0; i < regSz; i++) {
if (!compatRegs.get(i)) {
sourceRequirement += registers.get(i).getCategory();
}
}
return Math.max(sourceRequirement, resultRequirement);
}
/**
* Gets the instruction that is equivalent to this one, except that
* it uses sequential registers starting at {@code 0} (storing
* the result, if any, in register {@code 0} as well).
*
* @return {@code non-null;} the replacement
*/
public DalvInsn getLowRegVersion() {
RegisterSpecList regs =
registers.withExpandedRegisters(0, hasResult(), null);
return withRegisters(regs);
}
/**
* Gets the instruction prefix required, if any, to use in an expanded
* version of this instance. Will not generate moves for registers
* marked compatible to the format by the given BitSet.
*
* @see #expandedVersion
*
* @param compatRegs {@code non-null;} set of compatible registers
* @return {@code null-ok;} the prefix, if any
*/
public DalvInsn expandedPrefix(BitSet compatRegs) {
RegisterSpecList regs = registers;
boolean firstBit = compatRegs.get(0);
if (hasResult()) compatRegs.set(0);
regs = regs.subset(compatRegs);
if (hasResult()) compatRegs.set(0, firstBit);
if (regs.size() == 0) return null;
return new HighRegisterPrefix(position, regs);
}
/**
* Gets the instruction suffix required, if any, to use in an expanded
* version of this instance. Will not generate a move for a register
* marked compatible to the format by the given BitSet.
*
* @see #expandedVersion
*
* @param compatRegs {@code non-null;} set of compatible registers
* @return {@code null-ok;} the suffix, if any
*/
public DalvInsn expandedSuffix(BitSet compatRegs) {
if (hasResult() && !compatRegs.get(0)) {
RegisterSpec r = registers.get(0);
return makeMove(position, r, r.withReg(0));
} else {
return null;
}
}
/**
* Gets the instruction that is equivalent to this one, except that
* it replaces incompatible registers with sequential registers
* starting at {@code 0} (storing the result, if any, in register
* {@code 0} as well). The sequence of instructions from
* {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null)
* surrounding the result of a call to this method are the expanded
* transformation of this instance, and it is guaranteed that the
* number of low registers used will be the number returned by
* {@link #getMinimumRegisterRequirement}.
*
* @param compatRegs {@code non-null;} set of compatible registers
* @return {@code non-null;} the replacement
*/
public DalvInsn expandedVersion(BitSet compatRegs) {
RegisterSpecList regs =
registers.withExpandedRegisters(0, hasResult(), compatRegs);
return withRegisters(regs);
}
/**
* Gets the short identifier for this instruction. This is its
* address, if assigned, or its identity hashcode if not.
*
* @return {@code non-null;} the identifier
*/
public final String identifierString() {
if (address != -1) {
return String.format("%04x", address);
}
return Hex.u4(System.identityHashCode(this));
}
/**
* Returns the string form of this instance suitable for inclusion in
* a human-oriented listing dump. This method will return {@code null}
* if this instance should not appear in a listing.
*
* @param prefix {@code non-null;} prefix before the address; each follow-on
* line will be indented to match as well
* @param width {@code >= 0;} the width of the output or {@code 0} for
* unlimited width
* @param noteIndices whether to include an explicit notation of
* constant pool indices
* @return {@code null-ok;} the string form or {@code null} if this
* instance should not appear in a listing
*/
public final String listingString(String prefix, int width,
boolean noteIndices) {
String insnPerSe = listingString0(noteIndices);
if (insnPerSe == null) {
return null;
}
String addr = prefix + identifierString() + ": ";
int w1 = addr.length();
int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
}
/**
* Sets the output address.
*
* @param address {@code >= 0;} the output address
*/
public final void setAddress(int address) {
if (address < 0) {
throw new IllegalArgumentException("address < 0");
}
this.address = address;
}
/**
* Gets the address immediately after this instance. This is only
* calculable if this instance's address is known, and it is equal
* to the address plus the length of the instruction format of this
* instance's opcode.
*
* @return {@code >= 0;} the next address
*/
public final int getNextAddress() {
return getAddress() + codeSize();
}
/**
* Returns an instance that is just like this one, except that the
* register list is mapped by using {@code mapper}.
*
* @param mapper {@code non-null;} used to map registers
* @return {@code non-null;} an appropriately-constructed instance
*/
public DalvInsn withMapper(RegisterMapper mapper) {
return withRegisters(mapper.map(getRegisters()));
}
/**
* Gets the size of this instruction, in 16-bit code units.
*
* @return {@code >= 0;} the code size of this instruction
*/
public abstract int codeSize();
/**
* Writes this instance to the given output. This method should
* never annotate the output.
*
* @param out {@code non-null;} where to write to
*/
public abstract void writeTo(AnnotatedOutput out);
/**
* Returns an instance that is just like this one, except that its
* opcode is replaced by the one given, and its address is reset.
*
* @param opcode {@code non-null;} the new opcode
* @return {@code non-null;} an appropriately-constructed instance
*/
public abstract DalvInsn withOpcode(Dop opcode);
/**
* Returns an instance that is just like this one, except that all
* register references have been offset by the given delta, and its
* address is reset.
*
* @param delta the amount to offset register references by
* @return {@code non-null;} an appropriately-constructed instance
*/
public abstract DalvInsn withRegisterOffset(int delta);
/**
* Returns an instance that is just like this one, except that the
* register list is replaced by the given one, and its address is
* reset.
*
* @param registers {@code non-null;} new register list
* @return {@code non-null;} an appropriately-constructed instance
*/
public abstract DalvInsn withRegisters(RegisterSpecList registers);
/**
* Gets the string form for any arguments to this instance. Subclasses
* must override this.
*
* @return {@code null-ok;} the string version of any arguments or
* {@code null} if there are none
*/
protected abstract String argString();
/**
* Helper for {@link #listingString}, which returns the string
* form of this instance suitable for inclusion in a
* human-oriented listing dump, not including the instruction
* address and without respect for any output formatting. This
* method should return {@code null} if this instance should
* not appear in a listing.
*
* @param noteIndices whether to include an explicit notation of
* constant pool indices
* @return {@code null-ok;} the listing string
*/
protected abstract String listingString0(boolean noteIndices);
}