每个内存访问都有一个由机器代码指令指定的操作数大小。(寻址模式不是正确的术语:不同的寻址模式是指定要访问的内存块的最低地址的不同方式,例如[rdi]
vs. [rdi + rdx*8]
vs. [RIP + rel32]
)
不同操作数大小的编码是通过前缀(16 位、32 位和 64 位整数指令)或相同助记符(8 位整数)的不同操作码来完成的。或者使用可使用 xmm、ymm 或 zmm 寄存器的 AVX / AVX512 指令的 VEX 或 EVEX 前缀中的位。
解码还取决于当前模式,即默认操作数大小:32 表示 32 位和 64 位模式,16 表示 16 位模式。 A66
操作数大小前缀表示相反的大小。
在 64 位模式下,.W
REX 前缀中的(宽度)位将操作数大小设置为 64 位。 (还有一些说明,例如push
/pop
默认为 64 位操作数大小,无需前缀,但大多数指令如add
/sub
/mov
仍然默认为 32 位)
还有一个0x67
地址大小前缀,将寻址模式交换为其他大小。 (16 与 32 或在 64 位模式下 64 -> 32。)
例如,mov [rdi], eax
是一个双字存储,机器代码编码将通过在 16/32/64 位操作数大小的操作码上不使用特殊前缀来指定。 (看https://www.felixcloutier.com/x86/mov获取可用的编码。但请注意英特尔的手册doesn't提到66
每个条目中的操作数大小前缀:它有 2 个相同的编码,但大小不同。你必须知道哪一个需要66
基于当前模式默认值的前缀。)
16 位操作数大小如mov [rdi], ax
将具有相同的机器代码66
操作数大小前缀。
8 位操作数大小 (mov [rdi], al
) 有自己的操作码,不需要前缀。
movzx
/ movsx有趣的情况是:内存访问大小与目标寄存器不同。内存访问大小(字节或字)由操作码指定。操作数大小前缀仅影响目标大小。 x86-64 除外63 /r
movsxd
(dword->qword 符号扩展) 其中66
操作数大小前缀does将内存访问大小缩小到m16
以匹配目的地。
SIMD 指令也类似;指令编码唯一地确定存储器访问大小以及读取或写入的寄存器。