目次: 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はこの手のピタゴラスイッチの塊で、何を変えると望みの機能が実装できるか、全くわかりません。こんなもの良くメンテナンスできるなあ、と思います。
< | 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 | - | - | - | - |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)