web-dev-qa-db-ja.com

gccオプション-fomit-frame-pointerを理解しよう

Googleにgccオプションの意味を教えてください-fomit-frame-pointer、次のステートメントにリダイレクトされます。

-fomit-frame-pointer

フレームポインターを必要としない関数のレジスタに保持しないでください。これにより、フレームポインターの保存、設定、復元の指示が回避されます。また、多くの機能で追加のレジスタを使用できます。また、一部のマシンではデバッグが不可能になります。

各関数の私の知識に従って、すべてのローカル変数とその他の情報を保持するために、プロセスメモリのスタックにアクティベーションレコードが作成されます。このフレームポインターが関数のアクティベーションレコードのアドレスを意味することを願っています。

この場合、レジスタにフレームポインタを保持する必要がない関数のタイプは何ですか?この情報を取得したら、フレームポインタがレジスタに保持されていない場合、バイナリで一部の命令が省略されるため、(可能であれば)それに基づいて新しい関数を設計しようとします。これにより、多くの機能があるアプリケーションでパフォーマンスが著しく向上します。

73
rashok

ほとんどの小さな関数にはフレームポインターは必要ありません。大きな関数にはフレームポインターが必要な場合があります。

これは、コンパイラがスタックの使用方法とスタック上の場所(ローカル変数、現在の関数に渡される引数、呼び出される関数の準備中の引数)を追跡する方法を実際に管理しています。フレームポインターを必要とする、または必要としない関数を特徴付けるのは簡単だとは思いません(技術的には、関数はフレームポインターを持つ必要がありません。他のコード」)。

コーディングの戦略の一環として、「関数にフレームポインターを持たせないようにする」べきではないと思います-前述のように、単純な関数はそれらを必要としないので、-fomit-frame-pointer、およびレジスタアロケータで使用できるレジスタをもう1つ取得し、関数へのエントリ/出口に関する1〜3の命令を保存します。関数がフレームポインターを必要とする場合、それはコンパイラがフレームポインターを使用しないよりも優れたオプションであると判断するためです。フレームポインターのない関数を持つことは目標ではなく、正しくかつ高速に動作するコードを持つことが目標です。

「フレームポインターを持たない」ことでパフォーマンスは向上しますが、大きな改善をもたらす魔法の弾丸ではありません。特に、16個のレジスタが既にあるx86-64ではそうではありません。 32ビットx86では、レジスタが8つしかないため、そのうちの1つはスタックポインタであり、フレームポインタとして別のレジスタを使用すると、レジスタ空間の25%が使用されます。これを12.5%に変更すると、かなり改善されます。もちろん、64ビット用にコンパイルすることも非常に役立ちます。

52
Mats Petersson

これは、IntelプラットフォームでのBP/EBP/RBPレジスタに関するものです。このレジスタのデフォルトはスタックセグメントです(スタックセグメントにアクセスするために特別なプレフィックスは必要ありません)。

EBPは、データ構造、変数、およびスタック内の動的に割り当てられたワークスペースにアクセスするためのレジスタの最良の選択です。 EBPは、現在のTOSに関連するのではなく、スタック上の固定ポイントに関連するスタック上の要素にアクセスするためによく使用されます。通常、現在の手順に対して確立された現在のスタックフレームのベースアドレスを識別します。 EBPがオフセット計算でベースレジスタとして使用される場合、オフセットは現在のスタックセグメント(つまり、SSによって現在選択されているセグメント)で自動的に計算されます。 SSは明示的に指定する必要がないため、このような場合の命令エンコードはより効率的です。 EBPを使用して、他のセグメントレジスタを介してアドレス可能なセグメントにインデックスを付けることもできます。

(ソース- http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm

ほとんどの32ビットプラットフォームでは、データセグメントとスタックセグメントは同じであるため、このEBP/RBPとスタックの関連付けは問題になりません。 64ビットプラットフォームでも同様です。2003年にAMDによって導入されたx86-64アーキテクチャは、64ビットモードでのセグメンテーションのサポートを大幅に廃止しました。CS、SS、DS、およびESの4つのセグメントレジスタは0 x86 32ビットおよび64ビットプラットフォームのこれらの状況は、メモリにアクセスするプロセッサ命令で、プレフィックスなしでEBP/RBPレジスタを使用できることを本質的に意味します。

あなたが書いたコンパイラオプションは、BP/EBP/RBPを他の手段、例えば、ローカル変数を保持します。

「これにより、フレームポインターの保存、設定、復元の指示が回避されます」とは、各関数のエントリで次のコードを回避することを意味します。

Push ebp
mov ebp, esp

または、enter命令は、Intel 80286および80386プロセッサで非常に便利でした。

また、関数が戻る前に、次のコードが使用されます。

mov esp, ebp
pop ebp 

またはleave命令。

デバッグツールは、スタックデータをスキャンし、call sitesを特定しながらプッシュされたこれらのEBPレジスタデータを使用できます。つまり、関数の名前と引数を階層的に呼び出された順に表示します。

プログラマーは、広義ではなく(スタック内の1つのエンティティであり、関数呼び出しを1つだけ行い、戻りアドレス、引数、およびローカル変数を保持する)、狭い意味で-stack framesは、コンパイラオプションのコンテキストで言及されています。コンパイラの観点から見ると、スタックフレームはルーチンのエントリと終了コードであり、アンカーをスタックにプッシュします。これは、デバッグや例外処理にも使用できます。デバッグツールは、スタックデータをスキャンし、スタック内のcall sitesを特定しながら、これらのアンカーを使用してバックトレースを行います。つまり、関数の名前を階層的に呼び出された順に表示します。

コンパイラーがこのコードを生成するかどうかを制御できるため、コンパイラー・オプションの観点からスタック・フレームが何であるかをプログラマーに理解することが非常に重要である理由です。

場合によっては、コンパイラによってスタックフレーム(ルーチンのエントリと終了コード)を省略できます。変数には、便利なベースポインター(BP /)ではなく、スタックポインター(SP/ESP/RSP)から直接アクセスしますESP/RSP)。コンパイラが一部の関数のスタックフレームを省略する条件は異なる場合があります。たとえば、次のとおりです。(1)関数がリーフ関数(つまり、他の関数を呼び出さないエンドエンティティ)。 (2)例外は使用されません。 (3)スタック上の発信パラメーターで呼び出されるルーチンはありません。 (4)関数にはパラメーターがありません。

スタックフレーム(ルーチンのエントリおよび終了コード)を省略すると、コードが小さくなり、高速になりますが、スタック内のデータをバックトレースしてプログラマに表示するデバッガの機能に悪影響を与える可能性もあります。これらは、コンパイラがスタックフレームのエントリコードと終了コードを与えるために関数が満たすべき条件を決定するコンパイラオプションです。たとえば、コンパイラには、次の場合にそのようなエントリと終了コードを関数に追加するオプションがあります。(a)常に、(b)決して、(c)必要な場合(条件を指定)。

一般性から特殊性に戻る:-fomit-frame-pointer GCCコンパイラオプションを使用する場合、ルーチンのエントリコードと終了コードの両方で勝ち、追加のレジスタを持っている場合(デフォルトで既にオンになっていない限り)それ自体または他のオプションによって暗黙的に、この場合、あなたはすでにEBP/RBPレジスタを使用することの恩恵を受けており、すでに暗黙的にオンになっている場合、このオプションを明示的に指定しても追加の利得は得られません)。ただし、16ビットおよび32ビットモードでは、BPレジスタは、AXのように(ALおよびAH)8ビット部分にアクセスすることができません。

このオプションは、コンパイラーが最適化で汎用レジスターとしてEBPを使用できるようにするだけでなく、デバッグを複雑にするスタックフレームの終了コードと開始コードの生成も防止します- GCC documentation 明示的にこのオプションを有効にすることを示す(通常は太字で強調する) 一部のマシンでデバッグが不可能になる

デバッグまたは最適化に関連する他のコンパイラオプションは、-fomit-frame-pointerオプションを暗黙的にオンまたはオフにする可能性があることにも注意してください。

他のオプションが-fomit-frame-pointerx86プラットフォームでhttps://gcc.gnu.orgにどのように影響するかについて、gcc.gnu.orgで公式情報を見つけられませんでした。 /onlinedocs/gcc-3.4.4/gcc/Optimize-Options.html には、次のことのみが記載されています。

-Oは、デバッグを妨げないマシンで-fomit-frame-pointerもオンにします。

そのため、x-86プラットフォームで単一の-fomit-frame-pointerオプションでコンパイルした場合に-Oが有効になるかどうかは、明確ではありませんドキュメント自体から。経験的にテストすることもできますが、この場合、将来このオプションの動作を予告なく変更しないというGCC開発者のコ​​ミットメントはありません。

ただし、 Peter Cordes は、x86-16プラットフォームとx86-32/64プラットフォーム間で-fomit-frame-pointerのデフォルト設定に違いがあることをコメントで指摘しています。

このオプション--fomit-frame-pointer-は、GCCだけでなく Intel C++ Compiler 15.0に関連 です。

Intelコンパイラの場合、このオプションには/Oyというエイリアスがあります。

Intelがそれについて書いたものは次のとおりです。

これらのオプションは、最適化でEBPを汎用レジスターとして使用するかどうかを決定します。オプション-fomit-frame-pointerおよび/ Oyは、この使用を許可します。オプション-fno-omit-frame-pointerおよび/ Oy-はそれを禁止します。

一部のデバッガーは、EBPがスタックフレームポインターとして使用されることを想定しており、そうでない場合はスタックバックトレースを生成できません。 -fno-omit-frame-pointerおよび/ Oy-オプションは、すべての機能のスタックフレームポインターとしてEBPを維持および使用するコードを生成するようにコンパイラーに指示します。

-fno-omit-frame-pointerの場合:-O0を使用して最適化をオフにします/ Oy-の場合:/ O1、/ O2、または/ O3最適化をオフにします-fno-omit-frame-pointerオプションは、オプション-を指定すると設定されますO0または-gオプション。オプション-O1、-O2、または-O3を指定すると、-fomit-frame-pointerオプションが設定されます。

/ Oyオプションは、/ O1、/ O2、または/ O3オプションを指定すると設定されます。/Odオプションを指定すると、オプション/ Oy-が設定されます。

-fno-omit-frame-pointerまたは/ Oy-オプションを使用すると、使用可能な汎用レジスタの数が1つ減り、コードの効率がやや低下する可能性があります。

注Linux *システムの場合:現在、GCC 3.2例外処理に問題があります。そのため、GCC 3.2がC++用にインストールされ、例外処理がオンになっている場合(デフォルト)、インテル®コンパイラーはこのオプションを無視します。

上記の引用は、GCCではなく、Intel C++ 15コンパイラにのみ関連することに注意してください。

10
Maxim Masiutin