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

15.13 副作用式

ここまで説明してきた式コードは値を表すものであり、動作を 表すものではなかった。 しかし機械命令というものは決して値を生み出すものではない。 機械命令は、マシンの状態に副作用を与えるという点でのみ意味を持つ。 特別な式コードを使って、副作用を表現する。

命令の本体は、必ず以下の副作用を表すコードの一つである。 これまで説明してきた、値を表現するコードは、これらのオペランドとしてのみ 現れるのである。

(set lval x)

x の値を lval で表される場所に格納する動作を表現する。 lval は、その中に格納可能であるような場所を表す式である。 すなわち、reg(あるいは subreg、または strict_low_part)、 mempccc0 のどれかである。

lval が、regsubregmem のどれかなら、 マシンモードを持つ必要がある。その場合、x は、そのモードで 有効でなければならない。

lval が、マシンモードがそのレジスタの全幅より小さいような reg であれば、そのマシンモードにより指定されたレジスタの部分には 指定された値が与えられ、レジスタのその他の部分は未定義値となる。 同様に、lval が、レジスタのモードより狭いモードの subreg なら、 レジスタのその他の部分がどのように変更されるかは不定である。

lval が、subregstrict_low_part なら、 subreg のマシンモードで指定されたレジスタの部分は、 値 x が与えられ、その他の部分は変更されない。

lval(cc0) なら、マシンモードがないので、 xcompare 式か、任意のモードを持つ値で良い。 後者の場合は、“test” 命令を表現する。 (set (cc0) (reg:m n)) という式は、 (set (cc0) (compare (reg:m n) (const_int 0))) という 式に等価である。 前者の式を使ったほうが、コンパイル中のメモリを節約できる。

lval(pc) なら、それはジャンプ命令であり、 x の取りうる値は非常に限られてくる。 x は、label_ref 式とすることができる(無条件ジャンプ)。 if_then_else (条件ジャンプ)とすることもでき、その場合、 第二オペランドか第三オペランドのどちらかが (pc) (ジャンプしない場合に 使われる)でなければならず、またもう一方は label_ref (ジャンプする場合に使われる)でなければならない。 また、xmem または (plus:SI (pc) y) でも 良い。ここで、yregmem である。 この最後のパターンは分岐テーブル経由のジャンプを表現するのに使われる。

lval(cc0) でも (pc) でもなければ、 lval のモードは VOIDmode としてはならず、x のモードは lval のモードとして有効でなければならない。

lvalSET_DEST マクロで、xSET_SRC マクロで アクセスするようにするのが良い。

(return)

パターン中に単独の式として書くことで、現在の関数から戻ることを 表す。ただし、VAX のように一命令で戻ることができるマシンに 限られる。 関数から戻るためには、複数の命令からなる「エピローグ」を実行する必要が あるマシンでは、関数からの復帰は、エピローグの直前に置かれるラベルへ ジャンプすることで行なわれるので、return 式コードが使われることはない。

if_then_else 式の中に置いた場合は、呼び出し元に戻るための pc に置かれる値を表す。

(return) というパターンは、論理的には (set (pc) (return)) と等価であるが、後者の形式が使われることはない。

(call function nargs)

関数呼び出しを表現する。functionmem 式であり、 この式のアドレスは、呼び出される関数のアドレスである。 nargs は二つの目的で使われる式である。 あるマシンでは、スタックに積まれた引数のバイト数を表現する。 あるいは、引数レジスタの数を表現する。

どのマシンも function が持たなければならない標準的な マシンモードを持っている。マシン記述では、 FUNCTION_MODE というマクロを、その不可欠のモード名に 展開されるように定義している。 このモードの目的は、どの種類のアドレッシングが許されるかが アドレスの対象となるマシンモードに依存するマシンにおいて、 どの種類のアドレッシングが許されているかを指定することである。

(clobber x)

予期できない値を x に格納すること、または格納する可能性が あることを表す。x は、reg 式か、scratch 式か、 mem 式のどれかでなければならない。

これが使われるのは一つは、標準的な値を特定のハードレジスタに格納する 文字列命令においてである。 格納される値を記述する手間をかける必要はないが、 文字列命令を越えてその値を保持しようとしないように、 コンパイラにレジスタの値が変更されることを知らせるのが本質的な 事である。

もし x(mem:BLK (const_int 0)) なら、 全メモリ位置が上書きされると見なされなければならないことを意味する。

マシン記述では、ある種類のハードレジスタを「呼びだし時破壊」 (“call-clobbered”) として分類していることに注意。 全ての関数の呼びだし命令は、デフォルトでこれらのレジスタを破壊すると 仮定されているので、この事実を示すために clobber 式を 使う必要はない。 また、各関数呼び出しは、その関数が const と宣言されていない限り、 任意のメモリ位置を変更する可能性があると仮定されている。

parallel の最後の組の式が、それぞれ regmatch_scratch 式 (see section RTL テンプレート)を引数とする clobber 式なら、組合せフェーズは、そうすることでパターンが マッチするようになるなら、適切な clobber 式を、構築されたばかりの insn に追加する。

この機能は、例えば、乗算命令と加算命令は MQ レジスタを使わないが、 アキュムレータへの加算命令が MQ レジスタを上書きするようなマシンで 使うことができる。 一個の組み合わされた命令(? combined instruction)は一時レジスタを 必要とするが、一方、それを構成する命令は一時レジスタを必要としない 場合も同様である。

あるレジスタに対する clobber 式が、他に副作用のある parallel の中に現れた場合は、レジスタ確保部が、そのレジスタがその insn の前後 どちらにおいても占有されることがないことを保証する。 しかし、選ばれた選択肢に対して制約 ‘&’ が指定されていない限り、 再ロードパスが入力の一つとして使われたレジスタを確保する場合がある (see section 制約修飾子文字)。 特定のハードレジスタ、または疑似レジスタ、あるいは scratch 式の どれかを上書きすることができる。後の二つの場合には、 GNU CC は、その時点で一時的に使用可能なハードレジスタを確保する。

一時レジスタを必要とする命令については、疑似レジスタの代わりに scratch を使うべきである。 そうしておくと、組合せフェーズが必要なときに clobber を追加すること を許すからである。 このためには (clobber (match_scratch …)) と 書けば良い。 疑似レジスタを上書きするなら、他のどこにも現れていないものを 使うこと。つまり、そのたびに新しいものを生成して使うこと。 そうしないと、CSE のフェーズが混乱する。

parallel 中で疑似レジスタを上書きすることのもう一つの使い道が ある。insn の入力オペランドの一つがやりその insn により上書きされる 場合である。 この場合、insn 中の clobber の中と、別のところとに同じ疑似レジスタを 使うと期待どおりの結果が得られる。

(use x)

x の値が使われるということを表す。 プログラムのこの時点での x の値が必要であることを示す。 たとえ、なぜ必要であるかがはっきりしていなくてもである。 このため、GCC は、x に値を格納するという 効果しか持たない命令が直前にあっても、その命令を削除しない。 x は、reg 式でなければならない。

再ロードフェーズの間、パターンとして use がある insn は、 reg_equal ノートを保持することが可能である。 このような use insn は、再ロードフェーズが終了する前に 削除される。

遅延分岐スケジューリングのフェーズの間は、x は insn でも良い。 これは、x が以前にコード中のこの場所にあって、そのデータ依存関係 を考慮する必要があるということを意味する。 このような use insn は、遅延分岐スケジューリングのフェーズの 終了前に削除される。

並列に実行される様々な副作用を表す。 大カッコはベクトルを表す。parallel のオペランドは 式を要素とする一個のベクトルである。 x0x1 等は個々の副作用を表す式である。 これらの式のコードは、setcallreturnclobberuse のどれかである。

「並列に」という意味は、最初に個々の副作用で使われる全ての値が 計算され、次に全ての副作用が実行されることを表す。

 
(parallel [(set (reg:SI 1) (mem:SI (reg:SI 1)))
           (set (mem:SI (reg:SI 1)) (reg:SI 1))])

例えば、上の式はハードレジスタ 1 の値と、ハードレジスタ 1 により 指定されるメモリ位置の値を交換するということを曖昧さなしに 表している。 (reg:SI 1) がメモリアドレスとして現れているところでは、どちらも、 insn を実行するのレジスタ 1 の値を参照している。

このため、parallel を使ったときに、ある一つの set の 結果が次の set で使えると考えるのは間違いという ことになる。 例えば、人は良く、条件が 0 なら分岐する命令を以下のように表そうとする。

 
(parallel [(set (cc0) (reg:SI 34))
           (set (pc) (if_then_else
                        (eq (cc0) (const_int 0))
                        (label_ref …)
                        (pc)))])

しかし、この書き方は間違いである。何故なら、これだと分岐条件は この命令の前の条件コードの値に依存しており、 この命令により設定される新しい値を使っていないからである。

のぞき穴最適化は、最終フェーズのアセンブリコード出力と一緒に行なわれる。 この最適化により、一個の parallel からなるパターンの insn を 生成することができる。この場合、parallel の各要素は、 結果となるアセンブラコードを出力するのに必要なオペランド—多くの場合、 reg あるいは mem、定数式である。 これは、他のどのコンパイル過程でも適切な形式の RTL ではないが、 その後には、もはや実行すべき最適化過程が残っていないので、問題ないのである。 ただし、マクロ NOTICE_UPDATE_CC をもし定義するなら、 その定義では、なんらかののぞき穴最適化を定義するなら上記のような insn を取り扱わなければならない。

(sequence [insns …])

ある insn の列を表す。ベクトル中に現れる insns のそれぞれは、 insn の連鎖に現れるのに適したものであるために、insnjump_insncall_insncode_labebarriernote のどれかでなければならない。

sequence RTX は、RTL 生成の間は実際の insn には決して置かれること がない。 この RTX は define_expand から生ずる insn の列を表しており、 それは、insn が emit_insn に渡され、insn の連鎖に挿入 されるまえに行なわれる。 実際に挿入された時点で、個々のサブ insn が分離し、sequence が 捨てられる。

遅延スロットスケジューリングが完了した後、ある insn とその遅延スロットに 置かれている全ての insn は一まとめにされて、一個の sequence に 収められる。遅延スロットを必要とする insn は、ベクトル中の先頭の insn である。その後ろに続く insn は遅延スロットに置かれる。

遅延スロット中の insn には INSN_ANNULLED_BRANCH_P が設定され、 遅延スロット中の insn の効果を条件により無効化するような 分岐 insn を使うべきであることを指示する。

以下の式コードは副作用の代わりに、insn の本体として現れる。 だが、厳密に言えばいつでも副作用を表すわけではない。

(asm_input s)

文字列 s で表されるアセンブラコードそのものを表す。

(unspec [operands …] index)
(unspec_volatile [operands …] index)

operands についてのマシン固有の演算を表す。 index で、複数のマシン固有演算の一つを選び出す。 unspec_volatile を使って、揮発性の演算とトラップを起こす可能性の ある演算を表す。 その他の演算には unspec が使われる。

これらのコードは insn の pattern の内側、 parallel の内側、式の内側に現れる可能性がある。

(addr_vec:m [lr0 lr1 …])

ジャンプ先アドレスのテーブルを表す。 ベクトルの要素 lr0... 等は label_ref 式である。 モード m で各アドレスにどれだけのメモリを与えるかを指定する。 普通は mPmode になる。

(addr_diff_vec:m base [lr0 lr1 …] min max flags)

ジャンプ先アドレスを base からのオフセットで表したテーブルを 表す。ベクトルの要素 lr0... 等は、label_ref 式であり、 baselabel_ref 式である。 モード m で各アドレスの差分にどれだけのメモリを与えるかを 指定する。minmax は分岐の近距離化により設定され、 それぞれ最小と最大のアドレスを持つラベルを保持する。 flags は、それを保持する insn に対する baseminmax と、base に対する minmax の相対的な 位置を表す。詳細については ‘rtl.def’ を参照のこと。


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

This document was generated using texi2html 1.78.