jnr.x86asm.Assembler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-driver Show documentation
Show all versions of cassandra-driver Show documentation
Shaded version of DataStax Java Driver for Apache Cassandra
//
// Copyright (C) 2010 Wayne Meissner
// Copyright (c) 2008-2009, Petr Kobalicek
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
package jnr.x86asm;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import static jnr.x86asm.HINT.*;
import static jnr.x86asm.INST_CODE.*;
import static jnr.x86asm.InstructionGroup.*;
import static jnr.x86asm.OP.*;
import static jnr.x86asm.OperandFlags.*;
import static jnr.x86asm.PROPERTY.*;
import static jnr.x86asm.RELOC_MODE.*;
import static jnr.x86asm.REG.*;
import static jnr.x86asm.Util.*;
/**
* Low level code generation.
*/
public final class Assembler extends Serializer {
private final CodeBuffer _buffer = new CodeBuffer();
private final List _relocData = new LinkedList();
private final CpuInfo cpuInfo = CpuInfo.GENERIC;
private int _properties = 0;
/** Size of possible trampolines. */
int _trampolineSize;
private final Logger _logger = null;
private final CPU cpu;
@Override
boolean is64() {
return cpu == CPU.X86_64;
}
private static final int intValue(boolean b) {
return b ? 1 : 0;
}
public static final CPU I386 = CPU.I386;
public static final CPU X86_64 = CPU.X86_64;
public Assembler(CPU cpu) {
this.cpu = cpu;
}
public final int offset() {
return _buffer.offset();
}
/** Gets the required size of memory required to store all the generated code */
public final int codeSize() {
return _buffer.offset() + trampolineSize();
}
/** Return size of all possible trampolines needed to successfuly generate
* relative jumps to absolute addresses. This value is only non-zero if jmp
* of call instructions were used with immediate operand (this means jump or
* call absolute address directly).
*
* Currently only _emitJmpOrCallReloc() method can increase trampoline size
* value.
*/
int trampolineSize() {
return _trampolineSize;
}
//! @brief Set byte at position @a pos.
public final byte getByteAt(int pos) {
return _buffer.getByteAt(pos);
}
//! @brief Set word at position @a pos.
public final short getWordAt(int pos) {
return _buffer.getWordAt(pos);
}
//! @brief Set word at position @a pos.
public final int getDWordAt(int pos) {
return _buffer.getDWordAt(pos);
}
//! @brief Set word at position @a pos.
public final long getQWordAt(int pos) {
return _buffer.getQWordAt(pos);
}
//! @brief Set byte at position @a pos.
public final void setByteAt(int pos, byte x) {
_buffer.setByteAt(pos, x);
}
//! @brief Set word at position @a pos.
public final void setWordAt(int pos, short x) {
_buffer.setWordAt(pos, x);
}
//! @brief Set word at position @a pos.
public final void setDWordAt(int pos, int x) {
_buffer.setDWordAt(pos, x);
}
//! @brief Set word at position @a pos.
public final void setQWordAt(int pos, long x) {
_buffer.setQWordAt(pos, x);
}
//! @brief Set word at position @a pos.
public final int getInt32At(int pos) {
return (int) _buffer.getDWordAt(pos);
}
//! @brief Set int32 at position @a pos.
public final void setInt32At(int pos, long x) {
_buffer.setDWordAt(pos, (int) x);
}
public final void setVarAt(int pos, long i, boolean isUnsigned, int size) {
switch (size) {
case 1:
setByteAt(pos, (byte) i);
break;
case 2:
setWordAt(pos, (short) i);
break;
case 4:
setDWordAt(pos, (int) i);
break;
case 8:
setQWordAt(pos, i);
default:
throw new IllegalArgumentException("invalid size");
}
}
/** Emit Byte to internal buffer. */
final void _emitByte(int x) {
_buffer.emitByte((byte) x);
}
/** Emit Word (2 bytes) to internal buffer. */
final void _emitWord(int x) {
_buffer.emitWord((short) x);
}
/** Emit DWord (4 bytes) to internal buffer. */
final void _emitDWord(int x) {
_buffer.emitDWord(x);
}
/** Emit QWord (8 bytes) to internal buffer. */
final void _emitQWord(long x) {
_buffer.emitQWord(x);
}
/** Emit Int32 (4 bytes) to internal buffer. */
final void _emitInt32(int x) {
_buffer.emitDWord(x);
}
/** Emit system signed integer (4 or 8 bytes) to internal buffer. */
final void _emitSysInt(long x) {
if (is64()) {
_buffer.emitQWord(x);
} else {
_buffer.emitDWord((int) x);
}
}
//! @brief Emit single @a opCode without operands.
final void _emitOpCode(int opCode) {
// instruction prefix
if ((opCode & 0xFF000000) != 0) {
_emitByte((byte) ((opCode & 0xFF000000) >> 24));
}
// instruction opcodes
if ((opCode & 0x00FF0000) != 0) {
_emitByte((byte) ((opCode & 0x00FF0000) >> 16));
}
if ((opCode & 0x0000FF00) != 0) {
_emitByte((byte) ((opCode & 0x0000FF00) >> 8));
}
// last opcode is always emitted (can be also 0x00)
_emitByte((byte) (opCode & 0x000000FF));
}
void _emitSegmentPrefix(Operand rm) {
if (rm.isMem()) {
SEGMENT segmentPrefix = ((Mem) rm).segmentPrefix();
if (segmentPrefix != SEGMENT.SEGMENT_NONE) {
_emitByte(segmentPrefix.prefix());
}
}
}
void _emitImmediate(Immediate imm, int size) {
switch (size) {
case 1:
_emitByte(imm.byteValue());
break;
case 2:
_emitWord(imm.shortValue());
break;
case 4:
_emitDWord(imm.intValue());
break;
case 8:
if (!is64()) {
throw new IllegalArgumentException("64 bit immediate values not supported for 32bit");
}
_emitQWord(imm.longValue());
break;
default:
throw new IllegalArgumentException("invalid immediate operand size");
}
}
/** Emit REX prefix (64 bit mode only). */
void _emitRexR(int w, int opReg, int regCode) {
if (is64()) {
boolean r = (opReg & 0x8) != 0;
boolean b = (regCode & 0x8) != 0;
// w Default operand size(0=Default, 1=64 bits).
// r Register field (1=high bit extension of the ModR/M REG field).
// x Index field not used in RexR
// b Base field (1=high bit extension of the ModR/M or SIB Base field).
if (w != 0 || r || b || (_properties & (1 << PROPERTY_X86_FORCE_REX)) != 0) {
_emitByte(0x40 | (w << 3) | (intValue(r) << 2) | intValue(b));
}
}
}
void _emitRexR(boolean w, int opReg, int regCode) {
_emitRexR(intValue(w), opReg, regCode);
}
/** Emit REX prefix (64 bit mode only). */
void _emitRexRM(int w, int opReg, Operand rm) {
if (is64()) {
boolean r = (opReg & 0x8) != 0;
boolean x = false;
boolean b = false;
if (rm.isReg()) {
b = (((BaseReg) rm).code() & 0x8) != 0;
} else if (rm.isMem()) {
x = (((Mem) rm).index() & 0x8) != 0 && ((Mem) rm).index() != NO_REG;
b = (((Mem) rm).base() & 0x8) != 0 && ((Mem) rm).base() != NO_REG;
}
// w Default operand size(0=Default, 1=64 bits).
// r Register field (1=high bit extension of the ModR/M REG field).
// x Index field (1=high bit extension of the SIB Index field).
// b Base field (1=high bit extension of the ModR/M or SIB Base field).
if (w != 0 || r || x || b || (_properties & (1 << PROPERTY_X86_FORCE_REX)) != 0) {
_emitByte(0x40 | (w << 3) | (intValue(r) << 2) | (intValue(x) << 1) | intValue(b));
}
}
}
void _emitRexRM(boolean w, int opReg, Operand rm) {
_emitRexRM(intValue(w), opReg, rm);
}
void _emitModM(int opReg, Mem mem, int immSize) {
assert (mem.op() == OP_MEM);
int baseReg = mem.base() & 0x7;
int indexReg = mem.index() & 0x7;
long disp = mem.displacement();
int shift = mem.shift();
// [base + displacemnt]
if (mem.hasBase() && !mem.hasIndex()) {
// ESP/RSP/R12 == 4
if (baseReg == 4) {
int mod = 0;
if (disp != 0) {
mod = isInt8(disp) ? 1 : 2;
}
_emitMod(mod, opReg, 4);
_emitSib(0, 4, 4);
if (disp != 0) {
if (isInt8(disp)) {
_emitByte((byte) disp);
} else {
_emitInt32((int) disp);
}
}
} // EBP/RBP/R13 == 5
else if (baseReg != 5 && disp == 0) {
_emitMod(0, opReg, baseReg);
} else if (isInt8(disp)) {
_emitMod(1, opReg, baseReg);
_emitByte((byte) disp);
} else {
_emitMod(2, opReg, baseReg);
_emitInt32((int) disp);
}
}
// [base + index * scale + displacemnt]
else if (mem.hasBase() && mem.hasIndex()) {
// ASMJIT_ASSERT(indexReg != RID_ESP);
// EBP/RBP/R13 == 5
if (baseReg != 5 && disp == 0) {
_emitMod(0, opReg, 4);
_emitSib(shift, indexReg, baseReg);
} else if (isInt8(disp)) {
_emitMod(1, opReg, 4);
_emitSib(shift, indexReg, baseReg);
_emitByte((byte) disp);
} else {
_emitMod(2, opReg, 4);
_emitSib(shift, indexReg, baseReg);
_emitInt32((int) disp);
}
}
// Address | 32-bit mode | 64-bit mode
// ------------------------------+-------------+---------------
// [displacement] | ABSOLUTE | RELATIVE (RIP)
// [index * scale + displacemnt] | ABSOLUTE | ABSOLUTE (ZERO EXTENDED)
else {
// In 32 bit mode is used absolute addressing model.
// In 64 bit mode is used relative addressing model together with absolute
// addressing one. Main problem is that if instruction contains SIB then
// relative addressing (RIP) is not possible.
if (!is64()) {
if (mem.hasIndex()) {
// ASMJIT_ASSERT(mem.index() != 4); // ESP/RSP == 4
_emitMod(0, opReg, 4);
_emitSib(shift, indexReg, 5);
} else {
_emitMod(0, opReg, 5);
}
// X86 uses absolute addressing model, all relative addresses will be
// relocated to absolute ones.
if (mem.hasLabel()) {
Label label = mem.label();
int relocId = _relocData.size();
long destination = disp;
if (label.isBound()) {
destination += label.position();
// Dummy DWORD
_emitInt32(0);
} else {
_emitDisplacement(label, -4 - immSize, 4).relocId = relocId;
}
// Relative addressing will be relocated to absolute address.
RelocData rd = new RelocData(RelocData.Type.RELATIVE_TO_ABSOLUTE, 4, offset(), destination);
_relocData.add(rd);
} else {
// Absolute address
_emitInt32((int) (mem.target() + disp));
}
} else {
// X64 uses relative addressing model
if (mem.hasLabel()) {
Label label = mem.label();
if (mem.hasIndex()) {
// Indexing is not possible
throw new IllegalArgumentException("illegal addressing");
}
// Relative address (RIP +/- displacement)
_emitMod(0, opReg, 5);
disp -= (4 + immSize);
if (label.isBound()) {
disp += offset() - label.position();
_emitInt32((int) disp);
} else {
_emitDisplacement(label, disp, 4);
}
} else {
// Absolute address (truncated to 32 bits), this kind of address requires
// SIB byte (4)
_emitMod(0, opReg, 4);
if (mem.hasIndex()) {
// ASMJIT_ASSERT(mem.index() != 4); // ESP/RSP == 4
_emitSib(shift, indexReg, 5);
} else {
_emitSib(0, 4, 5);
}
// truncate to 32 bits
long target = mem.target() + disp;
if (target > 0xFFFFFFFFL) {
_logger.log("; Warning: Absolute address truncated to 32 bits\n");
}
_emitInt32((int) target);
}
}
}
}
void _emitX86Inl(int opCode, boolean i16bit, boolean rexw, int reg) {
_emitX86Inl(opCode, i16bit, intValue(rexw), reg);
}
void _emitX86Inl(int opCode, boolean i16bit, int rexw, int reg) {
// 16 bit prefix
if (i16bit) {
_emitByte(0x66);
}
// instruction prefix
if ((opCode & 0xFF000000) != 0) {
_emitByte(((opCode & 0xFF000000) >> 24));
}
// rex prefix
if (is64()) {
_emitRexR(rexw, 0, reg);
}
// instruction opcodes
if ((opCode & 0x00FF0000) != 0) {
_emitByte(((opCode & 0x00FF0000) >> 16));
}
if ((opCode & 0x0000FF00) != 0) {
_emitByte(((opCode & 0x0000FF00) >> 8));
}
_emitByte((opCode & 0x000000FF) + (reg & 0x7));
}
void _emitModRM(int opReg, Operand op, int immSize) {
assert (op.op() == OP_REG || op.op() == OP_MEM);
if (op.op() == OP_REG) {
_emitModR(opReg, ((BaseReg) op).code());
} else {
_emitModM(opReg, (Mem) op, immSize);
}
}
/** Emit MODR/M byte. */
void _emitMod(int m, int o, int r) {
_emitByte((byte) (((m & 0x03) << 6) | ((o & 0x07) << 3) | (r & 0x07)));
}
/** Emit SIB byte. */
void _emitSib(int s, int i, int b) {
_emitByte((byte) (((s & 0x03) << 6) | ((i & 0x07) << 3) | (b & 0x07)));
}
/** Emit Register / Register - calls _emitMod(3, opReg, r) */
void _emitModR(int opReg, int r) {
_emitMod(3, opReg, r);
}
/** Emit Register / Register - calls _emitMod(3, opReg, r.code()) */
void _emitModR(int opReg, BaseReg r) {
_emitMod(3, opReg, r.code());
}
void _emitX86RM(int opCode, boolean i16bit, boolean rexw, int o, Operand op, int immSize) {
_emitX86RM(opCode, i16bit, intValue(rexw), o, op, immSize);
}
void _emitX86RM(int opCode, boolean i16bit, int rexw, int o, Operand op, int immSize) {
// 16 bit prefix
if (i16bit) {
_emitByte(0x66);
}
// segment prefix
_emitSegmentPrefix(op);
// instruction prefix
if ((opCode & 0xFF000000) != 0) {
_emitByte(((opCode & 0xFF000000) >> 24));
}
// rex prefix
if (is64()) {
_emitRexRM(rexw, o, op);
}
// instruction opcodes
if ((opCode & 0x00FF0000) != 0) {
_emitByte((byte) ((opCode & 0x00FF0000) >> 16));
}
if ((opCode & 0x0000FF00) != 0) {
_emitByte((byte) ((opCode & 0x0000FF00) >> 8));
}
_emitByte((byte) (opCode & 0x000000FF));
// ModR/M
_emitModRM(o, op, immSize);
}
void _emitX86(INST_CODE code, Operand o1, Operand o2, Operand o3) {
InstructionDescription id = InstructionDescription.find(code);
switch (id.group) {
case I_EMIT:
{
_emitOpCode(id.opCode1);
return;
}
case I_ALU:
{
int opCode = id.opCode1;
int opReg = id.opCodeR;
// Mem <- Reg
if (o1.isMem() && o2.isReg())
{
_emitX86RM(opCode + intValue(!o2.isRegType(REG_GPB)),
o2.isRegType(REG_GPW),
o2.isRegType(REG_GPQ),
((Register) o2).code(),
o1,
0);
return;
}
// Reg <- Reg|Mem
if (o1.isReg() && o2.isRegMem())
{
_emitX86RM(opCode + 2 + intValue(!o1.isRegType(REG_GPB)),
o1.isRegType(REG_GPW),
o1.isRegType(REG_GPQ),
((Register) o1).code(),
o2,
0);
return;
}
// AL, AX, EAX, RAX register shortcuts
if (o1.isRegIndex(0) && o2.isImm())
{
if (o1.isRegType(REG_GPW))
_emitByte(0x66); // 16 bit
else if (o1.isRegType(REG_GPQ))
_emitByte(0x48); // REX.W
_emitByte((opReg << 3) | (0x04 + intValue(!o1.isRegType(REG_GPB))));
_emitImmediate(
(Immediate) o2, o1.size() <= 4 ? o1.size() : 4);
return;
}
if (o1.isRegMem() && o2.isImm())
{
final Immediate imm = (Immediate) o2;
int immSize = Util.isInt8(imm.value()) ? 1 : (o1.size() <= 4 ? o1.size() : 4);
_emitX86RM(id.opCode2 + (o1.size() != 1 ? (immSize != 1 ? 1 : 3) : 0),
o1.size() == 2,
o1.size() == 8,
opReg, o1,
immSize);
_emitImmediate(
(Immediate) o2,
immSize);
return;
}
break;
}
case I_BSWAP:
{
if (o1.isReg())
{
final Register dst = (Register) o1;
if (is64()) {
_emitRexR(dst.type() == REG_GPQ, 1, dst.code());
}
_emitByte(0x0F);
_emitModR(1, dst.code());
return;
}
break;
}
case I_BT:
{
if (o1.isRegMem() && o2.isReg())
{
final Operand dst = o1;
final Register src = (Register) o2;
_emitX86RM(id.opCode1,
src.isRegType(REG_GPW),
src.isRegType(REG_GPQ),
src.code(),
dst,
0);
return;
}
if (o1.isRegMem() && o2.isImm())
{
final Operand dst = o1;
final Immediate src = (Immediate) o2;
_emitX86RM(id.opCode2,
src.size() == 2,
src.size() == 8,
id.opCodeR,
dst,
1);
_emitImmediate(src, 1);
return;
}
break;
}
case I_CALL:
{
if (o1.isRegMem(is64() ? REG_GPQ : REG_GPD))
{
final Operand dst = o1;
_emitX86RM(0xFF,
false,
false, 2, dst,
0);
return;
}
if (o1.isImm())
{
final Immediate imm = (Immediate) o1;
_emitByte(0xE8);
_emitJmpOrCallReloc(I_CALL, imm.value());
return;
}
if (o1.isLabel())
{
Label label = (Label) o1;
if (label.isBound())
{
final int rel32_size = 5;
final int offs = label.position() - offset();
assert(offs <= 0);
_emitByte(0xE8);
_emitInt32((int)(offs - rel32_size));
}
else
{
_emitByte(0xE8);
_emitDisplacement(label, -4, 4);
}
return;
}
break;
}
case I_CRC32:
{
if (o1.isReg() && o2.isRegMem())
{
final Register dst = (Register) o1;
final Operand src = o2;
assert(dst.type() == REG_GPD || dst.type() == REG_GPQ);
_emitX86RM(id.opCode1 + intValue(src.size() != 1),
src.size() == 2,
dst.type() == 8, dst.code(), src,
0);
return;
}
break;
}
case I_ENTER:
{
if (o1.isImm() && o2.isImm())
{
_emitByte(0xC8);
_emitImmediate((Immediate) o1, 2);
_emitImmediate((Immediate) o2, 1);
}
break;
}
case I_IMUL:
{
// 1 operand
if (o1.isRegMem() && o2.isNone() && o3.isNone())
{
final Operand src = o1;
_emitX86RM(0xF6 + intValue(src.size() != 1),
src.size() == 2,
src.size() == 8, 5, src,
0);
return;
}
// 2 operands
else if (o1.isReg() && !o2.isNone() && o3.isNone())
{
final Register dst = (Register) o1;
assert(!dst.isRegType(REG_GPW));
if (o2.isRegMem())
{
final Operand src = o2;
_emitX86RM(0x0FAF,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ), dst.code(), src,
0);
return;
}
else if (o2.isImm())
{
final Immediate imm = (Immediate) o2;
if (isInt8(imm.value()) && imm.relocMode() == RELOC_NONE)
{
_emitX86RM(0x6B,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ), dst.code(), dst,
1);
_emitImmediate(imm, 1);
}
else
{
int immSize = dst.isRegType(REG_GPW) ? 2 : 4;
_emitX86RM(0x69,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ), dst.code(), dst,
immSize);
_emitImmediate(imm, immSize);
}
return;
}
}
// 3 operands
else if (o1.isReg() && o2.isRegMem() && o3.isImm())
{
final Register dst = (Register) o1;
final Operand src = o2;
final Immediate imm = (Immediate) o3;
if (isInt8(imm.value()) && imm.relocMode() == RELOC_NONE)
{
_emitX86RM(0x6B,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ), dst.code(), src,
1);
_emitImmediate(imm, 1);
}
else
{
int immSize = dst.isRegType(REG_GPW) ? 2 : 4;
_emitX86RM(0x69,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ), dst.code(), src,
immSize);
_emitImmediate(imm, immSize);
}
return;
}
break;
}
case I_INC_DEC:
{
if (o1.isRegMem())
{
final Operand dst = o1;
// INC [r16|r32] in 64 bit mode is not encodable.
if (!is64() && dst.isReg() && (dst.isRegType(REG_GPW) || dst.isRegType(REG_GPD)))
{
_emitX86Inl(id.opCode1,
dst.isRegType(REG_GPW),
0, ((BaseReg) dst).code());
return;
}
_emitX86RM(id.opCode2 + intValue(dst.size() != 1),
dst.size() == 2,
dst.size() == 8, id.opCodeR, dst,
0);
return;
}
break;
}
case I_J:
{
if (o1.isLabel())
{
Label label = (Label) o1;
boolean isShortJump = (code.ordinal() >= INST_J_SHORT.ordinal() && code.ordinal() <= INST_JMP_SHORT.ordinal());
final HINT hint = o2.isImm() ? HINT.valueOf((int) ((Immediate) o2).value()) : HINT.HINT_NONE;
// Emit jump hint if configured for that.
if (hint == HINT_TAKEN || hint == HINT_NOT_TAKEN
&& (_properties & (1 << PROPERTY_X86_JCC_HINTS)) != 0)
{
_emitByte(hint.value());
}
if (label.isBound())
{
final int rel8_size = 2;
final int rel32_size = 6;
final int offs = label.position() - offset();
assert(offs <= 0);
if (isInt8(offs - rel8_size))
{
_emitByte(0x70 | (id.opCode1 & 0xff));
_emitByte((byte) (offs - rel8_size));
}
else
{
if (isShortJump && _logger != null)
{
_logger.log("; WARNING: Emitting long conditional jump, but short jump instruction forced!");
}
_emitByte(0x0F);
_emitByte(0x80 | (id.opCode1 & 0xff));
_emitInt32((int) (offs - rel32_size));
}
}
else
{
if (isShortJump)
{
_emitByte(0x70 | (id.opCode1 & 0xff));
_emitDisplacement(label, -1, 1);
}
else
{
_emitByte(0x0F);
_emitByte(0x80 | (id.opCode1 & 0xff));
_emitDisplacement(label, -4, 4);
}
}
return;
}
break;
}
case I_JMP:
{
if (o1.isRegMem())
{
final Operand dst = o1;
_emitX86RM(0xFF,
false,
false, 4, dst,
0);
return;
}
if (o1.isImm())
{
final Immediate imm = (Immediate) o1;
_emitByte(0xE9);
_emitJmpOrCallReloc(I_JMP, imm.value());
return;
}
if (o1.isLabel())
{
Label label = (Label) o1;
boolean isShortJump = (code == INST_JMP_SHORT);
if (label.isBound())
{
final int rel8_size = 2;
final int rel32_size = 5;
final int offs = label.position() - offset();
if (isInt8(offs - rel8_size))
{
_emitByte(0xEB);
_emitByte((byte) (offs - rel8_size));
}
else
{
if (isShortJump && _logger != null)
{
_logger.log("; WARNING: Emitting long jump, but short jump instruction forced!");
}
_emitByte(0xE9);
_emitInt32((int)(offs - rel32_size));
}
}
else
{
if (isShortJump)
{
_emitByte(0xEB);
_emitDisplacement(label, -1, 1);
}
else
{
_emitByte(0xE9);
_emitDisplacement(label, -4, 4);
}
}
return;
}
break;
}
case I_LEA:
{
if (o1.isReg() && o2.isMem())
{
final Register dst = (Register) o1;
final Mem src = (Mem) o2;
_emitX86RM(0x8D,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ), dst.code(), src,
0);
return;
}
break;
}
case I_M:
{
if (o1.isMem())
{
_emitX86RM(id.opCode1, false, (byte) id.opCode2, id.opCodeR, (Mem) o1, 0);
return;
}
break;
}
case I_MOV:
{
final Operand dst = o1;
final Operand src = o2;
switch (dst.op() << 4 | src.op())
{
// Reg <- Reg/Mem
case (OP_REG << 4) | OP_REG:
{
assert(src.isRegType(REG_GPB) || src.isRegType(REG_GPW) ||
src.isRegType(REG_GPD) || src.isRegType(REG_GPQ));
// ... fall through ...
}
case (OP_REG << 4) | OP_MEM:
{
assert(dst.isRegType(REG_GPB) || dst.isRegType(REG_GPW) ||
dst.isRegType(REG_GPD) || dst.isRegType(REG_GPQ));
_emitX86RM(0x0000008A + intValue(!dst.isRegType(REG_GPB)),
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ),
((Register) dst).code(),
src,
0);
return;
}
// Reg <- Imm
case (OP_REG << 4) | OP_IMM:
{
final Immediate isrc = (Immediate) o2;
// in 64 bit mode immediate can be 8 byte long!
int immSize = dst.size();
// Optimize instruction size by using 32 bit immediate if value can
// fit to it
if (is64() && immSize == 8 && isInt32(isrc.value()) && isrc.relocMode() == RELOC_NONE)
{
_emitX86RM(0xC7,
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ),
0,
dst,
0);
immSize = 4;
}
else
{
_emitX86Inl((dst.size() == 1 ? 0xB0 : 0xB8),
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ),
((Register) dst).code());
}
_emitImmediate(isrc, immSize);
return;
}
// Mem <- Reg
case (OP_MEM << 4) | OP_REG:
{
assert(src.isRegType(REG_GPB) || src.isRegType(REG_GPW) ||
src.isRegType(REG_GPD) || src.isRegType(REG_GPQ));
_emitX86RM(0x88 + intValue(!src.isRegType(REG_GPB)),
src.isRegType(REG_GPW),
src.isRegType(REG_GPQ),
((Register) src).code(),
dst,
0);
return;
}
// Mem <- Imm
case (OP_MEM << 4) | OP_IMM:
{
int immSize = dst.size() <= 4 ? dst.size() : 4;
_emitX86RM(0xC6 + intValue(dst.size() != 1),
dst.size() == 2,
dst.size() == 8,
0,
dst,
immSize);
_emitImmediate((Immediate) src,
immSize);
return;
}
}
break;
}
case I_MOV_PTR:
{
if ((o1.isReg() && o2.isImm()) || (o1.isImm() && o2.isReg()))
{
boolean reverse = o1.op() == OP_REG;
int opCode = !reverse ? 0xA0 : 0xA2;
final Register reg = (Register)(!reverse ? o1 : o2);
final Immediate imm = (Immediate)(!reverse ? o2 : o1);
if (reg.index() != 0) throw new IllegalStateException("reg.index() != 0");
if (reg.isRegType(REG_GPW)) _emitByte(0x66);
if (is64()) {
_emitRexR(reg.size() == 8, 0, 0);
}
_emitByte(opCode + intValue(reg.size() != 1));
_emitImmediate(imm, is64() ? 8 : 4);
return;
}
break;
}
case I_MOVSX_MOVZX:
{
if (o1.isReg() && o2.isRegMem())
{
final Register dst = (Register)(o1);
final Operand src = (o2);
if (dst.isRegType(REG_GPB)) throw new IllegalArgumentException("not gpb");
if (src.size() != 1 && src.size() != 2) throw new IllegalArgumentException("src.size !=1 && src.size != 2");
if (src.size() == 2 && dst.isRegType(REG_GPW)) throw new IllegalArgumentException("not gpw");
_emitX86RM(id.opCode1 + intValue(src.size() != 1),
dst.isRegType(REG_GPW),
dst.isRegType(REG_GPQ),
dst.code(),
src,
0);
return;
}
break;
}
case I_MOVSXD:
{
if (!is64()) {
throw new IllegalStateException("illegal instruction");
}
if (o1.isReg() && o2.isRegMem())
{
final Register dst = (Register)(o1);
final Operand src = (o2);
_emitX86RM(0x00000063,
false,
1, dst.code(), src,
0);
return;
}
break;
}
case I_PUSH:
{
// This section is only for immediates, memory/register operands are handled in I_POP.
if (o1.isImm())
{
final Immediate imm = (Immediate)(o1);
if (isInt8(imm.value()) && imm.relocMode() == RELOC_NONE)
{
_emitByte(0x6A);
_emitImmediate(imm, 1);
}
else
{
_emitByte(0x68);
_emitImmediate(imm, 4);
}
return;
}
// ... goto I_POP ...
}
case I_POP:
{
if (o1.isReg())
{
assert(o1.isRegType(REG_GPW) || o1.isRegType(is64() ? REG_GPQ : REG_GPD));
_emitX86Inl(id.opCode1, o1.isRegType(REG_GPW), 0, ((Register) o1).code());
return;
}
if (o1.isMem())
{
_emitX86RM(id.opCode2, o1.size() == 2, 0, id.opCodeR, (o1), 0);
return;
}
break;
}
case I_R_RM:
{
if (o1.isReg() && o2.isRegMem())
{
final Register dst = (Register)(o1);
assert(dst.type() != REG_GPB);
final Operand src = (o2);
_emitX86RM(id.opCode1,
dst.type() == REG_GPW,
dst.type() == REG_GPQ, dst.code(), src,
0);
return;
}
break;
}
case I_RM_B:
{
if (o1.isRegMem())
{
final Operand op = (o1);
_emitX86RM(id.opCode1, false, false, 0, op, 0);
return;
}
break;
}
case I_RM:
{
if (o1.isRegMem())
{
final Operand op = (o1);
_emitX86RM(id.opCode1 + intValue(op.size() != 1),
op.size() == 2,
op.size() == 8, id.opCodeR, op,
0);
return;
}
break;
}
case I_RM_R:
{
if (o1.isRegMem() && o2.isReg())
{
final Operand dst = (o1);
final Register src = (Register)(o2);
_emitX86RM(id.opCode1 + intValue(src.type() != REG_GPB),
src.type() == REG_GPW,
src.type() == REG_GPQ, src.code(), dst,
0);
return;
}
break;
}
case I_RET:
{
if (o1.isNone())
{
_emitByte(0xC3);
return;
}
else if (o1.isImm())
{
final Immediate imm = (Immediate)(o1);
assert(isUInt16(imm.value()));
if (imm.value() == 0 && imm.relocMode() == RELOC_NONE)
{
_emitByte(0xC3);
}
else
{
_emitByte(0xC2);
_emitImmediate(imm, 2);
}
return;
}
break;
}
case I_ROT:
{
if (o1.isRegMem() && (o2.isRegCode(REG_CL) || o2.isImm()))
{
// generate opcode. For these operations is base 0xC0 or 0xD0.
boolean useImm8 = (o2.isImm() &&
(((Immediate) o2).value() != 1 ||
((Immediate) o2).relocMode() != RELOC_NONE));
int opCode = useImm8 ? 0xC0 : 0xD0;
// size and operand type modifies the opcode
if (o1.size() != 1) opCode |= 0x01;
if (o2.op() == OP_REG) opCode |= 0x02;
_emitX86RM(opCode,
o1.size() == 2,
o1.size() == 8,
id.opCodeR, (o1),
intValue(useImm8));
if (useImm8)
_emitImmediate((Immediate)(o2), 1);
return;
}
break;
}
case I_SHLD_SHRD:
{
if (o1.isRegMem() && o2.isReg() && (o3.isImm() || (o3.isReg() && o3.isRegCode(REG_CL))))
{
final Operand dst = (o1);
final Register src1 = (Register)(o2);
final Operand src2 = (o3);
assert(dst.size() == src1.size());
_emitX86RM(id.opCode1 + intValue(src2.isReg()),
src1.isRegType(REG_GPW),
src1.isRegType(REG_GPQ),
src1.code(), dst,
intValue(src2.isImm()));
if (src2.isImm())
_emitImmediate((Immediate)(src2), 1);
return;
}
break;
}
case I_TEST:
{
if (o1.isRegMem() && o2.isReg())
{
assert(o1.size() == o2.size());
_emitX86RM(0x84 + intValue(o2.size() != 1),
o2.size() == 2, o2.size() == 8,
((BaseReg) o2).code(),
(o1),
0);
return;
}
if (o1.isRegIndex(0) && o2.isImm())
{
int immSize = o1.size() <= 4 ? o1.size() : 4;
if (o1.size() == 2) _emitByte(0x66); // 16 bit
if (is64()) {
_emitRexRM(o1.size() == 8, 0, (o1));
}
_emitByte(0xA8 + intValue(o1.size() != 1));
_emitImmediate((Immediate)(o2), immSize);
return;
}
if (o1.isRegMem() && o2.isImm())
{
int immSize = o1.size() <= 4 ? o1.size() : 4;
if (o1.size() == 2) _emitByte(0x66); // 16 bit
_emitSegmentPrefix((o1)); // segment prefix
if (is64()) _emitRexRM(o1.size() == 8, 0, (o1));
_emitByte(0xF6 + intValue(o1.size() != 1));
_emitModRM(0, (o1), immSize);
_emitImmediate((Immediate)(o2), immSize);
return;
}
break;
}
case I_XCHG:
{
if (o1.isRegMem() && o2.isReg())
{
final Operand dst = (o1);
final Register src = (Register)(o2);
if (src.isRegType(REG_GPW)) _emitByte(0x66); // 16 bit
_emitSegmentPrefix(dst); // segment prefix
if (is64()) _emitRexRM(src.isRegType(REG_GPQ), src.code(), dst);
// Special opcode for index 0 registers (AX, EAX, RAX vs register)
if ((dst.op() == OP_REG && dst.size() > 1) &&
(((Register) dst).code() == 0 ||
((Register) src).code() == 0))
{
int index = ((Register) dst).code() | src.code();
_emitByte((byte) (0x90 + index));
return;
}
_emitByte(0x86 + intValue(!src.isRegType(REG_GPB)));
_emitModRM(src.code(), dst, 0);
return;
}
break;
}
case I_MOVBE:
{
if (o1.isReg() && o2.isMem())
{
_emitX86RM(0x000F38F0,
o1.isRegType(REG_GPW),
o1.isRegType(REG_GPQ),
((Register) o1).code(),
(Mem)(o2),
0);
return;
}
if (o1.isMem() && o2.isReg())
{
_emitX86RM(0x000F38F1,
o2.isRegType(REG_GPW),
o2.isRegType(REG_GPQ),
((Register) o2).code(),
(Mem)(o1),
0);
return;
}
break;
}
case I_X87_FPU:
{
if (o1.isRegType(REG_X87))
{
int i1 = ((X87Register) o1).index();
int i2 = 0;
if (code != INST_FCOM && code != INST_FCOMP)
{
if (!o2.isRegType(REG_X87)) throw new IllegalArgumentException("not x87 reg");
i2 = ((X87Register) o2).index();
}
else if (i1 != 0 && i2 != 0)
{
throw new IllegalArgumentException("illegal instruction");
}
_emitByte(i1 == 0
? ((id.opCode1 & 0xFF000000) >> 24)
: ((id.opCode1 & 0x00FF0000) >> 16));
_emitByte(i1 == 0
? ((id.opCode1 & 0x0000FF00) >> 8) + i2
: ((id.opCode1 & 0x000000FF) ) + i1);
return;
}
if (o1.isMem() && (o1.size() == 4 || o1.size() == 8) && o2.isNone())
{
final Mem m = (Mem)(o1);
// segment prefix
_emitSegmentPrefix(m);
_emitByte(o1.size() == 4
? ((id.opCode1 & 0xFF000000) >> 24)
: ((id.opCode1 & 0x00FF0000) >> 16));
_emitModM(id.opCodeR, m, 0);
return;
}
break;
}
case I_X87_STI:
{
if (o1.isRegType(REG_X87))
{
int i = ((X87Register) o1).index();
_emitByte(((id.opCode1 & 0x0000FF00) >> 8));
_emitByte(((id.opCode1 & 0x000000FF) + i));
return;
}
break;
}
case I_X87_FSTSW:
{
if (o1.isReg() &&
((BaseReg) o1).type() <= REG_GPQ &&
((BaseReg) o1).index() == 0)
{
_emitOpCode(id.opCode2);
return;
}
if (o1.isMem())
{
_emitX86RM(id.opCode1, false, 0, id.opCodeR, (Mem)(o1), 0);
return;
}
break;
}
case I_X87_MEM_STI:
{
if (o1.isRegType(REG_X87))
{
_emitByte(((id.opCode2 & 0xFF000000) >> 24));
_emitByte(((id.opCode2 & 0x00FF0000) >> 16) +
((X87Register) o1).index());
return;
}
// ... fall through to I_X87_MEM ...
}
case I_X87_MEM:
{
if (!o1.isMem()) throw new IllegalArgumentException("not x87 mem");
final Mem m = (Mem)(o1);
int opCode = 0x00, mod = 0;
if (o1.size() == 2 && (id.o1Flags & O_FM_2) != 0)
{
opCode = ((id.opCode1 & 0xFF000000) >> 24);
mod = id.opCodeR;
}
if (o1.size() == 4 && (id.o1Flags & O_FM_4) != 0)
{
opCode = ((id.opCode1 & 0x00FF0000) >> 16);
mod = id.opCodeR;
}
if (o1.size() == 8 && (id.o1Flags & O_FM_8) != 0)
{
opCode = ((id.opCode1 & 0x0000FF00) >> 8);
mod = ((id.opCode1 & 0x000000FF) );
}
if (opCode != 0)
{
_emitSegmentPrefix(m);
_emitByte(opCode);
_emitModM(mod, m, 0);
return;
}
break;
}
case I_MMU_MOV:
{
assert(id.o1Flags != 0);
assert(id.o2Flags != 0);
// Check parameters (X)MM|GP32_64 <- (X)MM|GP32_64|Mem|Imm
if ((o1.isMem() && (id.o1Flags & O_MEM) == 0) ||
(o1.isRegType(REG_MM ) && (id.o1Flags & O_MM ) == 0) ||
(o1.isRegType(REG_XMM) && (id.o1Flags & O_XMM) == 0) ||
(o1.isRegType(REG_GPD) && (id.o1Flags & O_G32) == 0) ||
(o1.isRegType(REG_GPQ) && (id.o1Flags & O_G64) == 0) ||
(o2.isRegType(REG_MM ) && (id.o2Flags & O_MM ) == 0) ||
(o2.isRegType(REG_XMM) && (id.o2Flags & O_XMM) == 0) ||
(o2.isRegType(REG_GPD) && (id.o2Flags & O_G32) == 0) ||
(o2.isRegType(REG_GPQ) && (id.o2Flags & O_G64) == 0) ||
(o2.isMem() && (id.o2Flags & O_MEM) == 0) )
{
throw new IllegalArgumentException("illegal instruction");
}
// Illegal
if (o1.isMem() && o2.isMem()) throw new IllegalArgumentException("illegal instruction");
int rexw = ((id.o1Flags|id.o2Flags) & O_NOREX) != 0
? 0
: intValue(o1.isRegType(REG_GPQ) || o1.isRegType(REG_GPQ));
// (X)MM|Reg <- (X)MM|Reg
if (o1.isReg() && o2.isReg())
{
_emitMmu(id.opCode1, rexw,
((BaseReg) o1).code(),
(BaseReg)(o2),
0);
return;
}
// (X)MM|Reg <- Mem
if (o1.isReg() && o2.isMem())
{
_emitMmu(id.opCode1, rexw,
((BaseReg) o1).code(),
(Mem)(o2),
0);
return;
}
// Mem <- (X)MM|Reg
if (o1.isMem() && o2.isReg())
{
_emitMmu(id.opCode2, rexw,
((BaseReg) o2).code(),
(Mem)(o1),
0);
return;
}
break;
}
case I_MMU_MOVD:
{
if ((o1.isRegType(REG_MM) || o1.isRegType(REG_XMM)) && (o2.isRegType(REG_GPD) || o2.isMem()))
{
_emitMmu(o1.isRegType(REG_XMM) ? 0x66000F6E : 0x00000F6E, 0,
((BaseReg) o1).code(),
(o2),
0);
return;
}
if ((o1.isRegType(REG_GPD) || o1.isMem()) && (o2.isRegType(REG_MM) || o2.isRegType(REG_XMM)))
{
_emitMmu(o2.isRegType(REG_XMM) ? 0x66000F7E : 0x00000F7E, 0,
((BaseReg) o2).code(),
(o1),
0);
return;
}
break;
}
case I_MMU_MOVQ:
{
if (o1.isRegType(REG_MM) && o2.isRegType(REG_MM))
{
_emitMmu(0x00000F6F, 0,
((MMRegister) o1).code(),
(MMRegister)(o2),
0);
return;
}
if (o1.isRegType(REG_XMM) && o2.isRegType(REG_XMM))
{
_emitMmu(0xF3000F7E, 0,
((XMMRegister) o1).code(),
(XMMRegister)(o2),
0);
return;
}
// Convenience - movdq2q
if (o1.isRegType(REG_MM) && o2.isRegType(REG_XMM))
{
_emitMmu(0xF2000FD6, 0,
((MMRegister) o1).code(),
(XMMRegister)(o2),
0);
return;
}
// Convenience - movq2dq
if (o1.isRegType(REG_XMM) && o2.isRegType(REG_MM))
{
_emitMmu(0xF3000FD6, 0,
((XMMRegister) o1).code(),
(MMRegister)(o2),
0);
return;
}
if (o1.isRegType(REG_MM) && o2.isMem())
{
_emitMmu(0x00000F6F, 0,
((MMRegister) o1).code(),
(Mem)(o2),
0);
return;
}
if (o1.isRegType(REG_XMM) && o2.isMem())
{
_emitMmu(0xF3000F7E, 0,
((XMMRegister) o1).code(),
(Mem)(o2),
0);
return;
}
if (o1.isMem() && o2.isRegType(REG_MM))
{
_emitMmu(0x00000F7F, 0,
((MMRegister) o2).code(),
(Mem)(o1),
0);
return;
}
if (o1.isMem() && o2.isRegType(REG_XMM))
{
_emitMmu(0x66000FD6, 0,
((XMMRegister) o2).code(),
(Mem)(o1),
0);
return;
}
if (is64()) {
if ((o1.isRegType(REG_MM) || o1.isRegType(REG_XMM)) && (o2.isRegType(REG_GPQ) || o2.isMem()))
{
_emitMmu(o1.isRegType(REG_XMM) ? 0x66000F6E : 0x00000F6E, 1,
((BaseReg) o1).code(),
(o2),
0);
return;
}
if ((o1.isRegType(REG_GPQ) || o1.isMem()) && (o2.isRegType(REG_MM) || o2.isRegType(REG_XMM)))
{
_emitMmu(o2.isRegType(REG_XMM) ? 0x66000F7E : 0x00000F7E, 1,
((BaseReg) o2).code(),
(o1),
0);
return;
}
}
break;
}
case I_MMU_PREFETCH:
{
if (o1.isMem() && o2.isImm())
{
final Mem mem = (Mem)(o1);
final Immediate hint = (Immediate)(o2);
_emitMmu(0x00000F18, 0, (int) hint.value(), mem, 0);
return;
}
break;
}
case I_MMU_PEXTR:
{
if (!(o1.isRegMem() &&
(o2.isRegType(REG_XMM) || (code == INST_PEXTRW && o2.isRegType(REG_MM))) &&
o3.isImm()))
{
throw new IllegalStateException("illegal instruction");
}
int opCode = id.opCode1;
boolean isGpdGpq = o1.isRegType(REG_GPD) || o1.isRegType(REG_GPQ);
if (code == INST_PEXTRB && (o1.size() != 0 && o1.size() != 1) && !isGpdGpq) throw new IllegalStateException("illegal instruction");
if (code == INST_PEXTRW && (o1.size() != 0 && o1.size() != 2) && !isGpdGpq) throw new IllegalStateException("illegal instruction");
if (code == INST_PEXTRD && (o1.size() != 0 && o1.size() != 4) && !isGpdGpq) throw new IllegalStateException("illegal instruction");
if (code == INST_PEXTRQ && (o1.size() != 0 && o1.size() != 8) && !isGpdGpq) throw new IllegalStateException("illegal instruction");
if (o2.isRegType(REG_XMM)) opCode |= 0x66000000;
if (o1.isReg())
{
_emitMmu(opCode, id.opCodeR | intValue(o1.isRegType(REG_GPQ)),
((BaseReg) o2).code(),
(BaseReg)(o1), 1);
_emitImmediate(
(Immediate)(o3), 1);
return;
}
if (o1.isMem())
{
_emitMmu(opCode, id.opCodeR,
((BaseReg) o2).code(),
(Mem)(o1), 1);
_emitImmediate(
(Immediate)(o3), 1);
return;
}
break;
}
case I_MMU_RMI:
{
assert(id.o1Flags != 0);
assert(id.o2Flags != 0);
// Check parameters (X)MM|GP32_64 <- (X)MM|GP32_64|Mem|Imm
if (!o1.isReg() ||
(o1.isRegType(REG_MM ) && (id.o1Flags & O_MM ) == 0) ||
(o1.isRegType(REG_XMM) && (id.o1Flags & O_XMM) == 0) ||
(o1.isRegType(REG_GPD) && (id.o1Flags & O_G32) == 0) ||
(o1.isRegType(REG_GPQ) && (id.o1Flags & O_G64) == 0) ||
(o2.isRegType(REG_MM ) && (id.o2Flags & O_MM ) == 0) ||
(o2.isRegType(REG_XMM) && (id.o2Flags & O_XMM) == 0) ||
(o2.isRegType(REG_GPD) && (id.o2Flags & O_G32) == 0) ||
(o2.isRegType(REG_GPQ) && (id.o2Flags & O_G64) == 0) ||
(o2.isMem() && (id.o2Flags & O_MEM) == 0) ||
(o2.isImm() && (id.o2Flags & O_IMM) == 0))
{
throw new IllegalStateException("illegal instruction");
}
int prefix =
((id.o1Flags & O_MM_XMM) == O_MM_XMM && o1.isRegType(REG_XMM)) ||
((id.o2Flags & O_MM_XMM) == O_MM_XMM && o2.isRegType(REG_XMM))
? 0x66000000
: 0x00000000;
int rexw = ((id.o1Flags|id.o2Flags) & O_NOREX) != 0
? 0
: intValue(o1.isRegType(REG_GPQ) || o1.isRegType(REG_GPQ));
// (X)MM <- (X)MM (opcode1)
if (o2.isReg())
{
if ((id.o2Flags & (O_MM_XMM | O_G32_64)) == 0) throw new IllegalStateException("illegal instruction");
_emitMmu(id.opCode1 | prefix, rexw,
((BaseReg) o1).code(),
(BaseReg)(o2), 0);
return;
}
// (X)MM <- Mem (opcode1)
if (o2.isMem())
{
if ((id.o2Flags & O_MEM) == 0) throw new IllegalStateException("illegal instruction");
_emitMmu(id.opCode1 | prefix, rexw,
((BaseReg) o1).code(),
(Mem)(o2), 0);
return;
}
// (X)MM <- Imm (opcode2+opcodeR)
if (o2.isImm())
{
if ((id.o2Flags & O_IMM) == 0) throw new IllegalStateException("illegal instruction");
_emitMmu(id.opCode2 | prefix, rexw,
id.opCodeR,
(BaseReg)(o1), 1);
_emitImmediate(
(Immediate)(o2), 1);
return;
}
break;
}
case I_MMU_RM_IMM8:
{
assert(id.o1Flags != 0);
assert(id.o2Flags != 0);
// Check parameters (X)MM|GP32_64 <- (X)MM|GP32_64|Mem|Imm
if (!o1.isReg() ||
(o1.isRegType(REG_MM ) && (id.o1Flags & O_MM ) == 0) ||
(o1.isRegType(REG_XMM) && (id.o1Flags & O_XMM) == 0) ||
(o1.isRegType(REG_GPD) && (id.o1Flags & O_G32) == 0) ||
(o1.isRegType(REG_GPQ) && (id.o1Flags & O_G64) == 0) ||
(o2.isRegType(REG_MM ) && (id.o2Flags & O_MM ) == 0) ||
(o2.isRegType(REG_XMM) && (id.o2Flags & O_XMM) == 0) ||
(o2.isRegType(REG_GPD) && (id.o2Flags & O_G32) == 0) ||
(o2.isRegType(REG_GPQ) && (id.o2Flags & O_G64) == 0) ||
(o2.isMem() && (id.o2Flags & O_MEM) == 0) ||
!o3.isImm())
{
throw new IllegalStateException("illegal instruction");
}
int prefix =
((id.o1Flags & O_MM_XMM) == O_MM_XMM && o1.isRegType(REG_XMM)) ||
((id.o2Flags & O_MM_XMM) == O_MM_XMM && o2.isRegType(REG_XMM))
? 0x66000000
: 0x00000000;
int rexw = ((id.o1Flags|id.o2Flags) & O_NOREX) != 0
? 0
: intValue(o1.isRegType(REG_GPQ) || o1.isRegType(REG_GPQ));
// (X)MM <- (X)MM (opcode1)
if (o2.isReg())
{
if ((id.o2Flags & (O_MM_XMM | O_G32_64)) == 0) throw new IllegalStateException("illegal instruction");
_emitMmu(id.opCode1 | prefix, rexw,
((BaseReg) o1).code(),
(BaseReg)(o2), 1);
_emitImmediate((Immediate)(o3), 1);
return;
}
// (X)MM <- Mem (opcode1)
if (o2.isMem())
{
if ((id.o2Flags & O_MEM) == 0) throw new IllegalStateException("illegal instruction");
_emitMmu(id.opCode1 | prefix, rexw,
((BaseReg) o1).code(),
(Mem)(o2), 1);
_emitImmediate((Immediate)(o3), 1);
return;
}
break;
}
case I_MMU_RM_3DNOW:
{
if (o1.isRegType(REG_MM) && (o2.isRegType(REG_MM) || o2.isMem()))
{
_emitMmu(id.opCode1, 0,
((BaseReg) o1).code(),
(Mem)(o2), 1);
_emitByte(id.opCode2);
return;
}
break;
}
}
}
void _emitFpu(int opCode) {
_emitOpCode(opCode);
}
void _emitFpuSTI(int opCode, int sti) {
// illegal stack offset
assert (0 <= sti && sti < 8);
_emitOpCode(opCode + sti);
}
void _emitFpuMEM(int opCode, int opReg, Mem mem) {
// segment prefix
_emitSegmentPrefix(mem);
// instruction prefix
if ((opCode & 0xFF000000) != 0) {
_emitByte(((opCode & 0xFF000000) >> 24));
}
// rex prefix
if (is64()) {
_emitRexRM(0, opReg, mem);
}
// instruction opcodes
if ((opCode & 0x00FF0000) != 0) {
_emitByte(((opCode & 0x00FF0000) >> 16));
}
if ((opCode & 0x0000FF00) != 0) {
_emitByte(((opCode & 0x0000FF00) >> 8));
}
_emitByte(((opCode & 0x000000FF)));
_emitModM(opReg, mem, 0);
}
void _emitMmu(int opCode, int rexw, int opReg,
Operand src, int immSize) {
// Segment prefix.
_emitSegmentPrefix(src);
// Instruction prefix.
if ((opCode & 0xFF000000) != 0) {
_emitByte(((opCode & 0xFF000000) >> 24));
}
// Rex prefix
if (is64()) {
_emitRexRM(rexw, opReg, src);
}
// Instruction opcodes.
if ((opCode & 0x00FF0000) != 0) {
_emitByte(((opCode & 0x00FF0000) >> 16));
}
// No checking, MMX/SSE instructions have always two opcodes or more.
_emitByte(((opCode & 0x0000FF00) >> 8));
_emitByte(((opCode & 0x000000FF)));
if (src.isReg()) {
_emitModR(opReg, ((BaseReg) src).code());
} else {
_emitModM(opReg, (Mem) src, immSize);
}
}
LinkData _emitDisplacement(Label label, long inlinedDisplacement, int size) {
assert (!label.isBound());
assert (size == 1 || size == 4);
// Chain with label.
LinkData link = new LinkData(offset(), inlinedDisplacement, -1);
label.link(link);
// Emit dummy DWORD.
if (size == 1) {
_emitByte(0x01);
} else // if (size == 4)
{
_emitDWord(0x04040404);
}
return link;
}
void _emitJmpOrCallReloc(InstructionGroup instruction, long target) {
if (is64()) {
// If we are compiling in 64-bit mode, we can use trampoline if relative jump
// is not possible.
_trampolineSize += TrampolineWriter.TRAMPOLINE_SIZE;
}
RelocData rd = new RelocData(RelocData.Type.ABSOLUTE_TO_RELATIVE_TRAMPOLINE, 4, offset(), target);
_relocData.add(rd);
// Emit dummy 32-bit integer (will be overwritten by relocCode()).
_emitInt32(0);
}
public void relocCode(ByteBuffer buffer, long address) {
// Copy code to virtual memory (this is a given _dst pointer).
int csize = codeSize();
// We are copying exactly size of generated code. Extra code for trampolines
// is generated on-the-fly by relocator (this code not exists at now).
_buffer.copyTo(buffer);
// Relocate recorded locations.
for (RelocData r : _relocData) {
long val;
// Whether to use trampoline, can be only used if relocation type is
// ABSOLUTE_TO_RELATIVE_TRAMPOLINE.
boolean useTrampoline = false;
// Be sure that reloc data structure is correct.
assert ((r.offset + r.size) <= csize);
switch (r.type) {
case ABSOLUTE_TO_ABSOLUTE:
val = r.destination;
break;
case RELATIVE_TO_ABSOLUTE:
val = address + r.destination;
break;
case ABSOLUTE_TO_RELATIVE:
case ABSOLUTE_TO_RELATIVE_TRAMPOLINE:
val = r.destination - (address + r.offset + 4);
if (is64() && r.type == RelocData.Type.ABSOLUTE_TO_RELATIVE_TRAMPOLINE && !isInt32(val)) {
val = (long) buffer.position() - (r.offset + 4);
useTrampoline = true;
}
break;
default:
throw new IllegalStateException("invalid relocation type");
}
switch (r.size) {
case 4:
buffer.putInt(r.offset, (int) val);
break;
case 8:
buffer.putLong(r.offset, val);
break;
default:
throw new IllegalStateException("invalid relocation size");
}
if (is64() && useTrampoline) {
if (_logger != null) {
_logger.log(String.format("; Trampoline from %x -> %x\n", address + r.offset, r.destination));
}
TrampolineWriter.writeTrampoline(buffer, r.destination);
}
}
}
// NOPs optimized for Intel:
// Intel 64 and IA-32 Architectures Software Developer's Manual
// - Volume 2B
// - Instruction Set Reference N-Z
// - NOP
// NOPs optimized for AMD:
// Software Optimization Guide for AMD Family 10h Processors (Quad-Core)
// - 4.13 - Code Padding with Operand-Size Override and Multibyte NOP
// Intel and AMD
private static final int nop1[] = { 0x90 };
private static final int nop2[] = { 0x66, 0x90 };
private static final int nop3[] = { 0x0F, 0x1F, 0x00 };
private static final int nop4[] = { 0x0F, 0x1F, 0x40, 0x00 };
private static final int nop5[] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 };
private static final int nop6[] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 };
private static final int nop7[] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 };
private static final int nop8[] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
private static final int nop9[] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
// AMD
private static final int nop10[] = { 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
private static final int nop11[] = { 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
public void align(long m) {
if (_logger != null) _logger.logAlign(m);
if (m < 1) return;
if (m > 64) {
assert (m <= 64);
return;
}
int i = (int) (m - (offset() % m));
if (i == m) return;
if ((_properties & (1 << PROPERTY_OPTIMIZE_ALIGN)) != 0) {
int n;
if (cpuInfo.vendor == CpuInfo.Vendor.INTEL &&
((cpuInfo.family & 0x0F) == 6 ||
(cpuInfo.family & 0x0F) == 15)) {
do {
int[] p;
switch (i) {
case 1: p = nop1; n = 1; break;
case 2: p = nop2; n = 2; break;
case 3: p = nop3; n = 3; break;
case 4: p = nop4; n = 4; break;
case 5: p = nop5; n = 5; break;
case 6: p = nop6; n = 6; break;
case 7: p = nop7; n = 7; break;
case 8: p = nop8; n = 8; break;
default: p = nop9; n = 9; break;
}
i -= n;
for (int idx = 0; n > 0; ++idx, --n) {
_emitByte(p[idx]);
}
} while (i > 0);
return;
}
if (cpuInfo.vendor == CpuInfo.Vendor.AMD
&& cpuInfo.family >= 0x0F) {
do {
int[] p;
switch (i) {
case 1: p = nop1 ; n = 1; break;
case 2: p = nop2 ; n = 2; break;
case 3: p = nop3 ; n = 3; break;
case 4: p = nop4 ; n = 4; break;
case 5: p = nop5 ; n = 5; break;
case 6: p = nop6 ; n = 6; break;
case 7: p = nop7 ; n = 7; break;
case 8: p = nop8 ; n = 8; break;
case 9: p = nop9 ; n = 9; break;
case 10: p = nop10; n = 10; break;
default: p = nop11; n = 11; break;
}
i -= n;
for (int idx = 0; n > 0; ++idx, --n) {
_emitByte(p[idx]);
}
} while (i > 0);
return;
}
if (!is64()) {
// legacy NOPs, 0x90 with 0x66 prefix.
do {
switch (i) {
default: _emitByte(0x66); i--;
case 3: _emitByte(0x66); i--;
case 2: _emitByte(0x66); i--;
case 1: _emitByte(0x90); i--;
}
} while (i > 0);
}
}
// legacy NOPs, only 0x90
// In 64-bit mode, we can't use 0x66 prefix
while (i-- > 0) {
_emitByte(0x90);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy