【问题标题】:Assembly using rN registers (r8,r9,...) have more code-size than other registers使用 rN 寄存器 (r8,r9,...) 的汇编比其他寄存器有更多的代码大小
【发布时间】:2020-01-09 08:46:30
【问题描述】:

我有一个关于使用 'rN' 寄存器的问题(r8,r9,r10,....)

我想在我的程序中使用 r8,r9,... 寄存器(大量使用!)但我发现代码大小问题!

例如,

mov eax, DWORD [rdi+4]

等于 (8b 47 04)(反汇编程序)

但是当我使用“rN”寄存器时,情况就不同了!

mov eax, DWORD [r9+4]

等于 (41 8b 41 04)(它有一个额外的 BYTE(前缀)!)

所以使用 rN 寄存器比使用其他寄存器有更多的代码大小!!!!!!!!!!!!!!!!!!!!!首先,为什么?!!!

其次,除了代码大小问题,如果我们使用 rN 个寄存器 (r8,r9,r10,...) 代替其他寄存器,是否还有其他问题,例如 (CACHE, CYCLE, ...) ?

【问题讨论】:

  • x86-64 ISA 和新寄存器是基于 x86-16 ISA 的 x86-32 ISA 的扩展。随着更多指令和寄存器的添加,ISA 无法满足所有要求,因此添加了特殊的 前缀 以告知下一条指令正在使用 ISA 的特定扩展(例如新的 64 位寄存器)。字节41(十六进制)大概就是这样一个前缀字节。
  • 是的......我知道(正如我所说的'前缀')但我想确定缓存,循环,......但也谢谢(有用的解释)
  • 0x41REX.B 前缀,它在指令中扩展了 Mod R/M 字节的 RM 字段,增加了一位,这是访问扩展寄存器所必需的。请参阅英特尔手册中的 2.2.1.2 更多关于 REX 前缀字段部分。
  • 您是否在问使用这些寄存器时性能是否会因为代码大小增加而变差?我想可能是在某些情况下,热代码部分接近溢出缓存行,但我不确定。

标签: assembly x86-64 machine-code


【解决方案1】:

所以使用 rN 寄存器比使用其他寄存器有更多的代码大小

是的,这是众所周知且有据可查的事实。 REX 前缀是 x86-64 机器代码与早期模式相比最重要的变化之一,这个问题只值得回答性能部分(见下文)。

x86 机器代码只有 3 位字段用于寄存器。第 4 位如果非零,则需要来自 REX 前缀。

这就是 AMD64 将 0x4? 操作码字节重新用于的用途(在 32 位机器代码中,它们是 1 字节的 inc/dec reg 指令)。

为了让 x86-64 解码在与 16/32 位模式解码相同的晶体管上运行,而不是在前端需要一个全新的解码器模块,AMD 选择大多不从头重新设计 x86 机器代码。所以他们被困在寄存器的 3 位字段中,不得不使用前缀字节。

阅读 Intel 的 vol.2 手册以了解有关 REX 前缀的更多信息。或者https://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix 有一些有用的东西,包括位含义的详细信息。它还解释了:

在以下情况下必须对 REX 前缀进行编码:

  • 使用 64 位操作数大小并且指令不默认为 64 位操作数大小(大多数指令默认为 32 位 操作数大小);或
  • 使用扩展寄存器之一(R8 到 R15、XMM8 到 XMM15、YMM8 到 YMM15、CR8 到 CR15 和 DR8 到 DR15);或
  • 使用统一字节寄存器 SPL、BPL、SIL 或 DIL 之一。

并且不能在使用 AH、CH、BH 或 DH 时使用。 (即使没有设置位,REX 前缀也会将 AH 的编码含义更改为 SPL,等等。)

(带有 VEX 前缀的指令(例如 AVX 和一些 BMI/BMI2)或 EVEX (AVX512) 使用它而不是 REX 来获取额外的寄存器位。2 字节 VEX 可以将 X/YMM8..15 编码为目标或第一个来源,无需使用更宽的 3 字节 VEX 前缀。)


其次,除了代码大小问题,如果我们使用 rN 个寄存器 (r8,r9,r10,...) 而不是其他寄存器,是否还有其他问题,例如 (CACHE, CYCLE, ...)?

不,只是代码大小(以及某些 CPU 的前缀总数)。具有 uop 缓存的 CPU 大多不会受到代码大小的直接影响直接,但诸如更大的 I-cache 占用空间(以及更不密集的 uop 缓存打包)等间接影响仍然是一个问题。当然还有大规模、更大的二进制文件。

但某些 CPU(尤其是 Silvermont 系列)在解码具有 3 个以上前缀的指令时速度较慢,因此例如任何具有 REX 前缀的 SSSE3 / SSE4 指令都会使解码器停止。见Agner Fog's microarch pdf。在 Silvermont 上,即使是 2 字节操作码的 0F 操作码转义字节也算作 3 种之一,以及 SIMD 指令编码的强制性前缀。

  401000:       66 0f 38 00 07          pshufb xmm0,XMMWORD PTR [rdi]   # 3 prefixes before the 00 opcode
  401005:       66 41 0f 38 00 00       pshufb xmm0,XMMWORD PTR [r8]    # 4 prefixes

后者在 Silvermont 上会特别慢。在其他具有 3 个前缀限制的 CPU 上很好(一些 AMD IIRC);只有 Silvermont 系列将 0F 字节视为前缀。

主流英特尔 CPU 可以解码任意数量的前缀而不会出现停顿,仅受在预解码阶段每个时钟周期可以查看多少字节机器代码的限制,该阶段找到指令和主指令之间的边界解码阶段,最多可将 5 条指令(或更多使用宏融合)转换为 5 条微指令。 (Skylake) 其中一个的长度限制为每个周期 16 个字节; IIRC 它是预解码的;如果重要,请查看 Agner Fog 的指南。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-25
    • 2014-11-21
    • 1970-01-01
    • 2020-11-05
    • 2014-04-04
    • 2016-11-11
    • 1970-01-01
    • 2014-02-26
    相关资源
    最近更新 更多