目次: 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 | - | - | - | - |
合計:
本日: