3

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

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

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

4

1 回答 1

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 位立即数的显式符号扩展。

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

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

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

imm8 编码的性能优势

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

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


adc/的特定情况下sbb,避免ax, imm16编码还有另一个优点:请参阅哪个英特尔微架构引入了 ADC reg,0 单 uop 特殊情况? 在通过 Haswell 的 Sandybridge 上,adc ax, 0特殊情况下是单 uop 指令,而不是 3 输入 uop(ax,flags,immediate)的普通 2。

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

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


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

如果您正在设计一种新的 asm 语法,您可能会考虑允许使用 override 关键字对编码进行更多控制。对于现有设计,请查看 NASMstrictnosplit关键字,以及 GAS 的{vex2}{vex3}{disp32}“前缀”

于 2019-06-10T11:48:23.577 回答