diff options
Diffstat (limited to 'lib/AsmJit/AssemblerX86X64.cpp')
-rw-r--r-- | lib/AsmJit/AssemblerX86X64.cpp | 2979 |
1 files changed, 2979 insertions, 0 deletions
diff --git a/lib/AsmJit/AssemblerX86X64.cpp b/lib/AsmJit/AssemblerX86X64.cpp new file mode 100644 index 0000000..904c1c9 --- /dev/null +++ b/lib/AsmJit/AssemblerX86X64.cpp @@ -0,0 +1,2979 @@ +// AsmJit - Complete JIT Assembler for C++ Language. + +// Copyright (c) 2008-2010, Petr Kobalicek <kobalicek.petr@gmail.com> +// +// 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. + +// We are using sprintf() here. +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif // _MSC_VER + +// [Dependencies] +#include "Assembler.h" +#include "CodeGenerator.h" +#include "CpuInfo.h" +#include "Defs.h" +#include "Logger.h" +#include "MemoryManager.h" +#include "Platform.h" +#include "Util_p.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +// A little bit C++. +#include <new> + +// [Api-Begin] +#include "ApiBegin.h" + +namespace AsmJit { + +#if defined(ASMJIT_X64) + +// ============================================================================ +// [AsmJit::TrampolineWriter] +// ============================================================================ + +//! @brief Class used to determine size of trampoline and as trampoline writer. +struct ASMJIT_HIDDEN TrampolineWriter +{ + // Size of trampoline + enum { + TRAMPOLINE_JMP = 6, + TRAMPOLINE_ADDR = sizeof(sysint_t), + + TRAMPOLINE_SIZE = TRAMPOLINE_JMP + TRAMPOLINE_ADDR + }; + + // Write trampoline into code at address @a code that will jump to @a target. + static void writeTrampoline(uint8_t* code, void* target) + { + // Jmp. + code[0] = 0xFF; + // ModM (RIP addressing). + code[1] = 0x25; + // Offset (zero). + ((uint32_t*)(code + 2))[0] = 0; + // Absolute address. + ((sysuint_t*)(code + TRAMPOLINE_JMP))[0] = (sysuint_t)target; + } +}; + +#endif // ASMJIT_X64 + +// ============================================================================ +// [AsmJit::AssemblerCore - Construction / Destruction] +// ============================================================================ + +AssemblerCore::AssemblerCore(CodeGenerator* codeGenerator) ASMJIT_NOTHROW : + _codeGenerator(codeGenerator != NULL ? codeGenerator : CodeGenerator::getGlobal()), + _zone(16384 - sizeof(Zone::Chunk) - 32), + _logger(NULL), + _error(0), + _properties((1 << PROPERTY_OPTIMIZE_ALIGN)), + _emitOptions(0), + _buffer(32), // Max instruction length is 15, but we can align up to 32 bytes. + _trampolineSize(0), + _unusedLinks(NULL), + _comment(NULL) +{ +} + +AssemblerCore::~AssemblerCore() ASMJIT_NOTHROW +{ +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Logging] +// ============================================================================ + +void AssemblerCore::setLogger(Logger* logger) ASMJIT_NOTHROW +{ + _logger = logger; +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Error Handling] +// ============================================================================ + +void AssemblerCore::setError(uint32_t error) ASMJIT_NOTHROW +{ + _error = error; + if (_error == ERROR_NONE) return; + + if (_logger) + { + _logger->logFormat("*** ASSEMBLER ERROR: %s (%u).\n", + getErrorCodeAsString(error), + (unsigned int)error); + } +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Properties] +// ============================================================================ + +uint32_t AssemblerCore::getProperty(uint32_t propertyId) +{ + return (_properties & (1 << propertyId)) != 0; +} + +void AssemblerCore::setProperty(uint32_t propertyId, uint32_t value) +{ + if (value) + _properties |= (1 << propertyId); + else + _properties &= ~(1 << propertyId); +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Buffer] +// ============================================================================ + +void AssemblerCore::clear() ASMJIT_NOTHROW +{ + _buffer.clear(); + _relocData.clear(); + _zone.clear(); + + if (_error) setError(ERROR_NONE); +} + +void AssemblerCore::free() ASMJIT_NOTHROW +{ + _zone.freeAll(); + _buffer.free(); + _relocData.free(); + + if (_error) setError(ERROR_NONE); +} + +uint8_t* AssemblerCore::takeCode() ASMJIT_NOTHROW +{ + uint8_t* code = _buffer.take(); + _relocData.clear(); + _zone.clear(); + + if (_error) setError(ERROR_NONE); + return code; +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Stream Setters / Getters] +// ============================================================================ + +void AssemblerCore::setVarAt(sysint_t pos, sysint_t i, uint8_t isUnsigned, uint32_t size) ASMJIT_NOTHROW +{ + if (size == 1 && !isUnsigned) setByteAt (pos, (int8_t )i); + else if (size == 1 && isUnsigned) setByteAt (pos, (uint8_t )i); + else if (size == 2 && !isUnsigned) setWordAt (pos, (int16_t )i); + else if (size == 2 && isUnsigned) setWordAt (pos, (uint16_t)i); + else if (size == 4 && !isUnsigned) setDWordAt(pos, (int32_t )i); + else if (size == 4 && isUnsigned) setDWordAt(pos, (uint32_t)i); +#if defined(ASMJIT_X64) + else if (size == 8 && !isUnsigned) setQWordAt(pos, (int64_t )i); + else if (size == 8 && isUnsigned) setQWordAt(pos, (uint64_t)i); +#endif // ASMJIT_X64 + else + ASMJIT_ASSERT(0); +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Assembler Emitters] +// ============================================================================ + +bool AssemblerCore::canEmit() ASMJIT_NOTHROW +{ + // If there is an error, we can't emit another instruction until last error + // is cleared by calling @c setError(ERROR_NONE). If something caused an error + // while generating code it's probably fatal in all cases. You can't use + // generated code, because you are not sure about its status. + if (_error) return false; + + // The ensureSpace() method returns true on success and false on failure. We + // are catching return value and setting error code here. + if (ensureSpace()) return true; + + // If we are here, there is memory allocation error. Note that this is HEAP + // allocation error, virtual allocation error can be caused only by + // AsmJit::VirtualMemory class! + setError(ERROR_NO_HEAP_MEMORY); + return false; +} + +void AssemblerCore::_emitSegmentPrefix(const Operand& rm) ASMJIT_NOTHROW +{ + static const uint8_t prefixes[] = { 0x00, 0x2E, 0x36, 0x3E, 0x26, 0x64, 0x65 }; + + if (rm.isMem()) + { + sysuint_t segmentPrefix = reinterpret_cast<const Mem&>(rm).getSegmentPrefix(); + if (segmentPrefix) _emitByte(prefixes[segmentPrefix]); + } +} + +void AssemblerCore::_emitModM( + uint8_t opReg, const Mem& mem, sysint_t immSize) ASMJIT_NOTHROW +{ + ASMJIT_ASSERT(mem.getType() == OPERAND_MEM); + + uint8_t baseReg = mem.getBase() & 0x7; + uint8_t indexReg = mem.getIndex() & 0x7; + sysint_t disp = mem.getDisplacement(); + uint32_t shift = mem.getShift(); + + if (mem.getMemType() == OPERAND_MEM_NATIVE) + { + // [base + displacemnt] + if (!mem.hasIndex()) + { + // ESP/RSP/R12 == 4 + if (baseReg == 4) + { + uint8_t mod = 0; + + if (disp) + { + mod = Util::isInt8(disp) ? 1 : 2; + } + + _emitMod(mod, opReg, 4); + _emitSib(0, 4, 4); + + if (disp) + { + if (Util::isInt8(disp)) + _emitByte((int8_t)disp); + else + _emitInt32((int32_t)disp); + } + } + // EBP/RBP/R13 == 5 + else if (baseReg != 5 && disp == 0) + { + _emitMod(0, opReg, baseReg); + } + else if (Util::isInt8(disp)) + { + _emitMod(1, opReg, baseReg); + _emitByte((int8_t)disp); + } + else + { + _emitMod(2, opReg, baseReg); + _emitInt32((int32_t)disp); + } + } + + // [base + index * scale + displacemnt] + else + { + // ASMJIT_ASSERT(indexReg != RID_ESP); + + // EBP/RBP/R13 == 5 + if (baseReg != 5 && disp == 0) + { + _emitMod(0, opReg, 4); + _emitSib(shift, indexReg, baseReg); + } + else if (Util::isInt8(disp)) + { + _emitMod(1, opReg, 4); + _emitSib(shift, indexReg, baseReg); + _emitByte((int8_t)disp); + } + else + { + _emitMod(2, opReg, 4); + _emitSib(shift, indexReg, baseReg); + _emitInt32((int32_t)disp); + } + } + } + + // Address | 32-bit mode | 64-bit mode + // ------------------------------+-------------+--------------- + // [displacement] | ABSOLUTE | RELATIVE (RIP) + // [index * scale + displacemnt] | ABSOLUTE | ABSOLUTE (ZERO EXTENDED) + else + { + // - In 32-bit mode the absolute addressing model is used. + // - In 64-bit mode the relative addressing model is used together with + // the absolute addressing. Main problem is that if instruction + // contains SIB then relative addressing (RIP) is not possible. + +#if defined(ASMJIT_X86) + + if (mem.hasIndex()) + { + // ASMJIT_ASSERT(mem.getMemIndex() != 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.getMemType() == OPERAND_MEM_LABEL) + { + LabelData& l_data = _labelData[mem._mem.base & OPERAND_ID_VALUE_MASK]; + RelocData r_data; + uint32_t relocId = _relocData.getLength(); + + // Relative addressing will be relocated to absolute address. + r_data.type = RelocData::RELATIVE_TO_ABSOLUTE; + r_data.size = 4; + r_data.offset = getOffset(); + r_data.destination = disp; + + if (l_data.offset != -1) + { + // Bound label. + r_data.destination += l_data.offset; + + // Add a dummy DWORD. + _emitInt32(0); + } + else + { + // Non-bound label. + _emitDisplacement(l_data, -4 - immSize, 4)->relocId = relocId; + } + + _relocData.append(r_data); + } + else + { + // Absolute address + _emitInt32( (int32_t)((uint8_t*)mem._mem.target + disp) ); + } + +#else + + // X64 uses relative addressing model + if (mem.getMemType() == OPERAND_MEM_LABEL) + { + LabelData& l_data = _labelData[mem._mem.base & OPERAND_ID_VALUE_MASK]; + + if (mem.hasIndex()) + { + // Indexing is not possible. + setError(ERROR_ILLEGAL_ADDRESING); + return; + } + + // Relative address (RIP +/- displacement). + _emitMod(0, opReg, 5); + + disp -= (4 + immSize); + + if (l_data.offset != -1) + { + // Bound label. + disp += getOffset() - l_data.offset; + + // Add a dummy DWORD. + _emitInt32((int32_t)disp); + } + else + { + // Non-bound label. + _emitDisplacement(l_data, 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.getMemIndex() != 4); // ESP/RSP == 4 + _emitSib(shift, indexReg, 5); + } + else + { + _emitSib(0, 4, 5); + } + + // Truncate to 32-bits. + sysuint_t target = (sysuint_t)((uint8_t*)mem._mem.target + disp); + + if (target > (sysuint_t)0xFFFFFFFF) + { + if (_logger) + { + _logger->logString("*** ASSEMBER WARNING - Absolute address truncated to 32-bits.\n"); + } + target &= 0xFFFFFFFF; + } + + _emitInt32( (int32_t)((uint32_t)target) ); + } + +#endif // ASMJIT_X64 + + } +} + +void AssemblerCore::_emitModRM( + uint8_t opReg, const Operand& op, sysint_t immSize) ASMJIT_NOTHROW +{ + ASMJIT_ASSERT(op.getType() == OPERAND_REG || op.getType() == OPERAND_MEM); + + if (op.getType() == OPERAND_REG) + _emitModR(opReg, reinterpret_cast<const BaseReg&>(op).getRegCode()); + else + _emitModM(opReg, reinterpret_cast<const Mem&>(op), immSize); +} + +void AssemblerCore::_emitX86Inl( + uint32_t opCode, uint8_t i16bit, uint8_t rexw, uint8_t reg, bool forceRexPrefix) ASMJIT_NOTHROW +{ + // 16-bit prefix. + if (i16bit) _emitByte(0x66); + + // Instruction prefix. + if (opCode & 0xFF000000) _emitByte((uint8_t)((opCode & 0xFF000000) >> 24)); + + // REX prefix. +#if defined(ASMJIT_X64) + _emitRexR(rexw, 0, reg, forceRexPrefix); +#endif // ASMJIT_X64 + + // Instruction opcodes. + if (opCode & 0x00FF0000) _emitByte((uint8_t)((opCode & 0x00FF0000) >> 16)); + if (opCode & 0x0000FF00) _emitByte((uint8_t)((opCode & 0x0000FF00) >> 8)); + + _emitByte((uint8_t)(opCode & 0x000000FF) + (reg & 0x7)); +} + +void AssemblerCore::_emitX86RM( + uint32_t opCode, uint8_t i16bit, uint8_t rexw, uint8_t o, + const Operand& op, sysint_t immSize, bool forceRexPrefix) ASMJIT_NOTHROW +{ + // 16-bit prefix. + if (i16bit) _emitByte(0x66); + + // Segment prefix. + _emitSegmentPrefix(op); + + // Instruction prefix. + if (opCode & 0xFF000000) _emitByte((uint8_t)((opCode & 0xFF000000) >> 24)); + + // REX prefix. +#if defined(ASMJIT_X64) + _emitRexRM(rexw, o, op, forceRexPrefix); +#endif // ASMJIT_X64 + + // Instruction opcodes. + if (opCode & 0x00FF0000) _emitByte((uint8_t)((opCode & 0x00FF0000) >> 16)); + if (opCode & 0x0000FF00) _emitByte((uint8_t)((opCode & 0x0000FF00) >> 8)); + _emitByte((uint8_t)(opCode & 0x000000FF)); + + // Mod R/M. + _emitModRM(o, op, immSize); +} + +void AssemblerCore::_emitFpu(uint32_t opCode) ASMJIT_NOTHROW +{ + _emitOpCode(opCode); +} + +void AssemblerCore::_emitFpuSTI(uint32_t opCode, uint32_t sti) ASMJIT_NOTHROW +{ + // Illegal stack offset. + ASMJIT_ASSERT(0 <= sti && sti < 8); + _emitOpCode(opCode + sti); +} + +void AssemblerCore::_emitFpuMEM(uint32_t opCode, uint8_t opReg, const Mem& mem) ASMJIT_NOTHROW +{ + // Segment prefix. + _emitSegmentPrefix(mem); + + // Instruction prefix. + if (opCode & 0xFF000000) _emitByte((uint8_t)((opCode & 0xFF000000) >> 24)); + + // REX prefix. +#if defined(ASMJIT_X64) + _emitRexRM(0, opReg, mem, false); +#endif // ASMJIT_X64 + + // Instruction opcodes. + if (opCode & 0x00FF0000) _emitByte((uint8_t)((opCode & 0x00FF0000) >> 16)); + if (opCode & 0x0000FF00) _emitByte((uint8_t)((opCode & 0x0000FF00) >> 8)); + + _emitByte((uint8_t)((opCode & 0x000000FF))); + _emitModM(opReg, mem, 0); +} + +void AssemblerCore::_emitMmu(uint32_t opCode, uint8_t rexw, uint8_t opReg, + const Operand& src, sysint_t immSize) ASMJIT_NOTHROW +{ + // Segment prefix. + _emitSegmentPrefix(src); + + // Instruction prefix. + if (opCode & 0xFF000000) _emitByte((uint8_t)((opCode & 0xFF000000) >> 24)); + + // REX prefix. +#if defined(ASMJIT_X64) + _emitRexRM(rexw, opReg, src, false); +#endif // ASMJIT_X64 + + // Instruction opcodes. + if (opCode & 0x00FF0000) _emitByte((uint8_t)((opCode & 0x00FF0000) >> 16)); + + // No checking, MMX/SSE instructions have always two opcodes or more. + _emitByte((uint8_t)((opCode & 0x0000FF00) >> 8)); + _emitByte((uint8_t)((opCode & 0x000000FF))); + + if (src.isReg()) + _emitModR(opReg, reinterpret_cast<const BaseReg&>(src).getRegCode()); + else + _emitModM(opReg, reinterpret_cast<const Mem&>(src), immSize); +} + +AssemblerCore::LabelLink* AssemblerCore::_emitDisplacement( + LabelData& l_data, sysint_t inlinedDisplacement, int size) ASMJIT_NOTHROW +{ + ASMJIT_ASSERT(l_data.offset == -1); + ASMJIT_ASSERT(size == 1 || size == 4); + + // Chain with label. + LabelLink* link = _newLabelLink(); + link->prev = l_data.links; + link->offset = getOffset(); + link->displacement = inlinedDisplacement; + + l_data.links = link; + + // Emit label size as dummy data. + if (size == 1) + _emitByte(0x01); + else // if (size == 4) + _emitDWord(0x04040404); + + return link; +} + +void AssemblerCore::_emitJmpOrCallReloc(uint32_t instruction, void* target) ASMJIT_NOTHROW +{ + RelocData rd; + + rd.type = RelocData::ABSOLUTE_TO_RELATIVE_TRAMPOLINE; + +#if defined(ASMJIT_X64) + // If we are compiling in 64-bit mode, we can use trampoline if relative jump + // is not possible. + _trampolineSize += TrampolineWriter::TRAMPOLINE_SIZE; +#endif // ARCHITECTURE_SPECIFIC + + rd.size = 4; + rd.offset = getOffset(); + rd.address = target; + + _relocData.append(rd); + + // Emit dummy 32-bit integer (will be overwritten by relocCode()). + _emitInt32(0); +} + +// Logging helpers. +static const char* operandSize[] = +{ + NULL, + "byte ptr ", + "word ptr ", + NULL, + "dword ptr ", + NULL, + NULL, + NULL, + "qword ptr ", + NULL, + "tword ptr ", + NULL, + NULL, + NULL, + NULL, + NULL, + "dqword ptr " +}; + +static const char segmentName[] = + "\0\0\0\0" + "cs:\0" + "ss:\0" + "ds:\0" + "es:\0" + "fs:\0" + "gs:\0"; + +ASMJIT_HIDDEN char* dumpInstructionName(char* buf, uint32_t code) ASMJIT_NOTHROW +{ + ASMJIT_ASSERT(code < _INST_COUNT); + return Util::mycpy(buf, instructionDescription[code].getName()); +} + +ASMJIT_HIDDEN char* dumpRegister(char* buf, uint32_t type, uint32_t index) ASMJIT_NOTHROW +{ + // NE == Not-Encodable. + const char reg8l[] = "al\0\0" "cl\0\0" "dl\0\0" "bl\0\0" "spl\0" "bpl\0" "sil\0" "dil\0" ; + const char reg8h[] = "ah\0\0" "ch\0\0" "dh\0\0" "bh\0\0" "NE\0\0" "NE\0\0" "NE\0\0" "NE\0\0"; + const char reg16[] = "ax\0\0" "cx\0\0" "dx\0\0" "bx\0\0" "sp\0\0" "bp\0\0" "si\0\0" "di\0\0"; + + switch (type) + { + case REG_TYPE_GPB_LO: + if (index < 8) + return buf + sprintf(buf, "%s", ®8l[index*4]); + else + return buf + sprintf(buf, "r%ub", (uint32_t)index); + case REG_TYPE_GPB_HI: + if (index < 4) + return buf + sprintf(buf, "%s", ®8h[index*4]); + else + return buf + sprintf(buf, "%s", "INVALID"); + case REG_TYPE_GPW: + if (index < 8) + return buf + sprintf(buf, "%s", ®16[index*4]); + else + return buf + sprintf(buf, "r%uw", (uint32_t)index); + case REG_TYPE_GPD: + if (index < 8) + return buf + sprintf(buf, "e%s", ®16[index*4]); + else + return buf + sprintf(buf, "r%ud", (uint32_t)index); + case REG_TYPE_GPQ: + if (index < 8) + return buf + sprintf(buf, "r%s", ®16[index*4]); + else + return buf + sprintf(buf, "r%u", (uint32_t)index); + case REG_TYPE_X87: + return buf + sprintf(buf, "st%u", (uint32_t)index); + case REG_TYPE_MM: + return buf + sprintf(buf, "mm%u", (uint32_t)index); + case REG_TYPE_XMM: + return buf + sprintf(buf, "xmm%u", (uint32_t)index); + default: + return buf; + } +} + +ASMJIT_HIDDEN char* dumpOperand(char* buf, const Operand* op) ASMJIT_NOTHROW +{ + if (op->isReg()) + { + const BaseReg& reg = reinterpret_cast<const BaseReg&>(*op); + return dumpRegister(buf, reg.getRegType(), reg.getRegIndex()); + } + else if (op->isMem()) + { + bool isAbsolute = false; + const Mem& mem = reinterpret_cast<const Mem&>(*op); + + if (op->getSize() <= 16) + { + buf = Util::mycpy(buf, operandSize[op->getSize()]); + } + + buf = Util::mycpy(buf, &segmentName[mem.getSegmentPrefix() * 4]); + + *buf++ = '['; + + switch (mem.getMemType()) + { + case OPERAND_MEM_NATIVE: + { + // [base + index*scale + displacement] + buf = dumpRegister(buf, REG_TYPE_GPN, mem.getBase()); + break; + } + case OPERAND_MEM_LABEL: + { + // [label + index*scale + displacement] + buf += sprintf(buf, "L.%u", mem.getBase() & OPERAND_ID_VALUE_MASK); + break; + } + case OPERAND_MEM_ABSOLUTE: + { + // [absolute] + isAbsolute = true; + buf = Util::myutoa(buf, (sysuint_t)mem.getTarget(), 16); + break; + } + } + + if (mem.hasIndex()) + { + buf = Util::mycpy(buf, " + "); + buf = dumpRegister(buf, REG_TYPE_GPN, mem.getIndex()); + + if (mem.getShift()) + { + buf = Util::mycpy(buf, " * "); + *buf++ = "1248"[mem.getShift() & 3]; + } + } + + if (mem.getDisplacement() && !isAbsolute) + { + sysint_t d = mem.getDisplacement(); + *buf++ = ' '; + *buf++ = (d < 0) ? '-' : '+'; + *buf++ = ' '; + buf = Util::myutoa(buf, d < 0 ? -d : d); + } + + *buf++ = ']'; + return buf; + } + else if (op->isImm()) + { + const Imm& i = reinterpret_cast<const Imm&>(*op); + return Util::myitoa(buf, (sysint_t)i.getValue()); + } + else if (op->isLabel()) + { + return buf + sprintf(buf, "L.%u", op->getId() & OPERAND_ID_VALUE_MASK); + } + else + { + return Util::mycpy(buf, "None"); + } +} + +static char* dumpInstruction(char* buf, + uint32_t code, + uint32_t emitOptions, + const Operand* o0, + const Operand* o1, + const Operand* o2) ASMJIT_NOTHROW +{ + if (emitOptions & EMIT_OPTION_REX_PREFIX ) buf = Util::mycpy(buf, "rex ", 4); + if (emitOptions & EMIT_OPTION_LOCK_PREFIX) buf = Util::mycpy(buf, "lock ", 5); + if (emitOptions & EMIT_OPTION_SHORT_JUMP ) buf = Util::mycpy(buf, "short ", 6); + + // Dump instruction. + buf = dumpInstructionName(buf, code); + + // Dump operands. + if (!o0->isNone()) { *buf++ = ' '; buf = dumpOperand(buf, o0); } + if (!o1->isNone()) { *buf++ = ','; *buf++ = ' '; buf = dumpOperand(buf, o1); } + if (!o2->isNone()) { *buf++ = ','; *buf++ = ' '; buf = dumpOperand(buf, o2); } + + return buf; +} + +static char* dumpComment(char* buf, sysuint_t len, const uint8_t* binaryData, sysuint_t binaryLen, const char* comment) +{ + sysuint_t currentLength = len; + sysuint_t commentLength = comment ? strlen(comment) : 0; + + if (binaryLen || commentLength) + { + sysuint_t align = 32; + char sep = ';'; + + // Truncate if comment is too long (it shouldn't be, larger than 80 seems to + // be an exploit). + if (commentLength > 80) commentLength = 80; + + for (sysuint_t i = (binaryLen == 0); i < 2; i++) + { + char* bufBegin = buf; + + // Append align. + if (currentLength < align) + { + buf = Util::myfill(buf, ' ', align - currentLength); + } + + // Append separator. + if (sep) + { + *buf++ = sep; + *buf++ = ' '; + } + + // Append binary data or comment. + if (i == 0) + { + buf = Util::myhex(buf, binaryData, binaryLen); + if (commentLength == 0) break; + } + else + { + buf = Util::mycpy(buf, comment, commentLength); + } + + currentLength += (sysuint_t)(buf - bufBegin); + align += 18; + sep = '|'; + } + } + + *buf++ = '\n'; + return buf; +} + +// Used for NULL operands to translate them to OPERAND_NONE. +static const uint8_t _none[sizeof(Operand)] = +{ + 0 +}; + +static const Operand::RegData _patchedHiRegs[4] = +{// op , size, { reserved0, reserved1 }, id , code + { OPERAND_REG, 1 , { 0 , 0 }, INVALID_VALUE, REG_TYPE_GPB_LO | 4 }, + { OPERAND_REG, 1 , { 0 , 0 }, INVALID_VALUE, REG_TYPE_GPB_LO | 5 }, + { OPERAND_REG, 1 , { 0 , 0 }, INVALID_VALUE, REG_TYPE_GPB_LO | 6 }, + { OPERAND_REG, 1 , { 0 , 0 }, INVALID_VALUE, REG_TYPE_GPB_LO | 7 } +}; + +void AssemblerCore::_emitInstruction(uint32_t code) ASMJIT_NOTHROW +{ + _emitInstruction(code, NULL, NULL, NULL); +} + +void AssemblerCore::_emitInstruction(uint32_t code, const Operand* o0) ASMJIT_NOTHROW +{ + _emitInstruction(code, o0, NULL, NULL); +} + +void AssemblerCore::_emitInstruction(uint32_t code, const Operand* o0, const Operand* o1) ASMJIT_NOTHROW +{ + _emitInstruction(code, o0, o1, NULL); +} + +void AssemblerCore::_emitInstruction(uint32_t code, const Operand* o0, const Operand* o1, const Operand* o2) ASMJIT_NOTHROW +{ + const Operand* _loggerOperands[3]; + + uint32_t bLoHiUsed = 0; +#if defined(ASMJIT_X86) + uint32_t forceRexPrefix = false; +#else + uint32_t forceRexPrefix = _emitOptions & EMIT_OPTION_REX_PREFIX; +#endif + +#if defined(ASMJIT_DEBUG) + bool assertIllegal = false; +#endif // ASMJIT_DEBUG + + const Imm* immOperand = NULL; + uint32_t immSize; + +#define _FINISHED() \ + goto end + +#define _FINISHED_IMMEDIATE(_Operand_, _Size_) \ + do { \ + immOperand = reinterpret_cast<const Imm*>(_Operand_); \ + immSize = (_Size_); \ + goto emitImmediate; \ + } while (0) + + // Convert operands to OPERAND_NONE if needed. + if (o0 == NULL) { o0 = reinterpret_cast<const Operand*>(_none); } else if (o0->isReg()) { bLoHiUsed |= o0->_reg.code & (REG_TYPE_GPB_LO | REG_TYPE_GPB_HI); } + if (o1 == NULL) { o1 = reinterpret_cast<const Operand*>(_none); } else if (o1->isReg()) { bLoHiUsed |= o1->_reg.code & (REG_TYPE_GPB_LO | REG_TYPE_GPB_HI); } + if (o2 == NULL) { o2 = reinterpret_cast<const Operand*>(_none); } else if (o2->isReg()) { bLoHiUsed |= o2->_reg.code & (REG_TYPE_GPB_LO | REG_TYPE_GPB_HI); } + + sysuint_t beginOffset = getOffset(); + const InstructionDescription* id = &instructionDescription[code]; + + if (code >= _INST_COUNT) + { + setError(ERROR_UNKNOWN_INSTRUCTION); + goto cleanup; + } + + // Check if register operand is BPL, SPL, SIL, DIL and do action that depends + // to current mode: + // - 64-bit: - Force REX prefix. + // + // Check if register operand is AH, BH, CH or DH and do action that depends + // to current mode: + // - 32-bit: - Patch operand index (index += 4), because we are using + // different index what is used in opcode. + // - 64-bit: - Check whether there is REX prefix and raise error if it is. + // - Do the same as in 32-bit mode - patch register index. + // + // NOTE: This is a hit hacky, but I added this to older code-base and I have + // no energy to rewrite it. Maybe in future all of this can be cleaned up! + if (bLoHiUsed | forceRexPrefix) + { + _loggerOperands[0] = o0; + _loggerOperands[1] = o1; + _loggerOperands[2] = o2; + +#if defined(ASMJIT_X64) + // Check if there is register that makes this instruction un-encodable. + + forceRexPrefix |= (uint32_t)o0->isExtendedRegisterUsed(); + forceRexPrefix |= (uint32_t)o1->isExtendedRegisterUsed(); + forceRexPrefix |= (uint32_t)o2->isExtendedRegisterUsed(); + + if (o0->isRegType(REG_TYPE_GPB_LO) && (o0->_reg.code & REG_INDEX_MASK) >= 4) forceRexPrefix = true; + else if (o1->isRegType(REG_TYPE_GPB_LO) && (o1->_reg.code & REG_INDEX_MASK) >= 4) forceRexPrefix = true; + else if (o2->isRegType(REG_TYPE_GPB_LO) && (o2->_reg.code & REG_INDEX_MASK) >= 4) forceRexPrefix = true; + + if ((bLoHiUsed & REG_TYPE_GPB_HI) != 0 && forceRexPrefix) + { + goto illegalInstruction; + } +#endif // ASMJIT_X64 + + // Patch GPB.HI operand index. + if ((bLoHiUsed & REG_TYPE_GPB_HI) != 0) + { + if (o0->isRegType(REG_TYPE_GPB_HI)) o0 = reinterpret_cast<const Operand*>(&_patchedHiRegs[o0->_reg.code & REG_INDEX_MASK]); + if (o1->isRegType(REG_TYPE_GPB_HI)) o1 = reinterpret_cast<const Operand*>(&_patchedHiRegs[o1->_reg.code & REG_INDEX_MASK]); + if (o2->isRegType(REG_TYPE_GPB_HI)) o2 = reinterpret_cast<const Operand*>(&_patchedHiRegs[o2->_reg.code & REG_INDEX_MASK]); + } + } + + // Check for buffer space (and grow if needed). + if (!canEmit()) goto cleanup; + + if (_emitOptions & EMIT_OPTION_LOCK_PREFIX) + { + if (!id->isLockable()) goto illegalInstruction; + _emitByte(0xF0); + } + + switch (id->group) + { + case InstructionDescription::G_EMIT: + { + _emitOpCode(id->opCode[0]); + _FINISHED(); + } + + case InstructionDescription::G_ALU: + { + uint32_t opCode = id->opCode[0]; + uint8_t opReg = (uint8_t)id->opCodeR; + + // Mem <- Reg + if (o0->isMem() && o1->isReg()) + { + _emitX86RM(opCode + (o1->getSize() != 1), + o1->getSize() == 2, + o1->getSize() == 8, + reinterpret_cast<const GPReg&>(*o1).getRegCode(), + reinterpret_cast<const Operand&>(*o0), + 0, forceRexPrefix); + _FINISHED(); + } + + // Reg <- Reg|Mem + if (o0->isReg() && o1->isRegMem()) + { + _emitX86RM(opCode + 2 + (o0->getSize() != 1), + o0->getSize() == 2, + o0->getSize() == 8, + reinterpret_cast<const GPReg&>(*o0).getRegCode(), + reinterpret_cast<const Operand&>(*o1), + 0, forceRexPrefix); + _FINISHED(); + } + + // AL, AX, EAX, RAX register shortcuts + if (o0->isRegIndex(0) && o1->isImm()) + { + if (o0->getSize() == 2) + _emitByte(0x66); // 16-bit. + else if (o0->getSize() == 8) + _emitByte(0x48); // REX.W. + + _emitByte((opReg << 3) | (0x04 + (o0->getSize() != 1))); + _FINISHED_IMMEDIATE(o1, o0->getSize() <= 4 ? o0->getSize() : 4); + } + + if (o0->isRegMem() && o1->isImm()) + { + const Imm& imm = reinterpret_cast<const Imm&>(*o1); + immSize = Util::isInt8(imm.getValue()) ? 1 : (o0->getSize() <= 4 ? o0->getSize() : 4); + + _emitX86RM(id->opCode[1] + (o0->getSize() != 1 ? (immSize != 1 ? 1 : 3) : 0), + o0->getSize() == 2, + o0->getSize() == 8, + opReg, reinterpret_cast<const Operand&>(*o0), + immSize, forceRexPrefix); + _FINISHED_IMMEDIATE(&imm, immSize); + } + + break; + } + + case InstructionDescription::G_BSWAP: + { + if (o0->isReg()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + +#if defined(ASMJIT_X64) + _emitRexR(dst.getRegType() == REG_TYPE_GPQ, 1, dst.getRegCode(), forceRexPrefix); +#endif // ASMJIT_X64 + _emitByte(0x0F); + _emitModR(1, dst.getRegCode()); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_BT: + { + if (o0->isRegMem() && o1->isReg()) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + const GPReg& src = reinterpret_cast<const GPReg&>(*o1); + + _emitX86RM(id->opCode[0], + src.isRegType(REG_TYPE_GPW), + src.isRegType(REG_TYPE_GPQ), + src.getRegCode(), + dst, + 0, forceRexPrefix); + _FINISHED(); + } + + if (o0->isRegMem() && o1->isImm()) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + const Imm& src = reinterpret_cast<const Imm&>(*o1); + + _emitX86RM(id->opCode[1], + dst.getSize() == 2, + dst.getSize() == 8, + (uint8_t)id->opCodeR, + dst, + 1, forceRexPrefix); + _FINISHED_IMMEDIATE(o1, 1); + } + + break; + } + + case InstructionDescription::G_CALL: + { + if (o0->isRegTypeMem(REG_TYPE_GPN)) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + _emitX86RM(0xFF, + 0, + 0, 2, dst, + 0, forceRexPrefix); + _FINISHED(); + } + + if (o0->isImm()) + { + const Imm& imm = reinterpret_cast<const Imm&>(*o0); + _emitByte(0xE8); + _emitJmpOrCallReloc(InstructionDescription::G_CALL, (void*)imm.getValue()); + _FINISHED(); + } + + if (o0->isLabel()) + { + LabelData& l_data = _labelData[reinterpret_cast<const Label*>(o0)->getId() & OPERAND_ID_VALUE_MASK]; + + if (l_data.offset != -1) + { + // Bound label. + static const sysint_t rel32_size = 5; + sysint_t offs = l_data.offset - getOffset(); + + ASMJIT_ASSERT(offs <= 0); + + _emitByte(0xE8); + _emitInt32((int32_t)(offs - rel32_size)); + } + else + { + // Non-bound label. + _emitByte(0xE8); + _emitDisplacement(l_data, -4, 4); + } + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_CRC32: + { + if (o0->isReg() && o1->isRegMem()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Operand& src = reinterpret_cast<const Operand&>(*o1); + ASMJIT_ASSERT(dst.getRegType() == REG_TYPE_GPD || dst.getRegType() == REG_TYPE_GPQ); + + _emitX86RM(id->opCode[0] + (src.getSize() != 1), + src.getSize() == 2, + dst.getRegType() == 8, dst.getRegCode(), src, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_ENTER: + { + if (o0->isImm() && o1->isImm()) + { + _emitByte(0xC8); + _emitWord((uint16_t)(sysuint_t)reinterpret_cast<const Imm&>(*o2).getValue()); + _emitByte((uint8_t )(sysuint_t)reinterpret_cast<const Imm&>(*o1).getValue()); + _FINISHED(); + } + break; + } + + case InstructionDescription::G_IMUL: + { + // 1 operand + if (o0->isRegMem() && o1->isNone() && o2->isNone()) + { + const Operand& src = reinterpret_cast<const Operand&>(*o0); + _emitX86RM(0xF6 + (src.getSize() != 1), + src.getSize() == 2, + src.getSize() == 8, 5, src, + 0, forceRexPrefix); + _FINISHED(); + } + // 2 operands + else if (o0->isReg() && !o1->isNone() && o2->isNone()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + ASMJIT_ASSERT(!dst.isRegType(REG_TYPE_GPW)); + + if (o1->isRegMem()) + { + const Operand& src = reinterpret_cast<const Operand&>(*o1); + + _emitX86RM(0x0FAF, + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), dst.getRegCode(), src, + 0, forceRexPrefix); + _FINISHED(); + } + else if (o1->isImm()) + { + const Imm& imm = reinterpret_cast<const Imm&>(*o1); + + if (Util::isInt8(imm.getValue())) + { + _emitX86RM(0x6B, + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), dst.getRegCode(), dst, + 1, forceRexPrefix); + _FINISHED_IMMEDIATE(&imm, 1); + } + else + { + immSize = dst.isRegType(REG_TYPE_GPW) ? 2 : 4; + _emitX86RM(0x69, + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), dst.getRegCode(), dst, + immSize, forceRexPrefix); + _FINISHED_IMMEDIATE(&imm, immSize); + } + } + } + // 3 operands + else if (o0->isReg() && o1->isRegMem() && o2->isImm()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Operand& src = reinterpret_cast<const Operand&>(*o1); + const Imm& imm = reinterpret_cast<const Imm&>(*o2); + + if (Util::isInt8(imm.getValue())) + { + _emitX86RM(0x6B, + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), dst.getRegCode(), src, + 1, forceRexPrefix); + _FINISHED_IMMEDIATE(&imm, 1); + } + else + { + immSize = dst.isRegType(REG_TYPE_GPW) ? 2 : 4; + _emitX86RM(0x69, + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), dst.getRegCode(), src, + immSize, forceRexPrefix); + _FINISHED_IMMEDIATE(&imm, immSize); + } + } + + break; + } + + case InstructionDescription::G_INC_DEC: + { + if (o0->isRegMem()) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + + // INC [r16|r32] in 64-bit mode is not encodable. +#if defined(ASMJIT_X86) + if ((dst.isReg()) && (dst.isRegType(REG_TYPE_GPW) || dst.isRegType(REG_TYPE_GPD))) + { + _emitX86Inl(id->opCode[0], + dst.isRegType(REG_TYPE_GPW), + 0, reinterpret_cast<const BaseReg&>(dst).getRegCode(), + false); + _FINISHED(); + } +#endif // ASMJIT_X86 + + _emitX86RM(id->opCode[1] + (dst.getSize() != 1), + dst.getSize() == 2, + dst.getSize() == 8, (uint8_t)id->opCodeR, dst, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_J: + { + if (o0->isLabel()) + { + LabelData& l_data = _labelData[reinterpret_cast<const Label*>(o0)->getId() & OPERAND_ID_VALUE_MASK]; + + uint32_t hint = (uint32_t)(o1->isImm() ? reinterpret_cast<const Imm&>(*o1).getValue() : 0); + bool isShortJump = (_emitOptions & EMIT_OPTION_SHORT_JUMP) != 0; + + // Emit jump hint if configured for that. + if ((hint & (HINT_TAKEN | HINT_NOT_TAKEN)) && (_properties & (1 << PROPERTY_JUMP_HINTS))) + { + if (hint & HINT_TAKEN) + _emitByte(HINT_BYTE_VALUE_TAKEN); + else if (hint & HINT_NOT_TAKEN) + _emitByte(HINT_BYTE_VALUE_NOT_TAKEN); + } + + if (l_data.offset != -1) + { + // Bound label. + static const sysint_t rel8_size = 2; + static const sysint_t rel32_size = 6; + sysint_t offs = l_data.offset - getOffset(); + + ASMJIT_ASSERT(offs <= 0); + + if (Util::isInt8(offs - rel8_size)) + { + _emitByte(0x70 | (uint8_t)id->opCode[0]); + _emitByte((uint8_t)(int8_t)(offs - rel8_size)); + + // Change the emit options so logger can log instruction correctly. + _emitOptions |= EMIT_OPTION_SHORT_JUMP; + } + else + { + if (isShortJump && _logger) + { + _logger->logString("*** ASSEMBLER WARNING: Emitting long conditional jump, but short jump instruction forced!\n"); + _emitOptions &= ~EMIT_OPTION_SHORT_JUMP; + } + + _emitByte(0x0F); + _emitByte(0x80 | (uint8_t)id->opCode[0]); + _emitInt32((int32_t)(offs - rel32_size)); + } + } + else + { + // Non-bound label. + if (isShortJump) + { + _emitByte(0x70 | (uint8_t)id->opCode[0]); + _emitDisplacement(l_data, -1, 1); + } + else + { + _emitByte(0x0F); + _emitByte(0x80 | (uint8_t)id->opCode[0]); + _emitDisplacement(l_data, -4, 4); + } + } + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_JMP: + { + if (o0->isRegMem()) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + + _emitX86RM(0xFF, + 0, + 0, 4, dst, + 0, forceRexPrefix); + _FINISHED(); + } + + if (o0->isImm()) + { + const Imm& imm = reinterpret_cast<const Imm&>(*o0); + _emitByte(0xE9); + _emitJmpOrCallReloc(InstructionDescription::G_JMP, (void*)imm.getValue()); + _FINISHED(); + } + + if (o0->isLabel()) + { + LabelData& l_data = _labelData[reinterpret_cast<const Label*>(o0)->getId() & OPERAND_ID_VALUE_MASK]; + bool isShortJump = (_emitOptions & EMIT_OPTION_SHORT_JUMP) != 0; + + if (l_data.offset != -1) + { + // Bound label. + const sysint_t rel8_size = 2; + const sysint_t rel32_size = 5; + sysint_t offs = l_data.offset - getOffset(); + + if (Util::isInt8(offs - rel8_size)) + { + _emitByte(0xEB); + _emitByte((uint8_t)(int8_t)(offs - rel8_size)); + + // Change the emit options so logger can log instruction correctly. + _emitOptions |= EMIT_OPTION_SHORT_JUMP; + } + else + { + if (isShortJump) + { + if (_logger) + { + _logger->logString("*** ASSEMBLER WARNING: Emitting long jump, but short jump instruction forced!\n"); + _emitOptions &= ~EMIT_OPTION_SHORT_JUMP; + } + } + + _emitByte(0xE9); + _emitInt32((int32_t)(offs - rel32_size)); + } + } + else + { + // Non-bound label. + if (isShortJump) + { + _emitByte(0xEB); + _emitDisplacement(l_data, -1, 1); + } + else + { + _emitByte(0xE9); + _emitDisplacement(l_data, -4, 4); + } + } + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_LEA: + { + if (o0->isReg() && o1->isMem()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Mem& src = reinterpret_cast<const Mem&>(*o1); + _emitX86RM(0x8D, + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), dst.getRegCode(), src, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_M: + { + if (o0->isMem()) + { + _emitX86RM(id->opCode[0], 0, (uint8_t)id->opCode[1], (uint8_t)id->opCodeR, reinterpret_cast<const Mem&>(*o0), 0, forceRexPrefix); + _FINISHED(); + } + break; + } + + case InstructionDescription::G_MOV: + { + const Operand& dst = *o0; + const Operand& src = *o1; + + switch (dst.getType() << 4 | src.getType()) + { + // Reg <- Reg/Mem + case (OPERAND_REG << 4) | OPERAND_REG: + { + ASMJIT_ASSERT(src.isRegType(REG_TYPE_GPB_LO) || + src.isRegType(REG_TYPE_GPB_HI) || + src.isRegType(REG_TYPE_GPW ) || + src.isRegType(REG_TYPE_GPD ) || + src.isRegType(REG_TYPE_GPQ ) ); + // ... fall through ... + } + case (OPERAND_REG << 4) | OPERAND_MEM: + { + ASMJIT_ASSERT(dst.isRegType(REG_TYPE_GPB_LO) || + dst.isRegType(REG_TYPE_GPB_HI) || + dst.isRegType(REG_TYPE_GPW ) || + dst.isRegType(REG_TYPE_GPD ) || + dst.isRegType(REG_TYPE_GPQ ) ); + + _emitX86RM(0x0000008A + (dst.getSize() != 1), + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), + reinterpret_cast<const GPReg&>(dst).getRegCode(), + reinterpret_cast<const Operand&>(src), + 0, forceRexPrefix); + _FINISHED(); + } + + // Reg <- Imm + case (OPERAND_REG << 4) | OPERAND_IMM: + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Imm& src = reinterpret_cast<const Imm&>(*o1); + + // In 64-bit mode the immediate can be 64-bits long if the + // destination operand type is register (otherwise 32-bits). + immSize = dst.getSize(); + +#if defined(ASMJIT_X64) + // Optimize instruction size by using 32-bit immediate if value can + // fit into it. + if (immSize == 8 && Util::isInt32(src.getValue())) + { + _emitX86RM(0xC7, + 0, // 16BIT + 1, // REX.W + 0, // O + dst, + 0, forceRexPrefix); + immSize = 4; + } + else + { +#endif // ASMJIT_X64 + _emitX86Inl((dst.getSize() == 1 ? 0xB0 : 0xB8), + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), + dst.getRegCode(), forceRexPrefix); +#if defined(ASMJIT_X64) + } +#endif // ASMJIT_X64 + + _FINISHED_IMMEDIATE(&src, immSize); + } + + // Mem <- Reg + case (OPERAND_MEM << 4) | OPERAND_REG: + { + ASMJIT_ASSERT(src.isRegType(REG_TYPE_GPB_LO) || + src.isRegType(REG_TYPE_GPB_HI) || + src.isRegType(REG_TYPE_GPW ) || + src.isRegType(REG_TYPE_GPD ) || + src.isRegType(REG_TYPE_GPQ ) ); + + _emitX86RM(0x88 + (src.getSize() != 1), + src.isRegType(REG_TYPE_GPW), + src.isRegType(REG_TYPE_GPQ), + reinterpret_cast<const GPReg&>(src).getRegCode(), + reinterpret_cast<const Operand&>(dst), + 0, forceRexPrefix); + _FINISHED(); + } + + // Mem <- Imm + case (OPERAND_MEM << 4) | OPERAND_IMM: + { + immSize = dst.getSize() <= 4 ? dst.getSize() : 4; + + _emitX86RM(0xC6 + (dst.getSize() != 1), + dst.getSize() == 2, + dst.getSize() == 8, + 0, + reinterpret_cast<const Operand&>(dst), + immSize, forceRexPrefix); + _FINISHED_IMMEDIATE(&src, immSize); + } + } + + break; + } + + case InstructionDescription::G_MOV_PTR: + { + if ((o0->isReg() && o1->isImm()) || (o0->isImm() && o1->isReg())) + { + bool reverse = o1->getType() == OPERAND_REG; + uint8_t opCode = !reverse ? 0xA0 : 0xA2; + const GPReg& reg = reinterpret_cast<const GPReg&>(!reverse ? *o0 : *o1); + const Imm& imm = reinterpret_cast<const Imm&>(!reverse ? *o1 : *o0); + + if (reg.getRegIndex() != 0) goto illegalInstruction; + + if (reg.isRegType(REG_TYPE_GPW)) _emitByte(0x66); +#if defined(ASMJIT_X64) + _emitRexR(reg.getSize() == 8, 0, 0, forceRexPrefix); +#endif // ASMJIT_X64 + _emitByte(opCode + (reg.getSize() != 1)); + _FINISHED_IMMEDIATE(&imm, sizeof(sysint_t)); + } + + break; + } + + case InstructionDescription::G_MOVSX_MOVZX: + { + if (o0->isReg() && o1->isRegMem()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Operand& src = reinterpret_cast<const Operand&>(*o1); + + if (dst.getSize() == 1) goto illegalInstruction; + if (src.getSize() != 1 && src.getSize() != 2) goto illegalInstruction; + if (src.getSize() == 2 && dst.getSize() == 2) goto illegalInstruction; + + _emitX86RM(id->opCode[0] + (src.getSize() != 1), + dst.isRegType(REG_TYPE_GPW), + dst.isRegType(REG_TYPE_GPQ), + dst.getRegCode(), + src, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + +#if defined(ASMJIT_X64) + case InstructionDescription::G_MOVSXD: + { + if (o0->isReg() && o1->isRegMem()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Operand& src = reinterpret_cast<const Operand&>(*o1); + _emitX86RM(0x00000063, + 0, + 1, dst.getRegCode(), src, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } +#endif // ASMJIT_X64 + + case InstructionDescription::G_PUSH: + { + // This section is only for immediates, memory/register operands are handled in G_POP. + if (o0->isImm()) + { + const Imm& imm = reinterpret_cast<const Imm&>(*o0); + + if (Util::isInt8(imm.getValue())) + { + _emitByte(0x6A); + _FINISHED_IMMEDIATE(&imm, 1); + } + else + { + _emitByte(0x68); + _FINISHED_IMMEDIATE(&imm, 4); + } + } + + // ... goto G_POP ... + } + + case InstructionDescription::G_POP: + { + if (o0->isReg()) + { + ASMJIT_ASSERT(o0->isRegType(REG_TYPE_GPW) || o0->isRegType(REG_TYPE_GPN)); + _emitX86Inl(id->opCode[0], o0->isRegType(REG_TYPE_GPW), 0, reinterpret_cast<const GPReg&>(*o0).getRegCode(), forceRexPrefix); + _FINISHED(); + } + + if (o0->isMem()) + { + _emitX86RM(id->opCode[1], o0->getSize() == 2, 0, (uint8_t)id->opCodeR, reinterpret_cast<const Operand&>(*o0), 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_R_RM: + { + if (o0->isReg() && o1->isRegMem()) + { + const GPReg& dst = reinterpret_cast<const GPReg&>(*o0); + const Operand& src = reinterpret_cast<const Operand&>(*o1); + ASMJIT_ASSERT(dst.getSize() != 1); + + _emitX86RM(id->opCode[0], + dst.getRegType() == REG_TYPE_GPW, + dst.getRegType() == REG_TYPE_GPQ, dst.getRegCode(), src, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_RM_B: + { + if (o0->isRegMem()) + { + const Operand& op = reinterpret_cast<const Operand&>(*o0); + + // Only BYTE register or BYTE/TYPELESS memory location can be used. + ASMJIT_ASSERT(op.getSize() <= 1); + + _emitX86RM(id->opCode[0], false, false, 0, op, 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_RM: + { + if (o0->isRegMem()) + { + const Operand& op = reinterpret_cast<const Operand&>(*o0); + _emitX86RM(id->opCode[0] + (op.getSize() != 1), + op.getSize() == 2, + op.getSize() == 8, (uint8_t)id->opCodeR, op, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_RM_R: + { + if (o0->isRegMem() && o1->isReg()) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + const GPReg& src = reinterpret_cast<const GPReg&>(*o1); + _emitX86RM(id->opCode[0] + (src.getSize() != 1), + src.getRegType() == REG_TYPE_GPW, + src.getRegType() == REG_TYPE_GPQ, src.getRegCode(), dst, + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_REP: + { + uint32_t opCode = id->opCode[0]; + uint32_t opSize = id->opCode[1]; + + // Emit REP prefix (1 BYTE). + _emitByte(opCode >> 24); + + if (opSize != 1) opCode++; // D, Q and W form. + if (opSize == 2) _emitByte(0x66); // 16-bit prefix. +#if defined(ASMJIT_X64) + else if (opSize == 8) _emitByte(0x48); // REX.W prefix. +#endif // ASMJIT_X64 + + // Emit opcode (1 BYTE). + _emitByte(opCode & 0xFF); + _FINISHED(); + } + + case InstructionDescription::G_RET: + { + if (o0->isNone()) + { + _emitByte(0xC3); + _FINISHED(); + } + else if (o0->isImm()) + { + const Imm& imm = reinterpret_cast<const Imm&>(*o0); + ASMJIT_ASSERT(Util::isUInt16(imm.getValue())); + + if (imm.getValue() == 0) + { + _emitByte(0xC3); + _FINISHED(); + } + else + { + _emitByte(0xC2); + _FINISHED_IMMEDIATE(&imm, 2); + } + } + + break; + } + + case InstructionDescription::G_ROT: + { + if (o0->isRegMem() && (o1->isRegCode(REG_CL) || o1->isImm())) + { + // generate opcode. For these operations is base 0xC0 or 0xD0. + bool useImm8 = o1->isImm() && reinterpret_cast<const Imm&>(*o1).getValue() != 1; + uint32_t opCode = useImm8 ? 0xC0 : 0xD0; + + // size and operand type modifies the opcode + if (o0->getSize() != 1) opCode |= 0x01; + if (o1->getType() == OPERAND_REG) opCode |= 0x02; + + _emitX86RM(opCode, + o0->getSize() == 2, + o0->getSize() == 8, + (uint8_t)id->opCodeR, reinterpret_cast<const Operand&>(*o0), + useImm8 ? 1 : 0, forceRexPrefix); + + if (useImm8) + _FINISHED_IMMEDIATE(o1, 1); + else + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_SHLD_SHRD: + { + if (o0->isRegMem() && o1->isReg() && (o2->isImm() || (o2->isReg() && o2->isRegCode(REG_CL)))) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + const GPReg& src1 = reinterpret_cast<const GPReg&>(*o1); + const Operand& src2 = reinterpret_cast<const Operand&>(*o2); + + ASMJIT_ASSERT(dst.getSize() == src1.getSize()); + + _emitX86RM(id->opCode[0] + src2.isReg(), + src1.isRegType(REG_TYPE_GPW), + src1.isRegType(REG_TYPE_GPQ), + src1.getRegCode(), dst, + src2.isImm() ? 1 : 0, forceRexPrefix); + if (src2.isImm()) + _FINISHED_IMMEDIATE(&src2, 1); + else + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_TEST: + { + if (o0->isRegMem() && o1->isReg()) + { + ASMJIT_ASSERT(o0->getSize() == o1->getSize()); + _emitX86RM(0x84 + (o1->getSize() != 1), + o1->getSize() == 2, o1->getSize() == 8, + reinterpret_cast<const BaseReg&>(*o1).getRegCode(), + reinterpret_cast<const Operand&>(*o0), + 0, forceRexPrefix); + _FINISHED(); + } + + if (o0->isRegIndex(0) && o1->isImm()) + { + immSize = o0->getSize() <= 4 ? o0->getSize() : 4; + + if (o0->getSize() == 2) _emitByte(0x66); // 16-bit. +#if defined(ASMJIT_X64) + _emitRexRM(o0->getSize() == 8, 0, reinterpret_cast<const Operand&>(*o0), forceRexPrefix); +#endif // ASMJIT_X64 + _emitByte(0xA8 + (o0->getSize() != 1)); + _FINISHED_IMMEDIATE(o1, immSize); + } + + if (o0->isRegMem() && o1->isImm()) + { + immSize = o0->getSize() <= 4 ? o0->getSize() : 4; + + if (o0->getSize() == 2) _emitByte(0x66); // 16-bit. + _emitSegmentPrefix(reinterpret_cast<const Operand&>(*o0)); // Segment prefix. +#if defined(ASMJIT_X64) + _emitRexRM(o0->getSize() == 8, 0, reinterpret_cast<const Operand&>(*o0), forceRexPrefix); +#endif // ASMJIT_X64 + _emitByte(0xF6 + (o0->getSize() != 1)); + _emitModRM(0, reinterpret_cast<const Operand&>(*o0), immSize); + _FINISHED_IMMEDIATE(o1, immSize); + } + + break; + } + + case InstructionDescription::G_XCHG: + { + if (o0->isRegMem() && o1->isReg()) + { + const Operand& dst = reinterpret_cast<const Operand&>(*o0); + const GPReg& src = reinterpret_cast<const GPReg&>(*o1); + + if (src.isRegType(REG_TYPE_GPW)) _emitByte(0x66); // 16-bit. + _emitSegmentPrefix(dst); // segment prefix +#if defined(ASMJIT_X64) + _emitRexRM(src.isRegType(REG_TYPE_GPQ), src.getRegCode(), dst, forceRexPrefix); +#endif // ASMJIT_X64 + + // Special opcode for index 0 registers (AX, EAX, RAX vs register). + if ((dst.getType() == OPERAND_REG && dst.getSize() > 1) && + (reinterpret_cast<const GPReg&>(dst).getRegCode() == 0 || + reinterpret_cast<const GPReg&>(src).getRegCode() == 0 )) + { + uint8_t index = reinterpret_cast<const GPReg&>(dst).getRegCode() | src.getRegCode(); + _emitByte(0x90 + index); + _FINISHED(); + } + + _emitByte(0x86 + (src.getSize() != 1)); + _emitModRM(src.getRegCode(), dst, 0); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_MOVBE: + { + if (o0->isReg() && o1->isMem()) + { + _emitX86RM(0x000F38F0, + o0->isRegType(REG_TYPE_GPW), + o0->isRegType(REG_TYPE_GPQ), + reinterpret_cast<const GPReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), + 0, forceRexPrefix); + _FINISHED(); + } + + if (o0->isMem() && o1->isReg()) + { + _emitX86RM(0x000F38F1, + o1->isRegType(REG_TYPE_GPW), + o1->isRegType(REG_TYPE_GPQ), + reinterpret_cast<const GPReg&>(*o1).getRegCode(), + reinterpret_cast<const Mem&>(*o0), + 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_X87_FPU: + { + if (o0->isRegType(REG_TYPE_X87)) + { + uint8_t i1 = reinterpret_cast<const X87Reg&>(*o0).getRegIndex(); + uint8_t i2 = 0; + + if (code != INST_FCOM && code != INST_FCOMP) + { + if (!o1->isRegType(REG_TYPE_X87)) goto illegalInstruction; + i2 = reinterpret_cast<const X87Reg&>(*o1).getRegIndex(); + } + else if (i1 != 0 && i2 != 0) + { + goto illegalInstruction; + } + + _emitByte(i1 == 0 + ? ((id->opCode[0] & 0xFF000000) >> 24) + : ((id->opCode[0] & 0x00FF0000) >> 16)); + _emitByte(i1 == 0 + ? ((id->opCode[0] & 0x0000FF00) >> 8) + i2 + : ((id->opCode[0] & 0x000000FF) ) + i1); + _FINISHED(); + } + + if (o0->isMem() && (o0->getSize() == 4 || o0->getSize() == 8) && o1->isNone()) + { + const Mem& m = reinterpret_cast<const Mem&>(*o0); + + // segment prefix + _emitSegmentPrefix(m); + + _emitByte(o0->getSize() == 4 + ? ((id->opCode[0] & 0xFF000000) >> 24) + : ((id->opCode[0] & 0x00FF0000) >> 16)); + _emitModM((uint8_t)id->opCodeR, m, 0); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_X87_STI: + { + if (o0->isRegType(REG_TYPE_X87)) + { + uint8_t i = reinterpret_cast<const X87Reg&>(*o0).getRegIndex(); + _emitByte((uint8_t)((id->opCode[0] & 0x0000FF00) >> 8)); + _emitByte((uint8_t)((id->opCode[0] & 0x000000FF) + i)); + _FINISHED(); + } + break; + } + + case InstructionDescription::G_X87_FSTSW: + { + if (o0->isReg() && + reinterpret_cast<const BaseReg&>(*o0).getRegType() <= REG_TYPE_GPQ && + reinterpret_cast<const BaseReg&>(*o0).getRegIndex() == 0) + { + _emitOpCode(id->opCode[1]); + _FINISHED(); + } + + if (o0->isMem()) + { + _emitX86RM(id->opCode[0], 0, 0, (uint8_t)id->opCodeR, reinterpret_cast<const Mem&>(*o0), 0, forceRexPrefix); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_X87_MEM_STI: + { + if (o0->isRegType(REG_TYPE_X87)) + { + _emitByte((uint8_t)((id->opCode[1] & 0xFF000000) >> 24)); + _emitByte((uint8_t)((id->opCode[1] & 0x00FF0000) >> 16) + + reinterpret_cast<const X87Reg&>(*o0).getRegIndex()); + _FINISHED(); + } + + // ... fall through to G_X87_MEM ... + } + + case InstructionDescription::G_X87_MEM: + { + if (!o0->isMem()) goto illegalInstruction; + const Mem& m = reinterpret_cast<const Mem&>(*o0); + + uint8_t opCode = 0x00, mod = 0; + + if (o0->getSize() == 2 && (id->oflags[0] & InstructionDescription::O_FM_2)) + { + opCode = (uint8_t)((id->opCode[0] & 0xFF000000) >> 24); + mod = (uint8_t)id->opCodeR; + } + if (o0->getSize() == 4 && (id->oflags[0] & InstructionDescription::O_FM_4)) + { + opCode = (uint8_t)((id->opCode[0] & 0x00FF0000) >> 16); + mod = (uint8_t)id->opCodeR; + } + if (o0->getSize() == 8 && (id->oflags[0] & InstructionDescription::O_FM_8)) + { + opCode = (uint8_t)((id->opCode[0] & 0x0000FF00) >> 8); + mod = (uint8_t)((id->opCode[0] & 0x000000FF) ); + } + + if (opCode) + { + _emitSegmentPrefix(m); + _emitByte(opCode); + _emitModM(mod, m, 0); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_MMU_MOV: + { + ASMJIT_ASSERT(id->oflags[0] != 0); + ASMJIT_ASSERT(id->oflags[1] != 0); + + // Check parameters (X)MM|GP32_64 <- (X)MM|GP32_64|Mem|Imm + if ((o0->isMem() && (id->oflags[0] & InstructionDescription::O_MEM) == 0) || + (o0->isRegType(REG_TYPE_MM ) && (id->oflags[0] & InstructionDescription::O_MM ) == 0) || + (o0->isRegType(REG_TYPE_XMM) && (id->oflags[0] & InstructionDescription::O_XMM) == 0) || + (o0->isRegType(REG_TYPE_GPD) && (id->oflags[0] & InstructionDescription::O_GD ) == 0) || + (o0->isRegType(REG_TYPE_GPQ) && (id->oflags[0] & InstructionDescription::O_GQ ) == 0) || + (o1->isRegType(REG_TYPE_MM ) && (id->oflags[1] & InstructionDescription::O_MM ) == 0) || + (o1->isRegType(REG_TYPE_XMM) && (id->oflags[1] & InstructionDescription::O_XMM) == 0) || + (o1->isRegType(REG_TYPE_GPD) && (id->oflags[1] & InstructionDescription::O_GD ) == 0) || + (o1->isRegType(REG_TYPE_GPQ) && (id->oflags[1] & InstructionDescription::O_GQ ) == 0) || + (o1->isMem() && (id->oflags[1] & InstructionDescription::O_MEM) == 0) ) + { + goto illegalInstruction; + } + + // Illegal. + if (o0->isMem() && o1->isMem()) goto illegalInstruction; + + uint8_t rexw = ((id->oflags[0]|id->oflags[1]) & InstructionDescription::O_NOREX) + ? 0 + : o0->isRegType(REG_TYPE_GPQ) | o0->isRegType(REG_TYPE_GPQ); + + // (X)MM|Reg <- (X)MM|Reg + if (o0->isReg() && o1->isReg()) + { + _emitMmu(id->opCode[0], rexw, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const BaseReg&>(*o1), + 0); + _FINISHED(); + } + + // (X)MM|Reg <- Mem + if (o0->isReg() && o1->isMem()) + { + _emitMmu(id->opCode[0], rexw, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), + 0); + _FINISHED(); + } + + // Mem <- (X)MM|Reg + if (o0->isMem() && o1->isReg()) + { + _emitMmu(id->opCode[1], rexw, + reinterpret_cast<const BaseReg&>(*o1).getRegCode(), + reinterpret_cast<const Mem&>(*o0), + 0); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_MMU_MOVD: + { + if ((o0->isRegType(REG_TYPE_MM) || o0->isRegType(REG_TYPE_XMM)) && (o1->isRegType(REG_TYPE_GPD) || o1->isMem())) + { + _emitMmu(o0->isRegType(REG_TYPE_XMM) ? 0x66000F6E : 0x00000F6E, 0, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const Operand&>(*o1), + 0); + _FINISHED(); + } + + if ((o0->isRegType(REG_TYPE_GPD) || o0->isMem()) && (o1->isRegType(REG_TYPE_MM) || o1->isRegType(REG_TYPE_XMM))) + { + _emitMmu(o1->isRegType(REG_TYPE_XMM) ? 0x66000F7E : 0x00000F7E, 0, + reinterpret_cast<const BaseReg&>(*o1).getRegCode(), + reinterpret_cast<const Operand&>(*o0), + 0); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_MMU_MOVQ: + { + if (o0->isRegType(REG_TYPE_MM) && o1->isRegType(REG_TYPE_MM)) + { + _emitMmu(0x00000F6F, 0, + reinterpret_cast<const MMReg&>(*o0).getRegCode(), + reinterpret_cast<const MMReg&>(*o1), + 0); + _FINISHED(); + } + + if (o0->isRegType(REG_TYPE_XMM) && o1->isRegType(REG_TYPE_XMM)) + { + _emitMmu(0xF3000F7E, 0, + reinterpret_cast<const XMMReg&>(*o0).getRegCode(), + reinterpret_cast<const XMMReg&>(*o1), + 0); + _FINISHED(); + } + + // Convenience - movdq2q + if (o0->isRegType(REG_TYPE_MM) && o1->isRegType(REG_TYPE_XMM)) + { + _emitMmu(0xF2000FD6, 0, + reinterpret_cast<const MMReg&>(*o0).getRegCode(), + reinterpret_cast<const XMMReg&>(*o1), + 0); + _FINISHED(); + } + + // Convenience - movq2dq + if (o0->isRegType(REG_TYPE_XMM) && o1->isRegType(REG_TYPE_MM)) + { + _emitMmu(0xF3000FD6, 0, + reinterpret_cast<const XMMReg&>(*o0).getRegCode(), + reinterpret_cast<const MMReg&>(*o1), + 0); + _FINISHED(); + } + + if (o0->isRegType(REG_TYPE_MM) && o1->isMem()) + { + _emitMmu(0x00000F6F, 0, + reinterpret_cast<const MMReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), + 0); + _FINISHED(); + } + + if (o0->isRegType(REG_TYPE_XMM) && o1->isMem()) + { + _emitMmu(0xF3000F7E, 0, + reinterpret_cast<const XMMReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), + 0); + _FINISHED(); + } + + if (o0->isMem() && o1->isRegType(REG_TYPE_MM)) + { + _emitMmu(0x00000F7F, 0, + reinterpret_cast<const MMReg&>(*o1).getRegCode(), + reinterpret_cast<const Mem&>(*o0), + 0); + _FINISHED(); + } + + if (o0->isMem() && o1->isRegType(REG_TYPE_XMM)) + { + _emitMmu(0x66000FD6, 0, + reinterpret_cast<const XMMReg&>(*o1).getRegCode(), + reinterpret_cast<const Mem&>(*o0), + 0); + _FINISHED(); + } + +#if defined(ASMJIT_X64) + if ((o0->isRegType(REG_TYPE_MM) || o0->isRegType(REG_TYPE_XMM)) && (o1->isRegType(REG_TYPE_GPQ) || o1->isMem())) + { + _emitMmu(o0->isRegType(REG_TYPE_XMM) ? 0x66000F6E : 0x00000F6E, 1, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const Operand&>(*o1), + 0); + _FINISHED(); + } + + if ((o0->isRegType(REG_TYPE_GPQ) || o0->isMem()) && (o1->isRegType(REG_TYPE_MM) || o1->isRegType(REG_TYPE_XMM))) + { + _emitMmu(o1->isRegType(REG_TYPE_XMM) ? 0x66000F7E : 0x00000F7E, 1, + reinterpret_cast<const BaseReg&>(*o1).getRegCode(), + reinterpret_cast<const Operand&>(*o0), + 0); + _FINISHED(); + } +#endif // ASMJIT_X64 + + break; + } + + case InstructionDescription::G_MMU_PREFETCH: + { + if (o0->isMem() && o1->isImm()) + { + const Mem& mem = reinterpret_cast<const Mem&>(*o0); + const Imm& hint = reinterpret_cast<const Imm&>(*o1); + + _emitMmu(0x00000F18, 0, (uint8_t)hint.getValue(), mem, 0); + _FINISHED(); + } + + break; + } + + case InstructionDescription::G_MMU_PEXTR: + { + if (!(o0->isRegMem() && + (o1->isRegType(REG_TYPE_XMM) || (code == INST_PEXTRW && o1->isRegType(REG_TYPE_MM))) && + o2->isImm())) + { + goto illegalInstruction; + } + + uint32_t opCode = id->opCode[0]; + uint8_t isGpdGpq = o0->isRegType(REG_TYPE_GPD) | o0->isRegType(REG_TYPE_GPQ); + + if (code == INST_PEXTRB && (o0->getSize() != 0 && o0->getSize() != 1) && !isGpdGpq) goto illegalInstruction; + if (code == INST_PEXTRW && (o0->getSize() != 0 && o0->getSize() != 2) && !isGpdGpq) goto illegalInstruction; + if (code == INST_PEXTRD && (o0->getSize() != 0 && o0->getSize() != 4) && !isGpdGpq) goto illegalInstruction; + if (code == INST_PEXTRQ && (o0->getSize() != 0 && o0->getSize() != 8) && !isGpdGpq) goto illegalInstruction; + + if (o1->isRegType(REG_TYPE_XMM)) opCode |= 0x66000000; + + if (o0->isReg()) + { + _emitMmu(opCode, id->opCodeR | (uint8_t)o0->isRegType(REG_TYPE_GPQ), + reinterpret_cast<const BaseReg&>(*o1).getRegCode(), + reinterpret_cast<const BaseReg&>(*o0), 1); + _FINISHED_IMMEDIATE(o2, 1); + } + + if (o0->isMem()) + { + _emitMmu(opCode, (uint8_t)id->opCodeR, + reinterpret_cast<const BaseReg&>(*o1).getRegCode(), + reinterpret_cast<const Mem&>(*o0), 1); + _FINISHED_IMMEDIATE(o2, 1); + } + + break; + } + + case InstructionDescription::G_MMU_RMI: + { + ASMJIT_ASSERT(id->oflags[0] != 0); + ASMJIT_ASSERT(id->oflags[1] != 0); + + // Check parameters (X)MM|GP32_64 <- (X)MM|GP32_64|Mem|Imm + if (!o0->isReg() || + (o0->isRegType(REG_TYPE_MM ) && (id->oflags[0] & InstructionDescription::O_MM ) == 0) || + (o0->isRegType(REG_TYPE_XMM) && (id->oflags[0] & InstructionDescription::O_XMM) == 0) || + (o0->isRegType(REG_TYPE_GPD) && (id->oflags[0] & InstructionDescription::O_GD ) == 0) || + (o0->isRegType(REG_TYPE_GPQ) && (id->oflags[0] & InstructionDescription::O_GQ ) == 0) || + (o1->isRegType(REG_TYPE_MM ) && (id->oflags[1] & InstructionDescription::O_MM ) == 0) || + (o1->isRegType(REG_TYPE_XMM) && (id->oflags[1] & InstructionDescription::O_XMM) == 0) || + (o1->isRegType(REG_TYPE_GPD) && (id->oflags[1] & InstructionDescription::O_GD ) == 0) || + (o1->isRegType(REG_TYPE_GPQ) && (id->oflags[1] & InstructionDescription::O_GQ ) == 0) || + (o1->isMem() && (id->oflags[1] & InstructionDescription::O_MEM) == 0) || + (o1->isImm() && (id->oflags[1] & InstructionDescription::O_IMM) == 0)) + { + goto illegalInstruction; + } + + uint32_t prefix = + ((id->oflags[0] & InstructionDescription::O_MM_XMM) == InstructionDescription::O_MM_XMM && o0->isRegType(REG_TYPE_XMM)) || + ((id->oflags[1] & InstructionDescription::O_MM_XMM) == InstructionDescription::O_MM_XMM && o1->isRegType(REG_TYPE_XMM)) + ? 0x66000000 + : 0x00000000; + uint8_t rexw = ((id->oflags[0]|id->oflags[1]) & InstructionDescription::O_NOREX) + ? 0 + : o0->isRegType(REG_TYPE_GPQ) | o0->isRegType(REG_TYPE_GPQ); + + // (X)MM <- (X)MM (opcode0) + if (o1->isReg()) + { + if ((id->oflags[1] & (InstructionDescription::O_MM_XMM | InstructionDescription::O_GQD)) == 0) goto illegalInstruction; + _emitMmu(id->opCode[0] | prefix, rexw, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const BaseReg&>(*o1), 0); + _FINISHED(); + } + // (X)MM <- Mem (opcode0) + if (o1->isMem()) + { + if ((id->oflags[1] & InstructionDescription::O_MEM) == 0) goto illegalInstruction; + _emitMmu(id->opCode[0] | prefix, rexw, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), 0); + _FINISHED(); + } + // (X)MM <- Imm (opcode1+opcodeR) + if (o1->isImm()) + { + if ((id->oflags[1] & InstructionDescription::O_IMM) == 0) goto illegalInstruction; + _emitMmu(id->opCode[1] | prefix, rexw, + (uint8_t)id->opCodeR, + reinterpret_cast<const BaseReg&>(*o0), 1); + _FINISHED_IMMEDIATE(o1, 1); + } + + break; + } + + case InstructionDescription::G_MMU_RM_IMM8: + { + ASMJIT_ASSERT(id->oflags[0] != 0); + ASMJIT_ASSERT(id->oflags[1] != 0); + + // Check parameters (X)MM|GP32_64 <- (X)MM|GP32_64|Mem|Imm + if (!o0->isReg() || + (o0->isRegType(REG_TYPE_MM ) && (id->oflags[0] & InstructionDescription::O_MM ) == 0) || + (o0->isRegType(REG_TYPE_XMM) && (id->oflags[0] & InstructionDescription::O_XMM) == 0) || + (o0->isRegType(REG_TYPE_GPD) && (id->oflags[0] & InstructionDescription::O_GD ) == 0) || + (o0->isRegType(REG_TYPE_GPQ) && (id->oflags[0] & InstructionDescription::O_GQ ) == 0) || + (o1->isRegType(REG_TYPE_MM ) && (id->oflags[1] & InstructionDescription::O_MM ) == 0) || + (o1->isRegType(REG_TYPE_XMM) && (id->oflags[1] & InstructionDescription::O_XMM) == 0) || + (o1->isRegType(REG_TYPE_GPD) && (id->oflags[1] & InstructionDescription::O_GD ) == 0) || + (o1->isRegType(REG_TYPE_GPQ) && (id->oflags[1] & InstructionDescription::O_GQ ) == 0) || + (o1->isMem() && (id->oflags[1] & InstructionDescription::O_MEM) == 0) || + !o2->isImm()) + { + goto illegalInstruction; + } + + uint32_t prefix = + ((id->oflags[0] & InstructionDescription::O_MM_XMM) == InstructionDescription::O_MM_XMM && o0->isRegType(REG_TYPE_XMM)) || + ((id->oflags[1] & InstructionDescription::O_MM_XMM) == InstructionDescription::O_MM_XMM && o1->isRegType(REG_TYPE_XMM)) + ? 0x66000000 + : 0x00000000; + uint8_t rexw = ((id->oflags[0]|id->oflags[1]) & InstructionDescription::O_NOREX) + ? 0 + : o0->isRegType(REG_TYPE_GPQ) | o0->isRegType(REG_TYPE_GPQ); + + // (X)MM <- (X)MM (opcode0) + if (o1->isReg()) + { + if ((id->oflags[1] & (InstructionDescription::O_MM_XMM | InstructionDescription::O_GQD)) == 0) goto illegalInstruction; + _emitMmu(id->opCode[0] | prefix, rexw, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const BaseReg&>(*o1), 1); + _FINISHED_IMMEDIATE(o2, 1); + } + // (X)MM <- Mem (opcode0) + if (o1->isMem()) + { + if ((id->oflags[1] & InstructionDescription::O_MEM) == 0) goto illegalInstruction; + _emitMmu(id->opCode[0] | prefix, rexw, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), 1); + _FINISHED_IMMEDIATE(o2, 1); + } + + break; + } + + case InstructionDescription::G_MMU_RM_3DNOW: + { + if (o0->isRegType(REG_TYPE_MM) && (o1->isRegType(REG_TYPE_MM) || o1->isMem())) + { + _emitMmu(id->opCode[0], 0, + reinterpret_cast<const BaseReg&>(*o0).getRegCode(), + reinterpret_cast<const Mem&>(*o1), 1); + _emitByte((uint8_t)id->opCode[1]); + _FINISHED(); + } + + break; + } + } + +illegalInstruction: + // Set an error. If we run in release mode assertion will be not used, so we + // must inform about invalid state. + setError(ERROR_ILLEGAL_INSTRUCTION); + +#if defined(ASMJIT_DEBUG) + assertIllegal = true; +#endif // ASMJIT_DEBUG + goto end; + +emitImmediate: + { + sysint_t value = immOperand->getValue(); + switch (immSize) + { + case 1: _emitByte ((uint8_t )(sysuint_t)value); break; + case 2: _emitWord ((uint16_t)(sysuint_t)value); break; + case 4: _emitDWord((uint32_t)(sysuint_t)value); break; +#if defined(ASMJIT_X64) + case 8: _emitQWord((uint64_t)(sysuint_t)value); break; +#endif // ASMJIT_X64 + default: ASMJIT_ASSERT(0); + } + } + +end: + if (_logger +#if defined(ASMJIT_DEBUG) + || assertIllegal +#endif // ASMJIT_DEBUG + ) + { + char bufStorage[512]; + char* buf = bufStorage; + + // Detect truncated operand. + Imm immTemporary(0); + + // Use the original operands, because BYTE some of them were replaced. + if (bLoHiUsed) + { + o0 = _loggerOperands[0]; + o1 = _loggerOperands[1]; + o2 = _loggerOperands[2]; + } + + if (immOperand) + { + sysint_t value = immOperand->getValue(); + bool isUnsigned = immOperand->isUnsigned(); + + switch (immSize) + { + case 1: if ( isUnsigned && !Util::isUInt8 (value)) { immTemporary.setValue((uint8_t)(sysuint_t)value, true ); break; } + if (!isUnsigned && !Util::isInt8 (value)) { immTemporary.setValue((uint8_t)(sysuint_t)value, false); break; } + break; + case 2: if ( isUnsigned && !Util::isUInt16(value)) { immTemporary.setValue((uint16_t)(sysuint_t)value, true ); break; } + if (!isUnsigned && !Util::isInt16 (value)) { immTemporary.setValue((uint16_t)(sysuint_t)value, false); break; } + break; + case 4: if ( isUnsigned && !Util::isUInt32(value)) { immTemporary.setValue((uint32_t)(sysuint_t)value, true ); break; } + if (!isUnsigned && !Util::isInt32 (value)) { immTemporary.setValue((uint32_t)(sysuint_t)value, false); break; } + break; + } + + if (immTemporary.getValue() != 0) + { + if (o0 == immOperand) o0 = &immTemporary; + if (o1 == immOperand) o1 = &immTemporary; + if (o2 == immOperand) o2 = &immTemporary; + } + } + + buf = dumpInstruction(buf, code, _emitOptions, o0, o1, o2); + + if (_logger->getLogBinary()) + buf = dumpComment(buf, (sysuint_t)(buf - bufStorage), getCode() + beginOffset, getOffset() - beginOffset, _comment); + else + buf = dumpComment(buf, (sysuint_t)(buf - bufStorage), NULL, 0, _comment); + + // We don't need to NULL terminate the resulting string. +#if defined(ASMJIT_DEBUG) + if (_logger) +#endif // ASMJIT_DEBUG + _logger->logString(bufStorage, (sysuint_t)(buf - bufStorage)); + +#if defined(ASMJIT_DEBUG) + if (assertIllegal) + { + // Here we need to NULL terminate. + buf[0] = '\0'; + + // Raise an assertion failure, because this situation shouldn't happen. + assertionFailure(__FILE__, __LINE__, bufStorage); + } +#endif // ASMJIT_DEBUG + } + +cleanup: + _comment = NULL; + _emitOptions = 0; +} + +void AssemblerCore::_emitJcc(uint32_t code, const Label* label, uint32_t hint) ASMJIT_NOTHROW +{ + if (!hint) + { + _emitInstruction(code, label, NULL, NULL); + } + else + { + Imm imm(hint); + _emitInstruction(code, label, &imm, NULL); + } +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Relocation helpers] +// ============================================================================ + +sysuint_t AssemblerCore::relocCode(void* _dst, sysuint_t addressBase) const ASMJIT_NOTHROW +{ + // Copy code to virtual memory (this is a given _dst pointer). + uint8_t* dst = reinterpret_cast<uint8_t*>(_dst); + + sysint_t coff = _buffer.getOffset(); + sysint_t csize = getCodeSize(); + + // We are copying the exact size of the generated code. Extra code for trampolines + // is generated on-the-fly by relocator (this code doesn't exist at the moment). + memcpy(dst, _buffer.getData(), coff); + +#if defined(ASMJIT_X64) + // Trampoline pointer. + uint8_t* tramp = dst + coff; +#endif // ASMJIT_X64 + + // Relocate all recorded locations. + sysint_t i; + sysint_t len = _relocData.getLength(); + + for (i = 0; i < len; i++) + { + const RelocData& r = _relocData[i]; + sysint_t val; + +#if defined(ASMJIT_X64) + // Whether to use trampoline, can be only used if relocation type is + // ABSOLUTE_TO_RELATIVE_TRAMPOLINE. + bool useTrampoline = false; +#endif // ASMJIT_X64 + + // Be sure that reloc data structure is correct. + ASMJIT_ASSERT((sysint_t)(r.offset + r.size) <= csize); + + switch (r.type) + { + case RelocData::ABSOLUTE_TO_ABSOLUTE: + val = (sysint_t)(r.address); + break; + + case RelocData::RELATIVE_TO_ABSOLUTE: + val = (sysint_t)(addressBase + r.destination); + break; + + case RelocData::ABSOLUTE_TO_RELATIVE: + case RelocData::ABSOLUTE_TO_RELATIVE_TRAMPOLINE: + val = (sysint_t)( (sysuint_t)r.address - (addressBase + (sysuint_t)r.offset + 4) ); + +#if defined(ASMJIT_X64) + if (r.type == RelocData::ABSOLUTE_TO_RELATIVE_TRAMPOLINE && !Util::isInt32(val)) + { + val = (sysint_t)( (sysuint_t)tramp - ((sysuint_t)_dst + (sysuint_t)r.offset + 4) ); + useTrampoline = true; + } +#endif // ASMJIT_X64 + break; + + default: + ASMJIT_ASSERT(0); + } + + switch (r.size) + { + case 4: + *reinterpret_cast<int32_t*>(dst + r.offset) = (int32_t)val; + break; + + case 8: + *reinterpret_cast<int64_t*>(dst + r.offset) = (int64_t)val; + break; + + default: + ASMJIT_ASSERT(0); + } + +#if defined(ASMJIT_X64) + if (useTrampoline) + { + if (getLogger()) + { + getLogger()->logFormat("; Trampoline from %p -> %p\n", (int8_t*)addressBase + r.offset, r.address); + } + + TrampolineWriter::writeTrampoline(tramp, r.address); + tramp += TrampolineWriter::TRAMPOLINE_SIZE; + } +#endif // ASMJIT_X64 + } + +#if defined(ASMJIT_X64) + return (sysuint_t)(tramp - dst); +#else + return (sysuint_t)coff; +#endif // ASMJIT_X64 +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Embed] +// ============================================================================ + +void AssemblerCore::embed(const void* data, sysuint_t size) ASMJIT_NOTHROW +{ + if (!canEmit()) return; + + if (_logger) + { + sysuint_t i, j; + sysuint_t max; + char buf[128]; + char dot[] = ".data "; + char* p; + + memcpy(buf, dot, ASMJIT_ARRAY_SIZE(dot) - 1); + + for (i = 0; i < size; i += 16) + { + max = (size - i < 16) ? size - i : 16; + p = buf + ASMJIT_ARRAY_SIZE(dot) - 1; + + for (j = 0; j < max; j++) + p += sprintf(p, "%0.2X", reinterpret_cast<const uint8_t *>(data)[i+j]); + + *p++ = '\n'; + *p = '\0'; + + _logger->logString(buf); + } + } + + _buffer.emitData(data, size); +} + +void AssemblerCore::embedLabel(const Label& label) ASMJIT_NOTHROW +{ + ASMJIT_ASSERT(label.getId() != INVALID_VALUE); + if (!canEmit()) return; + + LabelData& l_data = _labelData[label.getId() & OPERAND_ID_VALUE_MASK]; + RelocData r_data; + + if (_logger) + { + _logger->logFormat(sizeof(sysint_t) == 4 ? ".dd L.%u\n" : ".dq L.%u\n", (uint32_t)label.getId() & OPERAND_ID_VALUE_MASK); + } + + r_data.type = RelocData::RELATIVE_TO_ABSOLUTE; + r_data.size = sizeof(sysint_t); + r_data.offset = getOffset(); + r_data.destination = 0; + + if (l_data.offset != -1) + { + // Bound label. + r_data.destination = l_data.offset; + } + else + { + // Non-bound label. Need to chain. + LabelLink* link = _newLabelLink(); + + link->prev = (LabelLink*)l_data.links; + link->offset = getOffset(); + link->displacement = 0; + link->relocId = _relocData.getLength(); + + l_data.links = link; + } + + _relocData.append(r_data); + + // Emit dummy sysint (4 or 8 bytes that depends to address size). + _emitSysInt(0); +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Align] +// ============================================================================ + +void AssemblerCore::align(uint32_t m) ASMJIT_NOTHROW +{ + if (!canEmit()) return; + if (_logger) _logger->logFormat(".align %u", (uint)m); + + if (!m) return; + + if (m > 64) + { + ASMJIT_ASSERT(0); + return; + } + + sysint_t i = m - (getOffset() % m); + if (i == m) return; + + if (_properties & (1 << PROPERTY_OPTIMIZE_ALIGN)) + { + const CpuInfo* ci = getCpuInfo(); + + // 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. + static const uint8_t nop1[] = { 0x90 }; + static const uint8_t nop2[] = { 0x66, 0x90 }; + static const uint8_t nop3[] = { 0x0F, 0x1F, 0x00 }; + static const uint8_t nop4[] = { 0x0F, 0x1F, 0x40, 0x00 }; + static const uint8_t nop5[] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 }; + static const uint8_t nop6[] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }; + static const uint8_t nop7[] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }; + static const uint8_t nop8[] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uint8_t nop9[] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // AMD. + static const uint8_t nop10[] = { 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uint8_t nop11[] = { 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + const uint8_t* p; + sysint_t n; + + if (ci->vendorId == CPU_VENDOR_INTEL && + ((ci->family & 0x0F) == 6 || + (ci->family & 0x0F) == 15) + ) + { + do { + 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; + do { _emitByte(*p++); } while(--n); + } while (i); + + return; + } + + if (ci->vendorId == CPU_VENDOR_AMD && + ci->family >= 0x0F) + { + do { + 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; + do { _emitByte(*p++); } while(--n); + } while (i); + + return; + } +#if defined(ASMJIT_X86) + // 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); +#endif + } + + // legacy NOPs, only 0x90 + // In 64-bit mode, we can't use 0x66 prefix + do { + _emitByte(0x90); + } while(--i); +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Label] +// ============================================================================ + +Label AssemblerCore::newLabel() ASMJIT_NOTHROW +{ + Label label; + label._base.id = (uint32_t)_labelData.getLength() | OPERAND_ID_TYPE_LABEL; + + LabelData l_data; + l_data.offset = -1; + l_data.links = NULL; + _labelData.append(l_data); + + return label; +} + +void AssemblerCore::registerLabels(sysuint_t count) ASMJIT_NOTHROW +{ + // Duplicated newLabel() code, but we are not creating Label instances. + LabelData l_data; + l_data.offset = -1; + l_data.links = NULL; + + for (sysuint_t i = 0; i < count; i++) _labelData.append(l_data); +} + +void AssemblerCore::bind(const Label& label) ASMJIT_NOTHROW +{ + // Only labels created by newLabel() can be used by Assembler. + ASMJIT_ASSERT(label.getId() != INVALID_VALUE); + // Never go out of bounds. + ASMJIT_ASSERT((label.getId() & OPERAND_ID_VALUE_MASK) < _labelData.getLength()); + + // Get label data based on label id. + LabelData& l_data = _labelData[label.getId() & OPERAND_ID_VALUE_MASK]; + + // Label can be bound only once. + ASMJIT_ASSERT(l_data.offset == -1); + + // Log. + if (_logger) _logger->logFormat("L.%u:\n", (uint32_t)label.getId() & OPERAND_ID_VALUE_MASK); + + sysint_t pos = getOffset(); + + LabelLink* link = l_data.links; + LabelLink* prev = NULL; + + while (link) + { + sysint_t offset = link->offset; + + if (link->relocId != -1) + { + // If linked label points to RelocData then instead of writing relative + // displacement to assembler stream, we will write it to RelocData. + _relocData[link->relocId].destination += pos; + } + else + { + // Not using relocId, this means that we overwriting real displacement + // in assembler stream. + int32_t patchedValue = (int32_t)(pos - offset + link->displacement); + uint32_t size = getByteAt(offset); + + // Only these size specifiers are allowed. + ASMJIT_ASSERT(size == 1 || size == 4); + + if (size == 4) + { + setInt32At(offset, patchedValue); + } + else // if (size == 1) + { + if (Util::isInt8(patchedValue)) + { + setByteAt(offset, (uint8_t)(int8_t)patchedValue); + } + else + { + // Fatal error. + setError(ERROR_ILLEGAL_SHORT_JUMP); + } + } + } + + prev = link->prev; + link = prev; + } + + // Chain unused links. + link = l_data.links; + if (link) + { + if (prev == NULL) prev = link; + + prev->prev = _unusedLinks; + _unusedLinks = link; + } + + // Unlink label if it was linked. + l_data.offset = pos; + l_data.links = NULL; +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Make] +// ============================================================================ + +void* AssemblerCore::make() ASMJIT_NOTHROW +{ + // Do nothing on error state or when no instruction was emitted. + if (_error || getCodeSize() == 0) return NULL; + + void* p; + _error = _codeGenerator->generate(&p, reinterpret_cast<Assembler*>(this)); + return p; +} + +// ============================================================================ +// [AsmJit::AssemblerCore - Links] +// ============================================================================ + +AssemblerCore::LabelLink* AssemblerCore::_newLabelLink() ASMJIT_NOTHROW +{ + LabelLink* link = _unusedLinks; + + if (link) + { + _unusedLinks = link->prev; + } + else + { + link = (LabelLink*)_zone.zalloc(sizeof(LabelLink)); + if (link == NULL) return NULL; + } + + // clean link + link->prev = NULL; + link->offset = 0; + link->displacement = 0; + link->relocId = -1; + + return link; +} + +// ============================================================================ +// [AsmJit::Assembler - Construction / Destruction] +// ============================================================================ + +Assembler::Assembler(CodeGenerator* codeGenerator) ASMJIT_NOTHROW : + AssemblerIntrinsics(codeGenerator) +{ +} + +Assembler::~Assembler() ASMJIT_NOTHROW +{ +} + +} // AsmJit namespace + +// [Api-End] +#include "ApiEnd.h" |