Почему GCC вызывает sqrt() из libc, не используя его результат?
Описание проблемы:
Я использую GCC версии 6.3 и столкнулся с неожиданным поведением в сгенерированном ассемблерном коде для следующего фрагмента C++ kода:
#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
и сохраняет результат на стек. Если происходит переполнение, он вызывает функцию sqrt
из libc. Однако, после этого GCC не сохраняет значение в регистре xmm0
, и перед вторым вызовом оператора <<
восстанавливает значение из стека (поскольку xmm0
было перезаписано первым вызовом оператора <<
).
Когда я использую более простой вызов 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
?
1 ответ(ов)
В данном случае нет необходимости вызывать sqrt
для вычисления результата, так как он уже был рассчитан с помощью инструкции SQRTSD. Функция sqrt
вызывается для обеспечения требуемого поведения в соответствии со стандартом, когда в неё передаётся отрицательное число (например, чтобы установить errno
и/или вызвать исключение с плавающей запятой). Инструкции PXOR, UCOMISD и JBE проверяют, меньше ли аргумент 0, и пропускают вызов sqrt
, если это не так.
Является ли < быстрее, чем <=?
В чём разница между g++ и gcc?
Как вывести список символов из .so файла?
Неопределенная ссылка на виртуальную таблицу (vtable)
Ошибка: версия `CXXABI_1.3.8` не найдена (требуется для ...)