// AsmJit - Complete JIT Assembler for C++ Language. // Copyright (c) 2008-2010, Petr Kobalicek // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // 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 #include #include // A little bit C++. #include // [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(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(op).getRegCode()); else _emitModM(opReg, reinterpret_cast(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(src).getRegCode()); else _emitModM(opReg, reinterpret_cast(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(*op); return dumpRegister(buf, reg.getRegType(), reg.getRegIndex()); } else if (op->isMem()) { bool isAbsolute = false; const Mem& mem = reinterpret_cast(*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(*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(_Operand_); \ immSize = (_Size_); \ goto emitImmediate; \ } while (0) // Convert operands to OPERAND_NONE if needed. if (o0 == NULL) { o0 = reinterpret_cast(_none); } else if (o0->isReg()) { bLoHiUsed |= o0->_reg.code & (REG_TYPE_GPB_LO | REG_TYPE_GPB_HI); } if (o1 == NULL) { o1 = reinterpret_cast(_none); } else if (o1->isReg()) { bLoHiUsed |= o1->_reg.code & (REG_TYPE_GPB_LO | REG_TYPE_GPB_HI); } if (o2 == NULL) { o2 = reinterpret_cast(_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(&_patchedHiRegs[o0->_reg.code & REG_INDEX_MASK]); if (o1->isRegType(REG_TYPE_GPB_HI)) o1 = reinterpret_cast(&_patchedHiRegs[o1->_reg.code & REG_INDEX_MASK]); if (o2->isRegType(REG_TYPE_GPB_HI)) o2 = reinterpret_cast(&_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(*o1).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(*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(*o0), immSize, forceRexPrefix); _FINISHED_IMMEDIATE(&imm, immSize); } break; } case InstructionDescription::G_BSWAP: { if (o0->isReg()) { const GPReg& dst = reinterpret_cast(*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(*o0); const GPReg& src = reinterpret_cast(*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(*o0); const Imm& src = reinterpret_cast(*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(*o0); _emitX86RM(0xFF, 0, 0, 2, dst, 0, forceRexPrefix); _FINISHED(); } if (o0->isImm()) { const Imm& imm = reinterpret_cast(*o0); _emitByte(0xE8); _emitJmpOrCallReloc(InstructionDescription::G_CALL, (void*)imm.getValue()); _FINISHED(); } if (o0->isLabel()) { LabelData& l_data = _labelData[reinterpret_cast(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(*o0); const Operand& src = reinterpret_cast(*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(*o2).getValue()); _emitByte((uint8_t )(sysuint_t)reinterpret_cast(*o1).getValue()); _FINISHED(); } break; } case InstructionDescription::G_IMUL: { // 1 operand if (o0->isRegMem() && o1->isNone() && o2->isNone()) { const Operand& src = reinterpret_cast(*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(*o0); ASMJIT_ASSERT(!dst.isRegType(REG_TYPE_GPW)); if (o1->isRegMem()) { const Operand& src = reinterpret_cast(*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(*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(*o0); const Operand& src = reinterpret_cast(*o1); const Imm& imm = reinterpret_cast(*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(*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(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(o0)->getId() & OPERAND_ID_VALUE_MASK]; uint32_t hint = (uint32_t)(o1->isImm() ? reinterpret_cast(*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(*o0); _emitX86RM(0xFF, 0, 0, 4, dst, 0, forceRexPrefix); _FINISHED(); } if (o0->isImm()) { const Imm& imm = reinterpret_cast(*o0); _emitByte(0xE9); _emitJmpOrCallReloc(InstructionDescription::G_JMP, (void*)imm.getValue()); _FINISHED(); } if (o0->isLabel()) { LabelData& l_data = _labelData[reinterpret_cast(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(*o0); const Mem& src = reinterpret_cast(*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(*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(dst).getRegCode(), reinterpret_cast(src), 0, forceRexPrefix); _FINISHED(); } // Reg <- Imm case (OPERAND_REG << 4) | OPERAND_IMM: { const GPReg& dst = reinterpret_cast(*o0); const Imm& src = reinterpret_cast(*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(src).getRegCode(), reinterpret_cast(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(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(!reverse ? *o0 : *o1); const Imm& imm = reinterpret_cast(!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(*o0); const Operand& src = reinterpret_cast(*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(*o0); const Operand& src = reinterpret_cast(*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(*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(*o0).getRegCode(), forceRexPrefix); _FINISHED(); } if (o0->isMem()) { _emitX86RM(id->opCode[1], o0->getSize() == 2, 0, (uint8_t)id->opCodeR, reinterpret_cast(*o0), 0, forceRexPrefix); _FINISHED(); } break; } case InstructionDescription::G_R_RM: { if (o0->isReg() && o1->isRegMem()) { const GPReg& dst = reinterpret_cast(*o0); const Operand& src = reinterpret_cast(*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(*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(*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(*o0); const GPReg& src = reinterpret_cast(*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(*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(*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(*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(*o0); const GPReg& src1 = reinterpret_cast(*o1); const Operand& src2 = reinterpret_cast(*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(*o1).getRegCode(), reinterpret_cast(*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(*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(*o0)); // Segment prefix. #if defined(ASMJIT_X64) _emitRexRM(o0->getSize() == 8, 0, reinterpret_cast(*o0), forceRexPrefix); #endif // ASMJIT_X64 _emitByte(0xF6 + (o0->getSize() != 1)); _emitModRM(0, reinterpret_cast(*o0), immSize); _FINISHED_IMMEDIATE(o1, immSize); } break; } case InstructionDescription::G_XCHG: { if (o0->isRegMem() && o1->isReg()) { const Operand& dst = reinterpret_cast(*o0); const GPReg& src = reinterpret_cast(*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(dst).getRegCode() == 0 || reinterpret_cast(src).getRegCode() == 0 )) { uint8_t index = reinterpret_cast(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(*o0).getRegCode(), reinterpret_cast(*o1), 0, forceRexPrefix); _FINISHED(); } if (o0->isMem() && o1->isReg()) { _emitX86RM(0x000F38F1, o1->isRegType(REG_TYPE_GPW), o1->isRegType(REG_TYPE_GPQ), reinterpret_cast(*o1).getRegCode(), reinterpret_cast(*o0), 0, forceRexPrefix); _FINISHED(); } break; } case InstructionDescription::G_X87_FPU: { if (o0->isRegType(REG_TYPE_X87)) { uint8_t i1 = reinterpret_cast(*o0).getRegIndex(); uint8_t i2 = 0; if (code != INST_FCOM && code != INST_FCOMP) { if (!o1->isRegType(REG_TYPE_X87)) goto illegalInstruction; i2 = reinterpret_cast(*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(*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(*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(*o0).getRegType() <= REG_TYPE_GPQ && reinterpret_cast(*o0).getRegIndex() == 0) { _emitOpCode(id->opCode[1]); _FINISHED(); } if (o0->isMem()) { _emitX86RM(id->opCode[0], 0, 0, (uint8_t)id->opCodeR, reinterpret_cast(*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(*o0).getRegIndex()); _FINISHED(); } // ... fall through to G_X87_MEM ... } case InstructionDescription::G_X87_MEM: { if (!o0->isMem()) goto illegalInstruction; const Mem& m = reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } // (X)MM|Reg <- Mem if (o0->isReg() && o1->isMem()) { _emitMmu(id->opCode[0], rexw, reinterpret_cast(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } // Mem <- (X)MM|Reg if (o0->isMem() && o1->isReg()) { _emitMmu(id->opCode[1], rexw, reinterpret_cast(*o1).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(*o1).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } if (o0->isRegType(REG_TYPE_XMM) && o1->isRegType(REG_TYPE_XMM)) { _emitMmu(0xF3000F7E, 0, reinterpret_cast(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } // Convenience - movdq2q if (o0->isRegType(REG_TYPE_MM) && o1->isRegType(REG_TYPE_XMM)) { _emitMmu(0xF2000FD6, 0, reinterpret_cast(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } // Convenience - movq2dq if (o0->isRegType(REG_TYPE_XMM) && o1->isRegType(REG_TYPE_MM)) { _emitMmu(0xF3000FD6, 0, reinterpret_cast(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } if (o0->isRegType(REG_TYPE_MM) && o1->isMem()) { _emitMmu(0x00000F6F, 0, reinterpret_cast(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } if (o0->isRegType(REG_TYPE_XMM) && o1->isMem()) { _emitMmu(0xF3000F7E, 0, reinterpret_cast(*o0).getRegCode(), reinterpret_cast(*o1), 0); _FINISHED(); } if (o0->isMem() && o1->isRegType(REG_TYPE_MM)) { _emitMmu(0x00000F7F, 0, reinterpret_cast(*o1).getRegCode(), reinterpret_cast(*o0), 0); _FINISHED(); } if (o0->isMem() && o1->isRegType(REG_TYPE_XMM)) { _emitMmu(0x66000FD6, 0, reinterpret_cast(*o1).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(*o1).getRegCode(), reinterpret_cast(*o0), 0); _FINISHED(); } #endif // ASMJIT_X64 break; } case InstructionDescription::G_MMU_PREFETCH: { if (o0->isMem() && o1->isImm()) { const Mem& mem = reinterpret_cast(*o0); const Imm& hint = reinterpret_cast(*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(*o1).getRegCode(), reinterpret_cast(*o0), 1); _FINISHED_IMMEDIATE(o2, 1); } if (o0->isMem()) { _emitMmu(opCode, (uint8_t)id->opCodeR, reinterpret_cast(*o1).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(*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(*o0).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(*o0).getRegCode(), reinterpret_cast(*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(_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(dst + r.offset) = (int32_t)val; break; case 8: *reinterpret_cast(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(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(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"