[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
関数のコードの RTL 表現は、insn と呼ばれるオブジェクトの
二重線形リストである。insn は他に用途のない特別なコードを持つ式である。
insn の一部は実際の命令である。switch
文のための分岐テーブル
を表現するものもある。その他、分岐先ラベルや様々な宣言的な情報を
表現するものもある。
各 insn は、それ自身固有のデータを持つほか、現在の関数内の他の insn と
区別するための一意的な識別番号(遅延分岐スケジューリングの後で、
同じ識別番号を持つ insn のコピーが一つの関数内で複数存在することがあるが、
これらのコピーは全く同じであり、sequence
の中にしか現れない)、
それに直前と直後の insn を指すポインタを持っている。
この三つのフィールドはどの insn においても、insn の式コードによらずに、
同じ位置にある。
この三つのフィールドは XEXP
と XINT
を使ってアクセスしても
良いが、以下のような特別なマクロが三つあり、いつでも使うことができる。
INSN_UID (i)
insn i に一意的な識別番号をアクセスする。
PREV_INSN (i)
i の直前の insn へのポインタをアクセスする。 i が先頭の insn なら、ヌルポインタになる。
NEXT_INSN (i)
i の直後の insn へのポインタをアクセスする。 i が末尾の insn なら、ヌルポインタになる。
insn のリストの先頭の insn は、get_insns
で得ることができる。
末尾の insn は、get_last_insn
を使って得ることができる。
先頭の insn から末尾の insn の範囲内では、NEXT_INSN
と
PREV_INSN
のポインタは常に対応している必要がある。
つまり、insn が先頭の insn でなければ、
NEXT_INSN (PREV_INSN (insn)) == insn |
上の式は常に真であり、また、insn が末尾の insn でなければ、
PREV_INSN (NEXT_INSN (insn)) == insn |
は常に真である。
遅延分岐スケジューリング後、リスト中の insn の幾つかは
sequence
式である可能性がある。sequence
は、insn のベクトルを
含んでいる。このベクトルの中の insn の NEXT_INSN
の値は、
最後の insn を除いて、ベクトルの中での次の insn を指す。
ベクトルの最後の insn の NEXT_INSN
の値は、その insn を含む
sequence
に対する NEXT_INSN
の値と同じである。
PREV_INSN
についても同様である。
つまり、先に述べた恒等式は、sequence
の中の insn については
必ずしも真ではないのである。
特に、insn が sequence
の先頭の insn なら、
NEXT_INSN (PREV_INSN (insn))
は、
sequence
式を含む insn になり、同様に、
PREV_INSN (NEXT_INSN (insn))
は、sequence
式の
最後の insn になる。
これらの式を使って sequence
式を含む insn を取り出すことができる。
どの insn も以下のような6個の式コードの一つを持っている。
insn
式コード insn
は、ジャンプも関数呼び出しも行わない命令に対して
使う。sequence
式は、その中の insn の一つがジャンプや関数呼び出しを
行なっても、常にコード が insn
である insn に含まれる。
コードが insn
である insn は、上記の三つの必須フィールドの他に、
四つのフィールドを持つ。この四つのフィールドについては後出の表で
説明する。
jump_insn
式コード jump_insn
はジャンプを行なう可能性のある命令(あるいは、
もっと一般的に言うと、label_ref
式を含む命令)で使われる。
現在の関数から復帰する命令があるなら、やはり jump_insn
になる。
jump_insn
の insn には、コード insn
の insn と同じ追加の
フィールドがあり、同じようにしてアクセスされ、さらに
ジャンプ最適化が完了したときに一度だけ定義される JUMP_LABLE
という
フィールドも保持している。
単純な、条件ジャンプと無条件ジャンプの場合は、このフィールドは
code_label
を保持しており、ここに insn が(条件分岐の場合もある)
分岐していく。もっと複雑なジャンプでは、
JUMP_LABEL
が insn が参照しているラベルの一つを記録している。
その他のラベルを探すには insn の本体全体を走査するしかない。
関数から復帰する insn はジャンプ insn として数えるが、
何もラベルを参照していないので、JUMP_LABLE
フィールドは 0 になる。
call_insn
式コード call_insn
は、関数呼びだしを行なう可能性のある
命令に対して使われる。
これらの命令を区別するのは重要である。なぜなら、特定のレジスタや
メモリ位置の値を、予測できない形で変えてしまう可能性があるからである。
call_insn
insn は insn
insn と同じ追加のフィールドを
持ち、同じ方法で参照する。さらに付け加えて、CALL_INSN_FUNCTION_USAGE
というフィールドを持っており、このフィールドは一つのリスト(expr_list
式の連鎖である)を保持している。このリストは、被呼びだし関数により
使われたか、あるいは上書きされたハードレジスタを示す use
式と
clobber
式を含んでいる。
このリストの clobber
に指定されたレジスタは、call_insn
の
実行の後、変更される。一方、call_insn
の本体の clobber
で指定されたレジスタは、insn の実行が完了する前に上書きされる。
このリストの clobber
式は CALL_USED_REGISTERS
で
指定されたレジスタを増やす。(see section レジスタの基本的特徴)。
code_label
code_labe
insn は、分岐命令の分岐先となりうるラベルを
表す。標準の三つのフィールドに加えて二つの特別なフィールドがある。
CODE_LABEL_NUMBER
を使って ラベル番号、すなわち、
コンパイル単位(現在の関数の中だけではない)の全てのラベルの中から、
このラベルを一意的に特定する番号を保持する。
最終的には、このラベルはアセンブラ出力ではアセンブララベルとして
表現される。これは、通常は ‘Ln’ という形式になる。
ここで n はラベル番号である。
code_label
が RTL 式に現れるときは、普通はそのラベルのアドレスを
表す label_ref
の中に、番号として現れる。
フィールド LABEL_NUSES
は、ジャンプ最適化フェーズが完了した時に
一度だけ定義され、現在の関数の中でこのラベルが参照された回数を保持する。
barrier
バリアは、命令ストリームの、制御の流れが越えることのできない地点に
置かれる。無条件ジャンプ命令の後ろに置かれ、ジャンプが無条件であることを
知らせる。また、volatile
関数呼び出しの後ろにも置かれる。
これらの関数が戻る事はない(例えば exit
である)。
これらは、三つの標準フィールド以外の情報は持っていない。
note
note
insn は、付加的な、デバッグ情報および宣言に関する情報を
表すのに使われる。この insn には、非標準のフィールドが二つある。
一つは整数で、マクロ NOTE_LINE_NUMBER
で参照され、
もう一つは文字列で、マクロ NOTE_SOURCE_FILE
で参照される。
NOTE_LINE_NUMBER
が正なら、そのノートはソース行の位置を
表し、NOTE_SOURCE_FILE
は、その行が含まれるソースファイル名を
表す。このノートは、アセンブラ出力に行番号データを生成するの使われる。
NOTE_LINE_NUMBER
が正でないなら、実際の行番号ではなくては、
以下の値の一つを持つコードである。この場合、NOTE_SOURCE_FILE
は
ヌルポインタでなければならない。
NOTE_INSN_DELETED
このノートは完全に無視できる。幾つかのフェーズでは、 insn を削除するのを、それらの insn をこの種類のノートに変えてしまう ことで行なっている。
NOTE_INSN_BLOCK_BEG
NOTE_INSN_BLOCK_END
この型のノートは、変数名のスコープレベルの開始位置と終了位置を 表す。デバッグ情報の出力を制御する。
NOTE_INSN_EH_REGION_BEG
NOTE_INSN_EH_REGION_END
この型のノートは、例外処理のスコープレベルの開始位置と終了位置を
表す。NOTE_BLOCK_NUBMER
で、どの CODE_LABEL
が
指定された領域に付属するかを特定する。
NOTE_INSN_LOOP_BEG
NOTE_INSN_LOOP_END
この型のノートは、while
または for
ループの開始位置と
終了位置を表す。これらにより、ループ最適化部が素早くループを見つけられる
ようになる。
NOTE_INSN_LOOP_CONT
ループの中の、continue
文の飛び先位置に現れる。
NOTE_INSN_LOOP_VTOP
このノートは、終了テストが複製されるようなループにおいて、 終了テストが始まる位置を示す。 この位置は、ループ不変量を考えるときのもう一つの仮想的なループの 開始位置となる。
NOTE_INSN_FUNCTION_END
関数本体の終り近く、(一個の命令では関数から戻れないマシンでは)
return
文の飛び先ラベルの直前に現れるノートである。
NOTE_INSN_SETJMP
setjmp
や関係する関数の呼び出し毎に、その直後に現れる。
これらのコードは、デバッギングダンプにはシンボルで表示される。
insn のマシンモードは普通は VOIDmode
だが、
フェーズによってはモードを色々な目的で使用する。
共通部分式削除パスでは、処理済みのブロックの先頭の insn の場合、
その insn のモードを QImode
に設定する。
二回目の Haifa スケジューリングパスでは、複数命令の同時発行が可能な
ターゲットに対しては、insn が同時に発行される命令のグループの
始まりの命令であると思われる場合は、その insn のモードを TImode
に設定する。つまり、その命令は直前の命令と同時に発行できないものである。
以降のパス、特に機種依存の命令並べ替えではこれはに頼っている。
以下は、insn
、jump_insn
、call_insn
insn の
追加フィールドの表である。
PATTERN (i)
この insn により発生する副作用を表す式。
これは次のコードのどれか一つでなければならない。
set
、 call
、 use
、
clobber
、 return
、 asm_input
、 asm_output
、
addr_vec
、 addr_diff_vec
、 trap_if
、 unspec
、
unspec_volatile
、 parallel
、sequence
。
もし parallel
なら、parallel
の各要素もこれらのコードで
なければならない。ただし、parallel
式はネストできないこと、
addr_vec
と addr_diff_vec
は parallel
式の内側では
許されないという例外がある。
INSN_CODE (i)
マシン記述中のどのパターンがこの insn にマッチするかを特定する整数 である。照合がまだ行なわれていないなら -1 を返す。
use
、clobber
、asm_input
、addr_vec
、
addr_diff_vec
のどれか一個を使った式からなるパターンを
持つ insn については、照合は行なわれず、このフィールドは -1 のまま変わらない。
asm
文から生じる insn の場合も照合は行なわれない。
これらの insn は少なくとも一個の asm_operands
式を含む。
このような insn に対して、関数 asm_noperands
は非負の値を
返す。
デバッグ出力では、このフィールドは数字の後に記号名表現が出力される。 この記号名表現は、‘md’ ファイル中のパターンの位置を、 名前付きパターンからの小さな正または負のオフセットとして示す。
LOG_LINKS (i)
基本ブロック内の命令間の依存関係についての情報を与える
リスト(insn_list
式の連鎖)。
ジャンプやラベルは、関係のある insn の間には入らない。
REG_NOTES (i)
insn について種々雑多な情報を与えるリスト(expr_list
式と
insn_list
式の連鎖)。
その insn で使われているレジスタに関係する情報であることが多い。
insn の LOG_LINKS
フィールドは、insn_list
式の連鎖である。
それぞれの insn_list
式はオペランドを二つ取る。
一番目はある insn であり、二番目は別の insn_list
式(連鎖の次のもの)
である。連鎖の最後の insn_list
では、二番目のオペランドは
ヌルポインタとなる。
連鎖について重要なことは、どの insn が(insn_list
式の
一番目のオペランドとして)その中に現れるかということである。
順序は重要ではない。
このリストは最初、フロー解析パスにより設定される。
それまではヌルポインタになっている。フロー解析パスは、
命令組合せで使用可能なデータ依存関係についてのリンクを追加するだけである。
フロー解析パスは、insn 毎に、この insn で始めて使われる値をレジスタに
格納する insn へのリンクを追加する。
命令スケジューリングパスは、余分のリンクを追加して、あらゆる依存関係
が表現されるようにする。
リンクは、データ依存関係、逆依存関係、出力依存関係を表す。
リンクのマシンモードがこの三つの型を区別する。
逆依存関係のモードは REG_DEP_ANTI
であり、
出力依存関係のモードは REG_DEP_OUTPUT
であり、
データ依存関係のモードは VOIDmode
である。
insn の REG_NOTES
フィールドは、LOG_LINKS
と同様の
一個の連鎖であるが、insn_list
式に加えて expr_list
式も
含んでいる。色々な種類のレジスタノートがあり、それらはマシンモードに
より区別され、あるレジスタノート中では、実際に enum reg_note
で
あると理解される。
このノートの第一オペランド op は、ノートの種類によって
意味の異なるデータである。
マクロ REG_NOTE_KIND (x)
は、レジスタノートの種類を返す。
これと対をなすマクロ PUT_REG_NOTE_KIND(x, newkind)
は、
x のレジスタノートのタイプを newkind に設定する。
レジスタノートには三つのクラスがある。
何か insn への入力に関するもの、何か insn の出力に関するもの、
それに、二つの insn の間のリンケージを作るものである。
また、LOG_LINKS
でしか使われない値が一セットある。
以下のレジスタノートは、insn への入力についての注釈である。
REG_DEAD
op の中の値がこの insn で死ぬ。つまり、この insn の直後で この値を書き換えても、プログラムの以後の動作には影響を与えない。
これは、必ずしも、レジスタ op がこの insn 以後意味のある値を持たない
ということを意味しない。この insn の出力にもなることがあるからである。
しかし、その場合、REG_DEAD
ノートは冗長であり、普通は再ロードパス
迄は存在しない。だが、このことを前提としているコードはない。
REG_INC
レジスタ op は、この insn 内に埋め込まれた副作用により
インクリメント(あるいは、デクリメント。このレベルでは違いはない)
される。
すなわち、これは post_inc
式、または pre_inc
式、
post_dec
式、pre_dec
式に現れる。
REG_NONNEG
この insn に到達したとき、レジスタ op は非負の値を持つことが 知られている。これを使って、例えば m68k の dbra のような、 デクリメントして 0 でない間は分岐する(decrement and branch until zero) 命令がマッチすることが可能になる。
REG_NONNEG
ノートが insn に付加されるのは、
マシン記述に ‘decrement_and_branch_until_zero’
というパターンがある場合のみである。
REG_NO_CONFLICT
この insn は、たとえ衝突を引き起こすように見えても、 op とこの insn により設定されるアイテムとの間の 衝突を引き起こさない。 言い換えると、そうなっていないと代入先レジスタと op が 同じレジスタに割り当てられる場合でも、この insn はその割当を 妨げない。
このノートがある insn は、通常、あるブロックの一部である。
このブロックは複数ワードの疑似レジスタを指定する一個の clobber
insn
で始まり、それぞれが一ワードの値を設定し、REG_NO_CONFLICT
が付随
している insn のグループが一つ、最後に、計算される式を与える
REG_EQUAL
ノートをつけて、出力をそれ自身にコピーする insn が来る。
このブロックは、先頭と末尾の insn について、それぞれ REG_LIBCALL
ノートと REG_RETVAL
ノートで包み込まれている。
REG_LABEL
この insn は op、code_label
を使うが、jump_insn
では
ない。このノートにより、op が実際に使われることをジャンプ最適化
に対して知らせる。
以下のノートは、insn の出力の属性を記述する。
REG_EQUIV
REG_EQUAL
このノートは、一個のレジスタだけを設定する insn についてのみ有効であり、
そのレジスタが実行時には op に等しいことを示す。
これが等しくなる範囲は、二つのノートのタイプで異なる。
この insn が明示的にそのレジスタにコピーする値は op とは
違って見えるかもしれないが、実行時には等しくなる。
一個の set
の出力が strict_low_part
式なら、
このノートは subreg
式の SUBREG_REG
に含まれている
レジスタを参照している。
REG_EQUIV
の場合は、レジスタは関数全体を通して op に
等価であり、全て op で正当に置き換えることができる。
(ここで「正当に」はプログラムのデータ流を指す。単純な置き換えは
幾つかの insn を不正にする。)
例えば、ある定数が、他の値が決して代入されることのない、あるレジスタに
ロードされるとき、この種類のノートが使われる。
ある仮引数が関数の入り口点で疑似レジスタにコピーされるとき、 この種類のノートが仮引数が渡されたスタックスロットに等価なレジスタを 記録する。この場合、レジスタは他の insn により設定されることもあるが、 その関数内でそのレジスタをスタックスロットに置き換えることはなお有効である。
REG_EQUIV
は、仮引数を格納可能なスタックスロットがある場合、
ある関数の入り口でレジスタ仮引数を疑似レジスタにコピーする命令で使われる。
他の insn がその疑似レジスタを設定する可能性もあるが、
コンパイラにとっては、関数全体で疑似レジスタをそのスタックスロットに
置き換えるのは正当である。
ただし、コンパイラは、そのスタックスロットが、最初のコピー命令での
同じように置き換えを行なうことで適切に初期化されていることを保証されている
場合である。
これは、呼び出し規約でレジスタ仮引数についてスタックスペースを
割り当てるようになっている機種で使われる。
関数の引数のスタック渡し の REG_PARM_STACK_SPACE
を参照のこと。
REG_EQUAL
の場合は、この insn により設定されるレジスタは、
この insn の最後では実行時に op に等価になるが、
関数内の他の場所では必ずしも等価ではない。
この場合、op は算術式であることが多い。
例えば、ライブラリ呼び出しのような insn 列が、ある算術演算を
実行するのに使われるとき、この種類のノートが最終的な値を
生成したりコピーする insn に付随する。
これらの二つのノートは、コンパイラのパス群により異なる方法で使われる。
REG_EQUAL
は、レジスタ割当に先立つパス(例えば、
共通部分式削除とループ最適化)により使われ、その値をどう考えれば良いかを
そのパスに知らせる。
REG_EQUIV
ノートは、レジスタ割当により使われ、
利用可能なレジスタが足りなければ、レジスタの代わりに使われる
代入式(定数か、スタック上の仮引数の位置を表す mem
式)
が利用可能であることを示す。
仮引数のスタックのホームとそのライフタイムを通じてメモリ位置に
等かな疑似レジスタを除いて、全ての等価なものは、
付随する REG_EQUAL
ノートにより最初に示される。
ここで、仮引数のスタックホームは、REG_EQUIV
ノートにより
指示され、最適化の最初の方のパスでは役に立たない。
また、メモリ位置に等価な疑似レジスタは、コンパイルの後の段階に
なるまで検出されない。
op が定数であり、その insn が
その代入先レジスタの組を一つしか表現しないのであれば、
レジスタ割当の初期の段階で、REG_EQUAL
ノートは REG_EQUIV
ノートに変えられる。
こうして、レジスタ割当より前のコンパイラのパスでは、REG_EQUAL
ノートだけを検査する必要があり、レジスタ割当の後のパスでは
REG_EQUIV
ノートだけを検査すれば良い。
REG_UNUSED
この insn により設定されるレジスタ op は、次の insn では
使われない。
REG_DEAD
ノートとの違いは、REG_DEAD
の方は、
入力中の値が後で使われることがないことを示す点にある。
この二つのノートは独立であり、同じレジスタに対して両方とも
存在してもかまわない。
REG_WAS_0
この insn の一個の出力は、この insn の前にゼロを含んでいた。
op が、ゼロに設定した insn である。
このノートが存在し、かつ op が削除されていたり、note
に
変換されたりしていなければ、このノートを信頼することができる。
このノートが存在しない場合は、何も意味しない。
以下のノートは、insn 間のリンケージを記述する。 これらは、対で現れる。一個の insn には、ノートの対があり、それらは 二番目の insn を指す。二番目の insn には、最初のinsn を指す逆のノートが ある。
REG_RETVAL
この insn は、複数の insn から成る列(例えば、ライブラリ呼び出し)の 値をコピーする。op は、列の先頭の insn である (ライブラリ呼び出しの場合は、ライブラリ呼び出しの引数を 設定するように生成された先頭の insn である)。
ループ最適化はこのノートを使って、コードの移動の目的のために、 このような列を一個の演算として取り扱う。 また、フロー解析ではこのノートを使って、このような列で結果が死んでいる ものを削除する。
通常、REG_EQUAL
ノートもこの insn に付属し、
この列により計算される式を提供する。
これらのノートは再ロード後に削除される。もはや不正確だったり役に 立たなかったりするからである。
REG_LIBCALL
これは、REG_RETVAL
の逆である。
これは複数 insn の列の先頭の insn に置かれ、最後の insn を指す。
これらのノートは再ロード後に削除される。もはや不正確だったり役に 立たなかったりするからである。
REG_CC_SETTER
REG_CC_USER
cc0
を使う機種では、cc0
を設定したり使ったりする
insn はお互いに近くにある。だが、遅延分岐スロットを埋める処理が
行なわれると、もはや近くにあるとは言えなくなる可能性がある。
その場合、REG_CC_USER
ノートが、cc0
を設定する insn に
置かれて、cc0
を使う insn を指し、REG_CC_SETTER
ノートが
cc0
を使う insn に置かれて、cc0
を設定する insn を
指すようになる。
以下の値は、LOG_LINKS
フィールドでのみ使われ、
各リンクが表現する依存関係の型を指し示す。
データ依存関係(書き込み後の読み込み依存関係, read after write dependence)
を指し示すリンクはどのコードも使わず、単にモードが VOIDmode
であり、
何の説明文もなく出力される。
REG_DEP_ANTI
これは逆依存関係(WAR、読み込み後の書き込み依存) を示す。
REG_DEP_OUTPUT
これは出力依存関係(WAW、書き込み後の書き込み依存)を示す。
これらのノートは gcov によるプロファイルデータから収集した情報を
記述する。insn の REG_NOTES
フィールドに expr_list
として
格納される。
REG_EXEC_COUNT
プロファイルデータに基づく、基本ブロックの実行回数を示す。 このノートは、基本ブロックの先頭の insn に付加される。
REG_BR_PROB
プロファイルデータに基づく、ある分岐 insn の、分岐が行なわれた回数と
行なわれなかった回数の比を示す。
0 と REG_BR_PROB_BASE
の間の値として格納される。
値が大きくなると、分岐が成立する確率がより高くなることを示す。
REG_BR_PRED
このノートは、遅延分岐スケジューリングが起きた後のジャンプ insn に
現れる。これらは、ジャンプの方向と可能性を示す。
フォーマットは、ATTR_FLAG_*
値のビットマスクである。
REG_FRAME_RELATED_EXPR
これは、RTX_FRAME_RELATED_P
insn で使われる。
この insn では、付属した式が、実際の insn パターンの代わりに使われる。
これは、パターンが複雑か間違っている場合に行われる。
便宜上、insn_list
や expr_list
中のマシンモードは、
デバッグダンプでは、これらのシンボリックコードで出力される。
式コード insn_list
と expr_list
の唯一の相違点は、
insn_list
の先頭のオペランドは insn であると仮定され、
デバッグダンプ中では insn の一意的な ID として出力されること、
expr_list
の先頭のオペランドは式として通常の方法で
出力される点にある。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated
using texi2html 1.78.