Несмотря на то, что соглашение вызовов x64 резервирует небольшое пространство для параметров, вы не обязаны использовать его в таком качестве
Несмотря на то, что соглашение вызовов x64 резервирует место для размещения первых четырех параметров (переданных в регистрах), нет никаких требований относительно того, чтобы это место использовалось именно для передачи параметров. Это просто 32 байта памяти, доступной для временного использования вызываемой функцией.
У нас есть тестовая программа, которая работает корректно при отключенной оптимизации, но когда она скомпилирована с максимальным уровнем оптимизации, все сразу же идет наперекосяк. Вот эта функция не получает корректных значений параметров argc и argv:
int __cdecl wmain( int argc, WCHAR** argv ) { ... }
При отключенной оптимизации генерируется корректный код:
mov [rsp+10h],rdx // argv mov [rsp+8],ecx // argc sub rsp,158h // локальные переменные mov [rsp+130h],0FFFFFFFFFFFFFFFEh ...
Но когда мы запускаем компиляцию с оптимизацией, то происходит какая-то путаница:
mov rax,rsp push rsi push rdi push r13 sub rsp,0E0h mov qword ptr [rsp+78h],0FFFFFFFFFFFFFFFEh mov [rax+8],rbx // ??? должно быть ecx (argc) mov [rax+10h],rbp // ??? должно быть edx (argv)
Когда оптимизация отключена, компилятор Visual C++ x64 помещает все регистровые параметры в соответствующие слоты. У такого варианта есть приятный побочный эффект, заключающийся в небольшом упрощении отладки, но основная причина простоты отладки заключается в том, что вы отключили оптимизацию и теперь компилятор генерирует простой, прямолинейный код, без претензий на интеллектуальность.
Когда оптимизации включены, компилятор становится более агрессивным в отношении удаления ненужных операций и использования памяти для нескольких целей сразу, когда жизненные циклы переменных не пересекаются. Если он обнаруживает, что параметр argc не требуется сохранять в памяти (возможно, он поместит его в регистр), тогда он может использовать слот параметра argc для чего-нибудь другого, в данном случае он используется для сохранения значения rbx.
Вы можете наблюдать такое же поведение даже в x86-коде, когда память, используемая для передачи параметров, может быть повторно использована для других целей, после того как значение параметра больше не требуется в памяти. (Компилятор может загрузить значение в регистр и использовать его из регистра в оставшейся части функции, в этом случае участок памяти, в которой хранился этот параметр, становится неиспользуемым и может быть задействован для какой-либо другой цели).
С какой бы проблемой вы не столкнулись в вашей тестовой программе, в коде, приведенном в отчете о якобы дефекте нет никаких проблем с кодогенерацией. Проблема заключается в чем-то другом. (И скорее всего, она находится где-то в вашей программе. Не делайте поспешных выводов о том, что причина вашей проблемы — это ошибка в компиляторе).
Дополнение: В последующем уточнении (что, к сожалению, бывает очень редко) клиент признался, что проблема была в их программе. Они поместили вызов функции внутрь операции assert и отключили операции assert в неотладочной сборке (передав компилятору параметр /DNDEBUG), что означает, что в такой сборке эта функция никогда не вызывалась.
В продолжение темы: Сложности отладки оптимизированного x64-кода. Эта команда .frame /r действительно приводит к значительной экономии времени.