[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.16 Insns

関数のコードの RTL 表現は、insn と呼ばれるオブジェクトの 二重線形リストである。insn は他に用途のない特別なコードを持つ式である。 insn の一部は実際の命令である。switch 文のための分岐テーブル を表現するものもある。その他、分岐先ラベルや様々な宣言的な情報を 表現するものもある。

各 insn は、それ自身固有のデータを持つほか、現在の関数内の他の insn と 区別するための一意的な識別番号(遅延分岐スケジューリングの後で、 同じ識別番号を持つ insn のコピーが一つの関数内で複数存在することがあるが、 これらのコピーは全く同じであり、sequence の中にしか現れない)、 それに直前と直後の insn を指すポインタを持っている。 この三つのフィールドはどの insn においても、insn の式コードによらずに、 同じ位置にある。 この三つのフィールドは XEXPXINT を使ってアクセスしても 良いが、以下のような特別なマクロが三つあり、いつでも使うことができる。

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_INSNPREV_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 については 必ずしも真ではないのである。 特に、insnsequence の先頭の 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 に設定する。つまり、その命令は直前の命令と同時に発行できないものである。 以降のパス、特に機種依存の命令並べ替えではこれはに頼っている。

以下は、insnjump_insncall_insn insn の 追加フィールドの表である。

PATTERN (i)

この insn により発生する副作用を表す式。 これは次のコードのどれか一つでなければならない。 setcalluseclobberreturnasm_inputasm_outputaddr_vecaddr_diff_vectrap_ifunspecunspec_volatileparallelsequence。 もし parallel なら、parallel の各要素もこれらのコードで なければならない。ただし、parallel 式はネストできないこと、 addr_vecaddr_diff_vecparallel 式の内側では 許されないという例外がある。

INSN_CODE (i)

マシン記述中のどのパターンがこの insn にマッチするかを特定する整数 である。照合がまだ行なわれていないなら -1 を返す。

useclobberasm_inputaddr_vecaddr_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 は opcode_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_listexpr_list 中のマシンモードは、 デバッグダンプでは、これらのシンボリックコードで出力される。

式コード insn_listexpr_list の唯一の相違点は、 insn_list の先頭のオペランドは insn であると仮定され、 デバッグダンプ中では insn の一意的な ID として出力されること、 expr_list の先頭のオペランドは式として通常の方法で 出力される点にある。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.78.