当操作数具有相同大小时,为什么MOVZX不起作用?
使用Z2 dword ?,mov eax, Z2工作正常,但movzx eax, Z2会出现“无效指令操作数”错误。
我在这里有点困惑:即使Z2的大小与 相同eax,为什么程序集不能接受movzx这个?似乎movzx特别希望操作数的大小不同。
设计这样的指令的原因可能是什么?
如果它被设计为简单地允许相同大小的操作数,那么编码不是更容易吗?
回答
它确实有效(以机器代码),但效率低下。
这就是为什么大多数装配工阻止你用脚射击自己的原因。
设计这样的指令的原因是什么?
从窄源数据执行零扩展。
这就是助记符中的 ZX 的意思。
如果您有相同大小的操作数,您应该使用mov,
而不是尝试使用零扩展或符号扩展复制指令。
就像 MOVSXD 一样,即使可以使用 MOVZX 操作码来编码等效于 的指令mov r, r/m16,出于效率原因,也不建议这样做。
就像英特尔对 MOVSXD 所说的那样:不鼓励使用没有 REX.W(将编码movsxd r32, r/m32)的 MOVSXD 。应该使用常规 MOV 而不是使用没有 REX.W 的 MOVSXD。 (我从引用中去掉了“在 64 位模式下”,因为那是多余的;movsxd只存在于 64 位模式中;操作码在其他模式下意味着其他东西。)
无论如何,是的,movzx ax, bx在 x86 机器代码中是可能的,但是汇编程序可以使您免受自己的伤害,并且拒绝汇编该低效指令。 (2字节的操作码,而不是1 mov; movzx386是新的,所有的1字节的操作码在这之前已经用完。)
将源操作数(寄存器或内存位置)的内容复制到目标操作数(寄存器)并用零扩展该值。转换值的大小取决于操作数大小属性。
https://www.felixcloutier.com/x86/movzx
我使用以下 NASM 源在我的 Skylake CPU 上对其进行了测试,编写的可能也与 MASM 一起组装。(例如,db 66h而不是在行o16上使用NASM 前缀movzx。)
mov edx, -1
xor eax,eax
db 66h ; operand-size prefix that we're not telling the assembler about
movzx eax, dx
mov ax, dx ; for comparison
(超级简单,利用工具链的默认设置来解决这个从未打算成为合适程序的一次性使用。)
$ nasm -felf64 movzx.asm && ld -o movzx movzx.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
$ objdump -drwC -Mintel ./movzx
...
401000: ba ff ff ff ff mov edx,0xffffffff
401005: 48 b8 cc cc cc cc 44 33 22 11 movabs rax,0x11223344cccccccc
40100f: 66 0f b7 c2 movzx ax,dx
401013: 66 89 d0 mov ax,dx # note it's shorter.
# Fun fact: we can see NASM picked the mov r/m16, r form, since the ModRM byte is different.
有趣的是,GNU Binutils(objdump -d 和 GDB)中的反汇编器将其解码为movzx ax, dx, 或movzww %dx, %axAT&T 语法。
使用gdb ./movzx的静态可执行文件,我用layout reg和starti/stepi通过一步,看到寄存器的变化:
66 0f b7 c2 movzx ax,dx正常执行,并将
RAX 从 更改0x11223344cccccccc为0x11223344ccccffff,证明它的行为与 16 位完全一样mov,没有触及 RAX 的任何高位字节。(包括不隐式零扩展 RAX 的高 32 位,就像写入 EAX 那样。)
(然后退出 GDB,因为我没有包含退出的代码,只有我真正想要单步执行的代码。)
这是不可能的movzx al, dl- 16 位与 32 位与 64 位操作数大小由66或 REX 前缀选择以覆盖模式的默认值,但 8 位操作数大小仅通过操作码设置。没有前缀可以将指令覆盖为 8 位操作数大小。当然,没有movzx8 位目标操作数的形式。(如果你想将一个半字节零扩展到一个字节,复制和and reg, 0x0f。)
允许它的汇编程序:只有 GAS.intel_syntax模式?
NASM和YASM拒绝movzx ax, dx
这样做clang(带.intel_syntax noprefix)。
但是llvm-objdump -d会像 GNU Binutils 一样反汇编它。
但是GNU Binutils不仅反汇编它(Intel movzx ax,dx, AT&T movzww %dx, %ax),它( GNU as)还接受Intel 语法版本。气体:
.intel_syntax noprefix
movzx ax, dx # works, producing the above machine code.
.att_syntax
movzw %dx, %ax # Error: operand size mismatch for `movzw'
movzww %dx, %ax # Error: invalid instruction suffix for `movzw'
有关的:
- MOVZX 缺少 32 位寄存器到 64 位寄存器