link もっと前
   2020年 6月 6日 -
      2020年 5月 28日  
link もっと後

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

日々

link permalink

link 編集する

Kindle Fire の本が消える病

割と昔からですが Kindle Fire がストアで買った本を認識しない(ダウンロードしない、一覧に出てこない)病気が頻発し、買ったはずの本が消えて、非常に困っています。

Kindle Fire がロストした本は、Kindle for PC だとあっさり捕捉できますから(「新しい商品」順に並べると確認しやすい)、Kindle Fire のバグだと思われます。

根本対策は不明ですが、対症療法はいくつかあります。お手軽な順に、

  • 本の一覧を表示、フィルター「すべて」「ダウンロード済み」を何度か切り替える(3割くらい成功)
  • 対象の本のタイトルを検索ウインドウに入れて検索する(8割くらい成功)
  • Kindle ストアで対象の本を探し「ダウンロード」ボタンを押す(9割 9分成功)
  • ファクトリリセット(これでだめなら Kindle Fire のバグ)

Kindle ストアも割と曲者で、複数のダウンロードボタンがある変な仕様2018年 11月 17日の日記参照)です。1回押してダメでも諦めず、他の種類のダウンロードボタンも押してください。どれかが成功すればラッキーです。

最終手段ファクトリリセットには 1度だけお世話になりました。しかし再設定、再ダウンロードにかなり時間を浪費して辛いので、できればもう二度とやりたくありません。

Kindle Fire の困ったところ

Kindle Fire はちょっと変な挙動が多いです。仕様かバグか良くわからないものもあります。

  • 本の表示がずれて読めなくなる(再起動で直る)
  • 本がダウンロードできない病が頻発する(上述)
  • 本の一覧表示が遅い(アップデートで改悪したり、改善したり)
  • 一覧表示の分類がショボい、本が探せない
  • フィルタの「未読」「既読」の数が間違ってる

Kindle のメインであるはずの本の機能さえ、この有様なので、本以外(アプリ、ミュージック、ムービーなど)の機能は一体どうなっていることやら??

メモ: 技術系の話は Facebook から転記しておくことにした。大幅に追記。

[編集者: すずき]
[更新: 2020年 6月 6日 18:42]

コメント一覧

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



link permalink

link 編集する

FSF Copyright Assignment

FSF (Free Software Foundation) の Copyright Assignment にサインしてみました。とりあえず GCC と binutils です。ツールチェーン仲間としては glibc もやっておけば良かったかも??まあいいか。

手続きは特に難しくなく、

  • assign@gnu.org に特定のテンプレート(※)でメールを送る
  • 契約書が添付された PDF が返ってくる
  • 印刷してサイン+日付を書いた後、スキャンして PDF を送る
  • FSF のサイン+日付が入った PDF が送られてくる

これで完了です。


送られてくる契約書の先頭部分

FSF からの返事はそれぞれ数日〜1週間程度、という感じでした。GNU のソフトウェアにパッチを送りたいときは、個人でこの契約を交わす(今回はこっち)か、会社の業務で作成している場合は、会社でこの契約を交わす(こっちのやり方は知らない)必要があります。

FSF がなぜこんな面倒なことをしているのかについては、なぜ FSF は貢献者に著作権の譲渡をお願いしているのか - GNU プロジェクトに書いてあります。過去に訴えられたり何か嫌なことがあったんでしょうね。

テンプレートは Copyright Papers (Information for Maintainers of GNU Software) を見ると GNU のメンテナーに要求してくれと書いてあります。gnulib のリポジトリにも入っていて、私はこのファイルを送ったら OK でした(gnulib の git リポジトリにある request-assign.future

Copyright Assignment を読んでみて感じたこと

FSF の Copyright Assignment にはいろいろ書いてありますが、

契約者の書いたパッチが契約者のものだと保証せよ
これは契約者を雇っている会社が FSF に著作権を主張してくることを防ごうとしている。
業務上作成したパッチを投稿しなければ満たせる。
FSF の敵に対して協力せよ
契約者が上記の項に反した(会社のコードを投稿したなど)ときに要求されます。
FSF が裁判で勝てるような証拠集めに協力せよ。
FSF が万が一、著作権関係の訴えで負けたら「FSF の被った損害を補償せよ」

読んでいると割と怖い内容ですが、会社で書いたコードを投稿しなければ問題はないです。

契約の範囲はソフトウェア毎(GCC, binutils, glibc, などなど)に契約する必要があります。契約の種類は「1回限り(request-assign.changes)」と「今後ずっと(request-assign.future)」があります。ソフトウェアを改善し続ける場合、毎回契約するのは面倒なので「今後ずっと」を選択すると思います。

契約の種類によって FSF に送るメールのテンプレートが異なります。詳細は Copyright Papers (Information for Maintainers of GNU Software) に書いてあります。和訳ないのかなあ、これ。

[編集者: すずき]
[更新: 2020年 6月 6日 16:02]

コメント一覧

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



link permalink

link 編集する

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 はこんな仕組みで生成されていたのでした。ややこしいですね。

[編集者: すずき]
[更新: 2020年 6月 4日 12:18]

コメント一覧

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



link permalink

link 編集する

GCC を調べる - その 14-1 - genopinit と expand と enum optab_tag

目次: GCC を調べる - まとめリンク

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

バイナリは build_gcc/build/genopinit に生成されます(以降 build_gcc は GCC のビルドディレクトリを指すものとします)。実行する際の引数は下記の通りです。

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

上記のビルドログでは一時ファイルに出力していますが、最終的には build_gcc/insn-opinit.h, build_gcc/insn-opinit.c の 2つのファイルを生成します。

genopinit と enum optab_tag の生成

前回は optab_handler() にブレークポイントを設定するため、enum optab_tag の値を使いました。この値は genopinit が生成しています。

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;  //★★以前は op = mov_optab, mode = E_V64SImode でブレークを掛けた
  gcc_assert (op > LAST_CONV_OPTAB);
  return raw_optab_handler (scode);  //★★次回以降、調べます
}


// build_gcc/insn-opinit.h

enum optab_tag {
  unknown_optab,
  sext_optab,

...

  mov_optab,  //★★この値のこと

...

};

...

typedef enum optab_tag optab;

これらの値は gensupport.c の optab_def から生成されています。実際に optab_def の中身を定義するのは optabs.def ファイルです。この *.def ファイルは、C 言語で素直に書くと重複、冗長になる情報を簡潔に表すための、いわゆる DSL(Domain Specific Language)だと思われます。

DSL っぽいものを使うとき、下記のような邪悪な include の使い方をします。GCC 凶悪デザインパターンの 1つですね。良い子は真似してはいけません。

optab_handler(再掲)

// gcc/gensupport.c

#define OPTAB_DC(o, p, c)          { #o, p, NS, ZS, NS, o, c, c, 4 },
#define OPTAB_D(o, p)  { #o, p, NS, ZS, NS, o, UNKNOWN, UNKNOWN, 4 },

...

/* An array of all optabs.  Note that the same optab can appear more
   than once, with a different pattern.  */
optab_def optabs[] = {
  { "unknown_optab", NULL, NS, ZS, NS, unknown_optab, UNKNOWN, UNKNOWN, 0 },
#include "optabs.def"  //★★上記のように optabs.def 内で使われるマクロを、都合の良い定義に変えてから include
};


// gcc/optabs.def

OPTAB_DC(mov_optab, "mov$a", SET)  //★★マクロの定義は include される場所によって違うので、展開後の結果は場所による
OPTAB_DC(movstrict_optab, "movstrict$a", STRICT_LOW_PART)
OPTAB_D (movmisalign_optab, "movmisalign$a")

...


// gcc/genopinit.c

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

...

  /* Emit the optab enumeration for the header file.  */
  fprintf (h_file, "enum optab_tag {\n");
  for (i = j = 0; i < n; ++i)
    {
      optabs[i].op = i;
      fprintf (h_file, "  %s,\n", optabs[i].name);  //★★optabs の名前を出力
      if (optabs[i].kind != j)
	last_kind[j++] = i - 1;
    }
  fprintf (h_file, "  FIRST_CONV_OPTAB = %s,\n", optabs[last_kind[0]+1].name);

...

せっかく DSL っぽいものがあるのに、genopinit のようにコード自動生成ツールも混ぜて使うのはどうしてなんでしょう?GCC は理解不能です。

[編集者: すずき]
[更新: 2020年 6月 3日 23:12]

コメント一覧

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



link permalink

link 編集する

GCC を調べる - まとめリンク

日記が増えすぎて、一覧が欲しくなってきたので作りました。

今後、日記が増えたら追加します。

[編集者: すずき]
[更新: 2020年 6月 3日 23:11]

コメント一覧

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



link permalink

link 編集する

GCC を調べる - その 13-4 - ベクトル命令のオフセット付きアドレスだけを禁止

目次: GCC を調べる - まとめリンク

前回(2020年 5月 31日の日記参照)見たとおり、メモリというかオフセット付きアドレスを全部禁止するのは明らかにやりすぎで、他の命令に悪影響を及ぼしていました。スカラ命令はオフセット付きアドレスを許可し、ベクトル命令だけオフセット付きアドレスを禁止したいところです。

オペランドを許可する、しないを判断するコードを改造すればできるでしょうか?コードを見てみます。

constraint "m" の定義(再掲)

// gcc/common.md

(define_memory_constraint "TARGET_MEM_CONSTRAINT"
  "Matches any valid memory."
  (and (match_code "mem")
       (match_test "memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0),
						 MEM_ADDR_SPACE (op))")))
constraint "m" の条件判定コード

// gcc/recog.c

//★★各引数の値
//mode = E_V64SImode
//addr = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//as = 0

int
memory_address_addr_space_p (machine_mode mode ATTRIBUTE_UNUSED,
			     rtx addr, addr_space_t as)
{
#ifdef GO_IF_LEGITIMATE_ADDRESS
...
#else
  return targetm.addr_space.legitimate_address_p (mode, addr, 0, as);
#endif
}


// gcc/targhook.c

//★★各引数の値
//mode = E_V64SImode
//mem  = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//strict = 0
//as = 0

bool
default_addr_space_legitimate_address_p (machine_mode mode, rtx mem,
					 bool strict,
					 addr_space_t as ATTRIBUTE_UNUSED)
{
  return targetm.legitimate_address_p (mode, mem, strict);
}


// gcc/config/riscv/riscv.c

#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P	riscv_legitimate_address_p


// gcc/config/riscv/riscv.c

//★★各引数の値
//mode = E_V64SImode
//mem  = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//strict_p = 0

static bool
riscv_legitimate_address_p (machine_mode mode, rtx x, bool strict_p)
{
  struct riscv_address_info addr;

  return riscv_classify_address (&addr, x, mode, strict_p);
}


// gcc/config/riscv/riscv.c

//★★各引数の値
//x    = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//mode = E_V64SImode
//strict_p = 0

static bool
riscv_classify_address (struct riscv_address_info *info, rtx x,
			machine_mode mode, bool strict_p)
{
  switch (GET_CODE (x))
    {

...

    case PLUS:
      info->type = ADDRESS_REG;
      info->reg = XEXP (x, 0);
      info->offset = XEXP (x, 1);

      //★★各変数の値
      //info->reg    = x->u.fld[0].rt_rtx = (reg/f:SI 108)
      //info->offset = x->u.fld[1].rt_rtx = (const_int 256 [0x100])

      return (riscv_valid_base_register_p (info->reg, mode, strict_p)
	      && riscv_valid_offset_p (info->offset, mode));

...

    }
}

関数 memory_address_addr_space_p() の addr を見ると、メモリアドレスを表す RTL しか渡されません。この情報だけではスカラ命令のオペランドか、ベクトル命令のオペランドか、判断するのは困難です。

ベクトル命令は machine mode が V64SI であることを利用するとうまくいくかもしれません。関数 riscv_classify_address() を変更し、mode == V64SI だったら PLUS など REG 以外を使った RTL に対し false を返せば良さそうです。

mode == V64SI のとき false を返す

// gcc/config/riscv/riscv.c

//★★各引数の値
//x    = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//mode = E_V64SImode
//strict_p = 0

static bool
riscv_classify_address (struct riscv_address_info *info, rtx x,
			machine_mode mode, bool strict_p)
{
  switch (GET_CODE (x))
    {

...

    case PLUS:
      if (mode == E_V64SImode)  //★★ machine mode が V64SI ならオフセット付きアドレスは許可しない
        return false;

      info->type = ADDRESS_REG;
      info->reg = XEXP (x, 0);
      info->offset = XEXP (x, 1);

      return (riscv_valid_base_register_p (info->reg, mode, strict_p)
	      && riscv_valid_offset_p (info->offset, mode));

...

    }
}
mode == V64SI のとき false を返す変更を加えたとき、アセンブラの差分

$ diff -u b_before.s b_mod.s

--- b_before.s  2020-05-28 21:17:24.607184754 +0900
+++ b_mod.s     2020-05-30 22:37:02.370663628 +0900
@@ -35,7 +35,8 @@
 
 # 0 "" 2
  #NO_APP
-       vsw.v   v0,256(a5)
+       addi    a4,a5,256
+       vsw.v   v0,0(a4)
        .loc 1 9 2
        addi    a4,s0,-336
  #APP

結果だけ見ると良さそうですが、将来的にオフセットアドレスが使えるベクトル命令が出てきたときに、判別不能になり困ります。その場しのぎ感が否めません。もっと良い方法はあるでしょうか?

もっと簡単な方法

実はもっと簡単な方法で対処できます。変更すべき箇所については、この取り組みの発端「constraint "m" をチェックしていそうな箇所から調査を開始した」ことを思い出していただければ想像が付くと思います。constraint "m" を使っている箇所は define_insn です。

追加した define_insn(再掲)

;; gcc/config/riscv/riscv.md

(define_attr "vecmode" "unknown,V64SI"
  (const_string "unknown"))

(define_insn "*movv64si_internal"
  [(set (match_operand:V64SI 0 "nonimmediate_operand" "=v,v,m")
	(match_operand:V64SI 1 "move_operand"         " v,m,v"))]    //★★constraint "m" を使っている場所
  "(register_operand (operands[0], V64SImode)
    || reg_or_0_operand (operands[1], V64SImode))" return riscv_output_move (operands[0], operands[1]); 
  [(set_attr "move_type" "move,load,store")
   (set_attr "vecmode" "V64SI")])

この "m" を変更して、オフセット付きアドレスオペランドだけを拒否したいですが、そんな都合の良い constraint はあるでしょうか?実は RISC-V には既にあります。constraint "A" です。

constraint "A" の定義

;; gcc/config/riscv/constraints.md

(define_memory_constraint "A"
  "An address that is held in a general-purpose register."
  (and (match_code "mem")
       (match_test "GET_CODE(XEXP(op,0)) == REG")))

コードを見ると、constraint "A" が見ている条件は、種別がメモリであり、オペランドが REG(オフセットありだと PLUS などになる)であることです。先程の改造と同じ発想ですね。m を A に変更します。

変更後の define_insn

;; gcc/config/riscv/riscv.md

(define_insn "*movv64si_internal"
  [(set (match_operand:V64SI 0 "nonimmediate_operand" "=v,v,A")
	(match_operand:V64SI 1 "move_operand"         " v,A,v"))]    //★★constraint "A" に変更
...
constraint "A" を使ったとき、アセンブラの差分

$ diff -u b_before.s b_final.s

--- b_before.s  2020-05-28 21:17:24.607184754 +0900
+++ b_final.s   2020-05-28 21:20:20.219175573 +0900
@@ -35,7 +35,8 @@
 
 # 0 "" 2
  #NO_APP
-       vsw.v   v0,256(a5)
+       addi    a4,a5,256
+       vsw.v   v0,0(a4)
        .loc 1 9 2
        addi    a4,s0,-336
  #APP

出力結果のアセンブリも良い感じですし、ビルドの際にアセンブラも文句を言わなくなりました。

長きに渡りましたが、やっとベクトル型を使った GCC 拡張インラインアセンブラが書けるようになりました。良かった良かった。

[編集者: すずき]
[更新: 2020年 6月 1日 14:12]

コメント一覧

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



link permalink

link 編集する

GCC を調べる - その 13-3 - オフセット付きアドレスの分割

目次: GCC を調べる - まとめリンク

前回(2020年 5月 30日の日記参照)見たとおり、関数 process_alt_operands() がグローバル変数 goal_alt_win を変更しました。

フラグの変更が呼び出し元の curr_insn_transform() にどう影響するか調べます。RTL が変化するところに、適宜コメントを入れました。

goal_alt_win の影響

// gcc/lra-constraints.c

static bool
curr_insn_transform (bool check_only_p)
{

...

  if (process_alt_operands (reused_alternative_num))
    alt_p = true;

...

  n_outputs = 0;
  outputs[0] = -1;
  for (i = 0; i < n_operands; i++)
    {

      int regno;
      bool optional_p = false;
      rtx old, new_reg;
      rtx op = *curr_id->operand_loc[i];

      //★★ goal_alt_win = {false, true, }

      if (goal_alt_win[i])  //★★ i = 0 メモリオペランドのときは成立しない
        {

...
	}

      //★★ goal_alt_matches   = {-1, -1, }

      /* Operands that match previous ones have already been handled.  */
      if (goal_alt_matches[i] >= 0)  //★★成立しない
	continue;

      //★★ goal_alt_matched[i] = {-1, -47, ...}
      //★★ goal_alt_offmemok  = {true, false, }

      /* We should not have an operand with a non-offsettable address
	 appearing where an offsettable address will do.  It also may
	 be a case when the address should be special in other words
	 not a general one (e.g. it needs no index reg).  */
      if (goal_alt_matched[i][0] == -1 && goal_alt_offmemok[i] && MEM_P (op))  //★★成立する
	{
	  enum reg_class rclass;
	  rtx *loc = &XEXP (op, 0);
	  enum rtx_code code = GET_CODE (*loc);

	  push_to_sequence (before);
	  rclass = base_reg_class (GET_MODE (op), MEM_ADDR_SPACE (op),
				   MEM, SCRATCH);
	  if (GET_RTX_CLASS (code) == RTX_AUTOINC)
	    new_reg = emit_inc (rclass, *loc, *loc,
				/* This value does not matter for MODIFY.  */
				GET_MODE_SIZE (GET_MODE (op)));
	  else if (get_reload_reg (OP_IN, Pmode, *loc, rclass, FALSE,
				   "offsetable address", &new_reg))  //★★オフセットの設定が 2つの RTL に分割される
	    {
	      rtx addr = *loc;
	      enum rtx_code code = GET_CODE (addr);
	      
	      if (code == AND && CONST_INT_P (XEXP (addr, 1)))
		/* (and ... (const_int -X)) is used to align to X bytes.  */
		addr = XEXP (*loc, 0);
	      lra_emit_move (new_reg, addr);  //★★ベースレジスタにオフセットを加算する RTL を作成

	      //★★こんなのが作成される
	      //(insn 22 0 0 (set (reg:SI 114)
	      //        (plus:SI (reg/f:SI 108)
	      //            (const_int 256 [0x100]))) 3 {addsi3}
	      //     (nil))

	      if (addr != *loc)
		emit_move_insn (new_reg, gen_rtx_AND (GET_MODE (new_reg), new_reg, XEXP (*loc, 1)));
	    }
	  before = get_insns ();
	  end_sequence ();

          //★★*loc と new_reg の値
          //
	  //*loc = (plus:SI (reg/f:SI 108)
	  //    (const_int 256 [0x100]))
	  //
	  //new_reg = (reg:SI 114)

	  //★★Before:
	  //                                   ↓この RTL plus が reg に置き換わる
	  //(insn 10 11 13 2 (set (mem/c:V64SI (plus:SI (reg/f:SI 108)
	  //                (const_int 256 [0x100])) [1 v1+0 S256 A2048])
	  //        (reg:V64SI 109 [ v1 ])) "b.c":8:2 134 {*movv64si_internal}
	  //     (expr_list:REG_DEAD (reg:V64SI 109 [ v1 ])
	  //        (nil)))
	  //
	  //★★After:
	  //
	  //(insn 10 11 13 2 (set (mem/c:V64SI (reg:SI 114) [1 v1+0 S256 A2048])
	  //        (reg:V64SI 109 [ v1 ])) "b.c":8:2 134 {*movv64si_internal}
	  //     (expr_list:REG_DEAD (reg:V64SI 109 [ v1 ])
	  //        (nil)))

	  *loc = new_reg;
	  lra_update_dup (curr_id, i);
	}

...

  lra_process_new_insns (curr_insn, before, after, "Inserting insn reload");  //★★オフセットを事前計算する RTL が追加される
  return change_p;
}

最後の lra_process_new_insns() はちょっとややこしくて、1つ目の引数(curr_insn)の前に 2つ目の引数(before)の RTL を足し、後ろに 3つ目の引数を(after)を足す関数です。図示したほうがわかりやすいですね。

  • 呼び出し前: (curr_insn)
  • 呼び出し後: (before) (curr_insn) (after)

関数呼び出し前後の RTL の中身を下記に示します。今回は after は NULL なので、curr_insn の後ろには何も足されません。

lra_process_new_insns() の前

(insn 21 7 11 2 (set (reg:SI 113)
        (plus:SI (reg/f:SI 97 frame)
            (const_int -376 [0xfffffffffffffe88]))) "b.c":8:2 3 {addsi3}
     (nil))

(insn 11 21 10 2 (set (reg:V64SI 109 [ v1 ])
        (asm_operands/v:V64SI ("vlw.v %0, %1") ("=&v") 0 [
                (mem/c:SI (reg:SI 113) [1 b+40 S4 A64])
            ]
             [
                (asm_input:SI ("A") b.c:8)
            ]
             [] b.c:8)) "b.c":8:2 -1
     (nil))

★curr_insn は↓の RTL を指している

(insn 10 11 13 2 (set (mem/c:V64SI (reg:SI 114) [1 v1+0 S256 A2048])    ★*loc = new_reg; でオペランド変更済み
        (reg:V64SI 109 [ v1 ])) "b.c":8:2 134 {*movv64si_internal}
     (expr_list:REG_DEAD (reg:V64SI 109 [ v1 ])
        (nil)))

(insn 13 10 12 2 (set (reg:V64SI 110 [ v2 ])
        (asm_operands/v:V64SI ("vlw.v %0, %1") ("=&v") 0 [
                (mem/c:SI (plus:SI (reg/f:SI 97 frame)
                        (const_int -336 [0xfffffffffffffeb0])) [1 b+80 S4 A64])
            ]
             [
                (asm_input:SI ("A") b.c:9)
            ]
             [] b.c:9)) "b.c":9:2 -1
     (nil))

...
lra_process_new_insns() の後

(insn 21 7 11 2 (set (reg:SI 113)
        (plus:SI (reg/f:SI 97 frame)
            (const_int -376 [0xfffffffffffffe88]))) "b.c":8:2 3 {addsi3}
     (nil))

(insn 11 21 22 2 (set (reg:V64SI 109 [ v1 ])
        (asm_operands/v:V64SI ("vlw.v %0, %1") ("=&v") 0 [
                (mem/c:SI (reg:SI 113) [1 b+40 S4 A64])
            ]
             [
                (asm_input:SI ("A") b.c:8)
            ]
             [] b.c:8)) "b.c":8:2 -1
     (nil))

★before は↓の RTL を指している

(insn 22 11 10 2 (set (reg:SI 114)    ★この RTL が追加された(事前にアドレスを格納するレジスタにオフセットを足すため)
        (plus:SI (reg/f:SI 108)
            (const_int 256 [0x100]))) "b.c":8:2 3 {addsi3}
     (nil))

★curr_insn は↓の RTL を指している

(insn 10 22 13 2 (set (mem/c:V64SI (reg:SI 114) [1 v1+0 S256 A2048])
        (reg:V64SI 109 [ v1 ])) "b.c":8:2 134 {*movv64si_internal}
     (expr_list:REG_DEAD (reg:V64SI 109 [ v1 ])
        (nil)))

(insn 13 10 12 2 (set (reg:V64SI 110 [ v2 ])
        (asm_operands/v:V64SI ("vlw.v %0, %1") ("=&v") 0 [
                (mem/c:SI (plus:SI (reg/f:SI 97 frame)
                        (const_int -336 [0xfffffffffffffeb0])) [1 b+80 S4 A64])
            ]
             [
                (asm_input:SI ("A") b.c:9)
            ]
             [] b.c:9)) "b.c":9:2 -1
     (nil))

...

ベクトル命令の RTL の前にアドレスを計算する RTL が出力されました。うまくいってそうです。

うまくいっているけど、もう一息

話が長くなってきて忘れてしまいそうですが、やりたかったことは、ベクトル命令のオペランドにオフセット付きアドレスを指定しないことです。

先程まで見てきたように、常に win = false にすれば目的を達成しているように見えます。しかし残念ながら常にオフセット付きアドレスを拒絶することになり、スカラ命令にも影響が出てしまいます。変更前と変更後のアセンブラを比較します。

変更前後のアセンブラ比較

--- b_before.s  2020-05-28 21:17:24.607184754 +0900
+++ b_after.s   2020-05-28 21:14:31.375142095 +0900
@@ -21,8 +21,10 @@
        addi    s0,sp,1200
        .cfi_def_cfa 8, 0
        addi    a5,s0,-16

  #★★スカラ命令なのにアドレスオペランドのオフセット設定が分解されている(この分割は本来不要)

-       sw      a5,-1188(s0)
-       lw      a5,-1188(s0)
+       addi    a4,s0,-1188
+       sw      a5,0(a4)
+       addi    a5,s0,-1188
+       lw      a5,0(a5)
        addi    a5,a5,-1168
        addi    a5,a5,255
        srli    a5,a5,8
@@ -35,7 +37,8 @@
 
 # 0 "" 2
  #NO_APP

  #★★ベクトル命令のアドレスオペランドのオフセット設定が分解されるのは狙い通り

-       vsw.v   v0,256(a5)
+       addi    a4,a5,256
+       vsw.v   v0,0(a4)
        .loc 1 9 2
        addi    a4,s0,-336
  #APP

ベクトル命令は狙い通りですが、スカラ命令までアドレスオペランドのオフセット設定が分解されてしまいました。この分割は本来不要です。

これをどう抑えるかはまた次回。

[編集者: すずき]
[更新: 2020年 6月 1日 14:12]

コメント一覧

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



link permalink

link 編集する

GCC を調べる - その 13-2 - オフセット付きアドレス全部禁止

目次: GCC を調べる - まとめリンク

前回(2020年 5月 29日の日記参照)メモリというかオフセット付きアドレスの constraint をチェックしていそうな箇所を見つけました。

ベクトル命令の場合は、オフセット付きアドレスを拒否してほしいです。試しに win = true; の部分を win = false; に変更するとどんな動きをするでしょうか?

常に win = false にしたときの動作

// gcc/lra-constraints.c

static bool
process_alt_operands (int only_alternative)
{

...

  for (nalt = 0; nalt < n_alternatives; nalt++)  //★★基本的には全ての選択肢を検討するのだが、losers が 0 だと break する(ループ終端(1000 行後)で判定している)
    {

...

      for (nop = 0; nop < n_operands; nop++)
	{

...


	  costly_p = false;
	  do
	    {
	      switch ((c = *p, len = CONSTRAINT_LEN (c, p)), c)
		{

...

		default:
		  cn = lookup_constraint (p);
		  switch (get_constraint_type (cn))
		    {

...

		    case CT_MEMORY:
		      if (MEM_P (op)
			  && satisfies_memory_constraint_p (op, cn))  //★★今回はこちら
			win = false;  //★★常に false にする

...

	    }
	  while ((p += len), c);

	  scratch_p = (operand_reg[nop] != NULL_RTX
		       && lra_former_scratch_p (REGNO (operand_reg[nop])));
	  /* Record which operands fit this alternative.  */
	  if (win)  //★★不成立、this_alternative_win は false のまま
	    {
	      this_alternative_win = true;

...

	    }
	  else if (did_match)
	    this_alternative_match_win = true;
	  else
	    {
	      int const_to_mem = 0;
	      bool no_regs_p;

	      reject += op_reject;

...

	      /* If the operand is dying, has a matching constraint,
		 and satisfies constraints of the matched operand
		 which failed to satisfy the own constraints, most probably
		 the reload for this operand will be gone.  */
	      if (this_alternative_matches >= 0
		  && !curr_alt_win[this_alternative_matches]
		  && REG_P (op)
		  && find_regno_note (curr_insn, REG_DEAD, REGNO (op))
		  && (hard_regno[nop] >= 0
		      ? in_hard_reg_set_p (this_alternative_set,
					   mode, hard_regno[nop])
		      : in_class_p (op, this_alternative, NULL)))  //★★不成立
		{

...

		}
	      else
		{
		  /* Strict_low_part requires to reload the register
		     not the sub-register.  In this case we should
		     check that a final reload hard reg can hold the
		     value mode.  */
		  if (curr_static_id->operand[nop].strict_low
		      && REG_P (op)
		      && hard_regno[nop] < 0
		      && GET_CODE (*curr_id->operand_loc[nop]) == SUBREG
		      && ira_class_hard_regs_num[this_alternative] > 0
		      && (!targetm.hard_regno_mode_ok
			  (ira_class_hard_regs[this_alternative][0],
			   GET_MODE (*curr_id->operand_loc[nop]))))  //★★不成立
		    {

...

		    }
		  losers++;  //★★この変数が 0 以外だと、続けて他の選択肢も検討される
		}

...

	      if (MEM_P (op) && offmemok)
		addr_losers++;
	      else

...

	  curr_alt[nop] = this_alternative;
	  curr_alt_set[nop] = this_alternative_set;
	  curr_alt_win[nop] = this_alternative_win;  //★★false
	  curr_alt_match_win[nop] = this_alternative_match_win;
	  curr_alt_offmemok[nop] = this_alternative_offmemok;
	  curr_alt_matches[nop] = this_alternative_matches;

	  if (this_alternative_matches >= 0
	      && !did_match && !this_alternative_win)
	    curr_alt_win[this_alternative_matches] = false;

	  if (early_clobber_p && operand_reg[nop] != NULL_RTX)
	    early_clobbered_nops[early_clobbered_regs_num++] = nop;
	}

...

      ok_p = true;  //★★関数の返り値
      curr_alt_dont_inherit_ops_num = 0;

...

      /* If this alternative can be made to work by reloading, and it
	 needs less reloading than the others checked so far, record
	 it as the chosen goal for reloading.  */
      if ((best_losers != 0 && losers == 0)
	  || (((best_losers == 0 && losers == 0)
	       || (best_losers != 0 && losers != 0))
	      && (best_overall > overall
		  || (best_overall == overall
		      /* If the cost of the reloads is the same,
			 prefer alternative which requires minimal
			 number of reload regs.  */
		      && (reload_nregs < best_reload_nregs
			  || (reload_nregs == best_reload_nregs
			      && (best_reload_sum < reload_sum
				  || (best_reload_sum == reload_sum
				      && nalt < goal_alt_number))))))))
	{
	  for (nop = 0; nop < n_operands; nop++)
	    {
	      goal_alt_win[nop] = curr_alt_win[nop];  //★★false
	      goal_alt_match_win[nop] = curr_alt_match_win[nop];
	      goal_alt_matches[nop] = curr_alt_matches[nop];
	      goal_alt[nop] = curr_alt[nop];
	      goal_alt_offmemok[nop] = curr_alt_offmemok[nop];
	    }
	  goal_alt_dont_inherit_ops_num = curr_alt_dont_inherit_ops_num;
	  for (nop = 0; nop < curr_alt_dont_inherit_ops_num; nop++)
	    goal_alt_dont_inherit_ops[nop] = curr_alt_dont_inherit_ops[nop];
	  goal_alt_swapped = curr_swapped;
	  best_overall = overall;
	  best_losers = losers;
	  best_reload_nregs = reload_nregs;
	  best_reload_sum = reload_sum;
	  goal_alt_number = nalt;
	}
      if (losers == 0)  //★★最初の方の for (nalt = 0; nalt < n_alternatives; nalt++) を break するかどうか決めてる
	/* Everything is satisfied.  Do not process alternatives
	   anymore.  */
	break;
    fail:
      ;
    }
  return ok_p;
}

長ったらしくてわかりにくいですが、今回注目したい制御条件は win に関係する部分です。

  • this_alternative_win が false
  • curr_alt_win[nop] が false(nop = 0 のとき)
  • goal_alt_win[nop] が false(nop = 0 のとき)

関数 process_alt_operands() の返り値は bool 型で、false は他のオペランドの選択肢はない、true は他の選択肢があるという意味だそうです。今回は true を返します。だから何?と思いますが、あとでこの値がちょっとだけ出ます。

関数の最後に設定する goal_alt_win というグローバル変数が、process_alt_operands() 関数の「外側」の制御に影響を及ぼします。これはひどい。良い子の皆さんはこういうコードを書いてはいけません。

今回注目している insn のオペランドは 2つあり、nop = 0 がメモリのオペランド、nop = 1 がレジスタのオペランドです。デバッガで追いかけると this_ なんちゃらの値はそれぞれ下記のようになっていました。

各オペランドの win の値
---------- nop = 0
 op = (mem/c:V64SI (plus:SI (reg/f:SI 108)
        (const_int 256 [0x100])) [1 v1+0 S256 A2048])

 this_alternative = NO_REGS
 this_alternative_set = {elts = {0, 0}}
 this_alternative_win = false
 this_alternative_match_win = false
 this_alternative_offmemok = true
 this_alternative_matches = -1

---------- nop = 1
 op = (reg:V64SI 109 [ v1 ])

 this_alternative = VP_REGS
 this_alternative_set = {elts = {0, 4294967295}}
 this_alternative_win = true
 this_alternative_match_win = false
 this_alternative_offmemok = false
 this_alternative_matches = -1

似たような名前の変数 this_, curr_, goal_ が 3つ出てきます。勝ち抜き方式にして一番良い選択肢を残しているようです。一番内側の for ループで this_XX(ローカル変数)の値を作り、良さそうな値なら curr_alt_XX 配列(static 変数)に代入します。curr_alt_ はどんどん上書きされ、最後に残った値が goal_alt_XX 配列(グローバル変数)に代入されます。

処理の骨組みだけ示すと下記のようになります。

goal_alt_XX[op] が決まる仕組み

process_alt_operands
{
  for (nalt = 0; nalt < n_alternatives; nalt++)  //★★選択肢の数だけループ
    {
      for (nop = 0; nop < n_operands; nop++)  //★★オペランドの数だけループ
	{
          {
            //★★this_XX を決める戦い
          }

          //★★this_XX が決められないなら、次のオペランドを処理

          //★★選ばれし this_XX が curr_alt_XX[op] に保存(後の選択肢のほうがさらに良ければ上書きされる)
        }
    }

    //★★curr_alt_XX[op] を goal_alt_XX[op] に保存
}

最終的に goal_alt_XX の値がどうなるかというと、

最終的な goal_alt_XX の値
 goal_alt           = {NO_REGS, VP_REGS, }
 goal_alt_win       = {false, true, }
 goal_alt_match_win = {false, false, }
 goal_alt_offmemok  = {true, false, }
 goal_alt_matches   = {-1, -1, }

関数 process_alt_operands() の最後でブレークして goal_alt_XX の値をダンプしました。コードを追いかけて求めるのはかなり困難です……。

グローバル変数 goal_alt_win を変更したことによる影響は、また次回。

[編集者: すずき]
[更新: 2020年 6月 1日 14:12]

コメント一覧

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



link permalink

link 編集する

GCC を調べる - その 13-1 - オフセット付きアドレスを禁止する

目次: GCC を調べる - まとめリンク

メモリからのロード、ストアに問題があるので、おそらくメモリ周りの constraint 指定が間違っていて、本来受け付けてはいけないオペランドまで受け付けていると思われますが、証拠を掴む方法が全くわかりません。

今回追加した define_expand, define_insn のうち define_expand は constraint なし、define_insn は m という標準の constraint を使っています。手がかりになりそうな constraint m から追ってみます。メモリの constraint は下記に定義があります。

constraint "m" の定義

// gcc/defaults.h

#ifndef TARGET_MEM_CONSTRAINT
#define TARGET_MEM_CONSTRAINT 'm'
#endif


// gcc/common.md

(define_memory_constraint "TARGET_MEM_CONSTRAINT"
  "Matches any valid memory."
  (and (match_code "mem")
       (match_test "/* hogehoge */ memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0),
						 MEM_ADDR_SPACE (op))")))  //★★ここに適当なコメントを入れる

メモリの constraint は若干イレギュラーな定義で、アーキテクチャにより "m" 以外の文字になります(s390 のみ "e" を使います)。RISC-V では TARGET_MEM_CONSTRAINT は "m" ですから、特に気にしなくて良いです。

定義の最後にある match_test に続くコードに適当にコメントを入れて、ビルドしてからコメントの文字列で grep すると、build_gcc/tm-constrs.h の statisfies_constraint_m() という関数にコピペされていることがわかります。このルールは constraint "m" だけでなく他も同じです。関数 satisfies_constraint_(文字) が自動的に生成され、条件チェックされます。

この関数でブレークしたいですが static inline になっておりブレークが掛からないので、適当に for_break_tmp() 関数を一つ追加しておき、この関数で止めます。

constraint "m" の判定関数

// build_gcc/tm-constrs.h

static void for_break_tmp(rtx op)  //★★関数を足す
{
}

static inline bool
satisfies_constraint_m (rtx op)  //★★この関数にブレークは設定できないので…
{
  for_break_temp(op);  //★★関数呼び出しを足す

  return (GET_CODE (op) == MEM) && (
#line 26 "./gcc/gcc/common.md"
( /* hogehoge */ memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0),
						 MEM_ADDR_SPACE (op))));  //★★さっきいれた適当なコメントも一緒にコピーされる
}

無条件だと何度も止まって鬱陶しいので、machine mode で条件ブレークすると良いでしょう。

constraint "m" の判定関数でブレーク
(gdb) b for_break_tmp if op->mode == E_V64SImode

(gdb) r

Breakpoint 1, for_break_tmp (op=0x7ffff7bacab0) at tm-constrs.h:9
9       }

(gdb) bt

#0  for_break_tmp (op=0x7ffff7bacab0)
    at tm-constrs.h:9
#1  0x000000000217dfa8 in satisfies_constraint_m (op=0x7ffff7bacab0)
    at tm-constrs.h:13
#2  0x0000000000ebcc07 in constraint_satisfied_p (x=0x7ffff7bacab0, c=CONSTRAINT_m)
    at ./tm-preds.h:108
#3  0x0000000000ebe8cf in satisfies_memory_constraint_p (op=0x7ffff7bacab0,
    constraint=CONSTRAINT_m)
    at ./gcc/gcc/lra-constraints.c:421
#4  0x0000000000ec7149 in process_alt_operands (only_alternative=-1)
    at ./gcc/gcc/lra-constraints.c:2338
#5  0x0000000000eceb52 in curr_insn_transform (check_only_p=false)
    at ./gcc/gcc/lra-constraints.c:3977
#6  0x0000000000ed4e71 in lra_constraints (first_p=true)
    at ./gcc/gcc/lra-constraints.c:5027
#7  0x0000000000eacae4 in lra (f=0x454f640)
    at ./gcc/gcc/lra.c:2437
#8  0x0000000000e1a557 in do_reload ()
    at ./gcc/gcc/ira.c:5523
#9  0x0000000000e1af95 in (anonymous namespace)::pass_reload::execute (this=0x449fdf0)
    at ./gcc/gcc/ira.c:5709

パスは 282r.reload です。いくつかスタックフレームを遡ると process_alt_operands() 関数にたどり着きます。ベクトルレジスタの追加(2020年 3月 29日の日記参照)のときに見ました、懐かしいですね。それはさておき下記のコードから呼ばれています。

constraint "m" を判定する箇所

static bool
process_alt_operands (int only_alternative)
{

...

	  costly_p = false;
	  do
	    {
	      switch ((c = *p, len = CONSTRAINT_LEN (c, p)), c)
		{

...

		default:
		  cn = lookup_constraint (p);
		  switch (get_constraint_type (cn))
		    {
		    case CT_REGISTER:
		      cl = reg_class_for_constraint (cn);  //★★前回はこちらに来ていた
		      if (cl != NO_REGS)
			goto reg;
		      break;

		    case CT_CONST_INT:
		      if (CONST_INT_P (op)
			  && insn_const_int_ok_for_constraint (INTVAL (op), cn))
			win = true;
		      break;

		    case CT_MEMORY:
		      if (MEM_P (op)
			  && satisfies_memory_constraint_p (op, cn))  //★★今回はこちらに来る
			win = true;
		      else if (spilled_pseudo_p (op))
			win = true;

		      /* If we didn't already win, we can reload constants
			 via force_const_mem or put the pseudo value into
			 memory, or make other memory by reloading the
			 address like for 'o'.  */
		      if (CONST_POOL_OK_P (mode, op)
			  || MEM_P (op) || REG_P (op)
			  /* We can restore the equiv insn by a
			     reload.  */
			  || equiv_substition_p[nop])
			badop = false;
		      constmemok = true;
		      offmemok = true;
		      break;


// op が指す RTL はこんな感じ

(mem/c:V64SI (plus:SI (reg/f:SI 108)
        (const_int 256 [0x100])) [1 v1+0 S256 A2048])

渡された RTL に対し、satisfies_memory_constraint_p() は constraint "m" の条件と合致するか調べ、合致していたら win フラグを true にしています。

この辺りを変えれば、オフセット付きアドレスに変化が起きそうです。続きはまた今度。

[編集者: すずき]
[更新: 2020年 6月 1日 14:11]

コメント一覧

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



link permalink

link 編集する

STATIONflow のバグ

STATIONflow バグってますね……。ゲーム進行速度を最大にすると勝手に評価が下がってしまいます。これはあんまりだ。

ゲーム進行速度を最大の 3 にすると、評価がかなり下がります。速度 1 or 2 なら A+(1枚目)ですが、速度 3 だと B(2枚目)です。他は何も変えず、速度だけ変えた結果です。


STATIONflow 速度 2


STATIONflow 速度 3

STATIONflow は評価と収入が直結しており、評価が 2段階下がると大赤字です。画像の右下が収入ですが、A+ 316000 から B 158000 に下がってますよね?

速度 3 のときの客の評価値を見ると、利用施設の待ち時間に対する評価が軒並み悪化しています。速度 3 だけ待ち時間の計算をミスってるんじゃないでしょうか?

あまりにも評価が乱高下するから、何が起きたのか結構悩んでしまいました。無駄に悩んだ時間を返せ。

[編集者: すずき]
[更新: 2020年 5月 28日 22:32]

コメント一覧

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



link もっと前
   2020年 6月 6日 -
      2020年 5月 28日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 6/6 18:42

カレンダー

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

最近のコメント 5件

  • link 20年05月02日
    すずき 「ちょっと調べたところ、コアが焼けると騒ぎ...」
    (更新:05/08 15:43)
  • link 20年05月02日
    すずき 「結構、怖い制御に見えますね&hellip...」
    (更新:05/08 15:23)
  • link 20年05月02日
    hdk 「デスクトップ用のNVIDIA Quadr...」
    (更新:05/07 20:30)
  • link 20年01月27日
    すずき 「詳細は調べていないので、コード中のコメン...」
    (更新:04/18 23:05)
  • link 20年01月27日
    superzeros 「少し気になったのでglibcの履歴を調べ...」
    (更新:04/16 21:07)

最近の記事 3件

link もっとみる
  • link 20年06月06日
    すずき 「[Kindle Fire の本が消える病] 割と昔からですが Ki...」
    (更新:06/06 18:42)
  • link 20年06月05日
    すずき 「[FSF Copyright Assignment] FSF (F...」
    (更新:06/06 16:02)
  • link 20年06月04日
    すずき 「[GCC を調べる - その 14-2 - genopinit] ...」
    (更新:06/04 12:18)

こんてんつ

open/close wiki
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 過去日記について

その他の情報

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