Чому виділяється пам'ять стека, коли вона не використовується?


14

Розглянемо наступний приклад:

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

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

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

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

Чому він виділяє стековий простір? Він взагалі не використовується. pushІ popможе бути опущений. Оптимізовані збірки MSVC і gcc також використовують простір стеків для цієї функції (див. На godbolt ), тому повинна бути причина.


7
Ви враховували неявний thisпараметр?
dan04

1
@Bob__: Ні. Чому я повинен? vector::size()не визначено в прикладі для імітації того, що він не накреслений.
Доктор Гут

1
Отже, як компілятор може оптимізувати те, чого не знає?
Боб__

1
@Bob__: Я думаю, що знання реалізації vector::size()не має значення для виділення або не виділення кадру стека для vector::empty(). У empty()ньому просто називається, що б воно не було.
Доктор Гут

1
Ну, ви викликаєте функцію, яка щось повертає , вам потрібен простір для цього (якщо ви нічого краще не знаєте).
Боб__

Відповіді:


11

Він виділяє стек простору, тому стек вирівняний на 16 байт. Він необхідний, тому що зворотна адреса займає 8 байт, тому необхідний додатковий 8-байтовий простір, щоб стек 16-байт вирівнювався.

Вирівнювання кадрів стека може бути налаштовано аргументами командного рядка для деяких компіляторів.

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

Перевірте на godbolt .


Ось чому гуру c ++, фахівці завжди попереджали: розміщуйте членів структури / класів в порядку найдовшого / найбільшого розміру до найменшого ... тільки так це було б правильно ефективно
nonock

@geza: Дякую Я провів кілька досліджень для двох інших укладачів і написав це на вашу відповідь. Вам це подобається?
Доктор Гут

1
@ Dr.Gut: спасибі, ви зробили відповідь набагато кращою та повною. Зауважте, що вирівнювання стека зазвичай документується в ABI для системи (наприклад, для деяких систем, ось документи: github.com/hjl-tools/x86-psABI/wiki/X86-psABI ).
geza

@geza: Дякую
Доктор Гут
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.