为什么GCC在不使用结果的情况下调用libc的sqrt()?

时间:2017-04-09 04:36:25

标签: c++ gcc assembly x86-64

使用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没有使用libc xmm0计算的sqrt值?

1 个答案:

答案 0 :(得分:76)

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