目次: 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 | - | - | - | - |
25年10月6日
25年10月6日
25年9月29日
25年9月29日
20年8月24日
20年8月24日
16年2月14日
16年2月14日
25年7月20日
25年7月20日
25年7月20日
25年7月20日
25年7月20日
25年7月20日
20年8月16日
20年8月16日
20年8月16日
20年8月16日
24年6月17日
24年6月17日
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年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: