目次: GCC
前回(2020年5月23日の日記参照)の続きです。エラーの原因となる代入操作RTLはemit_move_multi_word() で出力されており、条件の分岐点であるemit_move_insn_1() が怪しそうです。分岐条件を司るoptab_handler() を追います。
// build_gcc/insn-opinit.h
enum optab_tag {
unknown_optab,
sext_optab,
...
mov_optab,
...
// gcc/optabs-query.h
/* Return the insn used to implement mode MODE of OP, or CODE_FOR_nothing
if the target does not have such an insn. */
inline enum insn_code
optab_handler (optab op, machine_mode mode)
{
unsigned scode = (op << 16) | mode; //★★もしブレーク掛けたければop = mov_optab, mode = E_V64SImodeで引っ掛けられる
gcc_assert (op > LAST_CONV_OPTAB);
return raw_optab_handler (scode); //★★これ
}
// build_gcc/insn-opinit.c
enum insn_code
raw_optab_handler (unsigned scode)
{
int i = lookup_handler (scode); //★★これが -1だとCODE_FOR_nothingが返る
return (i >= 0 && this_fn_optabs->pat_enable[i]
? pats[i].icode : CODE_FOR_nothing);
}
static int
lookup_handler (unsigned scode)
{
int l = 0, h = ARRAY_SIZE (pats), m;
while (h > l)
{
m = (h + l) / 2;
if (scode == pats[m].scode)
return m;
else if (scode < pats[m].scode)
h = m;
else
l = m + 1;
}
return -1;
}
//★★pats[] の定義
struct optab_pat {
unsigned scode;
enum insn_code icode;
};
static const struct optab_pat pats[NUM_OPTAB_PATTERNS] = {
{ 0x010405, CODE_FOR_extendqihi2 },
{ 0x010406, CODE_FOR_extendqisi2 },
{ 0x010407, CODE_FOR_extendqidi2 },
{ 0x010505, CODE_FOR_extendhihi2 },
...
//★★this_fn_optabs->pat_enable[] を初期化しているコード
void
init_all_optabs (struct target_optabs *optabs)
{
bool *ena = optabs->pat_enable;
ena[0] = HAVE_extendqihi2;
ena[1] = HAVE_extendqisi2;
...
このlookup_handler() が -1を返すとemit_move_multi_word() を実行します。lookup_handler() はpatsという構造体を2分探索する単純な関数ですから、大事なのはpatsの中身と変更方法です。
ところがpatsをどうやって作るか?については、手がかりがありません。GCCはクソコードすぎて辛い。とりあえずCODE_FOR_ なんとか、という部分と、ena[] = HAVE_ の部分は自動生成コードっぽいですから、適当にキーワードを考えてgrepします。
#### ChangeLogが良く引っかかってうざいので排除推奨 $ grep -r CODE_FOR_%s | grep -v ^ChangeLog genopinit.c: fprintf (s_file, " { %#08x, CODE_FOR_%s },\n", p->sort_num, p->name); gencodes.c: printf (",\n CODE_FOR_%s = CODE_FOR_nothing", name); gencodes.c: printf (",\n CODE_FOR_%s = %d", name, info->index); genemit.c: printf (" return CODE_FOR_%s;\n", instance->name); gentarget-def.c: printf ("#undef TARGET_CODE_FOR_%s\n", upper_name); gentarget-def.c: printf ("#define TARGET_CODE_FOR_%s ", upper_name); gentarget-def.c: printf ("CODE_FOR_%s\n", name); $ grep -r HAVE_ | grep 'ena\[' | grep -v ^ChangeLog genopinit.c: fprintf (s_file, " ena[%u] = HAVE_%s;\n", i, p->name);
コードの形と見比べるとgenopinit.cがpatsの作成者で間違いなさそうです。このプログラムはcc1の一部ではなく、補助ツールgenopinitのコードです。ツールの使い方がわからないのでビルドログを見ます。
# build_gcc/Makefile
s-opinit: $(MD_DEPS) build/genopinit$(build_exeext) insn-conditions.md
$(RUN_GEN) build/genopinit$(build_exeext) $(md_file) \
insn-conditions.md -htmp-opinit.h -ctmp-opinit.c
# ビルドログ
build/genopinit ./gcc/gcc/common.md ./gcc/gcc/config/riscv/riscv.md \
insn-conditions.md -htmp-opinit.h -ctmp-opinit.c
いくつか *.mdファイルを指定するだけです。genopinitのmain() 関数を見ると、2つの条件DEFINE_INSNもしくはDEFINE_EXPANDのときだけ gen_insn() を呼びます。
// gcc/genopinit.c
int
main (int argc, const char **argv)
{
FILE *h_file, *s_file;
unsigned int i, j, n, last_kind[5];
optab_pattern *p;
progname = "genopinit";
if (NUM_OPTABS > 0xffff || MAX_MACHINE_MODE >= 0xff)
fatal ("genopinit range assumptions invalid");
if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
return (FATAL_EXIT_CODE);
h_file = open_outfile (header_file_name);
s_file = open_outfile (source_file_name);
/* Read the machine description. */
md_rtx_info info;
while (read_md_rtx (&info))
switch (GET_CODE (info.def))
{
case DEFINE_INSN:
case DEFINE_EXPAND:
gen_insn (&info); //★★これ
break;
default:
break;
}
/* Sort the collected patterns. */
patterns.qsort (pattern_cmp);
...
この記事を読むような方は、既に何を変更すれば良いかご存知かもしれませんが、せっかくなのでgenopinitの動きを追います。gen_insn() にブレークを掛けて、DEFINE_INSNとDEFINE_EXPANDのときに何が起きるのか見ます。
$ gdb build_gcc/build/genopinit (gdb) b gen_insn Breakpoint 1 at 0x402920: file ./gcc/gcc/genopinit.c, line 45. (gdb) r common.md config/riscv/riscv.md build_gcc/insn-conditions.md -hhhh -cccc Breakpoint 1, gen_insn (info=0x7fffffffd970) at ./gcc/gcc/genopinit.c:45 45 if (find_optab (&p, XSTR (info->def, 0))) (gdb) p *info $3 = { def = 0x4bc030, loc = { filename = 0x7fffffffde93 "config/riscv/riscv.md", ★★ファイル名 lineno = 431, ★★行番号 colno = 1 }, index = 1 } (gdb) p info->def->code $5 = DEFINE_INSN
;; gcc/config/riscv/riscv.md:431
★★*.mdファイルのdefine_insnと紐付いている
(define_insn "add<mode>3"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(plus:ANYF (match_operand:ANYF 1 "register_operand" " f")
(match_operand:ANYF 2 "register_operand" " f")))]
"TARGET_HARD_FLOAT"
"fadd.<fmt>\t%0,%1,%2"
[(set_attr "type" "fadd")
(set_attr "mode" "<UNITMODE>")])
残ったのはDEFINE_EXPANDです。DEFINE_INSNでは止まってほしくないので、こういうときは条件付きブレークが便利です。
(gdb) b gen_insn if info->def->code == DEFINE_EXPAND Note: breakpoint 1 also set at pc 0x402920. Breakpoint 2 at 0x402920: file ./gcc/gcc/genopinit.c, line 45. (gdb) c Breakpoint 2, gen_insn (info=0x7fffffffd970) at ./gcc/gcc/genopinit.c:45 45 if (find_optab (&p, XSTR (info->def, 0))) (gdb) p *info $6 = { def = 0x4c4ef0, loc = { filename = 0x7fffffffde93 "config/riscv/riscv.md", ★★ファイル名 lineno = 635, ★★行番号 colno = 1 }, index = 352 }
;; gcc/config/riscv/riscv.md:635
★★*.mdファイルのdefine_expandと紐付いている
(define_expand "<u>mulditi3"
[(set (match_operand:TI 0 "register_operand")
(mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
(any_extend:TI (match_operand:DI 2 "register_operand"))))]
"TARGET_MUL && TARGET_64BIT"
{
rtx low = gen_reg_rtx (DImode);
emit_insn (gen_muldi3 (low, operands[1], operands[2]));
rtx high = gen_reg_rtx (DImode);
emit_insn (gen_<u>muldi3_highpart (high, operands[1], operands[2]));
emit_move_insn (gen_lowpart (DImode, operands[0]), low);
emit_move_insn (gen_highpart (DImode, operands[0]), high);
DONE;
})
つまり *.mdファイルにdefine_expandもしくはdefine_insnを定義すればpatsの中身が増えてlookup_handler() に引っかかるはずです。riscv.mdに既に存在するmovsiやmovdiを真似して追加します。
;; config/riscv/riscv.md
(define_expand "movv64si"
[(set (match_operand:V64SI 0 "")
(match_operand:V64SI 1 ""))]
""
{
if (riscv_legitimize_move (V64SImode, operands[0], operands[1])) //★★この呼び出しは正しいかわからないが、とりあえずそのまま
DONE;
})
実行してみると、先程追加したコードが引っかかってifの条件が成立し、emit_insn() が呼び出されます。念のためにコードを再掲します。
// gcc/expand.c
rtx_insn *
emit_move_insn_1 (rtx x, rtx y)
{
machine_mode mode = GET_MODE (x);
enum insn_code code;
gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
code = optab_handler (mov_optab, mode);
if (code != CODE_FOR_nothing) //★★CODE_FOR_nothingになるのが怪しい
return emit_insn (GEN_FCN (code) (x, y)); //★★こっちに行けばいいのだろうか??
...
// build_gcc/insn-opinit.h
/* Given an enum insn_code, access the function to construct
the body of that kind of insn. */
#define GEN_FCN(CODE) (insn_data[CODE].genfun)
実行してemit_move_insn_1() でブレークし、gdbで値をダンプすると下記のようになっているはずです。gen_movv64si() 関数が突然出てきますが、これは自動生成された関数です。
(gdb) p code $4 = CODE_FOR_movv64si ★★nothingからV64SIに変わった (gdb) p insn_data[code].genfun $5 = {func = 0x216ecea <gen_movv64si(rtx_def*, rtx_def*)>} (gdb) p insn_data[code] $6 = { name = 0x2d764ed "movv64si", output = { single = 0x0, multi = 0x0, function = 0x0 }, genfun = { func = 0x216eec5 <gen_movv64si(rtx_def*, rtx_def*)> }, operand = 0x2d73ef0 <operand_data+10512>, n_generator_args = 2 '\002', n_operands = 2 '\002', n_dups = 0 '\000', n_alternatives = 0 '\000', output_format = 0 '\000' }
// build_gcc/insn-emit.c
/* ./gcc/gcc/config/riscv/riscv.md:1308 */
rtx
gen_movv64si (rtx operand0,
rtx operand1)
{
rtx_insn *_val = 0;
start_sequence ();
{
rtx operands[2];
operands[0] = operand0;
operands[1] = operand1;
#define FAIL return (end_sequence (), _val)
#define DONE return (_val = get_insns (), end_sequence (), _val)
#line 1312 "./gcc/gcc/config/riscv/riscv.md"
{
if (riscv_legitimize_move (V64SImode, operands[0], operands[1])) //★★この呼び出しは正しい?
DONE;
}
#undef DONE
#undef FAIL
operand0 = operands[0];
(void) operand0;
operand1 = operands[1];
(void) operand1;
}
emit_insn (gen_rtx_SET (operand0,
operand1));
_val = get_insns ();
end_sequence ();
return _val;
}
近くにあったdefine_expand("movdi") をコピーして作ったので、riscv_legitimize_move() を呼んでいますが、この実装が正しいかどうか今はわかりません。動作がおかしいようなら、後で調べたり、直したりする必要があるかもしれません。
現状、生成されたRTLを見た限りsetのdestination側のmachine modeがmem/c:SIからmem/c:V64SIに変わっていますし、問題なさそうに見えます。
(insn 11 7 10 2 (set (reg:V64SI 109 [ v1 ])
(asm_operands/v:V64SI ("vlw.v %0, %1
") ("=&v") 0 [
(mem/c:SI (plus:SI (reg/f:SI 99 virtual-stack-vars)
(const_int -360 [0xfffffffffffffe98])) [1 b+40 S4 A64])
]
[
(asm_input:SI ("A") b.c:7)
]
[] b.c:7)) "b.c":7:2 -1
(nil))
(insn 10 11 0 2 (set (mem/c:V64SI (reg/f:SI 108) [1 v1+0 S256 A2048])
(reg:V64SI 109 [ v1 ])) "b.c":7:2 -1
(nil))
;;★★(参考)define_expand追加前のRTL
(insn 10 74 11 2 (set (mem/c:SI (reg/f:SI 108) [1 v1+0 S4 A2048])
(subreg:SI (reg:V64SI 109 [ v1 ]) 0)) "b.c":7:2 -1
(nil))
しかし残念ながらdefine_expandの追加だけではダメで、変更後はこんなエラーが出ます。
b.c: In function '_start': b.c:25:1: error: unrecognizable insn: 25 | } | ^ (insn 10 11 0 2 (set (mem/c:V64SI (reg/f:SI 108) [1 v1+0 S256 A2048]) (reg:V64SI 109 [ v1 ])) "b.c":7:2 -1 (nil)) during RTL pass: vregs dump file: b.c.237r.vregs b.c:25:1: internal compiler error: in extract_insn, at recog.c:2294
続きはまた次回。
< | 2020 | > | ||||
<< | < | 05 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | - | 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.)