web-dev-qa-db-fra.com

Pourquoi GCC appelle-t-il sqrt () de libc sans utiliser son résultat?

En utilisant GCC 6.3, le code C++ suivant:

#include <cmath>
#include <iostream>

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

génère l'assembly x86-64 suivant:

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)

Pour l'appel à std::sqrt, GCC le fait d'abord en utilisant sqrtsd et enregistre le résultat dans la pile. S'il déborde, il appelle alors la fonction libc sqrt. Mais cela ne sauve jamais le xmm0 après cela et avant le deuxième appel à operator<<, il restaure la valeur de la pile (car xmm0 a été perdu lors du premier appel à operator<<).

Avec un std::cout << n;, c'est encore plus évident:

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)

Pourquoi GCC n'utilise pas le xmm0 valeur calculée par libc sqrt?

56
Benoît

Il n'a pas besoin d'appeler sqrt pour calculer le résultat; il a déjà été calculé par l'instruction SQRTSD. Il appelle sqrt pour générer le comportement requis selon la norme lorsqu'un nombre négatif est passé à sqrt (par exemple, définissez errno et/ou déclenchez une exception à virgule flottante ). Les instructions PXOR, UCOMISD et JBE testent si l'argument est inférieur à 0 et ignorent l'appel à sqrt si ce n'est pas vrai.

76
Ross Ridge