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

标签 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 不使用 xmm0由 libc sqrt 计算的值?

最佳答案

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

关于c++ - 为什么 GCC 调用 libc 的 sqrt() 而不使用它的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43303090/

相关文章:

gcc - 如何从 GCC 插件在目标文件中嵌入元数据

汇编(MIPS)正确使用: Registers vs.堆栈

c++ - Qt Creator + OpenCV : Program runs from . exe 但不是来自编辑器

c++ - 带分数的正则表达式验证 - Visual C++ 2012

C++ 打印 chrono::duration 的天数、小时数、分钟数等

python - 使用非默认 Apache Web 服务器编译 mod_wsgi?

c - 当您使用 GCC 编译 C 代码时,堆栈是否以错误的方式增长

assembly - 向x86-64 ABI的指针添加32位偏移时是否需要符号或零扩展?

CLion、CMSIS 和错误 : "expected identifier or ' (' before ' __asm'"

java - 类似于 Object.toString 的 C++ toString 运算符