【问题标题】:Address-size override prefix in 64-bit or using 64-bit registers64 位或使用 64 位寄存器的地址大小覆盖前缀
【发布时间】:2020-03-27 22:37:25
【问题描述】:

在汇编寻址(64 位)中,哪种方式更好?

mov   cl, BYTE [ebx + .DATA]

mov   cl, BYTE [rbx + .DATA]

?

第一种方式的操作码是: 67 8a 4b .. 第二种方式的操作码是:8a 4b ..

所以如果我们使用 32 位寄存器,我们需要有一个 '0x67' 前缀(地址大小覆盖前缀)所以我认为我们增加了一个额外的工作!!!

但我听说过 (CACHE),最好使用“32 位”而不是“64 位”

那么哪种方式更好呢?为什么?

【问题讨论】:

  • 先定义这里的“更好”是什么意思?我认为将问题改写为“有什么区别”可能会更好,因为可以专门回答这个问题而没有不具体的“更好”
  • 我的意思是你选择哪一个?!为什么?

标签: assembly x86-64 micro-optimization addressing-mode


【解决方案1】:

TL:DR:您基本上不需要地址大小的前缀。使用 64 位寻址模式。

我听说过 (CACHE),最好使用“32 位”而不是“64 位”

您将地址大小与操作数大小混为一谈。 32 位整数占据了一半的空间,因此更多的整数适合一个高速缓存行。更好的空间局部性,更少的内存带宽。

选择 64 位模式的默认值是有原因的,并且是您在方便时应该首选的,以在其他条件相同时节省代码大小 (The advantages of using 32bit registers/instructions in x86-64):

  • 地址大小 = 64 位
  • 操作数大小 = 32 位

所以像mov ecx, [rdi] 这样的例子是最有效的;其他尺寸需要 REX 或其他前缀。字节操作数大小使用不同的操作码而不是前缀,但写入 8 位寄存器可能会对完整寄存器的旧值产生错误的依赖性。首选movzx 加载;对于 2 字节的操作码,这通常值得多出一个字节的代码大小。


如果您的号码正确地零扩展为 64 位,请避免使用地址大小前缀并使用

movzx ecx,  byte [rbx + .DATA]

写入 32 位寄存器会隐式零扩展为 64 位,因此您可以通过在内存中使用 32 位数据来节省缓存占用空间。

如果索引可能正确地以零或符号扩展为地址大小,您可能需要额外的指令来实现这一点(movsxd rcx, ebxmov ecx, ebx),这样您就可以使用 64 位寻址模式。

[reg + sign_extended_disp32] 寻址模式是一个有趣的例子:它们只适用于所有适合 32 位的符号地址。如果您知道整个数组位于虚拟地址空间的低 4GiB 中,您可能会使用[ebx + .DATA] 来避免额外的指令扩展到 64 位,如果您知道 RBX 的高半部分可能存在垃圾. (所以用户空间中的静态地址,但可能不在高半内核中,您可能在 high 32 位的 64 位虚拟地址空间中有静态数据。)


如果您知道指针可以安全地截断为 32 位(例如 mmap(MAP_32BIT) 或使用 x32 ABI),您甚至可以在循环中使用 mov edi, [rdi] 之类的指令遍历链表或树。可能对指针繁重的数据结构有用。

(您的问题是关于数组索引,而不是指针;在 asm 中,您通常希望将它们视为 32 位无符号整数,或者如果数组很大,则为 64。 或者使用指针而不是[reg+disp32] 来循环数组; disp32 绝对地址仅适用于 Linux 位置相关的可执行文件,或 Windows LARGEADDRESSAWARE=no。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多