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

时间:2019-06-10 09:51:05

标签: assembly x86 nasm machine-code opcode

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

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

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

1 个答案:

答案 0 :(得分:3)

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

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

在可用时使用sign-extended-imm8编码总是很好的

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

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

imm8编码的性能优势

如果需要操作数大小的前缀(例如在adc ax, 0x33的32位模式下),则将adc ax/eax/rax, imm16/32/32编码与操作数大小的前缀一起使用将在Intel上创建 LCP停顿CPU (更改长度的前缀表示前缀会更改指令的 rest 的长度。对于imm8编码,这不会发生,因为它仍然是(前缀)+操作码+ modrm + imm8,无论操作数大小如何。

请参见Agner Fog's microarch.pdfx86 tag wiki中的其他性能链接。另请参阅x86 instruction encoding how to choose opcode,该副本与adc是特例。


adc / sbb的特定情况下,避免使用ax, imm16编码还有另一个优点:请参见Which Intel microarchitecture introduced the ADC reg,0 single-uop special case?在Hasy上通过Haswell,adc ax, 0是特殊情况下的单uup指令,而不是3输入uop(ax,标志,立即数)的普通2。

但是,这种特殊的大小写不适用于no-ModRM短格式编码,因此3字节的adc ax, imm16仍可解码为2微秒。仅imm8格式的解码器在解码为单个uop之前会检查立即数是否为零。 (并且它仍然不适用于adc al, imm8。)

因此,即使在16位模式下,adc ax,0都不需要操作数大小的前缀,并且始终尽可能选择sign-extended-imm8对此也是最佳的选择。因此不会发生LCP停转的问题。


大多数汇编程序都不提供重写,以避免no-ModRM缩写形式。在设计它们时,除了在循环顶部或其他分支目标之前不加任何NOP的情况下,没有故意延长指令以获取对齐的性能用例:What methods can be used to efficiently extend instruction length on modern x86?

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