gcc 4.7.2 в Debian wheezy не всегда правильно выравнивает указатель стека. Это ошибка?

Случайно, читая ассемблерный листинг примера программы на C, я заметил, что указатель стека не выровнен по 16 битам перед вызовом функции foo:

void foo() { }
int func(int p) { foo(); return p; }  
int main() { return func(1); }

func:  
  pushq %rbp
  movq  %rsp, %rbp
  subq  $8, %rsp          ; See here
  movl  %edi, -4(%rbp)
  movl  $0, %eax
  call  foo
  movl  -4(%rbp), %eax
  leave
  ret

Инструкция subq $8, %rsp делает невыровненным RSP перед вызовом foo (должно быть "subq $16, %rsp").
В System V ABI, пар. 3.2.2, я прочитал: «значение (%rsp − 8) всегда кратно 16, когда управление передается точке входа в функцию».
Кто-нибудь может помочь мне понять, почему gcc не ставит subq $16, %rsp ?
Заранее спасибо.

Изменить: я забыл упомянуть свою ОС и версию компилятора:
Debian wheezy, gcc 4.7.2


person Antonio Rizzo    schedule 08.04.2014    source источник
comment
Возможно, это потому, что инструкция call func, которая привела вас туда, уже поместила что-то в стек, как и pushq %rbp. Возможно, перед call были и другие инструкции, которые передавали аргументы и т. д.   -  person twalberg    schedule 08.04.2014
comment
Возможно, это должно быть 16 бит? Например, кратно 16 битам, что означает выравнивание по словам (два байта). По моему опыту, именно так указатель стека должен быть... четным. Нечетные значения прямо!   -  person lornix    schedule 13.04.2014
comment
@лорникс. Нет. выравнивание в байтах. Вы можете указать предпочтительное значение для компилятора (см.: msdn.microsoft.com/en -us/library/83ythb65.aspx или gcc .gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html). Если вы попытаетесь разобрать другой код, вы заметите, что указатель стека всегда кратен 16 байтам. Я не понимаю, почему в простом примере, который я разместил, это неверно.   -  person Antonio Rizzo    schedule 14.04.2014
comment
@твальберг. Нет. Список main() на ассемблере показывает, что указатель стека выровнен по 16 байтам перед вызовом func(): pushq %rbp, movq %rsp, %rbp, movl $1, %edi, call func. Я предполагаю, что rsp правильно (16 байт) выровнен при запуске main().   -  person Antonio Rizzo    schedule 14.04.2014


Ответы (1)


Предполагая, что указатель стека выровнен по 16 байтам при вводе func, тогда комбинация

pushq %rbp              ; <- 8 bytes
movq  %rsp, %rbp
subq  $8, %rsp          ; <- 8 bytes

будет поддерживать его выравнивание по 16 байтам для последующего вызова foo().

Кажется, что поскольку компилятор знает о реализации foo() и о том, что это noop, он не беспокоится о выравнивании стека. Если foo() рассматривается только как объявление или прототип в единице перевода, где func() скомпилирован, вы увидите ожидаемое выравнивание стека.

person Michael Burr    schedule 14.04.2014
comment
@Берр. Нет: инструкция call func поместила rip (8 байт) в стек, затем pushq %rbp выравнивает rsp по 16 байтам. Но следующий subq $8, %rsp приводит к неправильному выравниванию rsp. Попробуйте добавить putchar('A'); в foo() и func() будет исправлено на subq $16, %rsp - person Antonio Rizzo; 14.04.2014
comment
Я вижу сейчас. Я думаю, что происходит то, что, поскольку компилятор знает о реализации foo() и о том, что это noop, он не беспокоится о выравнивании стека. Если foo() реализовано в другой единице перевода, вы увидите ожидаемое выравнивание стека. Я не уверен, почему это происходит даже в неоптимизированном коде. - person Michael Burr; 14.04.2014