Рассмотрим следующий игрушечный пример, который выделяет память в стеке с помощью функции alloca()
:
#include <alloca.h>
void foo() {
volatile int *p = alloca(4);
*p = 7;
}
Компиляция вышеупомянутой функции с использованием gcc 8.2 с -O3
приводит к следующему ассемблерному коду:
foo:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
leaq 15(%rsp), %rax
andq $-16, %rax
movl $7, (%rax)
leave
ret
Честно говоря, я ожидал бы более компактный ассемблерный код.
16-байтовое выравнивание для выделенной памяти
Инструкция andq $-16, %rax
вПриведенный выше код приводит к rax
, содержащему (только) 16-байтовый адрес между адресами rsp
и rsp + 15
(оба включительно).
Это принудительное выравнивание являетсяПервое, что я не понимаю: почему alloca()
выравнивает выделенную память по 16-байтовой границе?
Возможная пропущенная оптимизация?
Давайте все равно рассмотрим, что мы хотимпамять, выделенная alloca()
для выравнивания по 16 байтов.Тем не менее, в приведенном выше коде сборки, имея в виду, что GCC предполагает выравнивание стека по 16-байтовой границе в момент выполнения вызова функции (т. Е. call foo
), если мы обращаем внимание на состояниестек внутри foo()
сразу после нажатия на регистр rbp
:
Size Stack RSP mod 16 Description
-----------------------------------------------------------------------------------
------------------
| . |
| . |
| . |
------------------........0 at "call foo" (stack 16-byte aligned)
8 bytes | return address |
------------------........8 at foo entry
8 bytes | saved RBP |
------------------........0 <----- RSP is 16-byte aligned!!!
Я думаю, что используя красную зону (т.е. не нужно изменять rsp
) и тот факт, что rsp
уже содержит 16-байтовый выровненный адрес , вместо этого можно использовать следующий код:
foo:
pushq %rbp
movq %rsp, %rbp
movl $7, -16(%rbp)
leave
ret
Адрес, содержащийся в регистре rbp
, выровнен по 16 байтам, поэтому rbp - 16
также будет выровнен по 16-байтовой границе.
Еще лучше, создание нового кадра стека может бытьоптимизирован, поскольку rsp
не изменяется:
foo:
movl $7, -8(%rsp)
ret
Это просто пропущенная оптимизация или я что-то здесь упускаю?