x86 汇编 16 位与 8 位立即操作数编码

Jiř*_*lek 3 x86 assembly machine-code opcode instruction-encoding

我正在编写自己的汇编程序并尝试对 ADC 指令进行编码,我对立即值有疑问,尤其是在将 8 位值添加到 AX 寄存器时。

添加 16 位值时:adc ax, 0xff33被编码为15 33 ff正确的。但是如果adc ax, 0x33被编码为会重要15 33 00吗?

Nasm 将83 d0 33其编码为显然是正确的,但我的方法也正确吗?

Pet*_*des 5

x86 通常有超过 1 种有效的指令编码方式。例如,大多数op reg, reg指令都可以选择通过op r/m, regop reg, r/m操作码进行编码。

是的,通常您希望汇编程序始终为指令选择最短的编码。NASM 甚至将 x86-64 的mov rax, 1(7 个字节mov r64, sign_extended_imm32)优化为mov eax, 1(5 个字节),更改操作数大小以使用写入 32 位寄存器的零扩展,而不是 32 位立即数的显式符号扩展。

在可用时使用符号扩展 imm8 编码总是好的

它对于 16 位长度相等,但对于 32 位操作数大小较短,因此它简化了您的代码以始终选择imm8.

操作数大小为 32 位时,op eax, imm32为 5 个字节,而op r/m32, imm8仍然为 3 个字节。(不包括设置操作数大小或其他内容所需的任何前缀;两者都是相同的。)

imm8 编码的性能优势

如果需要操作数大小前缀(例如,在 32 位模式下adc ax, 0x33),使用adc ax/eax/rax, imm16/32/32带有操作数大小前缀的编码将在 Intel CPU 上创建LCP 停顿(长度更改前缀意味着前缀会更改其余部分的长度)指令。这不会发生在 imm8 编码中,因为它仍然是(前缀)+ 操作码 + modrm + imm8,无论操作数大小如何。

请参阅 x86 标签 wiki中的Agner Fog 的 microarch.pdf和其他性能链接。另请参阅x86 指令编码如何选择与此重复的操作码,但这是一种特殊情况。adc


adc/的特定情况下,sbb避免ax, imm16编码还有另一个优势:请参阅哪个英特尔微架构引入了 ADC reg,0 single-uop 特殊情况? 在 Sandybridge 上通过 Haswell,adc ax, 0作为单 uop 指令的特殊情况,而不是 3 输入 uop(ax、标志、立即数)的正常 2。

但是这种特殊的外壳不适用于 no-ModRM 短格式编码,因此 3 字节adc ax, imm16仍解码为 2 uops。只有imm8表单的解码器在解码为单个 uop 之前检查立即数是否为零。(它仍然不适用于adc al, imm8.)

因此,始终尽可能选择符号扩展的 imm8 也是最佳选择,即使在不需要操作数大小前缀的 16 位模式下,adc ax,0也不会发生 LCP 停顿问题。


大多数汇编程序不提供覆盖来避免 no-ModRM 短格式。在设计它们时,除了有意延长指令以获得对齐而不在循环顶部或其他分支目标之前添加 NOP 之外,没有其他性能用例:哪些方法可用于在现代 x86 上有效地延长指令长度?

如果您正在设计一种新的 asm 语法风格,您可能会考虑允许使用 override 关键字对编码进行更多控制。对于现有设计,退房NASM的strictnosplit关键字,和GAS的{vex2}{vex3}{disp32}等“前缀”

  • 我在汇编程序中设计了关键字 `CODE=`、`IMM=`、`DISP=`,而不是 `strict` 和 `nosplit`,我认为它们更直观和优雅,因为它们允许选择操作码、立即操作数和 ModR /M 位移编码独立,参见例如 https://euroassembler.eu/eatests/t3043.htm (2认同)

归档时间:

查看次数:

1327 次

最近记录:

4 年,4 月 前