Почему выделяется стековая память, когда она не используется?

Рассмотрим следующий пример:

struct vector {
    int  size() const;
    bool empty() const;
};

bool vector::empty() const
{
    return size() == 0;
}

Сгенерированный код сборки для vector::empty (по clang, с оптимизациями):

push    rax
call    vector::size() const
test    eax, eax
sete    al
pop     rcx
ret

Почему он выделяет место в стеке? Он вообще не используется. Вpush а также popможно опустить. Оптимизированные сборки MSVC и gcc также используют стековое пространство для этой функции (см. Godbolt), поэтому должна быть причина.

1 ответ

Решение

Он выделяет пространство стека, поэтому стек выровнен по 16 байт. Это необходимо, потому что адрес возврата занимает 8 байтов, поэтому требуется дополнительное 8-байтовое пространство для выравнивания 16-байтового стека.

Для некоторых компиляторов выравнивание кадров стека можно настроить с помощью аргументов командной строки.

  • MSVC: в документации сказано, что стек всегда выровнен по 16 байт. Никакой аргумент командной строки не может это изменить. Пример Godbolt показывает, что 40 байтов вычитаются изrsp в начале функции, что означает, что на это влияет что-то еще.
  • clang: The -mstack-alignment опция определяет выравнивание стека. Кажется, что значение по умолчанию - 16, хотя это не задокументировано. Если вы установите его на 8, распределение стека (push а также pop) исчезает из сгенерированного кода сборки.
  • gcc: The -mpreferred-stack-boundary опция определяет выравнивание стека. Если заданное значение равно N, это означает 2^N байтов выравнивания. Значение по умолчанию - 4, что означает 16 байтов. Если вы установите его на 3 (т.е. 8 байтов), выделение стека (sub а также add за rsp) исчезает из сгенерированного кода сборки.

Проверьте Godbolt.

Другие вопросы по тегам