Почему компилятор генерирует этот код? - PullRequest
11 голосов
/ 25 января 2012

Я разобрал объектный файл (скорее всего, сгенерированный с помощью компилятора Visual C ++) с помощью DumpBin и увидел следующий фрагмент кода:

...         ...
mov         dword ptr [ebp-4],eax       // Why save EAX?
push        dword ptr [ebp+14h]
push        dword ptr [ebp+10h]
push        dword ptr [ebp+0Ch]
push        dword ptr [ebp+8]
mov         eax,dword ptr [ebp-4]       // Why restore EAX? Did it change at all?
call        <function>
...         ...

Может кто-нибудь объяснить, почему регистр EAX сохраняется и восстанавливается в соответствии с этими 4 push инструкциями?

Ответы [ 3 ]

10 голосов
/ 25 января 2012

Также, возможно, она скомпилирована в режиме выпуска, но эта переменная была помечена как volatile, что говорит компилятору, что такая переменная может изменяться без ее ведома, поэтому она вынуждена непрерывно записывать / восстанавливать ее в / изстек

6 голосов
/ 25 января 2012

Это было встроено в режиме отладки? Если это так, компилятор сохраняет каждую локальную переменную в стеке, чтобы отладчик мог найти их согласованным образом.

Исключение таких ненужных хранилищ и перезагрузок является одной из оптимизаций, составляющих режим «релиза».

2 голосов
/ 25 января 2012

volatile или нет, единственная техническая причина, по которой EAX должна должна быть инициализирована непосредственно перед выполнением вызова функции в Windows, если объявлено function __syscall, то есть с использованием соглашения о вызовах Windows CS_SYSCALL. Концептуально это немного похоже на соглашение UN * X x86_64, где %al содержит число аргументов типа с плавающей запятой, переданных в регистрах %xmm.

Соглашение о вызове системного вызова в Windows идентично __cdecl, то есть аргументы функции в стеке в обратном порядке, но с добавлением, что AL содержит счетчик количества аргументов; это делается для того, чтобы код ядра, который обычно находится в конечном конце, знал, сколько данных нужно прочитать из пользовательского стека в стек ядра для получения аргументов.

EAX - это рабочий регистр для всех соглашений о вызовах в 32-битной Windows, его значение никогда не сохраняется в вызовах функций, инициализация его непосредственно перед выполнением вызова является избыточной. Даже если переменная содержит volatile, потому что простая перезагрузка не является барьером памяти и не "фиксирует" предыдущее хранилище. Кроме того, местоположение [EBP - 4] находится в стеке , поэтому переменная local (а квалификатор volatile не имеет особого смысла).

Если это не пропущенная оптимизация, то это может быть вызов __syscall function(...) с различным количеством аргументов, например, гипотетически,

__syscall printf_syscall_conv(char *fmt, ...);

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3)
{
    if (*strchr('%', fmt) == '\0')    // if no "%" in fmt, pass no args
        printf_syscall_conv(fmt);
    else
        printf_syscall_conv(fmt, val1, val2, val3);
}

Это может создать выходные данные сборки, как у вас.

...