私はアセンブリ言語の初心者であり、コンパイラが発行するx86コードは、通常、リリース/最適化モードでも、EBP
レジスタを他の何かに使用できる場合でもフレームポインタを保持していることに気付きました。
フレームポインターを使用するとコードのデバッグが容易になり、alloca()
が関数内で呼び出された場合に必要になることがある理由を理解しています。ただし、x86にはレジスタがほとんどなく、そのうち2つを使用してスタックフレームの位置を保持するだけで十分な場合は意味がありません。最適化/リリースビルドでも、フレームポインターを省略することが悪い考えと見なされるのはなぜですか?
フレームポインターは参照ポインターであり、デバッガーはローカル変数または引数が単一の定数オフセットでどこにあるかを知ることができます。 ESPの値は実行中に変化しますが、EBPは同じままで、同じオフセットで同じ変数に到達することができます(たとえば、最初のパラメーターは常にEBP + 8になりますが、ESP offsetsあなたは物事をプッシュ/ポップするため、大幅に変更できます)
コンパイラーがフレームポインターを捨てないのはなぜですか?フレームポインターを使用すると、デバッガーはEBPへの一定のオフセットにあることが保証されるため、ローカル変数と引数がシンボルテーブルを使用している場所を把握できます。そうでなければ、ローカル変数がコードのどこにあるのかを把握する簡単な方法はありません。
Gregが述べたように、EBPはスタックフレームの逆リンクリストを提供するため、デバッガーのスタックアンワインドにも役立ちます。したがって、デバッガーは関数のスタックフレーム(ローカル変数+引数)のサイズを把握できます。
ほとんどのコンパイラは、フレームポインターを省略するオプションを提供しますが、デバッグが非常に困難になります。そのオプションは、リリースコードであってもグローバルに使用しないでください。ユーザーのクラッシュをいつデバッグする必要があるかはわかりません。
すでに良い答えに2セントを加えるだけです。
スタックフレームのチェーンを持つことは、優れた言語アーキテクチャの一部です。 BPは、サブルーチンローカル変数が格納されている現在のフレームを指します。 (ローカルは負のオフセットにあり、引数は正のオフセットにあります。)
最適なレジスタが最適化で使用されるのを妨げているという考えは、最適化が実際にいつどこで価値があるのかという疑問を提起します。
最適化は、1)関数を呼び出さない、2)プログラムカウンターがその時間のかなりの部分を費やす、3)コンパイラが実際に見るコード(つまり、非ライブラリ関数)であるタイトループでのみ価値があります。これは通常、特に大規模なシステムでは、コード全体のごく一部です。
他のコードは、サイクルをなくすためにひねったり絞ったりすることができますが、プログラムカウンターは実際には存在しないので、単に問題ではありません。
これを聞かなかったことは知っていますが、私の経験では、パフォーマンスの問題の99%はコンパイラの最適化とはまったく関係ありません。彼らは過剰設計と関係がある。
確かにコンパイラに依存します。 EBPレジスタを汎用レジスタとして自由に使用するx86コンパイラが出力する最適化コードを見てきました。 (ただし、どのコンパイラーに気付いたのか覚えていません。)
コンパイラーは、例外処理中のスタックの巻き戻しを支援するためにEBPレジスターを維持することもできますが、これも正確なコンパイラー実装に依存します。
ただし、x86にはほとんどレジスタがありません
これは、オペコードが8つのレジスタのみをアドレス指定できるという意味でのみ当てはまります。プロセッサ自体には実際にはそれよりも多くのレジスタがあり、レジスタの名前変更、パイプライン処理、投機的実行、およびその他のプロセッサバズワードを使用してその制限を回避します。ウィキペディアには、登録の制限を克服するためにx86プロセッサーができることについての優れた導入段落があります: http://en.wikipedia.org/wiki/X86#Current_implementations 。
スタックフレームの使用は、リモートの現代のハードウェアでも信じられないほど安くなっています。安価なスタックフレームがある場合、レジスタをいくつか保存することはそれほど重要ではありません。高速スタックフレームとより多くのレジスタは、技術的なトレードオフであり、高速スタックフレームが勝ったと確信しています。
純粋なレジスターをいくら節約していますか?その価値はありますか?