2017-10-17 17 views
3

私たちはMIPSアセンブラを研究しています(この質問は一般的にアセンブリにも当てはまると思います)。先生はのに私たちを紹介しました。フレームポインタの利点は何ですか?

addiu $sp, $sp, -8 ; alloc 2 words in the stack 
sw $s0, 4($sp)  ; save caller function $s0 value in the stack 
sw $ra, ($sp)  ; save the return address for the callee function 

と関数エピローグで:私は関数プロローグを持っている場合は

は、私が直接スタックポインタを行うために使用

move $v0, $0   ; set 0 as return value 
lw $s0, 4($sp)  ; pick up caller $s0 value from the stack 
lw $ra, ($sp)  ; pick up return address to return to the caller 
addiu $sp, $sp, 8 ; dealloc the stack words I used 
jr $ra    ; return back to caller 

教師は、フレームポインタを使用することがあると言いました組み立て時に関数を書くときに私たち人間に有益です。

addiu $sp, $sp, -12 ; alloc 3 words in the stack 
sw $fp, 8($sp)  ; save caller frame pointer in the stack 
addiu $fp, $sp, 8 ; set $fp to the uppermost address of the activation frame 
sw $ra, -4($fp)  ; saving like the first example, but relative 
sw $s0, -8($fp)  ; to the frame pointer 

教師はまた、スタックポインターが他のスペースを割り振ることがあり、我々が注意を払う必要があるので、関数内のアクティベーションフレームを参照することがより困難であると述べました。 フレームポインタを使用すると、アクティブ化フレームへの静的ポインタがあります。

はい、でも、は、呼び出し元関数の保存されたデータだけを含んでいるので、関数内でのアクティベーションを使用する必要がありますか?

私は実装するのが難しいと思っています。フレームポインタがプログラマにとって大きな利点となる本当の実用的な例はありますか?

+0

フレームポインタを使う必要がない多くの命令セットでは、コンパイラ作成者のデバッグが簡単になりますが、実際には有効な言い訳ではありません。 gccなどを使用すると、そのオーバーヘッドを保存するために、フレームポインタを使用しないで、それを使用するビルドコードを使用しないことを選択できます。いくつかの既定値と既定値なし。関数内のオフセットをその関数に固定します。変数Xはその関数に対して常にfp-Nです。あなたは関数全体に対してX sp + Mを作ることができます。あるいは関数が進むにつれて、スペースを節約するために必要に応じてspを動かすことができます。 –

+0

しかし、一日の終わりに、教師の言うことは何でも、そのクラスのために、クラスを渡し、すべてを疑い、あなたがそれを信じているかどうかを見るためにそれを発見する。先生の宿題を簡単にすることができたのか、それともこのプログラミングルールは実際には単語の意味で「より良い」と教えられたのでしょうか?私はそれが人間にとってはより簡単になると思っていますが、同時にコンパイラや人間では、spを動かして関数のすべての使い方をカバーすると、フレームポインタがなければ同じように簡単です。 –

+1

これを試してください:5つ以上のフィールドを使って、あなたの頭にC構造を定義してください。スタックのためのスペースを割り当てます。それを初期化する。スタック上のパラメータとしてメンバを他の関数に渡します。それはフレームポインタの有無にかかわらず行います。 –

答えて

2

スタックに可変量のスペースを動的に割り当てるときは、フレームポインタが絶対に必要です。可変長配列および/またはallocaをC言語で使用する関数は、フレームポインタを必要とする関数の例です。関数が使用するスタックの量は可変であるため、変数にアクセスするためにスタックポインタからの定数オフセットを使用することはできません。また、関数が復帰したときに可変長割り当てを元に戻す方法が必要です。フレームポインタを使用すると、両方の問題が解決されます。これを使用して、定数オフセットを使用してスタック変数をアドレス指定し、スタックポインタを関数の開始時の値に復元することができます。

MIPSでは、合計スタック割り当てが32kを超える場合、固定サイズのスタック割り当てのみを使用する関数では、フレームポインタを最適化として使用することも理にかなっています。 MIPSでサポートされている制限されたアドレッシングモードでは、スタックポインタまたは他のレジスタとの相対的な16ビット符号拡張オフセットしか許されません。スタックポインタはスタックの最下位を指しているので、スタックポインタには負でないオフセットだけを使用できます。したがって、スタック上の32kだけが単一の命令で対処できます。フレームポインタを使用し、スタックフレームの中央(フレームの上端ではなく)を指すことによって、単一の命令で最大64kのスタックをアドレス指定することができます。

それ以外の場合は、フレームポインタを使用するのは、プログラムではなく、プログラマにのみ役立ちます。プログラム内のすべての関数がフレームポインタを持つ標準スタックフレームを使用する場合、スタックに格納されているフレームポインタと保存されているすべてのフレームポインタ値は、スタックフレームのリンクリストを形成します。このリンクされたリストは、デバッグ時に関数呼び出しのトラックバックを作成するために簡単に横断することができます。しかし、適切な最新のデバッガでは、フレームポインタが使用されていなくても、デバッガがスタックフレームを処理するために使用できる実行可能ファイルにメタデータ(アンワインド情報)を埋め込むことも可能です。これは現代のコンパイラが自動的に行うことができるものですが、アセンブリ言語では、それを動作させるために必要な余分な指令をすべて組み込むのは非常に苦しいことです。

関数内で固定サイズの割り当てと割り当て解除によってスタックポインタが複数回変更される可能性がある場合は、プログラムの任意の時点でスタックポインタを基準にして変数の位置を追跡することができます。それはいつもどんな所在地でも一定のオフセットにあるが、それは場所によって変わるだろう。そのオフセットを決定するのは難しく、エラーが発生する可能性があります。フレームポインタを使用すると、フレームポインタの値が変更されないため、各変数に決して変更されないフレームポインタに対するオフセットが与えられます。

フレームポインタを使用する必要があると感じる場合は、最初にアセンブリでプログラミングする理由を考慮する必要がある最後の2つの理由の1つが原因です。現代のコンパイラがフレームポインタを使う必要がないので、より良いコードを生成することができます。

1

フレームポインタの省略は、CおよびC++コンパイラが実装するstandard optimization optionです。利点は、他の用途のためにレジスタを解放できることです。しかし、それは非常に頻繁に無効になるので、プログラムのクラッシュが非常に困難になります。スタックトレースを生成するような基本的なことさえも非常に困難になります。親関数のフレームがどこにあるかわからないので、戻りアドレスを探す場所がわかりません。同様に局所変数の状態を検査することは苦痛を伴う。これは、アプリのデバッグ中に問題ではなく、プロダクションでプログラムがクラッシュしたときに戻ってくる。

デバッグメタデータが必要な場所を知るには、フレームのサイズを知る必要があります。一般的に、コードをデバッグして診断できるようにすることは、書くのが簡単であることよりも重要です。more典型的なプログラマは、書込みよりもデバッグとテストに多くの時間を費やします。

関連する問題