GCC と -fomit-frame-pointer オプションを使用して 32 ビット C コードをコンパイルすると、関数が stdcall と少なくとも 1 つのパラメーターを使用して Windows API 関数を呼び出さない限り、フレーム ポインター (ebp) は使用されません。
たとえば、パラメーター/引数を持たない Windows API から GetCommandLine() のみを使用すると、GCC はフレーム ポインターを省略し、他の処理に ebp を使用して、コードを高速化し、無駄なプロローグをなくします。
しかし、少なくとも 1 つの引数を受け入れる stdcall Win32 関数を呼び出した瞬間、GCC は -fomit-frame-pointer を完全に無視し、とにかくフレーム ポインターを使用します。汎用目的で ebp を使用できないため、コードの検査が悪化します。もの。言うまでもなく、フレームポインターはまったく無意味です。つまり、リリースおよび配布用にコンパイルしたいのですが、なぜデバッグを気にする必要があるのでしょうか? (デバッグしたい場合は、バグを再現した後、代わりにデバッグ ビルドを使用します)
私のスタックには、alloca のような動的割り当てが含まれていません。それで、スタックには定義された構造がありますが、GCCは私のオプションにもかかわらずダムメソッドを選択しますか? フレームポインターを使用しないようにするために欠けているものはありますか?
私が持っている2番目のグリップは、Win32関数に「プッシュ」命令を使用することを拒否することです. 私が試した他のすべてのコンパイラは、プッシュ命令を使用してスタックにプッシュしたため、よりコンパクトなコードになりました。言うまでもなく、stdcall の引数をプッシュする最も自然な方法です。しかし、GCC は頑固に "mov" 命令を使用して、スタック ポインターを完全に静的に保つ必要があるため、esp に対するオフセットで各スポットを手動で移動します。stdcall は呼び出し元にとって簡単になるように作られていますが、GCC は stdcall とのインターフェース時にこのくだらないコードを生成するため、stdcall の要点を完全に見逃しています。さらに悪いことに、スタック ポインターは静的なので、まだフレーム ポインターを使用しているのでしょうか。どうして?
-mpush-args を試しましたが、何もしません。
また、スタックをページ (4096 バイト) を超えるのに十分な大きさにすると、GCC は、4096 バイトごとに「ビット単位または」スタックをゼロにする (何もしない) 関数を含むプロローグを追加することにも気付きました。 . スタックが予約されている場合、スタックに触れてページフォールトでメモリを自動的にコミットするためだと思いますか?残念ながら、スタックの最初のコミット (予約ではありません) をスタックを保持するのに十分な高さに設定したとしても、これは行われます。最高の冗長コード。
これらのバグは GCC にありますか? または、オプションに欠けているものはありますか?他のものを使用する必要がありますか?いくつかのオプションが不足している場合は教えてください。
stdcall関数を呼び出してプッシュ命令を使用するためだけにインラインasmマクロを作成する必要がないことを真剣に願っています(これにより、フレームポインターも回避されると思います)。今日のコンパイラにあるはずの非常に基本的なものにとって、それは本当にやり過ぎに思えます。はい、私はGCC 4.8.1を使用しているため、古いバージョンではありません。
追加の質問として、GCC が関数プロローグでスタックにレジスタを保存しないようにすることは可能ですか? -nostartfiles 引数を指定して独自の直接エントリ ポイントを使用します。これは純粋な Windows アプリケーションであり、標準の lib スタートアップがなくても問題なく動作するためです。属性((noreturn))を使用すると、レジスタを復元するエピローグが破棄されますが、プロローグでスタックにプッシュされます。このエントリ ポイントのレジスタを保存しないように強制する方法があるかどうかはわかりません関数。いずれにせよ、少なくとも大したことではなく、より完全に感じられると思います. ありがとう!