diff options
Diffstat (limited to 'modules/arch/x86/x86id.c')
-rw-r--r-- | modules/arch/x86/x86id.c | 226 |
1 files changed, 125 insertions, 101 deletions
diff --git a/modules/arch/x86/x86id.c b/modules/arch/x86/x86id.c index c167866..e635704 100644 --- a/modules/arch/x86/x86id.c +++ b/modules/arch/x86/x86id.c @@ -26,7 +26,7 @@ */ #include <ctype.h> #include <util.h> -RCSID("$Id: x86id.c 2130 2008-10-07 05:38:11Z peter $"); +RCSID("$Id: x86id.c 2279 2010-01-19 07:57:43Z peter $"); #include <libyasm.h> #include <libyasm/phash.h> @@ -53,21 +53,18 @@ static const char *cpu_find_reverse(unsigned int cpu0, unsigned int cpu1, /* GAS suffix flags for instructions */ enum x86_gas_suffix_flags { - NONE = 0, - SUF_B = 1<<0, - SUF_W = 1<<1, - SUF_L = 1<<2, - SUF_Q = 1<<3, - SUF_S = 1<<4, - SUF_MASK = SUF_B|SUF_W|SUF_L|SUF_Q|SUF_S, + SUF_Z = 1<<0, /* no suffix */ + SUF_B = 1<<1, + SUF_W = 1<<2, + SUF_L = 1<<3, + SUF_Q = 1<<4, + SUF_S = 1<<5, + SUF_MASK = SUF_Z|SUF_B|SUF_W|SUF_L|SUF_Q|SUF_S, /* Flags only used in x86_insn_info */ - GAS_ONLY = 1<<5, /* Only available in GAS mode */ - GAS_ILLEGAL = 1<<6, /* Illegal in GAS mode */ - GAS_NO_REV = 1<<7, /* Don't reverse operands in GAS mode */ - - /* Flags only used in insnprefix_parse_data */ - WEAK = 1<<5 /* Relaxed operand mode for GAS */ + GAS_ONLY = 1<<6, /* Only available in GAS mode */ + GAS_ILLEGAL = 1<<7, /* Illegal in GAS mode */ + GAS_NO_REV = 1<<8 /* Don't reverse operands in GAS mode */ }; /* Miscellaneous flag tests for instructions */ @@ -114,9 +111,7 @@ enum x86_operand_type { */ OPT_MemrAX = 25, /* EAX memory operand only (EA) [special case for SVM skinit opcode] */ - OPT_MemEAX = 26, - /* SIMDReg with value equal to operand 0 SIMDReg */ - OPT_SIMDRegMatch0 = 27 + OPT_MemEAX = 26 }; enum x86_operand_size { @@ -164,18 +159,17 @@ enum x86_operand_action { OPA_JmpFar = 10, /* ea operand only sets address size (no actual ea field) */ OPA_AdSizeEA = 11, - OPA_DREX = 12, /* operand data goes into DREX "dest" field */ - OPA_VEX = 13, /* operand data goes into VEX "vvvv" field */ - /* operand data goes into BOTH VEX "vvvv" field and ea field */ - OPA_EAVEX = 14, - /* operand data goes into BOTH VEX "vvvv" field and spare field */ - OPA_SpareVEX = 15, + OPA_VEX = 12, /* operand data goes into VEX/XOP "vvvv" field */ + /* operand data goes into BOTH VEX/XOP "vvvv" field and ea field */ + OPA_EAVEX = 13, + /* operand data goes into BOTH VEX/XOP "vvvv" field and spare field */ + OPA_SpareVEX = 14, /* operand data goes into upper 4 bits of immediate byte (VEX is4 field) */ - OPA_VEXImmSrc = 16, + OPA_VEXImmSrc = 15, /* operand data goes into bottom 4 bits of immediate byte * (currently only VEX imz2 field) */ - OPA_VEXImm = 17 + OPA_VEXImm = 16 }; enum x86_operand_post_action { @@ -236,10 +230,10 @@ typedef struct x86_info_operand { typedef struct x86_insn_info { /* GAS suffix flags */ - unsigned int gas_flags:8; /* Enabled for these GAS suffixes */ + unsigned int gas_flags:9; /* Enabled for these GAS suffixes */ - /* Tests against BITS==64 and AVX */ - unsigned int misc_flags:6; + /* Tests against BITS==64, AVX, and XOP */ + unsigned int misc_flags:5; /* The CPU feature flags needed to execute this instruction. This is OR'ed * with arch-specific data[2]. This combined value is compared with @@ -277,17 +271,11 @@ typedef struct x86_insn_info { * 01: 66 * 10: F3 * 11: F2 + * 0x80 - 0x8F indicate a XOP prefix, with the four LSBs holding "WLpp": + * same meanings as VEX prefix. */ unsigned char special_prefix; - /* The DREX base byte value (almost). The only bit kept from this - * value is the OC0 bit (0x08). The MSB (0x80) of this value indicates - * if the DREX byte needs to be present in the instruction. - */ -#define NEED_DREX_MASK 0x80 -#define DREX_OC0_MASK 0x08 - unsigned char drex_oc0; - /* The length of the basic opcode */ unsigned char opcode_len; @@ -327,10 +315,10 @@ typedef struct x86_id_insn { unsigned int mode_bits:8; /* Suffix flags */ - unsigned int suffix:8; + unsigned int suffix:9; /* Tests against BITS==64 and AVX */ - unsigned int misc_flags:6; + unsigned int misc_flags:5; /* Parser enabled at the time of parsing the instruction */ unsigned int parser:2; @@ -394,9 +382,11 @@ x86_finalize_jmpfar(yasm_bytecode *bc, yasm_bytecode *prev_bc, const x86_insn_info *info) { x86_id_insn *id_insn = (x86_id_insn *)bc->contents; + unsigned char *mod_data = id_insn->mod_data; unsigned int mode_bits = id_insn->mode_bits; x86_jmpfar *jmpfar; yasm_insn_operand *op; + unsigned int i; jmpfar = yasm_xmalloc(sizeof(x86_jmpfar)); x86_finalize_common(&jmpfar->common, info, mode_bits); @@ -422,9 +412,44 @@ x86_finalize_jmpfar(yasm_bytecode *bc, yasm_bytecode *prev_bc, || yasm_value_finalize_expr(&jmpfar->segment, e, prev_bc, 16)) yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("jump target expression too complex")); + } else if (yasm_insn_op_next(op)) { + /* Two operand form (gas) */ + yasm_insn_operand *op2 = yasm_insn_op_next(op); + if (yasm_value_finalize_expr(&jmpfar->segment, op->data.val, prev_bc, + 16)) + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("jump target segment too complex")); + if (yasm_value_finalize_expr(&jmpfar->offset, op2->data.val, prev_bc, + 0)) + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("jump target offset too complex")); + if (op2->size == OPS_BITS) + jmpfar->common.opersize = (unsigned char)mode_bits; } else yasm_internal_error(N_("didn't get FAR expression in jmpfar")); + /* Apply modifiers */ + for (i=0; i<NELEMS(info->modifiers); i++) { + switch (info->modifiers[i]) { + case MOD_Gap: + break; + case MOD_Op0Add: + jmpfar->opcode.opcode[0] += mod_data[i]; + break; + case MOD_Op1Add: + jmpfar->opcode.opcode[1] += mod_data[i]; + break; + case MOD_Op2Add: + jmpfar->opcode.opcode[2] += mod_data[i]; + break; + case MOD_Op1AddSp: + jmpfar->opcode.opcode[1] += mod_data[i]<<3; + break; + default: + break; + } + } + yasm_x86__bc_apply_prefixes((x86_common *)jmpfar, NULL, info->def_opersize_64, id_insn->insn.num_prefixes, @@ -614,7 +639,7 @@ x86_find_match(x86_id_insn *id_insn, yasm_insn_operand **ops, continue; /* Match suffix (if required) */ - if (suffix != 0 && suffix != WEAK + if (id_insn->parser == X86_PARSER_GAS && ((suffix & SUF_MASK) & (gas_flags & SUF_MASK)) == 0) continue; @@ -667,7 +692,6 @@ x86_find_match(x86_id_insn *id_insn, yasm_insn_operand **ops, if (op->type == YASM_INSN__OPERAND_MEMORY) break; /*@fallthrough@*/ - case OPT_SIMDRegMatch0: case OPT_SIMDReg: if (op->type != YASM_INSN__OPERAND_REG) mismatch = 1; @@ -682,9 +706,6 @@ x86_find_match(x86_id_insn *id_insn, yasm_insn_operand **ops, break; } } - if (!mismatch && info_ops[i].type == OPT_SIMDRegMatch0 && - bypass != 7 && op->data.reg != use_ops[0]->data.reg) - mismatch = 1; break; case OPT_SegReg: if (op->type != YASM_INSN__OPERAND_SEGREG) @@ -839,7 +860,7 @@ x86_find_match(x86_id_insn *id_insn, yasm_insn_operand **ops, /* Check operand size */ size = size_lookup[info_ops[i].size]; - if (suffix != 0) { + if (id_insn->parser == X86_PARSER_GAS) { /* Require relaxed operands for GAS mode (don't allow * per-operand sizing). */ @@ -881,7 +902,8 @@ x86_find_match(x86_id_insn *id_insn, yasm_insn_operand **ops, break; /* Check for 64-bit effective address size in NASM mode */ - if (suffix == 0 && op->type == YASM_INSN__OPERAND_MEMORY) { + if (id_insn->parser != X86_PARSER_GAS && + op->type == YASM_INSN__OPERAND_MEMORY) { if (info_ops[i].eas64) { if (op->data.ea->disp.size != 64) mismatch = 1; @@ -1005,8 +1027,6 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) unsigned char im_len; unsigned char im_sign; unsigned char spare; - unsigned char drex; - unsigned char *pdrex; unsigned char vexdata, vexreg; unsigned int i; unsigned int size_lookup[] = {0, 8, 16, 32, 64, 80, 128, 256, 0}; @@ -1099,19 +1119,18 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) insn->def_opersize_64 = info->def_opersize_64; insn->special_prefix = info->special_prefix; spare = info->spare; - drex = info->drex_oc0 & DREX_OC0_MASK; vexdata = 0; vexreg = 0; im_len = 0; im_sign = 0; insn->postop = X86_POSTOP_NONE; insn->rex = 0; - pdrex = (info->drex_oc0 & NEED_DREX_MASK) ? &drex : NULL; - /* Move VEX data (stored in special prefix) to separate location to + /* Move VEX/XOP data (stored in special prefix) to separate location to * allow overriding of special prefix by modifiers. */ - if ((insn->special_prefix & 0xF0) == 0xC0) { + if ((insn->special_prefix & 0xF0) == 0xC0 || + (insn->special_prefix & 0xF0) == 0x80) { vexdata = insn->special_prefix; insn->special_prefix = 0; } @@ -1199,7 +1218,7 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) insn->x86_ea = yasm_x86__ea_create_reg(insn->x86_ea, (unsigned long)op->data.reg, &insn->rex, - pdrex, mode_bits); + mode_bits); break; case YASM_INSN__OPERAND_SEGREG: yasm_internal_error( @@ -1236,8 +1255,7 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) yasm_internal_error(N_("invalid operand conversion")); insn->x86_ea = yasm_x86__ea_create_reg(insn->x86_ea, - (unsigned long)op->data.reg, &insn->rex, pdrex, - mode_bits); + (unsigned long)op->data.reg, &insn->rex, mode_bits); vexreg = op->data.reg & 0xF; break; case OPA_Imm: @@ -1265,8 +1283,8 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) if (op->type == YASM_INSN__OPERAND_SEGREG) spare = (unsigned char)(op->data.reg&7); else if (op->type == YASM_INSN__OPERAND_REG) { - if (yasm_x86__set_rex_from_reg(&insn->rex, pdrex, - &spare, op->data.reg, mode_bits, X86_REX_R)) + if (yasm_x86__set_rex_from_reg(&insn->rex, &spare, + op->data.reg, mode_bits, X86_REX_R)) return; } else yasm_internal_error(N_("invalid operand conversion")); @@ -1274,16 +1292,16 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) case OPA_SpareVEX: if (op->type != YASM_INSN__OPERAND_REG) yasm_internal_error(N_("invalid operand conversion")); - if (yasm_x86__set_rex_from_reg(&insn->rex, pdrex, - &spare, op->data.reg, mode_bits, X86_REX_R)) + if (yasm_x86__set_rex_from_reg(&insn->rex, &spare, + op->data.reg, mode_bits, X86_REX_R)) return; vexreg = op->data.reg & 0xF; break; case OPA_Op0Add: if (op->type == YASM_INSN__OPERAND_REG) { unsigned char opadd; - if (yasm_x86__set_rex_from_reg(&insn->rex, pdrex, - &opadd, op->data.reg, mode_bits, X86_REX_B)) + if (yasm_x86__set_rex_from_reg(&insn->rex, &opadd, + op->data.reg, mode_bits, X86_REX_B)) return; insn->opcode.opcode[0] += opadd; } else @@ -1292,8 +1310,8 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) case OPA_Op1Add: if (op->type == YASM_INSN__OPERAND_REG) { unsigned char opadd; - if (yasm_x86__set_rex_from_reg(&insn->rex, pdrex, - &opadd, op->data.reg, mode_bits, X86_REX_B)) + if (yasm_x86__set_rex_from_reg(&insn->rex, &opadd, + op->data.reg, mode_bits, X86_REX_B)) return; insn->opcode.opcode[1] += opadd; } else @@ -1304,10 +1322,10 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) insn->x86_ea = yasm_x86__ea_create_reg(insn->x86_ea, (unsigned long)op->data.reg, &insn->rex, - pdrex, mode_bits); + mode_bits); if (!insn->x86_ea || - yasm_x86__set_rex_from_reg(&insn->rex, pdrex, - &spare, op->data.reg, mode_bits, X86_REX_R)) { + yasm_x86__set_rex_from_reg(&insn->rex, &spare, + op->data.reg, mode_bits, X86_REX_R)) { if (insn->x86_ea) yasm_xfree(insn->x86_ea); yasm_xfree(insn); @@ -1340,13 +1358,6 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) yasm_x86__ea_destroy(op->data.ea); break; } - case OPA_DREX: - if (op->type == YASM_INSN__OPERAND_REG) { - drex &= 0x0F; - drex |= (op->data.reg << 4) & 0xF0; - } else - yasm_internal_error(N_("invalid operand conversion")); - break; case OPA_VEX: if (op->type != YASM_INSN__OPERAND_REG) yasm_internal_error(N_("invalid operand conversion")); @@ -1441,9 +1452,7 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) } if (insn->x86_ea) { - yasm_x86__ea_init(insn->x86_ea, spare, drex, - (unsigned int)(info->drex_oc0 & NEED_DREX_MASK), - prev_bc); + yasm_x86__ea_init(insn->x86_ea, spare, prev_bc); for (i=0; i<id_insn->insn.num_segregs; i++) yasm_ea_set_segreg(&insn->x86_ea->ea, id_insn->insn.segregs[i]); } else if (id_insn->insn.num_segregs > 0 && insn->special_prefix == 0) { @@ -1513,8 +1522,7 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) * opcode 0 being a mov instruction! */ insn->x86_ea = yasm_x86__ea_create_reg(insn->x86_ea, - (unsigned long)insn->opcode.opcode[0]-0xB8, &rex_temp, - NULL, 64); + (unsigned long)insn->opcode.opcode[0]-0xB8, &rex_temp, 64); /* Make the imm32s form permanent. */ insn->opcode.opcode[0] = insn->opcode.opcode[1]; @@ -1526,31 +1534,46 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) break; } - /* Convert to VEX prefixes if requested. - * To save space in the insn structure, the VEX prefix is written into + /* Convert to VEX/XOP prefixes if requested. + * To save space in the insn structure, the VEX/XOP prefix is written into * special_prefix and the first 2 bytes of the instruction are set to - * the second two VEX bytes. During calc_len() it may be shortened to - * one VEX byte (this can only be done after knowledge of REX value). + * the second two VEX/XOP bytes. During calc_len() it may be shortened to + * one VEX byte (this can only be done after knowledge of REX value); this + * further optimization is not possible for XOP. */ if (vexdata) { + int xop = ((vexdata & 0xF0) == 0x80); unsigned char vex1 = 0xE0; /* R=X=B=1, mmmmm=0 */ unsigned char vex2; - /* Look at the first bytes of the opcode to see what leading bytes - * to encode in the VEX mmmmm field. Leave R=X=B=1 for now. - */ - if (insn->opcode.opcode[0] != 0x0F) - yasm_internal_error(N_("first opcode byte of VEX must be 0x0F")); - - if (insn->opcode.opcode[1] == 0x38) - vex1 |= 0x02; /* implied 0x0F 0x38 */ - else if (insn->opcode.opcode[1] == 0x3A) - vex1 |= 0x03; /* implied 0x0F 0x3A */ - else { - /* Originally a 0F-only opcode; move opcode byte back one position - * to make room for VEX prefix. + + if (xop) { + /* Look at the first bytes of the opcode for the XOP mmmmm field. + * Leave R=X=B=1 for now. */ + if (insn->opcode.opcode[0] != 0x08 && + insn->opcode.opcode[0] != 0x09) + yasm_internal_error(N_("first opcode byte of XOP must be 0x08 or 0x09")); + vex1 |= insn->opcode.opcode[0]; + /* Move opcode byte back one byte to make room for XOP prefix. */ insn->opcode.opcode[2] = insn->opcode.opcode[1]; - vex1 |= 0x01; /* implied 0x0F */ + } else { + /* Look at the first bytes of the opcode to see what leading bytes + * to encode in the VEX mmmmm field. Leave R=X=B=1 for now. + */ + if (insn->opcode.opcode[0] != 0x0F) + yasm_internal_error(N_("first opcode byte of VEX must be 0x0F")); + + if (insn->opcode.opcode[1] == 0x38) + vex1 |= 0x02; /* implied 0x0F 0x38 */ + else if (insn->opcode.opcode[1] == 0x3A) + vex1 |= 0x03; /* implied 0x0F 0x3A */ + else { + /* Originally a 0F-only opcode; move opcode byte back one + * position to make room for VEX prefix. + */ + insn->opcode.opcode[2] = insn->opcode.opcode[1]; + vex1 |= 0x01; /* implied 0x0F */ + } } /* Check for update of special prefix by modifiers */ @@ -1580,7 +1603,7 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) (vexdata & 0x7)); /* Lpp */ /* Save to special_prefix and opcode */ - insn->special_prefix = 0xC4; /* VEX prefix */ + insn->special_prefix = xop ? 0x8F : 0xC4; /* VEX/XOP prefix */ insn->opcode.opcode[0] = vex1; insn->opcode.opcode[1] = vex2; insn->opcode.len = 3; /* two prefix bytes and 1 opcode byte */ @@ -1730,7 +1753,7 @@ yasm_x86__parse_check_insnprefix(yasm_arch *arch, const char *id, lcaseid[i] = tolower(id[i]); lcaseid[id_len] = '\0'; - switch (arch_x86->parser) { + switch (PARSER(arch_x86)) { case X86_PARSER_NASM: pdata = insnprefix_nasm_find(lcaseid, id_len); break; @@ -1770,7 +1793,8 @@ yasm_x86__parse_check_insnprefix(yasm_arch *arch, const char *id, id_insn->mode_bits = arch_x86->mode_bits; id_insn->suffix = 0; id_insn->misc_flags = 0; - id_insn->parser = arch_x86->parser; + id_insn->parser = PARSER(arch_x86); + id_insn->force_strict = arch_x86->force_strict != 0; id_insn->default_rel = arch_x86->default_rel != 0; *bc = yasm_bc_create_common(&x86_id_insn_callback, id_insn, line); @@ -1801,7 +1825,7 @@ yasm_x86__parse_check_insnprefix(yasm_arch *arch, const char *id, id_insn->mode_bits = arch_x86->mode_bits; id_insn->suffix = pdata->flags; id_insn->misc_flags = pdata->misc_flags; - id_insn->parser = arch_x86->parser; + id_insn->parser = PARSER(arch_x86); id_insn->force_strict = arch_x86->force_strict != 0; id_insn->default_rel = arch_x86->default_rel != 0; *bc = yasm_bc_create_common(&x86_id_insn_callback, id_insn, line); @@ -1862,9 +1886,9 @@ yasm_x86__create_empty_insn(yasm_arch *arch, unsigned long line) id_insn->mod_data[2] = 0; id_insn->num_info = NELEMS(empty_insn); id_insn->mode_bits = arch_x86->mode_bits; - id_insn->suffix = 0; + id_insn->suffix = (PARSER(arch_x86) == X86_PARSER_GAS) ? SUF_Z : 0; id_insn->misc_flags = 0; - id_insn->parser = arch_x86->parser; + id_insn->parser = PARSER(arch_x86); id_insn->force_strict = arch_x86->force_strict != 0; id_insn->default_rel = arch_x86->default_rel != 0; |