[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
機種によっては、RTL 生成用の標準のパターン名を一個の insn では
扱えないが、RTL insn の列なら表現できるという場合がある。
そのようなターゲット機種の場合は、define_expand
を
書くことでその RTL 列を生成する方法を指定することができる。
define_expand
は、ある一個の RTL 式であり、
ほぼ define_insn
と同じである。
しかし、define_expand
は、define_insn
と違って、
RTL 生成にのみ使われ、一個以上の RTL insn を生成することができる。
define_expand
RTX にはオペランドが4つある。
define_expand
には名前がなければならない。
何故なら、名前で参照するのが唯一の使い方だからである。
define_peephole
の
RTL テンプレートと同じであり、それぞれが一個の insn である
RTL 式のベクトルになっている。
define_insn
の条件と同じである。
このため、条件がもしあるなら、一致する insn 中のデータに
依存してはならない。ターゲット機種タイプのフラグにしか依存しては
ならないのである。GNU CC は、初期化を行なっている際にこれらの
条件をテストする必要がある。これは、コンパイラの一回実行する毎に、
名前付き命令で利用可能なものはどれかを正確に知る必要があるからである。
普通これらの文は、RTL テンプレート中の内部的なオペランドとして
使われる一時レジスタを用意するが、emit_insn
等のルーチンを
呼び出すことで RTL insn を直接生成することもできる。
そうやって生成した insn は、RTL テンプレートから来る insn の前に
置かなければならない。
define_expand
により生成されるRTL insn はどれも、マシン記述中の
define_insn
のどれかにマッチしなければならない。
どれにもマッチしない場合、その insn 用のコードを生成しようとしたり、
最適化しようとしたときに異常終了してしまう。
RTL テンプレートは、RTL insn の生成を制御することに加えて、 このパターンが使われるときに指定する必要があるオペランドをも 記述する。 特に、各オペランドに述語を与える。
真のオペランドは、パターンから RTL を生成するために指定する必要があり、
RTL テンプレートの先頭に現れる match_operand
で記述する
必要がある。これにより、オペランドの述語についての情報が、その手の情報を
記録する表に入力される。GNU CC はこの情報を使って、正しい RTL コードとして
必要なら、オペランドをレジスタに事前ロードする。
このオペランドが複数回参照されているなら、それ以降で参照するときは
match_dup
を使う必要がある。
RTL テンプレートは、内部「オペランド」も参照する。
内部オペランドとは、一時レジスタやラベルであり、define_expand
で
作られる列の中でしか使われない。内部オペランドは、match_dup
に
より RTL テンプレートに代入される。match_operand
で代入される
ことはない。内部オペランドの値は、GNU CC がこのパターンを使う必要が
あるときに引数として渡されることはない。代わりに、
準備文により、パターン内で計算が行なわれる。準備文は、
値を計算し、operands
の適切な要素に格納するので、
match_dup
がそれを見つけられるようになる。
準備文の中で使う特別なマクロが二つ定義されている。
DONE
と FAIL
である。これらは後ろにセミコロンを付けて
文として使うこと。
DONE
マクロ DONE
を使って、このパターンに対する RTL 生成を終了させる。
この時にパターンから作られる RTL insn は、準備文の中で emit_insn
を明示的に呼び出すことによって既に出力済みのものだけになる。
RTL テンプレートは生成されない。
FAIL
パターンに対する照合を失敗させる。あるパターンが照合に失敗すると、 そのパターンが利用できないことを意味する。 GNU CC の呼びだし側ルーチンは、他のパターンを使って、コード生成の 別の戦略を試す。
失敗は、現在、二項演算(加算、乗算、シフト等)とビットフィールド演算
(extv
、extzv
、insv
)でのみ使える。
次の例は、SPUR チップの左シフトの定義である。
(define_expand "ashlsi3" [(set (match_operand:SI 0 "register_operand" "") (ashift:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")))] "" " |
{ if (GET_CODE (operands[2]) != CONST_INT || (unsigned) INTVAL (operands[2]) > 3) FAIL; }") |
この例では、define_expand
を使って、
シフト量がサポート範囲の 0 から 3 の間に収まっていればシフトを行なう
RTL insn を生成することができるようにしているが、機械命令が使えない
ようなそれ以外の場合には失敗するようにしている。
失敗した場合には、別のパターン(例えば、ライブラリ呼びだしなど)を使った
別の戦略を試みる。
GNU CC が、名前付きパターンの自明でない条件文字列を扱うことが
出来たなら、define_insn
をその場合に使うことができる。
以下に、define_expand
の威力をさらに利用する別の例(68000 での
ゼロ拡張である)を示す。
(define_expand "zero_extendhisi2" [(set (match_operand:SI 0 "general_operand" "") (const_int 0)) (set (strict_low_part (subreg:HI (match_dup 0) 0)) (match_operand:HI 1 "general_operand" ""))] "" "operands[1] = make_safe_from (operands[1], operands[0]);") |
ここでは二つの RTL insn が生成される。一つは出力オペランド全体を
クリアし、もう一つは入力オペランドを出力オペランドの下位半分に
コピーする。この insn 列は、入力オペランドが出力オペランド(の
古い値)を参照している場合は、正しくなくなる。
そのため、準備文でそうならないことを保証している。
関数 make_safe_from
は、operands[1]
が
operands[0]
を参照していれば、operands[1]
を
一時レジスタにコピーする。そのコピーは、もう一つの RTL insn を
生成することにより行なう。
最後に、三番目の例で内部オペランドの使い方を説明する。
SPUR チップのゼロ拡張は、結果を半語のマスクとの and
を取ることで
行なわれる。
だが、このマスクは、定数値としては、この機種で正しいものになるには大きすぎて、
const_int
では表現できない。このため、force_reg
で
レジスタにコピーして、そのレジスタを and
の中で使わなければ
ならない。
(define_expand "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "") (and:SI (subreg:SI (match_operand:HI 1 "register_operand" "") 0) (match_dup 2)))] "" "operands[2] = force_reg (SImode, GEN_INT (65535)); ") |
注意: define_expand
を標準的な二項演算や単項演算、
あるいはビットフィールド演算に使うときは、それが最後に生成する insn は、
code_label
や barrier
や note
であってはならない。
insn
か jump_insn
か call_insn
でなくてはならない。
最後に実際の insn を置く必要がなければ、演算の結果を自分自身にコピーする
insn を生成する。そういう insn はコードはなんら生成しないが、
GCC に問題が起きるのを回避する。
This document was generated
using texi2html 1.78.