/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexander V. Astapchuk */ #include "enc_base.h" //#include <climits> #include <string.h> #define USE_ENCODER_DEFINES #include "enc_prvt.h" #include <stdio.h> //#define JET_PROTO #ifdef JET_PROTO #include "dec_base.h" #include "jvmti_dasm.h" #endif ENCODER_NAMESPACE_START /** * @file * @brief Main encoding routines and structures. */ #ifndef _WIN32 #define strcmpi strcasecmp #endif int EncoderBase::dummy = EncoderBase::buildTable(); const unsigned char EncoderBase::size_hash[OpndSize_64+1] = { // 0xFF, // OpndSize_Null = 0, 3, // OpndSize_8 = 0x1, 2, // OpndSize_16 = 0x2, 0xFF, // 0x3 1, // OpndSize_32 = 0x4, 0xFF, // 0x5 0xFF, // 0x6 0xFF, // 0x7 0, // OpndSize_64 = 0x8, // }; const unsigned char EncoderBase::kind_hash[OpndKind_Mem+1] = { // //gp reg -> 000 = 0 //memory -> 001 = 1 //immediate -> 010 = 2 //xmm reg -> 011 = 3 //segment regs -> 100 = 4 //fp reg -> 101 = 5 //mmx reg -> 110 = 6 // 0xFF, // 0 OpndKind_Null=0, 0<<2, // 1 OpndKind_GPReg = // OpndKind_MinRegKind=0x1, 4<<2, // 2 OpndKind_SReg=0x2, #ifdef _HAVE_MMX_ 6<<2, // 3 #else 0xFF, // 3 #endif 5<<2, // 4 OpndKind_FPReg=0x4, 0xFF, 0xFF, 0xFF, // 5, 6, 7 3<<2, // OpndKind_XMMReg=0x8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 9, 0xA, 0xB, 0xC, 0xD, // 0xE, 0xF 0xFF, // OpndKind_MaxRegKind = // OpndKind_StatusReg = // OpndKind_OtherReg=0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x11-0x18 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x19-0x1F 2<<2, // OpndKind_Immediate=0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x21-0x28 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x29-0x30 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x31-0x38 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x39-0x3F 1<<2, // OpndKind_Memory=0x40 }; char * EncoderBase::curRelOpnd[3]; char* EncoderBase::encode_aux(char* stream, unsigned aux, const Operands& opnds, const OpcodeDesc * odesc, unsigned * pargsCount, Rex * prex) { const unsigned byte = aux; OpcodeByteKind kind = (OpcodeByteKind)(byte & OpcodeByteKind_KindMask); // The '>>' here is to force the switch to be table-based) instead of // set of CMP+Jcc. if (*pargsCount >= COUNTOF(opnds)) { assert(false); return stream; } switch(kind>>8) { case OpcodeByteKind_SlashR>>8: // /r - Indicates that the ModR/M byte of the instruction contains // both a register operand and an r/m operand. { assert(opnds.count() > 1); // not true anymore for MOVQ xmm<->r //assert((odesc->opnds[0].kind & OpndKind_Mem) || // (odesc->opnds[1].kind & OpndKind_Mem)); unsigned memidx = odesc->opnds[0].kind & OpndKind_Mem ? 0 : 1; unsigned regidx = memidx == 0 ? 1 : 0; memidx += *pargsCount; regidx += *pargsCount; ModRM& modrm = *(ModRM*)stream; if (memidx >= COUNTOF(opnds) || regidx >= COUNTOF(opnds)) { assert(false); break; } if (opnds[memidx].is_mem()) { stream = encodeModRM(stream, opnds, memidx, odesc, prex); } else { modrm.mod = 3; // 11 modrm.rm = getHWRegIndex(opnds[memidx].reg()); #ifdef _EM64T_ if (opnds[memidx].need_rex() && needs_rex_r(opnds[memidx].reg())) { prex->b = 1; } #endif ++stream; } modrm.reg = getHWRegIndex(opnds[regidx].reg()); #ifdef _EM64T_ if (opnds[regidx].need_rex() && needs_rex_r(opnds[regidx].reg())) { prex->r = 1; } #endif *pargsCount += 2; } break; case OpcodeByteKind_SlashNum>>8: // /digit - A digit between 0 and 7 indicates that the // ModR/M byte of the instruction uses only the r/m // (register or memory) operand. The reg field contains // the digit that provides an extension to the instruction's // opcode. { const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask); assert(lowByte <= 7); ModRM& modrm = *(ModRM*)stream; unsigned idx = *pargsCount; assert(opnds[idx].is_mem() || opnds[idx].is_reg()); if (opnds[idx].is_mem()) { stream = encodeModRM(stream, opnds, idx, odesc, prex); } else { modrm.mod = 3; // 11 modrm.rm = getHWRegIndex(opnds[idx].reg()); #ifdef _EM64T_ if (opnds[idx].need_rex() && needs_rex_r(opnds[idx].reg())) { prex->b = 1; } #endif ++stream; } modrm.reg = (char)lowByte; *pargsCount += 1; } break; case OpcodeByteKind_plus_i>>8: // +i - A number used in floating-point instructions when one // of the operands is ST(i) from the FPU register stack. The // number i (which can range from 0 to 7) is added to the // hexadecimal byte given at the left of the plus sign to form // a single opcode byte. { unsigned idx = *pargsCount; const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask); *stream = (char)lowByte + getHWRegIndex(opnds[idx].reg()); ++stream; *pargsCount += 1; } break; case OpcodeByteKind_ib>>8: case OpcodeByteKind_iw>>8: case OpcodeByteKind_id>>8: #ifdef _EM64T_ case OpcodeByteKind_io>>8: #endif //_EM64T_ // ib, iw, id - A 1-byte (ib), 2-byte (iw), or 4-byte (id) // immediate operand to the instruction that follows the // opcode, ModR/M bytes or scale-indexing bytes. The opcode // determines if the operand is a signed value. All words // and double words are given with the low-order byte first. { unsigned idx = *pargsCount; *pargsCount += 1; assert(opnds[idx].is_imm()); if (kind == OpcodeByteKind_ib) { *(unsigned char*)stream = (unsigned char)opnds[idx].imm(); curRelOpnd[idx] = stream; stream += 1; } else if (kind == OpcodeByteKind_iw) { *(unsigned short*)stream = (unsigned short)opnds[idx].imm(); curRelOpnd[idx] = stream; stream += 2; } else if (kind == OpcodeByteKind_id) { *(unsigned*)stream = (unsigned)opnds[idx].imm(); curRelOpnd[idx] = stream; stream += 4; } #ifdef _EM64T_ else { assert(kind == OpcodeByteKind_io); *(long long*)stream = (long long)opnds[idx].imm(); curRelOpnd[idx] = stream; stream += 8; } #else else { assert(false); } #endif } break; case OpcodeByteKind_cb>>8: assert(opnds[*pargsCount].is_imm()); *(unsigned char*)stream = (unsigned char)opnds[*pargsCount].imm(); curRelOpnd[*pargsCount]= stream; stream += 1; *pargsCount += 1; break; case OpcodeByteKind_cw>>8: assert(opnds[*pargsCount].is_imm()); *(unsigned short*)stream = (unsigned short)opnds[*pargsCount].imm(); curRelOpnd[*pargsCount]= stream; stream += 2; *pargsCount += 1; break; case OpcodeByteKind_cd>>8: assert(opnds[*pargsCount].is_imm()); *(unsigned*)stream = (unsigned)opnds[*pargsCount].imm(); curRelOpnd[*pargsCount]= stream; stream += 4; *pargsCount += 1; break; //OpcodeByteKind_cp = 0x0B00, //OpcodeByteKind_co = 0x0C00, //OpcodeByteKind_ct = 0x0D00, case OpcodeByteKind_rb>>8: case OpcodeByteKind_rw>>8: case OpcodeByteKind_rd>>8: // +rb, +rw, +rd - A register code, from 0 through 7, // added to the hexadecimal byte given at the left of // the plus sign to form a single opcode byte. assert(opnds.count() > 0); assert(opnds[*pargsCount].is_reg()); { const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask); *(unsigned char*)stream = (unsigned char)lowByte + getHWRegIndex(opnds[*pargsCount].reg()); #ifdef _EM64T_ if (opnds[*pargsCount].need_rex() && needs_rex_r(opnds[*pargsCount].reg())) { prex->b = 1; } #endif ++stream; *pargsCount += 1; } break; default: assert(false); break; } return stream; } char * EncoderBase::encode(char * stream, Mnemonic mn, const Operands& opnds) { #ifdef _DEBUG if (opnds.count() > 0) { if (opnds[0].is_mem()) { assert(getRegKind(opnds[0].base()) != OpndKind_SReg); } else if (opnds.count() >1 && opnds[1].is_mem()) { assert(getRegKind(opnds[1].base()) != OpndKind_SReg); } } #endif #ifdef JET_PROTO char* saveStream = stream; #endif const OpcodeDesc * odesc = lookup(mn, opnds); #if !defined(_EM64T_) bool copy_opcode = true; Rex *prex = NULL; #else // We need rex if // either of registers used as operand or address form is new extended register // it's explicitly specified by opcode // So, if we don't have REX in opcode but need_rex, then set rex here // otherwise, wait until opcode is set, and then update REX bool copy_opcode = true; unsigned char _1st = odesc->opcode[0]; Rex *prex = (Rex*)stream; if (opnds.need_rex() && ((_1st == 0x66) || (_1st == 0xF2 || _1st == 0xF3) && odesc->opcode[1] == 0x0F)) { // Special processing // copy_opcode = false; // *(unsigned char*)stream = _1st; ++stream; // prex = (Rex*)stream; prex->dummy = 4; prex->w = 0; prex->b = 0; prex->x = 0; prex->r = 0; ++stream; // memcpy(stream, &odesc->opcode[1], odesc->opcode_len-1); stream += odesc->opcode_len-1; } else if (_1st != 0x48 && opnds.need_rex()) { prex = (Rex*)stream; prex->dummy = 4; prex->w = 0; prex->b = 0; prex->x = 0; prex->r = 0; ++stream; } #endif // ifndef EM64T if (copy_opcode) { if (odesc->opcode_len==1) { *(unsigned char*)stream = *(unsigned char*)&odesc->opcode; } else if (odesc->opcode_len==2) { *(unsigned short*)stream = *(unsigned short*)&odesc->opcode; } else if (odesc->opcode_len==3) { *(unsigned short*)stream = *(unsigned short*)&odesc->opcode; *(unsigned char*)(stream+2) = odesc->opcode[2]; } else if (odesc->opcode_len==4) { *(unsigned*)stream = *(unsigned*)&odesc->opcode; } stream += odesc->opcode_len; } unsigned argsCount = odesc->first_opnd; if (odesc->aux0) { stream = encode_aux(stream, odesc->aux0, opnds, odesc, &argsCount, prex); if (odesc->aux1) { stream = encode_aux(stream, odesc->aux1, opnds, odesc, &argsCount, prex); } } #ifdef JET_PROTO //saveStream Inst inst; unsigned len = DecoderBase::decode(saveStream, &inst); assert(inst.mn == mn); assert(len == (unsigned)(stream-saveStream)); if (mn == Mnemonic_CALL || mn == Mnemonic_JMP || Mnemonic_RET == mn || (Mnemonic_JO<=mn && mn<=Mnemonic_JG)) { assert(inst.argc == opnds.count()); InstructionDisassembler idi(saveStream); for (unsigned i=0; i<inst.argc; i++) { const EncoderBase::Operand& original = opnds[i]; const EncoderBase::Operand& decoded = inst.operands[i]; assert(original.kind() == decoded.kind()); assert(original.size() == decoded.size()); if (original.is_imm()) { assert(original.imm() == decoded.imm()); assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Imm); if (mn == Mnemonic_CALL) { assert(idi.get_type() == InstructionDisassembler::RELATIVE_CALL); } else if (mn == Mnemonic_JMP) { assert(idi.get_type() == InstructionDisassembler::RELATIVE_JUMP); } else if (mn == Mnemonic_RET) { assert(idi.get_type() == InstructionDisassembler::RET); } else { assert(idi.get_type() == InstructionDisassembler::RELATIVE_COND_JUMP); } } else if (original.is_mem()) { assert(original.base() == decoded.base()); assert(original.index() == decoded.index()); assert(original.scale() == decoded.scale()); assert(original.disp() == decoded.disp()); assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Mem); if (mn == Mnemonic_CALL) { assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL); } else if (mn == Mnemonic_JMP) { assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP); } else { assert(false); } } else { assert(original.is_reg()); assert(original.reg() == decoded.reg()); assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Reg); if (mn == Mnemonic_CALL) { assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL); } else if (mn == Mnemonic_JMP) { assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP); } else { assert(false); } } } Inst inst2; len = DecoderBase::decode(saveStream, &inst2); } // if(idi.get_length_with_prefix() != (int)len) { //__asm { int 3 }; // } #endif return stream; } char* EncoderBase::encodeModRM(char* stream, const Operands& opnds, unsigned idx, const OpcodeDesc * odesc, Rex * prex) { const Operand& op = opnds[idx]; assert(op.is_mem()); assert(idx < COUNTOF(curRelOpnd)); ModRM& modrm = *(ModRM*)stream; ++stream; SIB& sib = *(SIB*)stream; // we need SIB if // we have index & scale (nb: having index w/o base and w/o scale // treated as error) // the base is EBP w/o disp, BUT let's use a fake disp8 // the base is ESP (nb: cant have ESP as index) RegName base = op.base(); // only disp ?.. if (base == RegName_Null && op.index() == RegName_Null) { assert(op.scale() == 0); // 'scale!=0' has no meaning without index // ... yes - only have disp // On EM64T, the simply [disp] addressing means 'RIP-based' one - // must have to use SIB to encode 'DS: based' #ifdef _EM64T_ modrm.mod = 0; // 00 - .. modrm.rm = 4; // 100 - have SIB sib.base = 5; // 101 - none sib.index = 4; // 100 - none sib.scale = 0; // ++stream; // bypass SIB #else // ignore disp_fits8, always use disp32. modrm.mod = 0; modrm.rm = 5; #endif *(unsigned*)stream = (unsigned)op.disp(); curRelOpnd[idx]= stream; stream += 4; return stream; } //climits: error when targeting compal #define CHAR_MIN -127 #define CHAR_MAX 127 const bool disp_fits8 = CHAR_MIN <= op.disp() && op.disp() <= CHAR_MAX; /*&& op.base() != RegName_Null - just checked above*/ if (op.index() == RegName_Null && getHWRegIndex(op.base()) != getHWRegIndex(REG_STACK)) { assert(op.scale() == 0); // 'scale!=0' has no meaning without index // ... luckily no SIB, only base and may be a disp // EBP base is a special case. Need to use [EBP] + disp8 form if (op.disp() == 0 && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) { modrm.mod = 0; // mod=00, no disp et all } else if (disp_fits8) { modrm.mod = 1; // mod=01, use disp8 *(unsigned char*)stream = (unsigned char)op.disp(); curRelOpnd[idx]= stream; ++stream; } else { modrm.mod = 2; // mod=10, use disp32 *(unsigned*)stream = (unsigned)op.disp(); curRelOpnd[idx]= stream; stream += 4; } modrm.rm = getHWRegIndex(op.base()); if (is_em64t_extra_reg(op.base())) { prex->b = 1; } return stream; } // cool, we do have SIB. ++stream; // bypass SIB in stream // {E|R}SP cannot be scaled index, however, R12 which has the same index in modrm - can assert(op.index() == RegName_Null || !equals(op.index(), REG_STACK)); // Only GPRegs can be encoded in the SIB assert(op.base() == RegName_Null || getRegKind(op.base()) == OpndKind_GPReg); assert(op.index() == RegName_Null || getRegKind(op.index()) == OpndKind_GPReg); modrm.rm = 4; // r/m = 100, means 'we have SIB here' if (op.base() == RegName_Null) { // no base. // already checked above if // the first if() //assert(op.index() != RegName_Null); modrm.mod = 0; // mod=00 - here it means 'no base, but disp32' sib.base = 5; // 101 with mod=00 ^^^ // encode at least fake disp32 to avoid having [base=ebp] *(unsigned*)stream = op.disp(); curRelOpnd[idx]= stream; stream += 4; unsigned sc = op.scale(); if (sc == 1 || sc==0) { sib.scale = 0; } // SS=00 else if (sc == 2) { sib.scale = 1; } // SS=01 else if (sc == 4) { sib.scale = 2; } // SS=10 else if (sc == 8) { sib.scale = 3; } // SS=11 sib.index = getHWRegIndex(op.index()); if (is_em64t_extra_reg(op.index())) { prex->x = 1; } return stream; } if (op.disp() == 0 && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) { modrm.mod = 0; // mod=00, no disp } else if (disp_fits8) { modrm.mod = 1; // mod=01, use disp8 *(unsigned char*)stream = (unsigned char)op.disp(); curRelOpnd[idx]= stream; stream += 1; } else { modrm.mod = 2; // mod=10, use disp32 *(unsigned*)stream = (unsigned)op.disp(); curRelOpnd[idx]= stream; stream += 4; } if (op.index() == RegName_Null) { assert(op.scale() == 0); // 'scale!=0' has no meaning without index // the only reason we're here without index, is that we have {E|R}SP // or R12 as a base. Another possible reason - EBP without a disp - // is handled above by adding a fake disp8 #ifdef _EM64T_ assert(op.base() != RegName_Null && (equals(op.base(), REG_STACK) || equals(op.base(), RegName_R12))); #else // _EM64T_ assert(op.base() != RegName_Null && equals(op.base(), REG_STACK)); #endif //_EM64T_ sib.scale = 0; // SS = 00 sib.index = 4; // SS + index=100 means 'no index' } else { unsigned sc = op.scale(); if (sc == 1 || sc==0) { sib.scale = 0; } // SS=00 else if (sc == 2) { sib.scale = 1; } // SS=01 else if (sc == 4) { sib.scale = 2; } // SS=10 else if (sc == 8) { sib.scale = 3; } // SS=11 sib.index = getHWRegIndex(op.index()); if (is_em64t_extra_reg(op.index())) { prex->x = 1; } // not an error by itself, but the usage of [index*1] instead // of [base] is discouraged assert(op.base() != RegName_Null || op.scale() != 1); } sib.base = getHWRegIndex(op.base()); if (is_em64t_extra_reg(op.base())) { prex->b = 1; } return stream; } char * EncoderBase::nops(char * stream, unsigned howMany) { // Recommended multi-byte NOPs from the Intel architecture manual static const unsigned char nops[10][9] = { { 0, }, // 0, this line is dummy and not used in the loop below { 0x90, }, // 1-byte NOP { 0x66, 0x90, }, // 2 { 0x0F, 0x1F, 0x00, }, // 3 { 0x0F, 0x1F, 0x40, 0x00, }, // 4 { 0x0F, 0x1F, 0x44, 0x00, 0x00, }, // 5 { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, }, // 6 { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, }, // 7 { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, }, // 8 { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 9-byte NOP }; // Start from delivering the longest possible NOPs, then proceed with shorter ones for (unsigned nopSize=9; nopSize!=0; nopSize--) { while(howMany>=nopSize) { const unsigned char* nopBytes = nops[nopSize]; for (unsigned i=0; i<nopSize; i++) { stream[i] = nopBytes[i]; } stream += nopSize; howMany -= nopSize; } } char* end = stream + howMany; return end; } char * EncoderBase::prefix(char* stream, InstPrefix pref) { if (pref== InstPrefix_Null) { // nothing to do return stream; } *stream = (char)pref; return stream + 1; } /** * */ bool EncoderBase::extAllowed(OpndExt opndExt, OpndExt instExt) { if (instExt == opndExt || instExt == OpndExt_Any || opndExt == OpndExt_Any) { return true; } //asm("int3"); assert(0); return false; } /** * */ static bool match(const EncoderBase::OpcodeDesc& odesc, const EncoderBase::Operands& opnds) { assert(odesc.roles.count == opnds.count()); for(unsigned j = 0; j < odesc.roles.count; j++) { const EncoderBase::OpndDesc& desc = odesc.opnds[j]; const EncoderBase::Operand& op = opnds[j]; // location must match exactly if ((desc.kind & op.kind()) != op.kind()) { //assert(0); return false; } // size must match exactly if (desc.size != op.size()) { //assert(0); return false; } // extentions should be consistent if (!EncoderBase::extAllowed(op.ext(), desc.ext)) { return false; } } return true; } static bool try_match(const EncoderBase::OpcodeDesc& odesc, const EncoderBase::Operands& opnds, bool strict) { assert(odesc.roles.count == opnds.count()); for(unsigned j=0; j<odesc.roles.count; j++) { // - the location must match exactly if ((odesc.opnds[j].kind & opnds[j].kind()) != opnds[j].kind()) { return false; } if (strict) { // the size must match exactly if (odesc.opnds[j].size != opnds[j].size()) { return false; } } else { // must match only for def operands, and dont care about use ones // situations like 'mov r8, imm32/mov r32, imm8' so the // destination operand defines the overall size if (EncoderBase::getOpndRoles(odesc.roles, j) & OpndRole_Def) { if (odesc.opnds[j].size != opnds[j].size()) { return false; } } } } return true; } // //Subhash implementaion - may be useful in case of many misses during fast //opcode lookup. // #ifdef ENCODER_USE_SUBHASH static unsigned subHash[32]; static unsigned find(Mnemonic mn, unsigned hash) { unsigned key = hash % COUNTOF(subHash); unsigned pack = subHash[key]; unsigned _hash = pack & 0xFFFF; if (_hash != hash) { stat.miss(mn); return EncoderBase::NOHASH; } unsigned _mn = (pack >> 24)&0xFF; if (_mn != _mn) { stat.miss(mn); return EncoderBase::NOHASH; } unsigned idx = (pack >> 16) & 0xFF; stat.hit(mn); return idx; } static void put(Mnemonic mn, unsigned hash, unsigned idx) { unsigned pack = hash | (idx<<16) | (mn << 24); unsigned key = hash % COUNTOF(subHash); subHash[key] = pack; } #endif const EncoderBase::OpcodeDesc * EncoderBase::lookup(Mnemonic mn, const Operands& opnds) { const unsigned hash = opnds.hash(); unsigned opcodeIndex = opcodesHashMap[mn][hash]; #ifdef ENCODER_USE_SUBHASH if (opcodeIndex == NOHASH) { opcodeIndex = find(mn, hash); } #endif if (opcodeIndex == NOHASH) { // fast-path did no work. try to lookup sequentially const OpcodeDesc * odesc = opcodes[mn]; int idx = -1; bool found = false; for (idx=0; !odesc[idx].last; idx++) { const OpcodeDesc& opcode = odesc[idx]; if (opcode.platf == OpcodeInfo::decoder) { continue; } if (opcode.roles.count != opnds.count()) { continue; } if (try_match(opcode, opnds, true)) { found = true; break; } } if (!found) { for (idx=0; !odesc[idx].last; idx++) { const OpcodeDesc& opcode = odesc[idx]; if (opcode.platf == OpcodeInfo::decoder) { continue; } if (opcode.roles.count != opnds.count()) { continue; } if (try_match(opcode, opnds, false)) { found = true; break; } } } assert(found); opcodeIndex = idx; #ifdef ENCODER_USE_SUBHASH put(mn, hash, opcodeIndex); #endif } assert(opcodeIndex != NOHASH); const OpcodeDesc * odesc = &opcodes[mn][opcodeIndex]; assert(!odesc->last); assert(odesc->roles.count == opnds.count()); assert(odesc->platf != OpcodeInfo::decoder); #if !defined(_EM64T_) // tuning was done for IA32 only, so no size restriction on EM64T //assert(sizeof(OpcodeDesc)==128); #endif return odesc; } char* EncoderBase::getOpndLocation(int index) { assert(index < 3); return curRelOpnd[index]; } Mnemonic EncoderBase::str2mnemonic(const char * mn_name) { for (unsigned m = 1; m<Mnemonic_Count; m++) { if (!strcmpi(mnemonics[m].name, mn_name)) { return (Mnemonic)m; } } return Mnemonic_Null; } static const char * conditionStrings[ConditionMnemonic_Count] = { "O", "NO", "B", "AE", "Z", "NZ", "BE", "A", "S", "NS", "P", "NP", "L", "GE", "LE", "G", }; const char * getConditionString(ConditionMnemonic cm) { return conditionStrings[cm]; } static const struct { char sizeString[12]; OpndSize size; } sizes[] = { { "Sz8", OpndSize_8 }, { "Sz16", OpndSize_16 }, { "Sz32", OpndSize_32 }, { "Sz64", OpndSize_64 }, #if !defined(TESTING_ENCODER) { "Sz80", OpndSize_80 }, { "Sz128", OpndSize_128 }, #endif { "SzAny", OpndSize_Any }, }; OpndSize getOpndSize(const char * sizeString) { assert(sizeString); for (unsigned i = 0; i<COUNTOF(sizes); i++) { if (!strcmpi(sizeString, sizes[i].sizeString)) { return sizes[i].size; } } return OpndSize_Null; } const char * getOpndSizeString(OpndSize size) { for( unsigned i = 0; i<COUNTOF(sizes); i++ ) { if( sizes[i].size==size ) { return sizes[i].sizeString; } } return NULL; } static const struct { char kindString[16]; OpndKind kind; } kinds[] = { { "Null", OpndKind_Null }, { "GPReg", OpndKind_GPReg }, { "SReg", OpndKind_SReg }, { "FPReg", OpndKind_FPReg }, { "XMMReg", OpndKind_XMMReg }, #ifdef _HAVE_MMX_ { "MMXReg", OpndKind_MMXReg }, #endif { "StatusReg", OpndKind_StatusReg }, { "Reg", OpndKind_Reg }, { "Imm", OpndKind_Imm }, { "Mem", OpndKind_Mem }, { "Any", OpndKind_Any }, }; const char * getOpndKindString(OpndKind kind) { for (unsigned i = 0; i<COUNTOF(kinds); i++) { if (kinds[i].kind==kind) { return kinds[i].kindString; } } return NULL; } OpndKind getOpndKind(const char * kindString) { assert(kindString); for (unsigned i = 0; i<COUNTOF(kinds); i++) { if (!strcmpi(kindString, kinds[i].kindString)) { return kinds[i].kind; } } return OpndKind_Null; } /** * A mapping between register string representation and its RegName constant. */ static const struct { char regstring[7]; RegName regname; } registers[] = { #ifdef _EM64T_ {"RAX", RegName_RAX}, {"RBX", RegName_RBX}, {"RCX", RegName_RCX}, {"RDX", RegName_RDX}, {"RBP", RegName_RBP}, {"RSI", RegName_RSI}, {"RDI", RegName_RDI}, {"RSP", RegName_RSP}, {"R8", RegName_R8}, {"R9", RegName_R9}, {"R10", RegName_R10}, {"R11", RegName_R11}, {"R12", RegName_R12}, {"R13", RegName_R13}, {"R14", RegName_R14}, {"R15", RegName_R15}, #endif {"EAX", RegName_EAX}, {"ECX", RegName_ECX}, {"EDX", RegName_EDX}, {"EBX", RegName_EBX}, {"ESP", RegName_ESP}, {"EBP", RegName_EBP}, {"ESI", RegName_ESI}, {"EDI", RegName_EDI}, #ifdef _EM64T_ {"R8D", RegName_R8D}, {"R9D", RegName_R9D}, {"R10D", RegName_R10D}, {"R11D", RegName_R11D}, {"R12D", RegName_R12D}, {"R13D", RegName_R13D}, {"R14D", RegName_R14D}, {"R15D", RegName_R15D}, #endif {"AX", RegName_AX}, {"CX", RegName_CX}, {"DX", RegName_DX}, {"BX", RegName_BX}, {"SP", RegName_SP}, {"BP", RegName_BP}, {"SI", RegName_SI}, {"DI", RegName_DI}, {"AL", RegName_AL}, {"CL", RegName_CL}, {"DL", RegName_DL}, {"BL", RegName_BL}, #if !defined(_EM64T_) {"AH", RegName_AH}, {"CH", RegName_CH}, {"DH", RegName_DH}, {"BH", RegName_BH}, #else {"SPL", RegName_SPL}, {"BPL", RegName_BPL}, {"SIL", RegName_SIL}, {"DIL", RegName_DIL}, {"R8L", RegName_R8L}, {"R9L", RegName_R9L}, {"R10L", RegName_R10L}, {"R11L", RegName_R11L}, {"R12L", RegName_R12L}, {"R13L", RegName_R13L}, {"R14L", RegName_R14L}, {"R15L", RegName_R15L}, #endif {"ES", RegName_ES}, {"CS", RegName_CS}, {"SS", RegName_SS}, {"DS", RegName_DS}, {"FS", RegName_FS}, {"GS", RegName_GS}, {"FP0", RegName_FP0}, /* {"FP1", RegName_FP1}, {"FP2", RegName_FP2}, {"FP3", RegName_FP3}, {"FP4", RegName_FP4}, {"FP5", RegName_FP5}, {"FP6", RegName_FP6}, {"FP7", RegName_FP7}, */ {"FP0S", RegName_FP0S}, {"FP1S", RegName_FP1S}, {"FP2S", RegName_FP2S}, {"FP3S", RegName_FP3S}, {"FP4S", RegName_FP4S}, {"FP5S", RegName_FP5S}, {"FP6S", RegName_FP6S}, {"FP7S", RegName_FP7S}, {"FP0D", RegName_FP0D}, {"FP1D", RegName_FP1D}, {"FP2D", RegName_FP2D}, {"FP3D", RegName_FP3D}, {"FP4D", RegName_FP4D}, {"FP5D", RegName_FP5D}, {"FP6D", RegName_FP6D}, {"FP7D", RegName_FP7D}, {"XMM0", RegName_XMM0}, {"XMM1", RegName_XMM1}, {"XMM2", RegName_XMM2}, {"XMM3", RegName_XMM3}, {"XMM4", RegName_XMM4}, {"XMM5", RegName_XMM5}, {"XMM6", RegName_XMM6}, {"XMM7", RegName_XMM7}, #ifdef _EM64T_ {"XMM8", RegName_XMM8}, {"XMM9", RegName_XMM9}, {"XMM10", RegName_XMM10}, {"XMM11", RegName_XMM11}, {"XMM12", RegName_XMM12}, {"XMM13", RegName_XMM13}, {"XMM14", RegName_XMM14}, {"XMM15", RegName_XMM15}, #endif {"XMM0S", RegName_XMM0S}, {"XMM1S", RegName_XMM1S}, {"XMM2S", RegName_XMM2S}, {"XMM3S", RegName_XMM3S}, {"XMM4S", RegName_XMM4S}, {"XMM5S", RegName_XMM5S}, {"XMM6S", RegName_XMM6S}, {"XMM7S", RegName_XMM7S}, #ifdef _EM64T_ {"XMM8S", RegName_XMM8S}, {"XMM9S", RegName_XMM9S}, {"XMM10S", RegName_XMM10S}, {"XMM11S", RegName_XMM11S}, {"XMM12S", RegName_XMM12S}, {"XMM13S", RegName_XMM13S}, {"XMM14S", RegName_XMM14S}, {"XMM15S", RegName_XMM15S}, #endif {"XMM0D", RegName_XMM0D}, {"XMM1D", RegName_XMM1D}, {"XMM2D", RegName_XMM2D}, {"XMM3D", RegName_XMM3D}, {"XMM4D", RegName_XMM4D}, {"XMM5D", RegName_XMM5D}, {"XMM6D", RegName_XMM6D}, {"XMM7D", RegName_XMM7D}, #ifdef _EM64T_ {"XMM8D", RegName_XMM8D}, {"XMM9D", RegName_XMM9D}, {"XMM10D", RegName_XMM10D}, {"XMM11D", RegName_XMM11D}, {"XMM12D", RegName_XMM12D}, {"XMM13D", RegName_XMM13D}, {"XMM14D", RegName_XMM14D}, {"XMM15D", RegName_XMM15D}, #endif {"EFLGS", RegName_EFLAGS}, }; const char * getRegNameString(RegName reg) { for (unsigned i = 0; i<COUNTOF(registers); i++) { if (registers[i].regname == reg) { return registers[i].regstring; } } return NULL; } RegName getRegName(const char * regname) { if (NULL == regname) { return RegName_Null; } for (unsigned i = 0; i<COUNTOF(registers); i++) { if (!strcmpi(regname,registers[i].regstring)) { return registers[i].regname; } } return RegName_Null; } ENCODER_NAMESPACE_END