diff options
Diffstat (limited to 'libpixelflinger/codeflinger/x86/libenc/enc_base.cpp')
-rw-r--r-- | libpixelflinger/codeflinger/x86/libenc/enc_base.cpp | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_base.cpp b/libpixelflinger/codeflinger/x86/libenc/enc_base.cpp new file mode 100644 index 0000000..0562ce8 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_base.cpp @@ -0,0 +1,1137 @@ +/* + * 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 *dest = (unsigned char *) (stream); + unsigned char *src = (unsigned char *) (& (odesc->opcode)); + *dest = *src; + } + else if (odesc->opcode_len==2) { + short *dest = (short *) (stream); + void *ptr = (void *) (& (odesc->opcode)); + short *src = (short *) (ptr); + *dest = *src; + } + else if (odesc->opcode_len==3) { + unsigned short *dest = (unsigned short *) (stream); + void *ptr = (void *) (& (odesc->opcode)); + unsigned short *src = (unsigned short *) (ptr); + *dest = *src; + + //Now handle the last part + unsigned char *dest2 = (unsigned char *) (stream + 2); + *dest2 = odesc->opcode[2]; + } + else if (odesc->opcode_len==4) { + unsigned int *dest = (unsigned int *) (stream); + void *ptr = (void *) (& (odesc->opcode)); + unsigned int *src = (unsigned int *) (ptr); + *dest = *src; + } + 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 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 |