コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2020年5月24日 >>> 2020年5月24日
link もっと後

2020年5月24日

GCCを調べる - その11-3 - エラーを引き起こすRTLを回避する(define_expand追加編)

目次: GCC

前回(2020年5月23日の日記参照)の続きです。エラーの原因となる代入操作RTLはemit_move_multi_word() で出力されており、条件の分岐点であるemit_move_insn_1() が怪しそうです。分岐条件を司るoptab_handler() を追います。

分岐条件の1つoptab_handler()

// build_gcc/insn-opinit.h

enum optab_tag {
  unknown_optab,
  sext_optab,

...

  mov_optab,

...


// gcc/optabs-query.h

/* Return the insn used to implement mode MODE of OP, or CODE_FOR_nothing
   if the target does not have such an insn.  */

inline enum insn_code
optab_handler (optab op, machine_mode mode)
{
  unsigned scode = (op << 16) | mode;  //★★もしブレーク掛けたければop = mov_optab, mode = E_V64SImodeで引っ掛けられる
  gcc_assert (op > LAST_CONV_OPTAB);
  return raw_optab_handler (scode);  //★★これ
}


// build_gcc/insn-opinit.c

enum insn_code
raw_optab_handler (unsigned scode)
{
  int i = lookup_handler (scode);  //★★これが -1だとCODE_FOR_nothingが返る
  return (i >= 0 && this_fn_optabs->pat_enable[i]
          ? pats[i].icode : CODE_FOR_nothing);
}

static int
lookup_handler (unsigned scode)
{
  int l = 0, h = ARRAY_SIZE (pats), m;
  while (h > l)
    {
      m = (h + l) / 2;
      if (scode == pats[m].scode)
        return m;
      else if (scode < pats[m].scode)
        h = m;
      else
        l = m + 1;
    }
  return -1;
}


//★★pats[] の定義

struct optab_pat {
  unsigned scode;
  enum insn_code icode;
};

static const struct optab_pat pats[NUM_OPTAB_PATTERNS] = {
  { 0x010405, CODE_FOR_extendqihi2 },
  { 0x010406, CODE_FOR_extendqisi2 },
  { 0x010407, CODE_FOR_extendqidi2 },
  { 0x010505, CODE_FOR_extendhihi2 },

...


//★★this_fn_optabs->pat_enable[] を初期化しているコード

void
init_all_optabs (struct target_optabs *optabs)
{
  bool *ena = optabs->pat_enable;
  ena[0] = HAVE_extendqihi2;
  ena[1] = HAVE_extendqisi2;

...

このlookup_handler() が -1を返すとemit_move_multi_word() を実行します。lookup_handler() はpatsという構造体を2分探索する単純な関数ですから、大事なのはpatsの中身と変更方法です。

ところがpatsをどうやって作るか?については、手がかりがありません。GCCはクソコードすぎて辛い。とりあえずCODE_FOR_ なんとか、という部分と、ena[] = HAVE_ の部分は自動生成コードっぽいですから、適当にキーワードを考えてgrepします。

patsの作り方を捜索
#### ChangeLogが良く引っかかってうざいので排除推奨

$ grep -r CODE_FOR_%s | grep -v ^ChangeLog

genopinit.c:    fprintf (s_file, "  { %#08x, CODE_FOR_%s },\n", p->sort_num, p->name);
gencodes.c:     printf (",\n   CODE_FOR_%s = CODE_FOR_nothing", name);
gencodes.c:     printf (",\n  CODE_FOR_%s = %d", name, info->index);
genemit.c:      printf ("    return CODE_FOR_%s;\n", instance->name);
gentarget-def.c:  printf ("#undef TARGET_CODE_FOR_%s\n", upper_name);
gentarget-def.c:  printf ("#define TARGET_CODE_FOR_%s ", upper_name);
gentarget-def.c:    printf ("CODE_FOR_%s\n", name);

$ grep -r HAVE_ | grep 'ena\[' | grep -v ^ChangeLog

genopinit.c:    fprintf (s_file, "  ena[%u] = HAVE_%s;\n", i, p->name);

コードの形と見比べるとgenopinit.cがpatsの作成者で間違いなさそうです。このプログラムはcc1の一部ではなく、補助ツールgenopinitのコードです。ツールの使い方がわからないのでビルドログを見ます。

genopinitの使い方

# build_gcc/Makefile

s-opinit: $(MD_DEPS) build/genopinit$(build_exeext) insn-conditions.md
	$(RUN_GEN) build/genopinit$(build_exeext) $(md_file) \
	  insn-conditions.md -htmp-opinit.h -ctmp-opinit.c


# ビルドログ

build/genopinit ./gcc/gcc/common.md ./gcc/gcc/config/riscv/riscv.md \
  insn-conditions.md -htmp-opinit.h -ctmp-opinit.c

いくつか *.mdファイルを指定するだけです。genopinitのmain() 関数を見ると、2つの条件DEFINE_INSNもしくはDEFINE_EXPANDのときだけ gen_insn() を呼びます。

genopinitのmain関数

// gcc/genopinit.c

int
main (int argc, const char **argv)
{
  FILE *h_file, *s_file;
  unsigned int i, j, n, last_kind[5];
  optab_pattern *p;

  progname = "genopinit";

  if (NUM_OPTABS > 0xffff || MAX_MACHINE_MODE >= 0xff)
    fatal ("genopinit range assumptions invalid");

  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
    return (FATAL_EXIT_CODE);

  h_file = open_outfile (header_file_name);
  s_file = open_outfile (source_file_name);

  /* Read the machine description.  */
  md_rtx_info info;
  while (read_md_rtx (&info))
    switch (GET_CODE (info.def))
      {
      case DEFINE_INSN:
      case DEFINE_EXPAND:
	gen_insn (&info);  //★★これ
	break;

      default:
	break;
      }

  /* Sort the collected patterns.  */
  patterns.qsort (pattern_cmp);

...

この記事を読むような方は、既に何を変更すれば良いかご存知かもしれませんが、せっかくなのでgenopinitの動きを追います。gen_insn() にブレークを掛けて、DEFINE_INSNとDEFINE_EXPANDのときに何が起きるのか見ます。

genopinitの動きを観察(DEFINE_INSN)
$ gdb build_gcc/build/genopinit

(gdb) b gen_insn

Breakpoint 1 at 0x402920: file ./gcc/gcc/genopinit.c, line 45.

(gdb) r common.md config/riscv/riscv.md build_gcc/insn-conditions.md -hhhh -cccc

Breakpoint 1, gen_insn (info=0x7fffffffd970)
    at ./gcc/gcc/genopinit.c:45
45        if (find_optab (&p, XSTR (info->def, 0)))


(gdb) p *info

$3 = {
  def = 0x4bc030,
  loc = {
    filename = 0x7fffffffde93 "config/riscv/riscv.md",  ★★ファイル名
    lineno = 431,  ★★行番号
    colno = 1
  },
  index = 1
}

(gdb) p info->def->code

$5 = DEFINE_INSN
genopinitの動きを観察(DEFINE_INSN)

;; gcc/config/riscv/riscv.md:431

★★*.mdファイルのdefine_insnと紐付いている

(define_insn "add<mode>3"
  [(set (match_operand:ANYF            0 "register_operand" "=f")
	(plus:ANYF (match_operand:ANYF 1 "register_operand" " f")
		   (match_operand:ANYF 2 "register_operand" " f")))]
  "TARGET_HARD_FLOAT"
  "fadd.<fmt>\t%0,%1,%2"
  [(set_attr "type" "fadd")
   (set_attr "mode" "<UNITMODE>")])

残ったのはDEFINE_EXPANDです。DEFINE_INSNでは止まってほしくないので、こういうときは条件付きブレークが便利です。

genopinitの動きを観察(DEFINE_EXPAND)
(gdb) b gen_insn if info->def->code == DEFINE_EXPAND

Note: breakpoint 1 also set at pc 0x402920.
Breakpoint 2 at 0x402920: file ./gcc/gcc/genopinit.c, line 45.

(gdb) c

Breakpoint 2, gen_insn (info=0x7fffffffd970)
    at ./gcc/gcc/genopinit.c:45
45        if (find_optab (&p, XSTR (info->def, 0)))


(gdb) p *info
$6 = {
  def = 0x4c4ef0,
  loc = {
    filename = 0x7fffffffde93 "config/riscv/riscv.md",  ★★ファイル名
    lineno = 635,  ★★行番号
    colno = 1
  },
  index = 352
}
genopinitの動きを観察(DEFINE_EXPAND)

;; gcc/config/riscv/riscv.md:635

★★*.mdファイルのdefine_expandと紐付いている

(define_expand "<u>mulditi3"
  [(set (match_operand:TI                         0 "register_operand")
	(mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
		 (any_extend:TI (match_operand:DI 2 "register_operand"))))]
  "TARGET_MUL && TARGET_64BIT"
{
  rtx low = gen_reg_rtx (DImode);
  emit_insn (gen_muldi3 (low, operands[1], operands[2]));

  rtx high = gen_reg_rtx (DImode);
  emit_insn (gen_<u>muldi3_highpart (high, operands[1], operands[2]));

  emit_move_insn (gen_lowpart (DImode, operands[0]), low);
  emit_move_insn (gen_highpart (DImode, operands[0]), high);
  DONE;
})

つまり *.mdファイルにdefine_expandもしくはdefine_insnを定義すればpatsの中身が増えてlookup_handler() に引っかかるはずです。riscv.mdに既に存在するmovsiやmovdiを真似して追加します。

define_expandを追加

;; config/riscv/riscv.md

(define_expand "movv64si"
  [(set (match_operand:V64SI 0 "")
	(match_operand:V64SI 1 ""))]
  ""
{
  if (riscv_legitimize_move (V64SImode, operands[0], operands[1]))    //★★この呼び出しは正しいかわからないが、とりあえずそのまま
    DONE;
})

実行してみると、先程追加したコードが引っかかってifの条件が成立し、emit_insn() が呼び出されます。念のためにコードを再掲します。

分岐条件の1つoptab_handler()(再掲)

// gcc/expand.c

rtx_insn *
emit_move_insn_1 (rtx x, rtx y)
{
  machine_mode mode = GET_MODE (x);
  enum insn_code code;

  gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);

  code = optab_handler (mov_optab, mode);
  if (code != CODE_FOR_nothing)    //★★CODE_FOR_nothingになるのが怪しい
    return emit_insn (GEN_FCN (code) (x, y));    //★★こっちに行けばいいのだろうか??

...


// build_gcc/insn-opinit.h

/* Given an enum insn_code, access the function to construct
   the body of that kind of insn.  */
#define GEN_FCN(CODE) (insn_data[CODE].genfun)

実行してemit_move_insn_1() でブレークし、gdbで値をダンプすると下記のようになっているはずです。gen_movv64si() 関数が突然出てきますが、これは自動生成された関数です。

define_expand追加後のcode
(gdb) p code

$4 = CODE_FOR_movv64si  ★★nothingからV64SIに変わった


(gdb) p insn_data[code].genfun

$5 = {func = 0x216ecea <gen_movv64si(rtx_def*, rtx_def*)>}


(gdb) p insn_data[code]

$6 = {
  name = 0x2d764ed "movv64si",
  output = {
    single = 0x0,
    multi = 0x0,
    function = 0x0
  },
  genfun = {
    func = 0x216eec5 <gen_movv64si(rtx_def*, rtx_def*)>
  },
  operand = 0x2d73ef0 <operand_data+10512>,
  n_generator_args = 2 '\002',
  n_operands = 2 '\002',
  n_dups = 0 '\000',
  n_alternatives = 0 '\000',
  output_format = 0 '\000'
}
define_expand追加後に生成された関数gen_movv64si()

// build_gcc/insn-emit.c

/* ./gcc/gcc/config/riscv/riscv.md:1308 */
rtx
gen_movv64si (rtx operand0,
	rtx operand1)
{
  rtx_insn *_val = 0;
  start_sequence ();
  {
    rtx operands[2];
    operands[0] = operand0;
    operands[1] = operand1;
#define FAIL return (end_sequence (), _val)
#define DONE return (_val = get_insns (), end_sequence (), _val)
#line 1312 "./gcc/gcc/config/riscv/riscv.md"
{
  if (riscv_legitimize_move (V64SImode, operands[0], operands[1]))    //★★この呼び出しは正しい?
    DONE;
}
#undef DONE
#undef FAIL
    operand0 = operands[0];
    (void) operand0;
    operand1 = operands[1];
    (void) operand1;
  }
  emit_insn (gen_rtx_SET (operand0,
	operand1));
  _val = get_insns ();
  end_sequence ();
  return _val;
}

近くにあったdefine_expand("movdi") をコピーして作ったので、riscv_legitimize_move() を呼んでいますが、この実装が正しいかどうか今はわかりません。動作がおかしいようなら、後で調べたり、直したりする必要があるかもしれません。

現状、生成されたRTLを見た限りsetのdestination側のmachine modeがmem/c:SIからmem/c:V64SIに変わっていますし、問題なさそうに見えます。

define_expand追加後の236r.expand

(insn 11 7 10 2 (set (reg:V64SI 109 [ v1 ])
        (asm_operands/v:V64SI ("vlw.v %0, %1
") ("=&v") 0 [
                (mem/c:SI (plus:SI (reg/f:SI 99 virtual-stack-vars)
                        (const_int -360 [0xfffffffffffffe98])) [1 b+40 S4 A64])
            ]
             [
                (asm_input:SI ("A") b.c:7)
            ]
             [] b.c:7)) "b.c":7:2 -1
     (nil))
(insn 10 11 0 2 (set (mem/c:V64SI (reg/f:SI 108) [1 v1+0 S256 A2048])
        (reg:V64SI 109 [ v1 ])) "b.c":7:2 -1
     (nil))


;;★★(参考)define_expand追加前のRTL

(insn 10 74 11 2 (set (mem/c:SI (reg/f:SI 108) [1 v1+0 S4 A2048])
        (subreg:SI (reg:V64SI 109 [ v1 ]) 0)) "b.c":7:2 -1
     (nil))

しかし残念ながらdefine_expandの追加だけではダメで、変更後はこんなエラーが出ます。

define_expand追加後のエラー
b.c: In function '_start':
b.c:25:1: error: unrecognizable insn:
   25 | }
      | ^
(insn 10 11 0 2 (set (mem/c:V64SI (reg/f:SI 108) [1 v1+0 S256 A2048])
        (reg:V64SI 109 [ v1 ])) "b.c":7:2 -1
     (nil))
during RTL pass: vregs
dump file: b.c.237r.vregs
b.c:25:1: internal compiler error: in extract_insn, at recog.c:2294

続きはまた次回。

編集者:すずき(2023/09/24 11:49)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2020年5月24日 >>> 2020年5月24日
link もっと後

管理用メニュー

link 記事を新規作成

<2020>
<<<05>>>
-----12
3456789
10111213141516
17181920212223
24252627282930
31------

最近のコメント5件

  • link 21年3月13日
    すずきさん (03/05 15:13)
    「あー、このプログラムがまずいんですね。ご...」
  • link 21年3月13日
    emkさん (03/05 12:44)
    「キャストでvolatileを外してアクセ...」
  • link 24年1月24日
    すずきさん (02/19 18:37)
    「簡単にできる方法はPowerShellの...」
  • link 24年1月24日
    KKKさん (02/19 02:30)
    「追伸です。\nネットで調べたらマイクロソ...」
  • link 24年1月24日
    KKKさん (02/19 02:25)
    「私もエラーで困ってます\n手動での回復パ...」

最近の記事3件

  • link 24年3月25日
    すずき (03/26 03:20)
    「[Might and Magic Book One TASのその後] 目次: Might and Magicファミコン版以前(...」
  • link 21年10月4日
    すずき (03/26 03:14)
    「[Might and Magicファミコン版 - まとめリンク] 目次: Might and Magicファミコン版TASに挑...」
  • link 24年3月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/26 03:20