在 x86-64 索引寻址模式下使用 8 位寄存器

Using 8-bit registers in x86-64 indexed addressing modes

是否可以在 x86-64 的索引寻址模式下使用 8 位寄存器 (al, ah, bl, bh, r8b)?例如:

mov ecx, [rsi + bl]
mov edx, [rdx + dh * 2]

特别是,这可以让您使用寄存器的低 8 位作为 0-255 偏移量,这对某些内核可能很有用。

我翻阅了 Intel 手册,它们并没有明确说明这个问题,但它们提供的所有示例都只有 32 位或 64 位基址寄存器和索引寄存器。在 32 位代码中,我只看到了 16 位或 32 位寄存器。查看 mod-r/m 和 SIB 字节编码的细节似乎也指向 "no" 但这已经足够复杂了,有足够多的极端情况,我不确定我是否做对了。

我最感兴趣的是 x86-64 的行为,当然,如果它在 32 位模式下可行,我当然想知道。

作为一个太小且相关的附加问题,不值得再提出一个 post - 16 位寄存器可以用于基数或索引吗?例如,mov rax, [rbx + cx]。我的调查指向与上面基本相同的答案:可能不会

不行,64位模式下不能使用8位或16位寄存器进行寻址计算,32位模式下也不能使用8位寄存器。您 可以 在 32 位模式下使用 16 位寄存器,在 64 位模式下使用 32 位寄存器,通过使用 0x67 地址大小前缀 字节.

(但是使用更窄的寄存器会使整个地址变窄,而不是相对于 32 位数组地址的 16 位索引。任何寄存器都需要与通常要匹配的地址的宽度相同您所处的模式,除非您在地址的低 16 位或低 32 位中有内容 space。)

This table 很好地总结了操作数和地址大小的各种选项。一般模式是默认地址大小与当前模式相同(即 32 位模式下为 32 位,64 位模式下为 64 位)1,并且那么如果包含 0x67 前缀,地址大小将更改为通常大小的一半(即,32 位模式下为 16 位,64 位模式下为 32 位)。

这是上面链接的完整 table 的摘录,仅针对 REX.W0x66 操作数和 [=10= 的各种值显示 64 位长模式行为] 地址大小前缀:

REX.W 0x66 prefix (operand) 0x67 prefix (address) Operand size (footnote 2) Address size
0 No No 32-bit 64-bit
0 No Yes 32-bit 32-bit
0 Yes No 16-bit 64-bit
0 Yes Yes 16-bit 32-bit
1 Ignored No 64-bit 64-bit
1 ignored Yes 64-bit 32-bit

1 这看起来很明显,但这与 操作数 大小在 64 位模式下的工作方式相反:大多数默认为32 位,即使在 64 位模式下,需要 REX 前缀 才能将它们提升为 64 位。

2 Some instructions 默认为 64 位操作数大小,没有任何 REX 前缀,特别是 pushpopcall 和条件跳转,正如彼得在下面指出的那样,这会导致奇怪的情况,其中至少有一些指令(包括 pushpop)无法编码使用32 位操作数,但可以使用 16 位操作数(带有 0x66 前缀)。