RSQRTSS指令是否会打破对目标寄存器的依赖关系?
问题背景
我使用uiCA工具为以下代码生成了执行跟踪表:
cvtsi2ss xmm0, eax addss xmm0, xmm0
从结果可以看到,每条cvtsi2ss必须等待上一轮迭代执行完成,原因是该指令依赖xmm0寄存器的32:127位——指令执行时这部分位保持不变,会直接透传到输出结果。
但将cvtsi2ss替换为rsqrtss后,工具给出的执行表现结果出现了很大差异,对应测试代码如下:
rsqrtss xmm0, xmm1 addss xmm0, xmm0
此时uiCA的跟踪结果显示每条rsqrtss都可以和上一轮迭代的指令并行执行。我对此存在疑惑:rsqrtss和cvtsi2ss的行为一致,写入目标寄存器时都会保留32:127位不变,按理应当和cvtsi2ss一样,需要等待目标寄存器上的所有前序操作完成后才能执行,不应该出现并行执行的情况。
实际硬件验证
查阅相关解答后我运行了简单的性能测试,基本可以确认uiCA在该场景下存在模拟bug,同时IACA也未能识别到这一输出依赖。如果测试代码存在问题欢迎指正,测试代码与运行结果如下:
测试代码
__asm__ ( R"(.section .text .balign 16 noXor: mov eax, 0x3f800000 movd xmm1, eax rdtscp shl rdx, 32 or rax, rdx mov rdi, rax mov ecx, 1 << 30 jmp noXor_loop .balign 16 noXor_loop: rsqrtss xmm0, xmm1 addss xmm0, xmm0 dec ecx jnz noXor_loop rdtscp shl rdx, 32 or rax, rdx sub rax, rdi ret .balign 16 yesXor: mov eax, 0x3f800000 movd xmm1, eax rdtscp shl rdx, 32 or rax, rdx mov rdi, rax mov ecx, 1 << 30 jmp yesXor_loop .balign 16 yesXor_loop: xorps xmm0, xmm0 rsqrtss xmm0, xmm1 addss xmm0, xmm0 dec ecx jnz yesXor_loop rdtscp shl rdx, 32 or rax, rdx sub rax, rdi ret)" ); unsigned long long noXor(void); unsigned long long yesXor(void); #include <stdio.h> int main() { for (int i = 0; i < 4; ++i) { printf("noXor: %llu yesXor: %llu\n", noXor(), yesXor()); } return 0; }
运行结果
noXor: 4978836501 yesXor: 696810039 noXor: 4971780086 yesXor: 690780109 noXor: 4977293771 yesXor: 687404710 noXor: 5499602729 yesXor: 687954399
测试结果显示,未提前清零xmm0的noXor版本运行耗时是提前清零版本的7倍左右,证明rsqrtss确实存在对xmm0高位的依赖,会阻塞后续指令执行,uiCA和IACA的静态模拟结果均不符合实际硬件表现。
内容的提问来源于stack exchange,提问作者xiver77
相关产品推荐
相关产品推荐

