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
?