56 votes

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'assemblage 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 sur la pile. S'il déborde, il appelle la fonction libc sqrt fonction. Mais elle n'enregistre jamais le xmm0 après cela et avant le deuxième appel à operator<< il restaure la valeur de la pile (parce que xmm0 a été perdue lors du premier appel à operator<< ).

Avec un système plus simple 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-t-il pas l'option xmm0 valeur calculée par libc sqrt ?

76voto

Ross Ridge Points 5534

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X