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其编码为显然是正确的,但我的方法也正确吗?
x86 通常有超过 1 种有效的指令编码方式。例如,大多数op reg, reg指令都可以选择通过op r/m, reg或op reg, r/m操作码进行编码。
是的,通常您希望汇编程序始终为指令选择最短的编码。NASM 甚至将 x86-64 的mov rax, 1(7 个字节mov r64, sign_extended_imm32)优化为mov eax, 1(5 个字节),更改操作数大小以使用写入 32 位寄存器的零扩展,而不是 32 位立即数的显式符号扩展。
它对于 16 位长度相等,但对于 32 位操作数大小较短,因此它简化了您的代码以始终选择imm8.
操作数大小为 32 位时,op eax, imm32为 5 个字节,而op r/m32, imm8仍然为 3 个字节。(不包括设置操作数大小或其他内容所需的任何前缀;两者都是相同的。)
如果需要操作数大小前缀(例如,在 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的strict和nosplit关键字,和GAS的{vex2},{vex3},{disp32}等“前缀”
nosplitLEA 强制使用更长更有效的编码。GNU 汇编器 x86 指令后缀如“mov.s”中的“.s”如何工作?(GAS{disp32}等,以及{load}或{store}选择其中的op r/m, r与op r, r/m编码你喜欢。)
MOV moffs32 在 64 位模式下对地址进行符号或零扩展?在 64 位模式下,a32 mov eax, [0x123456]使用 no-modrmmoffs编码会导致 Intel CPU 上的 LCP 停顿。对于绝对寻址,它比 modrm+SIB+disp32 短,但可能更慢。
mov rax,1(5 字节)与mov rax, strict dword 1(7 字节)与mov rax, strict qword 1(10 字节imm64编码)中的寄存器| 归档时间: |
|
| 查看次数: |
1327 次 |
| 最近记录: |