目次: GCC
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と判定します。レジスタじゃないですよね?意味不明です。
// 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__UNKNOWNが返ってきますが、なぜかCT_REGISTERだと思って処理し始め、最終的にエラーとして弾きます。結果オーライですがこれで良いんでしょうか。GCCのコードは訳がわかりません……。
ではvを正当なconstraintの一員にするにはどうしたら良いでしょうか?
第一歩としてはGCCのconfigディレクトリの下にある *.mdファイル(MarkdownではなくMachine Descriptorです)を編集します。
;; 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() に変化が生じます。
// 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はこの手のピタゴラスイッチの塊で、何を変えると望みの機能が実装できるか、全くわかりません。こんなもの良くメンテナンスできるなあ、と思います。
 この記事にコメントする
目次: GCC
全てのRTLの定義はgcc/rtl.defに定義されています。例えばinsnの定義は下記のとおりです。
/* An instruction that cannot jump.  */
DEF_RTL_EXPR(INSN, "insn", "uuBeiie", RTX_INSN)
何となくわかるけど、何だこれ?と思いますよね、私は思いました。それもそのはずで、実はこのファイルは単体では意味をなさず、Cファイルから何度も再利用されるからです。Cファイルからrtl.defをインクルードして使いますが、その際にDEF_RTL_EXPR() の意味が変わります。
比較的わかりやすい例を挙げるとしたら、gcc/rtl.cのrtx_formatの定義です。DEF_RTL_EXPR() を定義してから #include "rtl.def" を行う様子がわかるかと思います。
/* Indexed by rtx code, gives a sequence of operand-types for
   rtx's of that code.  The sequence is a C string in which
   each character describes one operand.  */
const char * const rtx_format[NUM_RTX_CODE] = {
  /* "*" undefined.
         can cause a warning message
     "0" field is unused (or used in a phase-dependent manner)
         prints nothing
     "i" an integer
         prints the integer
     "n" like "i", but prints entries from `note_insn_name'
     "w" an integer of width HOST_BITS_PER_WIDE_INT
         prints the integer
     "s" a pointer to a string
         prints the string
     "S" like "s", but optional:
	 the containing rtx may end before this operand
     "T" like "s", but treated specially by the RTL reader;
         only found in machine description patterns.
     "e" a pointer to an rtl expression
         prints the expression
     "E" a pointer to a vector that points to a number of rtl expressions
         prints a list of the rtl expressions
     "V" like "E", but optional:
	 the containing rtx may end before this operand
     "u" a pointer to another insn
         prints the uid of the insn.
     "b" is a pointer to a bitmap header.
     "B" is a basic block pointer.
     "t" is a tree pointer.
     "r" a register.
     "p" is a poly_uint16 offset.  */
#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS)   FORMAT ,
#include "rtl.def"		/* rtl expressions are defined here */
#undef DEF_RTL_EXPR
};
このrtx_formatの場合は、第3引数(FORMAT)とカンマを残して、他は無視するようにDEF_RTL_EXPR() を定義しています。
コメントなどを無視して考えるとrtl.defをインクルードしたあとは、コードは下記のようになります。
const char * const rtx_format[NUM_RTX_CODE] = {
DEF_RTL_EXPR(UNKNOWN, "UnKnown", "*", RTX_EXTRA)
DEF_RTL_EXPR(VALUE, "value", "0", RTX_OBJ)
DEF_RTL_EXPR(DEBUG_EXPR, "debug_expr", "0", RTX_OBJ)
...
};
マクロDEF_RTL_EXPR() の展開後はこうなります。
const char * const rtx_format[NUM_RTX_CODE] = {
"*",
"0",
"0",
...
};
最終的にrtx_formatは文字列の配列になります。
私もrtl.defの用途全てを理解しているわけではなく、全てを説明することもできないので、またわかったら書くことにします。
 この記事にコメントする
目次: Zephyr
導入、ブート周り
ボード、ドライバなど
SMP対応編
浮動小数点数命令など
その他
目次: 一覧の一覧
 この記事にコメントする
| < | 2020 | > | ||||
| << | < | 03 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 | 
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 8 | 9 | 10 | 11 | 12 | 13 | 14 | 
| 15 | 16 | 17 | 18 | 19 | 20 | 21 | 
| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 
| 29 | 30 | 31 | - | - | - | - | 
 wiki
 Linux JM
 Java API
 2002年
 2003年
 2004年
 2005年
 2006年
 2007年
 2008年
 2009年
 2010年
 2011年
 2012年
 2013年
 2014年
 2015年
 2016年
 2017年
 2018年
 2019年
 2020年
 2021年
 2022年
 2023年
 2024年
 2025年
 過去日記について
 アクセス統計
 サーバ一覧
 サイトの情報合計: 
本日: