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

16.14 命令の分割方法を定義する

あるパターンを複数の insn に分割する方法を指定しなければならない場合が 二つある。遅延スロット(see section 遅延スロットの定義)を必要とする命令や 出力が複数サイクルの間利用できない命令(see section insn スケジューリングに必要な情報の指定)を 持つ機種では、これらのケースを最適化するコンパイラフェーズでは、 insn を一つの命令の遅延スロットに移動できる必要がある。しかし、 insn の中には複数の機械命令を生成するものもある。そういう insn は 遅延スロットに置くことはできない。

一個の insn を個々の insn のリストに書き直せることが多い。 この場合、個々の insn はそれぞれ一個の機械命令に対応する。 こうすると不利な点は、コンパイルが遅くなり、必要とするメモリスペースが 多くなることである。書き換え後の insn が複雑過ぎると、そのために いくつかの最適化を妨げることにもなる。命令スケジューリングまたは 遅延スロットスケジューリングが改善されると信じる根拠がある場合は、 コンパイラは insn を分割する。

insn の組合せフェーズでは、交換可能な insn の分割も行なう。 三つの insn が複雑な式の一つの insn に統合されたものの、 なんらかの define_insn パターンにマッチすることが 不可能になった場合、組合せフェーズは複雑なパターンを認識できる 二つの insn に分割しようとする。普通は、複雑なパターンを いくつかの部分式に分割することで二つのパターンに分ける。 だが、場合によっては、RISC 機種で、ある大きな定数の加算を二つの insn で 行なうような場合、その加算を二つの insn に分割する方法は 機種依存である。

define_split の定義で、GNU CC に対し、複雑な insn をいくつかの もっと単純な insn に分割する方法を指定する。

 
(define_split
  [insn-pattern]
  "condition"
  [new-insn-pattern-1
   new-insn-pattern-2
   …]
  "preparation statements")

insn-pattern は、分割する必要のあるパターンであり、 condtiondefine_insn の場合と同様、 最後にテストされる条件である。insn-pattern にマッチし、 condition を満たす insn が見つかると、 その insn は、insn のリストの中で、new-insn-pattern-1new-insn-pattern-2 等で与えられるinsn に置き換えられる。

preparation statements は、define_expand ( see section コード生成のための RTL 列の定義)で指定される文と同様のもので、 生成されるコードの準備をしたり、パターンが固定していない insn を いくつか生成したりするために、新しい RTL が生成される前に実行される。 ただし、define_expand にあるものと違って、これらの文は 疑似レジスタを新たに生成してはならない。 また、再ロードが一度完了したら、スタックフレームからスペースを 割り当ててはならない。

パターンが insn-pattern とマッチするのには、二つの異なる状況がある。 ある insn が遅延スロットスケジューリングや命令スケジューリング向けに 分割する必要がある場合は、その insn は有効であることが既に知られている。 この事は、何かの define_insn にマッチしていなければならず、 reload_completed がゼロでなければ、その define_insn の 制約を満たすことが知られているということを意味する。その場合、 新しい insn パターンも何かの define_insn にマッチしなければならず、 reload_completed がゼロでなければ、その定義の制約を満たさなければ ならない。

この define_split の使い方の例として、‘a29k.md’ から 取った以下の例を考えてみる。この例では、HImode から SImode への sign_extend を二つのシフト insn の対に分割している。

 
(define_split
  [(set (match_operand:SI 0 "gen_reg_operand" "")
        (sign_extend:SI (match_operand:HI 1 "gen_reg_operand" "")))]
  ""
  [(set (match_dup 0)
        (ashift:SI (match_dup 1)
                   (const_int 16)))
   (set (match_dup 0)
        (ashiftrt:SI (match_dup 0)
                     (const_int 16)))]
  "
{ operands[1] = gen_lowpart (SImode, operands[1]); }")

命令組合せ過程が insn パターンを分割しようとするのは、いつでも、 そのパターンがどの define_insn にもマッチしない場合 である。組合せ過程は、最初に一個の set 式を分割しようとし、 次に parallel の中にあるが、スクラッチレジスタとして使うための 疑似レジスタの clobber が後ろに続くような、同じ set 式を 分割する。 これらの場合、組合せ過程は、新しい insn パターンがちょうど二つ 生成されることを想定する。組合せ過程がこれらのパターンが なんらかの define_insn による定義にマッチするかどうかを 検査するので、読者が自分で define_split の中でこの検査を 行なう必要はない(当たり前だが、マッチする insn を決して 生成しないように define_split を書くのは意味がない)。

以下に define_split のこの使い方の例を示す。‘rs6000.md’ から取ったものである。

 
(define_split
  [(set (match_operand:SI 0 "gen_reg_operand" "")
        (plus:SI (match_operand:SI 1 "gen_reg_operand" "")
                 (match_operand:SI 2 "non_add_cint_operand" "")))]
  ""
  [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 3)))
   (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 4)))]
"
{
  int low = INTVAL (operands[2]) & 0xffff;
  int high = (unsigned) INTVAL (operands[2]) >> 16;

  if (low & 0x8000)
    high++, low |= 0xffff0000;

  operands[3] = GEN_INT (high << 16);
  operands[4] = GEN_INT (low);
}")

ここで述語 non_add_cint_operand は、一個の加算 insn の有効な オペランドではない const_int にマッチする。 小さな変位付きの加算は、次の演算のアドレスに代入できるように 書かれる。

同じファイルから、スクラッチレジスタの使い方の例を見てみる。 この例では、レジスタと大きな定数の等値性比較を生成している。

 
(define_split
  [(set (match_operand:CC 0 "cc_reg_operand" "")
        (compare:CC (match_operand:SI 1 "gen_reg_operand" "")
                    (match_operand:SI 2 "non_short_cint_operand" "")))
   (clobber (match_operand:SI 3 "gen_reg_operand" ""))]
  "find_single_use (operands[0], insn, 0)
   && (GET_CODE (*find_single_use (operands[0], insn, 0)) == EQ
       || GET_CODE (*find_single_use (operands[0], insn, 0)) == NE)"
  [(set (match_dup 3) (xor:SI (match_dup 1) (match_dup 4)))
   (set (match_dup 0) (compare:CC (match_dup 3) (match_dup 5)))]
  "
{
  /* Get the constant we are comparing against, C, and see what it
     looks like sign-extended to 16 bits.  Then see what constant
     could be XOR'ed with C to get the sign-extended value.  */

  int c = INTVAL (operands[2]);
  int sextc = (c << 16) >> 16;
  int xorv = c ^ sextc;

  operands[4] = GEN_INT (xorv);
  operands[5] = GEN_INT (sextc);
}")

混乱を避けるために、一個の define_split を、ある define_insn にマッチする insn とマッチしない insn を同じように受け付けるように 書いてはならない。代わりに、二つの define_split を 別々に書いて、一つを有効な insn 用とし他方を有効でない insn 用とすること。


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

This document was generated using texi2html 1.78.