64 位或使用 64 位寄存器的地址大小覆盖前缀
Address-size override prefix in 64-bit or using 64-bit registers
在汇编寻址(64 位)中,哪种方式更好?
mov cl, BYTE [ebx + .DATA]
或
mov cl, BYTE [rbx + .DATA]
?
第一种方式的操作码是:
67 8a 4b ..
第二种方式的操作码是:8a 4b ..
所以如果我们使用 32 位寄存器,我们需要有一个 '0x67' 前缀(地址大小覆盖前缀)所以我认为我们添加了一个额外的工作!!!
但我听说了一些关于 (CACHE) 的事情,最好使用“32 位”而不是“64 位”
那么哪种方式更好呢?为什么?
TL:DR:您基本上不需要地址大小前缀。 使用 64 位寻址模式。
I heard something about (CACHE) and it's better to use '32-bit' instead of '64-bit'
您混淆了地址大小和操作数大小。 32 位整数占 space 的一半,因此更多的整数适合一个缓存行.更好的空间局部性,更少的内存带宽。
选择 64 位模式中的默认值是有原因的,并且是您在方便时应该更喜欢的,以便在其他条件相同的情况下节省代码大小():
- 地址大小 = 64 位
- 操作数大小 = 32 位
所以像 mov ecx, [rdi]
是最有效的情况;其他尺寸需要 REX 或其他前缀。字节操作数大小使用不同的操作码而不是前缀,但写入 8 位寄存器可能对完整寄存器的旧值有错误的依赖性。更喜欢movzx
负载;对于 2 字节的操作码,这通常值得额外的代码大小字节。
如果您的号码正确地零扩展为 64 位,请避免使用地址大小前缀并使用
movzx ecx, byte [rbx + .DATA]
写入 32 位位寄存器隐式零扩展到 64 位,因此您可以通过在内存中使用 32 位数据来节省缓存占用空间。
如果一个索引可能不能被正确地零扩展或符号扩展到地址大小,您可能需要一个额外的指令来实现这一点(movsxd rcx, ebx
或mov ecx, ebx
) 所以你可以使用 64 位寻址模式。
[reg + sign_extended_disp32]
寻址模式是一个有趣的案例:它们仅适用于所有适合 32 位的符号地址。如果您知道整个数组位于虚拟地址 space 的低 4GiB 中,您可能会使用 [ebx + .DATA]
以避免额外的指令扩展到 64 位,如果您知道可能存在垃圾在 RBX 的上半部分。 (所以 user-space 中的静态地址,但可能不在 high-half 内核中,你可能在 64 位虚拟地址的 high 32 位中有静态数据space.)
如果您知道您的指针可以安全地截断为 32 位(例如 mmap(MAP_32BIT)
或使用 x32 ABI),您甚至可以使用像 [=] 这样的指令遍历链表或树18=] 循环。可能对指针密集型数据结构有用。
(您的问题是关于数组索引,而不是指针;在 asm 中,您通常希望将它们视为 32 位无符号整数,如果数组可以很大,则为 64。
或者使用指针而不是 [reg+disp32]
来遍历数组; disp32 绝对地址仅适用于 Linux 位置相关的可执行文件,或 Windows LARGEADDRESSAWARE=no.)
在汇编寻址(64 位)中,哪种方式更好?
mov cl, BYTE [ebx + .DATA]
或
mov cl, BYTE [rbx + .DATA]
?
第一种方式的操作码是:
67 8a 4b ..
第二种方式的操作码是:8a 4b ..
所以如果我们使用 32 位寄存器,我们需要有一个 '0x67' 前缀(地址大小覆盖前缀)所以我认为我们添加了一个额外的工作!!!
但我听说了一些关于 (CACHE) 的事情,最好使用“32 位”而不是“64 位”
那么哪种方式更好呢?为什么?
TL:DR:您基本上不需要地址大小前缀。 使用 64 位寻址模式。
I heard something about (CACHE) and it's better to use '32-bit' instead of '64-bit'
您混淆了地址大小和操作数大小。 32 位整数占 space 的一半,因此更多的整数适合一个缓存行.更好的空间局部性,更少的内存带宽。
选择 64 位模式中的默认值是有原因的,并且是您在方便时应该更喜欢的,以便在其他条件相同的情况下节省代码大小(
- 地址大小 = 64 位
- 操作数大小 = 32 位
所以像 mov ecx, [rdi]
是最有效的情况;其他尺寸需要 REX 或其他前缀。字节操作数大小使用不同的操作码而不是前缀,但写入 8 位寄存器可能对完整寄存器的旧值有错误的依赖性。更喜欢movzx
负载;对于 2 字节的操作码,这通常值得额外的代码大小字节。
如果您的号码正确地零扩展为 64 位,请避免使用地址大小前缀并使用
movzx ecx, byte [rbx + .DATA]
写入 32 位位寄存器隐式零扩展到 64 位,因此您可以通过在内存中使用 32 位数据来节省缓存占用空间。
如果一个索引可能不能被正确地零扩展或符号扩展到地址大小,您可能需要一个额外的指令来实现这一点(movsxd rcx, ebx
或mov ecx, ebx
) 所以你可以使用 64 位寻址模式。
[reg + sign_extended_disp32]
寻址模式是一个有趣的案例:它们仅适用于所有适合 32 位的符号地址。如果您知道整个数组位于虚拟地址 space 的低 4GiB 中,您可能会使用 [ebx + .DATA]
以避免额外的指令扩展到 64 位,如果您知道可能存在垃圾在 RBX 的上半部分。 (所以 user-space 中的静态地址,但可能不在 high-half 内核中,你可能在 64 位虚拟地址的 high 32 位中有静态数据space.)
如果您知道您的指针可以安全地截断为 32 位(例如 mmap(MAP_32BIT)
或使用 x32 ABI),您甚至可以使用像 [=] 这样的指令遍历链表或树18=] 循环。可能对指针密集型数据结构有用。
(您的问题是关于数组索引,而不是指针;在 asm 中,您通常希望将它们视为 32 位无符号整数,如果数组可以很大,则为 64。
或者使用指针而不是 [reg+disp32]
来遍历数组; disp32 绝对地址仅适用于 Linux 位置相关的可执行文件,或 Windows LARGEADDRESSAWARE=no.)