Waarom roept GCC libc’s sqrt() aan zonder het resultaat te gebruiken?

Gebruik GCC 6.3, de volgende C++-code:

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

genereert de volgende x86-64-assembly:

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)

Voor de aanroep naar std::sqrtdoet GCC dit eerst met behulp van sqrtsden slaat het resultaat op de stapel op. Als het overloopt, roept het de functie libc sqrtaan. Maar daarna slaat het nooit de xmm0op en vóór de tweede aanroep van operator<<herstelt het de waarde van de stapel (omdat xmm0ging verloren bij de eerste oproep naar operator<<).

Met een eenvoudigere std::cout << n;, het is nog duidelijker:

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)

Waarom gebruikt GCC de xmm0-waarde niet berekend door libc sqrt?


Antwoord 1, autoriteit 100%

Het is niet nodig om sqrtaan te roepen om het resultaat te berekenen; het is al berekend door de SQRTSD-instructie. Het roept sqrtaan om het vereiste gedrag volgens de standaard te genereren wanneer een negatief getal wordt doorgegeven aan sqrt(stel bijvoorbeeld errnoin en/of een drijvende-komma-uitzondering opwerpen). De PXOR-, UCOMISD- en JBE-instructies testen of het argument kleiner is dan 0 en sla de aanroep over naar sqrtals dit niet waar is.

Other episodes