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

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

日々

link permalink

link 編集する

GCC を調べる - その 6 - GCC の register_constraint

GCC のインラインアセンブラでは __asm__("ins %0" : "=r"(aaa) : : ); のように、"=r" という不思議な文字列が出てきます。

これは constraints と呼ばれ(GCC のマニュアルへのリンク)、引数がレジスタ(r)なのかメモリアドレス(m)なのか、書き換えられるのか(=)などを説明しています。

どんな文字でも書けるわけではなく、変な文字(例えば 'v')を指定すると「impossible constraint in 'asm'」と怒られます。これは一体どこでチェックしているのでしょう?また、どうやって足せばよいでしょうか?

エラーを出している箇所

このエラーを出すのは、パスでいうと 234r.vregs です。

ちなみに build_gcc というディレクトリ名は GCC のビルドディレクトリのことです。GCC は自動生成コードをかなりの量出力するので、そちらも合わせて見る必要があります。

エラーメッセージを出している箇所

// gcc/function.c

static void
instantiate_virtual_regs_in_insn (rtx_insn *insn)
{
...
  if (asm_noperands (PATTERN (insn)) >= 0)
    {
      if (!check_asm_operands (PATTERN (insn)))  //★★このエラーチェックに引っかかっている
	{
	  error_for_asm (insn, "impossible constraint in %<asm%>");  //★★このエラーメッセージが出ている


// gcc/recog.c

int
check_asm_operands (rtx x)
{
...
  for (i = 0; i < noperands; i++)
    {
      const char *c = constraints[i];
      if (c[0] == '%')
	c++;
      if (! asm_operand_ok (operands[i], c, constraints))  //★★このエラーチェックに引っかかっている
	return 0;
    }

このチェックは何をしているのかというと、lookup_constraint() で文字(例えば 'v')をテーブルから探し、constraint の種類に変換します。未知の文字の場合は CONSTRAINT_UNKNOWN になります。

次に get_constraint_type() でどのカテゴリに属するか見ます。この関数はおかしくて、なぜか CONSTRAINT__UNKNOWN を CT_REGISTER と判定します。レジスタじゃないですよね?意味不明です。

asm 文の constraint を見ている箇所

// gcc/recog.c

int
asm_operand_ok (rtx op, const char *constraint, const char **constraints)
{
  int result = 0;
...
  while (*constraint)
    {
      enum constraint_num cn;
      char c = *constraint;
      int len;
      switch (c)
	{
...
	default:
	  cn = lookup_constraint (constraint);  //★★文字から constraint_num に変換
						//    未知の文字の場合 CONSTRAINT__UNKNOWN になる
	  switch (get_constraint_type (cn))
	    {
	    case CT_REGISTER:  //★★なぜかここに行く
	      if (!result
		  && reg_class_for_constraint (cn) != NO_REGS  //★★レジスタの constraint が NO_REGS になると NG
		  && GET_MODE (op) != BLKmode
		  && register_operand (op, VOIDmode))
		result = 1;
	      break;


// build_gcc/gcc/tm-preds.h

static inline enum constraint_num
lookup_constraint (const char *p)
{
  unsigned int index = lookup_constraint_array[(unsigned char) *p];  //★★この配列がカギ
  return (index == UCHAR_MAX
          ? lookup_constraint_1 (p)
          : (enum constraint_num) index);
}

enum constraint_num
{
  CONSTRAINT__UNKNOWN = 0,
  CONSTRAINT_r,
  CONSTRAINT_f,
...

static inline enum constraint_type
get_constraint_type (enum constraint_num c)
{
  if (c >= CONSTRAINT_p)
    {
      if (c >= CONSTRAINT_G)
        return CT_FIXED_FORM;
      return CT_ADDRESS;
    }
  if (c >= CONSTRAINT_m)
    return CT_MEMORY;
  if (c >= CONSTRAINT_I)
    return CT_CONST_INT;
  return CT_REGISTER;  //★★c が CONSTRAINT__UNKNOWN つまり 0 の場合、どれにも当てはまらず CT_REGISTER と判断される
}


// build_gcc/insn-preds.c

const unsigned char lookup_constraint_array[] = {
  CONSTRAINT__UNKNOWN,
  CONSTRAINT__UNKNOWN,
...
  MIN ((int) CONSTRAINT_r, (int) UCHAR_MAX),
  MIN ((int) CONSTRAINT_s, (int) UCHAR_MAX),
  CONSTRAINT__UNKNOWN,
  CONSTRAINT__UNKNOWN,
  CONSTRAINT__UNKNOWN,  //★★'v' は未定義、未知の文字の場合は全て CONSTRAINT__UNKNOWN になる
  CONSTRAINT__UNKNOWN,
...


// build_gcc/gcc/tm-preds.h

static inline enum reg_class
reg_class_for_constraint (enum constraint_num c)
{
  if (insn_extra_register_constraint (c))
    return reg_class_for_constraint_1 (c);  //★★ここで見つからないと NO_REGS が返されて NG
  return NO_REGS;
}


// build_gcc/gcc/insn-preds.c

enum reg_class
reg_class_for_constraint_1 (enum constraint_num c)
{
  switch (c)
    {
    case CONSTRAINT_r: return GENERAL_REGS;
    case CONSTRAINT_f: return TARGET_HARD_FLOAT ? FP_REGS : NO_REGS;
    case CONSTRAINT_j: return SIBCALL_REGS;
    case CONSTRAINT_l: return JALR_REGS;
    default: break;  //★★CONSTRAINT__UNKNOWN はどの case にも当てはまらないので NO_REGS が返されて NG
    }
  return NO_REGS;
}

明らかにレジスタとは思えない CONSTRAINT__UNKNKNOWN が返ってきますが、なぜか CT_REGISTER だと思って処理し始め、最終的にエラーとして弾きます。結果オーライですがこれで良いんでしょうか。GCC のコードは訳がわかりません……。

register_constraint の足し方

では v を正当な constraint の一員にするにはどうしたら良いでしょうか?

第一歩としては GCC の config ディレクトリの下にある *.md ファイル(Markdown ではなく Machine Descriptor です)を編集します。

define_register_constraint を足す

;; config/riscv/riscv.md

(include "predicates.md")
(include "constraints.md")


;; config/riscv/constraints.md

(define_register_constraint "v" "TARGET_VECTOR ? VP_REGS : NO_REGS"
  "A vector register (if available).")

これを足すと(他にも色々やらないといけないんですけど)、先程の reg_class_for_constraint_1() に変化が生じます。

asm 文の constraint チェックが変わる

// build_gcc/gcc/insn-preds.c

enum reg_class
reg_class_for_constraint_1 (enum constraint_num c)
{
  switch (c)
    {
    case CONSTRAINT_r: return GENERAL_REGS;
    case CONSTRAINT_f: return TARGET_HARD_FLOAT ? FP_REGS : NO_REGS;
    case CONSTRAINT_j: return SIBCALL_REGS;
    case CONSTRAINT_v: return TARGET_VECTOR ? VP_REGS : NO_REGS;  //★★これが足されて通過するようになる
    case CONSTRAINT_l: return JALR_REGS;
    default: break;
    }
  return NO_REGS;
}

GCC はこの手のピタゴラスイッチの塊で、何を変えると望みの機能が実装できるか、全くわかりません。こんなもの良くメンテナンスできるなあ、と思います。

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

コメント一覧

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



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

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

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

最終更新: 3/15 02:33

カレンダー

<2020>
<<<03>>>
1234567
891011121314
15161718192021
22232425262728
293031----

最近のコメント 5件

  • link 13年11月28日
    すずき 「ご指摘ありがとうございます。投稿の古さに...」
    (更新:03/08 17:39)
  • link 13年11月28日
    yut 「古いので、見てもらえるか不明ですので、、...」
    (更新:03/08 13:16)
  • link 13年11月28日
    yut シムズ 「古い投稿に対して、申し訳ありません。\n...」
    (更新:03/08 13:14)
  • link 19年09月01日
    すずき 「私も正直びっくりです。間違って違う製品を...」
    (更新:09/04 23:39)
  • link 19年09月01日
    hdk 「車向けの製品の中でも、車載コンピューター...」
    (更新:09/02 23:20)

最近の記事 3件

link もっとみる
  • link 20年03月10日
    すずき 「[誕生日] 37歳になりました。おめでとう俺、ありがとう俺。30代...」
    (更新:03/15 02:33)
  • link 20年03月14日
    すずき 「[GCC を調べる - その 7 - machine mode] ...」
    (更新:03/15 02:21)
  • link 20年03月06日
    すずき 「[GCC を調べる - その 6 - GCC の regist] ...」
    (更新:03/11 22:47)

こんてんつ

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 サイトの情報