为什么GCC调用libc的sqrt()而不使用它的结果呢?

使用GCC 6.3,下面的C ++代码:

#include <cmath> #include <iostream> void norm(double r, double i) { double n = std::sqrt(r * r + i * i); std::cout << "norm = " << n; } 

生成以下x86-64程序集:

 norm(double, double): mulsd %xmm1, %xmm1 subq $24, %rsp mulsd %xmm0, %xmm0 addsd %xmm1, %xmm0 pxor %xmm1, %xmm1 ucomisd %xmm0, %xmm1 sqrtsd %xmm0, %xmm2 movsd %xmm2, 8(%rsp) jbe .L2 call sqrt .L2: movl std::cout, %edi movl $7, %edx movl $.LC1, %esi call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) movsd 8(%rsp), %xmm0 movl std::cout, %edi addq $24, %rsp jmp std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double) 

对于std::sqrt的调用,GCC首先使用sqrtsdsqrtsd ,并将结果保存到堆栈中。 如果溢出,则会调用libc sqrt函数。 但是在xmm0之后和第二次调用operator<<之前,它从不保存xmm0 ,它从栈中恢复值(因为xmm0在第一次调用operator<<丢失了)。

用更简单的std::cout << n; ,更明显的是:

 subq $24, %rsp movsd %xmm1, 8(%rsp) call sqrt movsd 8(%rsp), %xmm1 movl std::cout, %edi addq $24, %rsp movapd %xmm1, %xmm0 jmp std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double) 

为什么GCC不使用libc sqrt计算的xmm0值?

不需要调用sqrt来计算结果; 它已经由SQRTSD指令计算出来了。 当负数传递给sqrt (例如,设置errno和/或引发浮点exception),它会调用sqrt来根据标准生成所需的行为。 PXOR,UCOMISD和JBE指令testing参数是否小于0,如果不正确,则跳过对sqrt的调用。