如何通过操作码或反汇编查找使用哪种形式的指令?

Eri*_*tch 1 assembly x86-64 machine-code disassembly micro-architecture

https://uops.info/和 Agner Fog 的指令表,甚至英特尔自己的手册这样的网站,都列出了相同指令的各种形式。例如add m, r(在 Agner 的表格中)或add (m64, r64)在 uops.info 上,或ADD r/m64, r64在英特尔的手册中(https://www.felixcloutier.com/x86/add)。


这是我在 Godbolt 上运行的一个简单示例

__thread int a;
void Test() {
    a+=5;
}
Run Code Online (Sandbox Code Playgroud)

添加是add DWORD PTR fs:0xfffffffffffffffc,0x5。它以操作码开头64 83 04 25

有几种方法可以编写我的真实代码,但我想查找这可能需要多少个周期以及其他信息。我怎么找到这个指令的参考?我尝试在https://uops.info/table.html 中输入“add”并检查我的架构。但我不知道哪个条目是正在使用的指令。

现在在这种特定情况下,我猜测操作码是Add m64, r64但我不知道fs:在地址之前使用是否有任何惩罚,或者是否有办法查看操作码,以便我可以确认我正在查看正确的参考

Pet*_*des 6

http://ref.x86asm.net/coder64.html有一个操作码映射,但如果有一些经验,你大部分时间都不需要。特别是当您进行反汇编时,您可以查看该助记符的手册条目( https://www.felixcloutier.com/x86/add ),并查看它是哪个可能的操作码 ( 83 /0 add r/m32, imm8)。

很明显,它有一个 32 位操作数大小 ( dword ptr) 内存目标,源是一个立即数(数字常量)。, r64出于两个不同的原因,这排除了注册源。所以即使不看机器码,也肯定是add r/m32, imm带有imm8或imm32的。任何理智的汇编程序当然会选择 imm8 作为适合有符号 8 位整数的小常量。

一般来说,同一条指令的不同编码方式并不特殊,所以源码级的汇编/反汇编是可以的,只要你了解什么是寄存器,什么是内存,什么是立即数。

但是有一些特殊情况,例如 Agner Fog 的指导说明使用短格式编码旋转 1 比使用rol reg, imm8imm8=1 时还要慢,因为旋转 1 的标志更新特殊情况实际上取决于操作码,而不是立即计数。(英特尔的文档显然假设您的汇编程序将始终选择按常量 1 旋转的缩写形式。有关“屏蔽计数”的部分可能仅适用于旋转cl. https://www.felixcloutier.com/x86/rcl:rcr:角色:ror#flags-影响. 我最近没有测试过这个,我不能 100% 确定我在更新 OF 时记得正确(但 SPAZO 组中的其他标志总是保持不变),但是 IIRC 这就是为什么旋转 1 (2 uops) 和 cl (3 uop) 很慢,而在 Intel 上由其他直接计数 (1 uop) 旋转)。

https://github.com/travisdowns/uarch-bench/wiki/Intel-Performance-Quirks。具体来说,我的意思是哪个英特尔微体系结构引入了 ADC reg,0 single-uop 特殊情况?- 即使在 Haswell/Skylake 上,adc al,0(使用没有 modrm 字节的简短形式)也是 2 uop,等价的adc eax, 12345. 但是adc edx, 12345使用非特殊情况是 1 uop。)然后您必须检查机器代码,或者知道您的汇编程序将如何选择对给定指令进行编码。(优化尺寸)。


顺便说一句,使用具有非零基数的段会为地址生成 IIRC 增加 1 个延迟周期,但不会显着降低吞吐量。(当然,除非它是其中一部分的延迟链上的吞吐量瓶颈......)