使用 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
并将结果保存到堆栈中。如果溢出,则调用 libc sqrt
功能。但它永远不会保存 xmm0
之后和第二次调用 operator<<
之前,它从堆栈中恢复值(因为 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 不使用 xmm0
由 libc sqrt
计算的值?
最佳答案
计算结果不需要调用sqrt
;它已由 SQRTSD 指令计算。它调用 sqrt
以在将负数传递给 sqrt
时根据标准生成所需的行为(例如,设置 errno
和/或引发浮点异常)。 PXOR、UCOMISD 和 JBE 指令测试参数是否小于 0,如果不是,则跳过对 sqrt
的调用。
关于c++ - 为什么 GCC 调用 libc 的 sqrt() 而不使用它的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43303090/