From 7e64aca4cdc87cf8b02e3ffa23c2d5c5b6399149 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 3 Sep 2004 23:01:51 +0000 Subject: [PATCH] * x86id.re (yasm_x86__parse_insn): Add checking of effective address size. * x86arch.h (x86_new_insn_data): Add shortmov_op for shortmov post-action. * x86bc.c (x86_insn): Likewise. (yasm_x86__bc_create_insn): Copy shortmov_op to instruction. (x86_bc_insn_resolve): Handle shortmov_op post-action. * x86id.re (yasm_x86__parse_insn): Set shortmov_op post-action if desired. * x86id.re (mov_insn): Through reorder and use of new shortmov_op post-action, change generated code for mov on AMD64. On AMD64, the short mov (opcode A0/A1/A2/A3), generated when moving to AL/AX/EAX/RAX from an absolute address (no registers) has a 64-bit size in 64-bit mode. While an address override can reduce it to 32-bits, automatically generating such an override does not fit well with the model of not doing anything behind the programmer's back. Instead, we now generate the 32-bit address size MOD/RM form unless the address size is specifically set to 64 bits using [qword 0] notation (this is the equivalent of the GNU AS movabs pseudo-instruction). The short mov is still generated in 32-bit mode, whether obtained via BITS setting or an a32 prefix in BITS 64 mode. (The a32 prefix handling necessitated the new shortmov post-action.) Examples (pulled from new mem64.asm): mov ax, [0] ; 66 8B 04 25 00 00 00 00 mov rax, [dword 0] ; 48 8B 04 25 00 00 00 00 mov al, [qword 0xfedcba9876543210] ; A0 10 32 54 76 98 BA DC FE mov al, [0xfedcba9876543210] ; 8A 04 25 10 32 54 76 (+ warning) a32 mov rax, [0] ; 67 48 A1 00 00 00 00 * mem64.asm: Update test to match code changes. * mem64.hex: Likewise. * mem64.errwarn: Likewise. Related to: Bugzilla Bug 33 Reported by: Jeff Lawson svn path=/trunk/yasm/; revision=1134 --- modules/arch/x86/tests/mem64.asm | 9 +- modules/arch/x86/tests/mem64.errwarn | 1 + modules/arch/x86/tests/mem64.hex | 32 +++++- modules/arch/x86/x86arch.h | 1 + modules/arch/x86/x86bc.c | 21 ++++ modules/arch/x86/x86id.re | 150 +++++++++++++++++++++------ 6 files changed, 172 insertions(+), 42 deletions(-) diff --git a/modules/arch/x86/tests/mem64.asm b/modules/arch/x86/tests/mem64.asm index a9e1c35c..f88d81d1 100644 --- a/modules/arch/x86/tests/mem64.asm +++ b/modules/arch/x86/tests/mem64.asm @@ -1,12 +1,15 @@ [bits 64] -mov ax, [0] ; 66 A1 00 00 00 00 00 00 00 00 +mov ax, [0] ; 66 8B 04 25 00 00 00 00 mov rax, [qword 0] ; 48 A1 00 00 00 00 00 00 00 00 -mov rax, [dword 0] ; 67 48 A1 00 00 00 00 -mov al, [0xfedcba9876543210] ; 48 A0 10 32 54 76 98 BA DC FE +mov rax, [0] ; 48 8B 04 25 00 00 00 00 +mov rax, [dword 0] ; 48 8B 04 25 00 00 00 00 +mov al, [qword 0xfedcba9876543210] ; A0 10 32 54 76 98 BA DC FE +mov al, [0xfedcba9876543210] ; 8A 04 25 10 32 54 76 (+ warning) a32 mov rax, [0] ; 67 48 A1 00 00 00 00 a32 mov eax, [0] ; 67 A1 00 00 00 00 mov ecx, [0] ; 8B 0C 25 00 00 00 00 mov edx, [dword 0] ; 8B 14 25 00 00 00 00 +mov rbx, [0] ; 48 8B 1C 25 00 00 00 00 a32 mov rbx, [0] ; 67 48 8B 1C 25 00 00 00 00 mov ebx, [rcx] ; 8B 19 mov r8, [r9] ; 4D 8B 01 diff --git a/modules/arch/x86/tests/mem64.errwarn b/modules/arch/x86/tests/mem64.errwarn index e69de29b..0b47ad9b 100644 --- a/modules/arch/x86/tests/mem64.errwarn +++ b/modules/arch/x86/tests/mem64.errwarn @@ -0,0 +1 @@ +-:7: warning: value does not fit in 32 bit field diff --git a/modules/arch/x86/tests/mem64.hex b/modules/arch/x86/tests/mem64.hex index ae433679..eeb726b8 100644 --- a/modules/arch/x86/tests/mem64.hex +++ b/modules/arch/x86/tests/mem64.hex @@ -1,26 +1,33 @@ 66 -a1 +8b +04 +25 00 00 00 00 +48 +a1 00 00 00 00 -48 -a1 00 00 00 00 +48 +8b +04 +25 00 00 00 00 -67 48 -a1 +8b +04 +25 00 00 00 @@ -34,6 +41,13 @@ a0 ba dc fe +8a +04 +25 +10 +32 +54 +76 67 48 a1 @@ -61,6 +75,14 @@ a1 00 00 00 +48 +8b +1c +25 +00 +00 +00 +00 67 48 8b diff --git a/modules/arch/x86/x86arch.h b/modules/arch/x86/x86arch.h index 397d7907..6bff6d1f 100644 --- a/modules/arch/x86/x86arch.h +++ b/modules/arch/x86/x86arch.h @@ -167,6 +167,7 @@ typedef struct x86_new_insn_data { unsigned char im_sign; unsigned char shift_op; unsigned char signext_imm8_op; + unsigned char shortmov_op; } x86_new_insn_data; yasm_bytecode *yasm_x86__bc_create_insn(yasm_arch *arch, x86_new_insn_data *d); diff --git a/modules/arch/x86/x86bc.c b/modules/arch/x86/x86bc.c index 4267d610..778c0458 100644 --- a/modules/arch/x86/x86bc.c +++ b/modules/arch/x86/x86bc.c @@ -29,6 +29,7 @@ #define YASM_LIB_INTERNAL #define YASM_BC_INTERNAL +#define YASM_EXPR_INTERNAL #include #include "x86arch.h" @@ -103,6 +104,13 @@ typedef struct x86_insn { */ unsigned char signext_imm8_op; + /* HACK, similar to those above, for optimizing long (modrm+sib) mov + * instructions in amd64 into short mov instructions if a 32-bit address + * override is applied in 64-bit mode to an EA of just an offset (no + * registers) and the target register is al/ax/eax/rax. + */ + unsigned char shortmov_op; + unsigned char mode_bits; } x86_insn; @@ -238,6 +246,7 @@ yasm_x86__bc_create_insn(yasm_arch *arch, x86_new_insn_data *d) insn->rex = d->rex; insn->shift_op = d->shift_op; insn->signext_imm8_op = d->signext_imm8_op; + insn->shortmov_op = d->shortmov_op; insn->mode_bits = arch_x86->mode_bits; @@ -644,6 +653,18 @@ x86_bc_insn_resolve(yasm_bytecode *bc, int save, temp = yasm_expr_copy(ea->disp); assert(temp != NULL); + /* Handle shortmov special-casing */ + if (insn->shortmov_op && insn->mode_bits == 64 && + insn->addrsize == 32 && + !yasm_expr__contains(temp, YASM_EXPR_REG)) { + yasm_x86__ea_set_disponly((yasm_effaddr *)&eat); + + if (save) { + /* Make the short form permanent. */ + insn->opcode[0] = insn->opcode[1]; + } + } + /* Check validity of effective address and calc R/M bits of * Mod/RM byte and SIB byte. We won't know the Mod field * of the Mod/RM byte until we know more about the diff --git a/modules/arch/x86/x86id.re b/modules/arch/x86/x86id.re index fdfdb2b8..9384840b 100644 --- a/modules/arch/x86/x86id.re +++ b/modules/arch/x86/x86id.re @@ -105,6 +105,9 @@ RCSID("$IdPath$"); * 2 = SHORT * 3 = FAR * 4 = TO + * - 1 bit = effective address size + * 0 = any address size allowed except for 64-bit + * 1 = only 64-bit address size allowed * * MSBs than the above are actions: what to do with the operand if the * instruction matches. Essentially describes what part of the output bytecode @@ -128,11 +131,12 @@ RCSID("$IdPath$"); * additional data (stored in the second byte of the opcode with a one-byte * opcode) is passed to later stages of the assembler with flags set to * indicate postponed actions. - * - 2 bits = postponed action: + * - 3 bits = postponed action: * 0 = none * 1 = shift operation with a ,1 short form (instead of imm8). * 2 = large imm16/32 that can become a sign-extended imm8. * 3 = can be far jump + * 4 = could become a short opcode mov with bits=64 and a32 prefix */ #define OPT_Imm 0x0 #define OPT_Reg 0x1 @@ -171,30 +175,35 @@ RCSID("$IdPath$"); #define OPS_Relaxed (1UL<<8) #define OPS_RMASK (1UL<<8) -#define OPTM_None (0UL<<9) -#define OPTM_Near (1UL<<9) -#define OPTM_Short (2UL<<9) -#define OPTM_Far (3UL<<9) -#define OPTM_To (4UL<<9) -#define OPTM_MASK (7UL<<9) - -#define OPA_None (0UL<<12) -#define OPA_EA (1UL<<12) -#define OPA_Imm (2UL<<12) -#define OPA_SImm (3UL<<12) -#define OPA_Spare (4UL<<12) -#define OPA_Op0Add (5UL<<12) -#define OPA_Op1Add (6UL<<12) -#define OPA_SpareEA (7UL<<12) -#define OPA_JmpRel (8UL<<12) -#define OPA_AdSizeR (9UL<<12) -#define OPA_MASK (0xFUL<<12) - -#define OPAP_None (0UL<<16) -#define OPAP_ShiftOp (1UL<<16) -#define OPAP_SImm8Avail (2UL<<16) -#define OPAP_JmpFar (3UL<<16) -#define OPAP_MASK (3UL<<16) +#define OPEAS_Not64 (0UL<<9) +#define OPEAS_64 (1UL<<9) +#define OPEAS_MASK (1UL<<9) + +#define OPTM_None (0UL<<10) +#define OPTM_Near (1UL<<10) +#define OPTM_Short (2UL<<10) +#define OPTM_Far (3UL<<10) +#define OPTM_To (4UL<<10) +#define OPTM_MASK (7UL<<10) + +#define OPA_None (0UL<<13) +#define OPA_EA (1UL<<13) +#define OPA_Imm (2UL<<13) +#define OPA_SImm (3UL<<13) +#define OPA_Spare (4UL<<13) +#define OPA_Op0Add (5UL<<13) +#define OPA_Op1Add (6UL<<13) +#define OPA_SpareEA (7UL<<13) +#define OPA_JmpRel (8UL<<13) +#define OPA_AdSizeR (9UL<<13) +#define OPA_MASK (0xFUL<<13) + +#define OPAP_None (0UL<<17) +#define OPAP_ShiftOp (1UL<<17) +#define OPAP_SImm8Avail (2UL<<17) +#define OPAP_JmpFar (3UL<<17) +#define OPAP_ShortMov (4UL<<17) +#define OPAP_MASK (7UL<<17) typedef struct x86_insn_info { /* The CPU feature flags needed to execute this instruction. This is OR'ed @@ -303,24 +312,63 @@ static const x86_insn_info twobytemem_insn[] = { /* Move instructions */ static const x86_insn_info mov_insn[] = { - { CPU_Any, 0, 0, 0, 0, 1, {0xA0, 0, 0}, 0, 2, + /* Absolute forms for non-64-bit mode */ + { CPU_Not64, 0, 0, 0, 0, 1, {0xA0, 0, 0}, 0, 2, {OPT_Areg|OPS_8|OPA_None, OPT_MemOffs|OPS_8|OPS_Relaxed|OPA_EA, 0} }, - { CPU_Any, 0, 16, 0, 0, 1, {0xA1, 0, 0}, 0, 2, + { CPU_Not64, 0, 16, 0, 0, 1, {0xA1, 0, 0}, 0, 2, {OPT_Areg|OPS_16|OPA_None, OPT_MemOffs|OPS_16|OPS_Relaxed|OPA_EA, 0} }, - { CPU_386, 0, 32, 0, 0, 1, {0xA1, 0, 0}, 0, 2, + { CPU_386|CPU_Not64, 0, 32, 0, 0, 1, {0xA1, 0, 0}, 0, 2, {OPT_Areg|OPS_32|OPA_None, OPT_MemOffs|OPS_32|OPS_Relaxed|OPA_EA, 0} }, - { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0xA1, 0, 0}, 0, 2, - {OPT_Areg|OPS_64|OPA_None, OPT_MemOffs|OPS_64|OPS_Relaxed|OPA_EA, 0} }, - { CPU_Any, 0, 0, 0, 0, 1, {0xA2, 0, 0}, 0, 2, + { CPU_Not64, 0, 0, 0, 0, 1, {0xA2, 0, 0}, 0, 2, {OPT_MemOffs|OPS_8|OPS_Relaxed|OPA_EA, OPT_Areg|OPS_8|OPA_None, 0} }, - { CPU_Any, 0, 16, 0, 0, 1, {0xA3, 0, 0}, 0, 2, + { CPU_Not64, 0, 16, 0, 0, 1, {0xA3, 0, 0}, 0, 2, {OPT_MemOffs|OPS_16|OPS_Relaxed|OPA_EA, OPT_Areg|OPS_16|OPA_None, 0} }, - { CPU_386, 0, 32, 0, 0, 1, {0xA3, 0, 0}, 0, 2, + { CPU_386|CPU_Not64, 0, 32, 0, 0, 1, {0xA3, 0, 0}, 0, 2, {OPT_MemOffs|OPS_32|OPS_Relaxed|OPA_EA, OPT_Areg|OPS_32|OPA_None, 0} }, + + /* 64-bit absolute forms for 64-bit mode */ + { CPU_Hammer|CPU_64, 0, 0, 0, 0, 1, {0xA0, 0, 0}, 0, 2, + {OPT_Areg|OPS_8|OPA_None, + OPT_MemOffs|OPS_8|OPS_Relaxed|OPEAS_64|OPA_EA, 0} }, + { CPU_Hammer|CPU_64, 0, 16, 0, 0, 1, {0xA1, 0, 0}, 0, 2, + {OPT_Areg|OPS_16|OPA_None, + OPT_MemOffs|OPS_16|OPS_Relaxed|OPEAS_64|OPA_EA, 0} }, + { CPU_Hammer|CPU_64, 0, 32, 0, 0, 1, {0xA1, 0, 0}, 0, 2, + {OPT_Areg|OPS_32|OPA_None, + OPT_MemOffs|OPS_32|OPS_Relaxed|OPEAS_64|OPA_EA, 0} }, + { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0xA1, 0, 0}, 0, 2, + {OPT_Areg|OPS_64|OPA_None, + OPT_MemOffs|OPS_64|OPS_Relaxed|OPEAS_64|OPA_EA, 0} }, + + { CPU_Hammer|CPU_64, 0, 0, 0, 0, 1, {0xA2, 0, 0}, 0, 2, + {OPT_MemOffs|OPS_8|OPS_Relaxed|OPEAS_64|OPA_EA, + OPT_Areg|OPS_8|OPA_None, 0} }, + { CPU_Hammer|CPU_64, 0, 16, 0, 0, 1, {0xA3, 0, 0}, 0, 2, + {OPT_MemOffs|OPS_16|OPS_Relaxed|OPEAS_64|OPA_EA, + OPT_Areg|OPS_16|OPA_None, 0} }, + { CPU_Hammer|CPU_64, 0, 32, 0, 0, 1, {0xA3, 0, 0}, 0, 2, + {OPT_MemOffs|OPS_32|OPS_Relaxed|OPEAS_64|OPA_EA, + OPT_Areg|OPS_32|OPA_None, 0} }, { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0xA3, 0, 0}, 0, 2, - {OPT_MemOffs|OPS_64|OPS_Relaxed|OPA_EA, OPT_Areg|OPS_64|OPA_None, 0} }, + {OPT_MemOffs|OPS_64|OPS_Relaxed|OPEAS_64|OPA_EA, + OPT_Areg|OPS_64|OPA_None, 0} }, + /* General 32-bit forms using Areg / short absolute option */ + { CPU_Any, 0, 0, 0, 0, 1, {0x88, 0xA2, 0}, 0, 2, + {OPT_RM|OPS_8|OPS_Relaxed|OPA_EA|OPAP_ShortMov, OPT_Areg|OPS_8|OPA_Spare, + 0} }, + { CPU_Any, 0, 16, 0, 0, 1, {0x89, 0xA3, 0}, 0, 2, + {OPT_RM|OPS_16|OPS_Relaxed|OPA_EA|OPAP_ShortMov, + OPT_Areg|OPS_16|OPA_Spare, 0} }, + { CPU_386, 0, 32, 0, 0, 1, {0x89, 0xA3, 0}, 0, 2, + {OPT_RM|OPS_32|OPS_Relaxed|OPA_EA|OPAP_ShortMov, + OPT_Areg|OPS_32|OPA_Spare, 0} }, + { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0x89, 0xA3, 0}, 0, 2, + {OPT_RM|OPS_64|OPS_Relaxed|OPA_EA|OPAP_ShortMov, + OPT_Areg|OPS_64|OPA_Spare, 0} }, + + /* General 32-bit forms */ { CPU_Any, 0, 0, 0, 0, 1, {0x88, 0, 0}, 0, 2, {OPT_RM|OPS_8|OPS_Relaxed|OPA_EA, OPT_Reg|OPS_8|OPA_Spare, 0} }, { CPU_Any, 0, 16, 0, 0, 1, {0x89, 0, 0}, 0, 2, @@ -330,6 +378,21 @@ static const x86_insn_info mov_insn[] = { { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0x89, 0, 0}, 0, 2, {OPT_RM|OPS_64|OPS_Relaxed|OPA_EA, OPT_Reg|OPS_64|OPA_Spare, 0} }, + /* General 32-bit forms using Areg / short absolute option */ + { CPU_Any, 0, 0, 0, 0, 1, {0x8A, 0xA0, 0}, 0, 2, + {OPT_Areg|OPS_8|OPA_Spare, OPT_RM|OPS_8|OPS_Relaxed|OPA_EA|OPAP_ShortMov, + 0} }, + { CPU_Any, 0, 16, 0, 0, 1, {0x8B, 0xA1, 0}, 0, 2, + {OPT_Areg|OPS_16|OPA_Spare, + OPT_RM|OPS_16|OPS_Relaxed|OPA_EA|OPAP_ShortMov, 0} }, + { CPU_386, 0, 32, 0, 0, 1, {0x8B, 0xA1, 0}, 0, 2, + {OPT_Areg|OPS_32|OPA_Spare, + OPT_RM|OPS_32|OPS_Relaxed|OPA_EA|OPAP_ShortMov, 0} }, + { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0x8B, 0xA1, 0}, 0, 2, + {OPT_Areg|OPS_64|OPA_Spare, + OPT_RM|OPS_64|OPS_Relaxed|OPA_EA|OPAP_ShortMov, 0} }, + + /* General 32-bit forms */ { CPU_Any, 0, 0, 0, 0, 1, {0x8A, 0, 0}, 0, 2, {OPT_Reg|OPS_8|OPA_Spare, OPT_RM|OPS_8|OPS_Relaxed|OPA_EA, 0} }, { CPU_Any, 0, 16, 0, 0, 1, {0x8B, 0, 0}, 0, 2, @@ -339,6 +402,7 @@ static const x86_insn_info mov_insn[] = { { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0x8B, 0, 0}, 0, 2, {OPT_Reg|OPS_64|OPA_Spare, OPT_RM|OPS_64|OPS_Relaxed|OPA_EA, 0} }, + /* Segment register forms */ { CPU_Any, 0, 0, 0, 0, 1, {0x8C, 0, 0}, 0, 2, {OPT_Mem|OPS_16|OPS_Relaxed|OPA_EA, OPT_SegReg|OPS_16|OPS_Relaxed|OPA_Spare, 0} }, @@ -357,6 +421,7 @@ static const x86_insn_info mov_insn[] = { { CPU_Hammer|CPU_64, 0, 0, 0, 0, 1, {0x8E, 0, 0}, 0, 2, {OPT_SegReg|OPS_16|OPS_Relaxed|OPA_Spare, OPT_Reg|OPS_64|OPA_EA, 0} }, + /* Immediate forms */ { CPU_Any, 0, 0, 0, 0, 1, {0xB0, 0, 0}, 0, 2, {OPT_Reg|OPS_8|OPA_Op0Add, OPT_Imm|OPS_8|OPS_Relaxed|OPA_Imm, 0} }, { CPU_Any, 0, 16, 0, 0, 1, {0xB8, 0, 0}, 0, 2, @@ -383,6 +448,7 @@ static const x86_insn_info mov_insn[] = { { CPU_Hammer|CPU_64, 0, 64, 0, 0, 1, {0xC7, 0, 0}, 0, 2, {OPT_RM|OPS_64|OPA_EA, OPT_Imm|OPS_32|OPS_Relaxed|OPA_Imm, 0} }, + /* CR/DR forms */ { CPU_586|CPU_Priv|CPU_Not64, 0, 0, 0, 0, 2, {0x0F, 0x22, 0}, 0, 2, {OPT_CR4|OPS_32|OPA_Spare, OPT_Reg|OPS_32|OPA_EA, 0} }, { CPU_386|CPU_Priv|CPU_Not64, 0, 0, 0, 0, 2, {0x0F, 0x22, 0}, 0, 2, @@ -1983,6 +2049,18 @@ yasm_x86__parse_insn(yasm_arch *arch, const unsigned long data[4], } } + if (mismatch) + break; + + /* Check for 64-bit effective address size */ + if (op->type == YASM_INSN__OPERAND_MEMORY) { + if ((info->operands[i] & OPEAS_MASK) == OPEAS_64) { + if (op->data.ea->len != 8) + mismatch = 1; + } else if (op->data.ea->len == 8) + mismatch = 1; + } + if (mismatch) break; @@ -2076,6 +2154,7 @@ yasm_x86__parse_insn(yasm_arch *arch, const unsigned long data[4], d.im_sign = 0; d.shift_op = 0; d.signext_imm8_op = 0; + d.shortmov_op = 0; d.rex = 0; /* Apply modifiers */ @@ -2255,6 +2334,9 @@ yasm_x86__parse_insn(yasm_arch *arch, const unsigned long data[4], case OPAP_SImm8Avail: d.signext_imm8_op = 1; break; + case OPAP_ShortMov: + d.shortmov_op = 1; + break; default: yasm_internal_error( N_("unknown operand postponed action")); -- 2.40.0