/** * Copyright 2013, Landz and its contributors. All rights reserved. * * 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 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> _relocData = new LinkedList<RelocData>(); 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); } } }