为什么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首先使用sqrtsd
来sqrtsd
,并将结果保存到堆栈中。 如果溢出,则会调用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
的调用。