実際、それはまさにleave
命令が行うことであり、高級言語をサポートするために導入されました。ただし、あまり使用されません。ほとんどのコンパイラは、明示的なmov esp, ebp; pop ebp
シーケンスを実行するだけです。この質問も参照してください。
ただし、「フレームポインタを省略」の最適化を実行できる場合もあります。これにより、EBPが解放されて汎用レジスタとして使用できるようになりますが、ユーザー(またはコンパイラ)は、関数全体でESPの変更を追跡し、変更される可能性のあるオフセットを使用してローカル変数または入力引数をアドレス指定する必要があります。その場合は、ポップまたは明示的な追加を使用して、ESPを元の値に復元してから返す必要があります。
上記は機能全体(つまり、プロローグ/エピローグ)に関係していることに注意してください。__cdecl
関数の途中で特定の関数を呼び出す必要がある場合、ESPをEBP値に復元することはできません。これは、その値がローカル変数にスペースが割り当てられる前の関数の最初でのみ有効であるためです。ここには2つのアプローチがあります。
1)引数をプッシュし、呼び出し後にESPを復元します。
push offset msg
call _printf
pop ecx ; clobbers ECX but shorter than add esp, 4
2)引数を予約済みのスタックスロットに移動します。その場合、ESPを復元する必要はありません。
mov dword ptr [esp+0], offset msg
call _printf
; no need to change ESP
2番目のオプションを選択する場合は、それらのスロットにローカル変数を格納しないようにする必要があります。また、このようなmov
命令は通常、プッシュよりもかなり長いため、コードサイズが問題になる場合は考慮する必要があります。