Ассемблер push rdi, всплывающий вызов функции

Какова цель push rdi и pop rdi при вызове функции в C ++?
VS2010, x64, отладка, без оптимизации

C ++

int calc()
{
return 8 + 7;
}

Разборка:

int calc()
{
000000013F0B1020  push        rdi
return 8 + 7;
000000013F0B1022  mov         eax,0Fh
}
000000013F0B1027  pop         rdi
000000013F0B1028  ret

2

Решение

В этом нет никакой цели. Это распространенный артефакт неоптимизированного кода. Генератор кода испускает push edi инструкция в ожидании необходимости выполнения сложения. Регистр EDI должен быть сохранен во всех вызовах функций. Но потом выясняется, что сложение может быть выполнено во время компиляции.

Чтобы избавиться от постороннего кода, как это требует «оптимизация глазка». Но эта оптимизация не включена в сборке Debug. Чтобы узнать, как выглядит настоящий код, вы должны включить оптимизатор, лучше всего сделать это при сборке Release. Это фактически полностью исключит функцию, вы можете предотвратить это с помощью:

__declspec(noline) int calc()
{
return 8 + 7;
}

Который производит в сборке Release:

    return 8 + 7;
000007F7038E1000  mov         eax,0Fh
000007F7038E1005  ret
7

Другие решения

Слышали ли вы о регистрах «сохраняемых абонентов» и «сохраняемых абонентов»?

Так как ваш ЦП имеет только ограниченное число регистров, для вызывающих / вызываемых функций обычно невозможно использовать разные регистры. Если вызывающая функция и вызываемая функция хотят использовать один и тот же регистр, это означает, что значение в вызывающем вызове должно быть сохранено / восстановлено до / после вызова.

Сохранение / восстановление значений в регистре может быть выполнено как вызывающим, так и вызываемым пользователем, что является условием. Преимущество регистров «сохранение вызывающего абонента» состоит в том, что если вызывающий объект знает, что ему не понадобится значение в регистре XYZ после вызова, он может пропустить операции сохранения / восстановления. Преимущество регистров «callee-save» заключается в том, что если вызываемый объект знает, что он не изменит значение в регистре XYZ, он может пропустить операции сохранения / восстановления.

Я предполагаю, что ваш компилятор рассматривает RDI как регистр сохранения вызываемого абонента, но не пропускает ненужные операции сохранения / восстановления, если у вас не включена оптимизация компилятора. (Если кто-то знает, что это неверно, пожалуйста, оставьте другой ответ!)

ОБНОВИТЬ: Я нашел статью о соглашениях о вызовах x86: http://en.wikipedia.org/wiki/X86_calling_conventions

Кажется, это подтверждает, что с большинством соглашений о вызовах RDI будет сохранен. Это не объясняет, почему он не выдвигает и не выталкивает все остальные регистры сохранения вызовов. Может быть, здесь что-то еще происходит.

3

По вопросам рекламы [email protected]