コグノスケ


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

link もっと前
2020年6月4日 >>> 2020年6月4日
link もっと後

2020年6月4日

GCCを調べる - その14-2 - genopinitとexpandとpats[].scode

目次: GCC

以前(2020年5月24日の日記参照)、変数の代入操作をexpandで展開する際、代入を分割するか分割しないか、を決める条件の1つとして、optab_handler() という関数が出てきました。この関数の動作に関わるgenopinitというツールの動作を調べます。

前回(2020年6月3日の日記参照)は分岐条件に関わるenum optab_tagの生成について調べました。今回はもう少し複雑なpats[].scodeの生成について調べます。

pats[].scodeって何だ?

突然pats[].scodeと言われても何だかわからないと思います。私もこんなひどい名前の変数、全く覚えられません。復習も兼ねてoptab_handler() を見直します。

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

// 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;  //★★scodeの意味はここにある通り
  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);  //★★これ
  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 },

...

  { 0x021c1b, CODE_FOR_truncdfsf2 },  //★★この変な数字(scode)0x021c1bは誰が作るのか?

...

コードからscodeの意味、上位16ビットがoptabで、下位16ビットがmachine mode、は理解できると思います。patsにはscodeが無数に並んでいますが、この数字を誰が作るかというとgenopinitです。

optabsとpattern

前に見たoptabsの定義は色々ありますが、必ず2つ以上の引数を取り、1番目がname(mov_optab, trunc_optabなど)、2番目がpatternとなっており、この2つは全ての定義に存在します。

optabsの定義とname, pattern

// gcc/optabs.def

OPTAB_CL(trunc_optab, "trunc$b$a2", TRUNCATE, "trunc", gen_trunc_conv_libfunc)
         '-- name      `-- pattern

このうちnameはenum optab_tagの変数名として使っていました。その他にもcode_to_optab_[] という配列の定義にも使いますが、今はどうでもいいです。前後に文字が付くくらいで、基本的に名前がそのまま使われます。

一方2番目のpattern(例えば "mov$a", "trunc$b$a2" など)はちょっと変わっています。$aや $bという不思議な文字が入ります。

genopinitと *.mdとscode

前回説明したとおりgenopinitの入力はgcc/common.md, gcc/config/riscv/riscv.md, build_gcc/insn-conditions.mdの3つの *.mdファイルです(mdはmachine descriptorの略)。Lispっぽい記法で、define_insnやdefine_expandを追加したファイルです。

基本的なgenopinitの動作は、

  • *.mdを読む
  • optabs.defのpatternとパターンマッチを掛け、pats[].scodeを生成
  • ファイルに出力

パターンマッチのコードはこんな感じです。

genopinitパターンマッチ部分

// gcc/gensupport.h

/* Information about an .md define_* rtx.  */
class md_rtx_info {
public:
  /* The rtx itself.  */
  rtx def;

  /* The location of the first line of the rtx.  */
  file_location loc;

  /* The unique number attached to the rtx.  Currently all define_insns,
     define_expands, define_splits, define_peepholes and define_peephole2s
     share the same insn_code index space.  */
  int index;
};


// gcc/genopinit.c

int
main (int argc, const char **argv)
{

...

  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))  //★★ *.mdを解析
    return (FATAL_EXIT_CODE);

...

  /* Read the machine description.  */
  md_rtx_info info;
  while (read_md_rtx (&info))  //★★RTXを1つずつ取り出す
    switch (GET_CODE (info.def))
      {
      case DEFINE_INSN:    //★★define_insn, define_expandだけ注目
      case DEFINE_EXPAND:
	gen_insn (&info);  //★★これ
	break;


static void
gen_insn (md_rtx_info *info)
{
  optab_pattern p;
  if (find_optab (&p, XSTR (info->def, 0)))  //★★これ
    patterns.safe_push (p);
}


// gcc/gensupport.c

bool
find_optab (optab_pattern *p, const char *name)
{

...

  /* See if NAME matches one of the patterns we have for the optabs
     we know about.  */
  for (unsigned int pindex = 0; pindex < ARRAY_SIZE (optabs); pindex++)  //★★全てのpatternを試す
    {
      p->m1 = p->m2 = 0;
      if (match_pattern (p, name, optabs[pindex].pattern))  //★★これ
	{
	  p->name = name;
	  p->op = optabs[pindex].op;
	  p->sort_num = (p->op << 16) | (p->m2 << 8) | p->m1;  //★★scodeを作る
	  return true;
	}


// gcc/gensupport.h

/* Information about an instruction name that matches an optab pattern.  */
struct optab_pattern
{
  /* The name of the instruction.  */
  const char *name;

  /* The matching optab.  */
  unsigned int op;

  /* The optab modes.  M2 is only significant for conversion optabs;
     it is zero otherwise.  */
  unsigned int m1, m2;

  /* An index that provides a lexicographical sort of (OP, M2, M1).
     Used by genopinit.c.  */
  unsigned int sort_num;
};

申し訳ないですが *.mdファイルの解析関数init_rtx_reader_args_cb(), read_md_rtx() 辺りは調べる予定がありません。どなたか調べてくれたら嬉しいです。

パターンマッチはnameに対して行われ、全てのpatternとマッチするか試します。マッチしなければmatch_pattern() はfalseを返しますから、次のpatternを試します。マッチしたら、結果はoptab_pattern *pに格納され、match_patter() が作り出すp->m1, p->m2という謎の数からscodeが生成されます。

なぜgenopinitではsort_numという変数名にしたんでしょうね?scodeにすればもう少しわかりやすいのに。

パターンマッチm1, m2とscode

パターンマッチのルールと、m1, m2が何者か?については、文章で説明できる気がしないので、例としてname = "truncdfsf2" を見ながら説明したいと思います。マッチするpatternは "trunc$b$a2" です。他のpatternはマッチしません。

genopinitパターンマッチの一例

// gcc/config/riscv/riscv.md

//★★nameの例

(define_insn "truncdfsf2"    ★★名前
  [(set (match_operand:SF     0 "register_operand" "=f")
	(float_truncate:SF
	    (match_operand:DF 1 "register_operand" " f")))]
  "TARGET_DOUBLE_FLOAT"
  "fcvt.s.dt%0,%1"
  [(set_attr "type" "fcvt")
   (set_attr "mode" "SF")])


// gcc/optab.def

//★★ マッチするpatternの定義部分(2番目の引数)

OPTAB_CL(trunc_optab, "trunc$b$a2", TRUNCATE, "trunc", gen_trunc_conv_libfunc)


// gcc/gensupport.c

//★★
//pは結果格納用の変数
//name = "truncdfsf2"
//pat  = "trunc$b$a2"

static bool
match_pattern (optab_pattern *p, const char *name, const char *pat)
{

...
パターンマッチしたときの各変数のダンプ
★★match_pattern() がtrueを返した後、find_optab() がreturn trueで終了する直前でダンプ

(gdb) p optabs[pindex]

$13 = {
  name = 0x4574b5 "trunc_optab",
  pattern = 0x4574c1 "trunc$b$a2",
  base = 0x4574cc ""trunc"",
  suffix = 0x457478 "'\0'",
  libcall = 0x4574d4 "gen_trunc_conv_libfunc",
  op = 2,
  fcode = TRUNCATE,
  rcode = UNKNOWN,
  kind = 1
}


(gdb) p/x *p

$15 = {
  name = 0x4dd270,
  op = 0x2,
  m1 = 0x1b,
  m2 = 0x1c,
  sort_num = 0x21c1b
}

pは結果格納用のoptab_pattern *p

この定義のpatternは2番目の引数 "trunc$b$a2" です。全部繋がっていてわかりにくいですが、下記の4つの要素から構成されます。

  • trunc
  • $b: パターンマッチ その2
  • $a: パターンマッチ その1
  • 2

このうち $aや $bはmachine modeの名前にマッチします。具体的には下記のようになります。

$a, $bパターンマッチの仕組み
nameとpatが

  name = "truncdfsf2"
  pat  = "trunc$b$a2"

の場合、

  $b -> df
  $a -> sf

にマッチします。$a, $bのマッチを調べるときはmode_nameを先頭から検索(大文字小文字の違いは無視)します。
全モードの名前はmode_name[] という配列に入っています。

  $bはmode_name[28] = "DF" と一致する。
  $aはmode_name[27] = "SF" と一致する。

match_pattern() は一致したモードの番号をm1, m2に格納します。
つまりoptab_patternのm1, m2の意味はそれぞれ

  m1: $aがマッチしたモードのインデックス、今回だと27 = 0x1b (E_SFmode)
  m2: $bがマッチしたモードのインデックス、今回だと28 = 0x1c (E_DFmode)

結果scodeはこうなります。

  p->sort_num = (p->op << 16) | (p->m2 << 8) | p->m1;
              = (2 << 16) | (28 << 8) | 27;
              = 0x21c1b

// build_gcc/insn-opinit.c

static const struct optab_pat pats[NUM_OPTAB_PATTERNS] = {
...
  { 0x021c1b, CODE_FOR_truncdfsf2 },  //★★この値が生成された

謎のpats[].scodeはこんな仕組みで生成されていたのでした。ややこしいですね。

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

コメント一覧

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



link もっと前
2020年6月4日 >>> 2020年6月4日
link もっと後

管理用メニュー

link 記事を新規作成

<2020>
<<<06>>>
-123456
78910111213
14151617181920
21222324252627
282930----

最近のコメント5件

  • link 20年6月19日
    すずきさん (04/06 22:54)
    「ディレクトリを予め作成しておけば良いです...」
  • link 20年6月19日
    斎藤さん (04/06 16:25)
    「「Preferencesというメニューか...」
  • 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の...」

最近の記事3件

  • link 24年4月17日
    すずき (04/18 22:44)
    「[VSCodeとMarkdownとPlantUMLのローカルサーバー] 目次: LinuxVSCodeのPlantUML Ex...」
  • link 23年4月10日
    すずき (04/18 22:30)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linuxのstruct pageって何?Linu...」
  • link 20年2月22日
    すずき (04/17 02:22)
    「[Zephyr - まとめリンク] 目次: Zephyr導入、ブート周りHello! Zephyr OS!!Hello! Ze...」
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

最終更新: 04/18 22:44