0

Почему GCC вызывает sqrt() из libc, не используя его результат?

1

Описание проблемы:

Я использую 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 ответ(ов)

0

В данном случае нет необходимости вызывать sqrt для вычисления результата, так как он уже был рассчитан с помощью инструкции SQRTSD. Функция sqrt вызывается для обеспечения требуемого поведения в соответствии со стандартом, когда в неё передаётся отрицательное число (например, чтобы установить errno и/или вызвать исключение с плавающей запятой). Инструкции PXOR, UCOMISD и JBE проверяют, меньше ли аргумент 0, и пропускают вызов sqrt, если это не так.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь