From 017fbf35c3291a30a5e526a3aa55d5ae3ea7266e Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Wed, 9 May 2007 04:31:19 +0000 Subject: [PATCH] Fix handling of SSE4 crc opcodes with 64-bit register operands. Noticed by: arkon@ragestorm.net Fix bug relating to recognizing crc32 rax, bh as an error: the opersize=64 changing into REX prefix was happening too late to be caught as an error. Move this logic from tobytes()/len() functions into finalize(), with proper prefix handling. MFC after: 2 days svn path=/trunk/yasm/; revision=1835 --- modules/arch/x86/tests/Makefile.inc | 2 ++ modules/arch/x86/tests/sse4-err.asm | 4 ++++ modules/arch/x86/tests/sse4-err.errwarn | 2 ++ modules/arch/x86/tests/sse4.asm | 2 +- modules/arch/x86/tests/sse4.hex | 9 +++----- modules/arch/x86/x86arch.h | 4 ++-- modules/arch/x86/x86bc.c | 30 ++++++++++++------------- modules/arch/x86/x86id.c | 18 ++++++++++----- 8 files changed, 41 insertions(+), 30 deletions(-) create mode 100644 modules/arch/x86/tests/sse4-err.asm create mode 100644 modules/arch/x86/tests/sse4-err.errwarn diff --git a/modules/arch/x86/tests/Makefile.inc b/modules/arch/x86/tests/Makefile.inc index b83f30a3..ad735995 100644 --- a/modules/arch/x86/tests/Makefile.inc +++ b/modules/arch/x86/tests/Makefile.inc @@ -140,6 +140,8 @@ EXTRA_DIST += modules/arch/x86/tests/sse3.asm EXTRA_DIST += modules/arch/x86/tests/sse3.hex EXTRA_DIST += modules/arch/x86/tests/sse4.asm EXTRA_DIST += modules/arch/x86/tests/sse4.hex +EXTRA_DIST += modules/arch/x86/tests/sse4-err.asm +EXTRA_DIST += modules/arch/x86/tests/sse4-err.errwarn EXTRA_DIST += modules/arch/x86/tests/ssse3.asm EXTRA_DIST += modules/arch/x86/tests/ssse3.c EXTRA_DIST += modules/arch/x86/tests/ssse3.hex diff --git a/modules/arch/x86/tests/sse4-err.asm b/modules/arch/x86/tests/sse4-err.asm new file mode 100644 index 00000000..1375834e --- /dev/null +++ b/modules/arch/x86/tests/sse4-err.asm @@ -0,0 +1,4 @@ +[bits 64] +crc32 r8d, bh ; error +crc32 rax, bh ; error + diff --git a/modules/arch/x86/tests/sse4-err.errwarn b/modules/arch/x86/tests/sse4-err.errwarn new file mode 100644 index 00000000..b26a14d1 --- /dev/null +++ b/modules/arch/x86/tests/sse4-err.errwarn @@ -0,0 +1,2 @@ +-:2: cannot use A/B/C/DH with instruction needing REX +-:3: cannot use A/B/C/DH with instruction needing REX diff --git a/modules/arch/x86/tests/sse4.asm b/modules/arch/x86/tests/sse4.asm index c6fede5e..9c76ab16 100644 --- a/modules/arch/x86/tests/sse4.asm +++ b/modules/arch/x86/tests/sse4.asm @@ -34,7 +34,7 @@ crc32 r8d, ebx crc32 r8d, dword [0] crc32 rax, bl -crc32 rax, bh ; error +;crc32 rax, bh ; error crc32 rax, r9b crc32 rax, byte [0] crc32 rax, rbx diff --git a/modules/arch/x86/tests/sse4.hex b/modules/arch/x86/tests/sse4.hex index 30b8cdf1..e5e37481 100644 --- a/modules/arch/x86/tests/sse4.hex +++ b/modules/arch/x86/tests/sse4.hex @@ -210,22 +210,19 @@ f1 00 00 f2 +48 0f 38 f0 c3 f2 -0f -38 -f0 -c7 -f2 -41 +49 0f 38 f0 c1 f2 +48 0f 38 f0 diff --git a/modules/arch/x86/x86arch.h b/modules/arch/x86/x86arch.h index 5f8abfae..5659ba5f 100644 --- a/modules/arch/x86/x86arch.h +++ b/modules/arch/x86/x86arch.h @@ -252,8 +252,8 @@ void yasm_x86__bc_transform_jmp(yasm_bytecode *bc, x86_jmp *jmp); void yasm_x86__bc_transform_jmpfar(yasm_bytecode *bc, x86_jmpfar *jmpfar); void yasm_x86__bc_apply_prefixes - (x86_common *common, unsigned char *rex, int num_prefixes, - uintptr_t **prefixes); + (x86_common *common, unsigned char *rex, unsigned int def_opersize_64, + int num_prefixes, uintptr_t **prefixes); /* Check an effective address. Returns 0 if EA was successfully determined, * 1 if invalid EA, or 2 if indeterminate EA. diff --git a/modules/arch/x86/x86bc.c b/modules/arch/x86/x86bc.c index a558982e..b1fa1a28 100644 --- a/modules/arch/x86/x86bc.c +++ b/modules/arch/x86/x86bc.c @@ -286,7 +286,8 @@ yasm_x86__ea_create_imm(yasm_expr *imm, unsigned int im_len) void yasm_x86__bc_apply_prefixes(x86_common *common, unsigned char *rex, - int num_prefixes, uintptr_t **prefixes) + unsigned int def_opersize_64, int num_prefixes, + uintptr_t **prefixes) { int i; int first = 1; @@ -304,6 +305,14 @@ yasm_x86__bc_apply_prefixes(x86_common *common, unsigned char *rex, break; case X86_OPERSIZE: common->opersize = (unsigned char)prefixes[i][1]; + if (common->mode_bits == 64 && common->opersize == 64 && + def_opersize_64 != 64) { + if (*rex == 0xff) + yasm_warn_set(YASM_WARN_GENERAL, + N_("REX prefix not allowed on this instruction, ignoring")); + else + *rex = 0x48; + } break; case X86_SEGREG: /* This is a hack.. we should really be putting this in the @@ -595,10 +604,7 @@ x86_bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, bc->len += insn->opcode.len; bc->len += x86_common_calc_len(&insn->common); bc->len += (insn->special_prefix != 0) ? 1:0; - if (insn->rex != 0xff && - (insn->rex != 0 || - (insn->common.mode_bits == 64 && insn->common.opersize == 64 && - insn->def_opersize_64 != 64))) + if (insn->rex != 0xff && insn->rex != 0) bc->len++; return 0; } @@ -796,16 +802,10 @@ x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, x86_ea ? (unsigned int)(x86_ea->ea.segreg>>8) : 0); if (insn->special_prefix != 0) YASM_WRITE_8(*bufp, insn->special_prefix); - if (insn->rex != 0xff) { - if (insn->common.mode_bits == 64 && insn->common.opersize == 64 && - insn->def_opersize_64 != 64) - insn->rex |= 0x48; - if (insn->rex != 0) { - if (insn->common.mode_bits != 64) - yasm_internal_error( - N_("x86: got a REX prefix in non-64-bit mode")); - YASM_WRITE_8(*bufp, insn->rex); - } + if (insn->rex != 0xff && insn->rex != 0) { + if (insn->common.mode_bits != 64) + yasm_internal_error(N_("x86: got a REX prefix in non-64-bit mode")); + YASM_WRITE_8(*bufp, insn->rex); } /* Opcode */ diff --git a/modules/arch/x86/x86id.c b/modules/arch/x86/x86id.c index 6363bcee..2e5c3925 100644 --- a/modules/arch/x86/x86id.c +++ b/modules/arch/x86/x86id.c @@ -2047,7 +2047,7 @@ static const x86_insn_info crc32_insn[] = { {OPT_Reg|OPS_32|OPA_Spare, OPT_RM|OPS_16|OPA_EA, 0} }, { CPU_SSE42, MOD_GasSufL, 32, 0, 0xF2, 3, {0x0F, 0x38, 0xF1}, 0, 2, {OPT_Reg|OPS_32|OPA_Spare, OPT_RM|OPS_32|OPS_Relaxed|OPA_EA, 0} }, - { CPU_SSE42|CPU_64, MOD_GasSufB, 0, 0, 0xF2, 3, {0x0F, 0x38, 0xF0}, 0, 2, + { CPU_SSE42|CPU_64, MOD_GasSufB, 64, 0, 0xF2, 3, {0x0F, 0x38, 0xF0}, 0, 2, {OPT_Reg|OPS_64|OPA_Spare, OPT_RM|OPS_8|OPA_EA, 0} }, { CPU_SSE42|CPU_64, MOD_GasSufQ, 64, 0, 0xF2, 3, {0x0F, 0x38, 0xF1}, 0, 2, {OPT_Reg|OPS_64|OPA_Spare, OPT_RM|OPS_64|OPS_Relaxed|OPA_EA, 0} } @@ -2325,7 +2325,8 @@ x86_finalize_jmpfar(yasm_arch *arch, yasm_bytecode *bc, yasm_bytecode *prev_bc, } else yasm_internal_error(N_("didn't get FAR expression in jmpfar")); - yasm_x86__bc_apply_prefixes((x86_common *)jmpfar, NULL, num_prefixes, + yasm_x86__bc_apply_prefixes((x86_common *)jmpfar, NULL, + info->def_opersize_64, num_prefixes, prefixes); /* Transform the bytecode */ @@ -2439,8 +2440,8 @@ x86_finalize_jmp(yasm_arch *arch, yasm_bytecode *bc, yasm_bytecode *prev_bc, jmp->op_sel = JMP_NEAR_FORCED; } - yasm_x86__bc_apply_prefixes((x86_common *)jmp, NULL, num_prefixes, - prefixes); + yasm_x86__bc_apply_prefixes((x86_common *)jmp, NULL, info->def_opersize_64, + num_prefixes, prefixes); /* Transform the bytecode */ yasm_x86__bc_transform_jmp(bc, jmp); @@ -3012,6 +3013,11 @@ yasm_x86__finalize_insn(yasm_arch *arch, yasm_bytecode *bc, /*mod_data >>= 8;*/ } + /* In 64-bit mode, if opersize is 64 and default is not 64, force REX byte */ + if (mode_bits == 64 && insn->common.opersize == 64 && + insn->def_opersize_64 != 64) + insn->rex = 0x48; + /* Go through operands and assign */ if (operands) { yasm_insn_operand **use_ops = ops; @@ -3187,8 +3193,8 @@ yasm_x86__finalize_insn(yasm_arch *arch, yasm_bytecode *bc, } else insn->imm = NULL; - yasm_x86__bc_apply_prefixes((x86_common *)insn, &insn->rex, num_prefixes, - prefixes); + yasm_x86__bc_apply_prefixes((x86_common *)insn, &insn->rex, + insn->def_opersize_64, num_prefixes, prefixes); if (insn->postop == X86_POSTOP_ADDRESS16 && insn->common.addrsize) { yasm_warn_set(YASM_WARN_GENERAL, N_("address size override ignored")); -- 2.40.0