目次: GCC
最初に書いておくと、vuseから追う解析は正解には至りませんでしたが、試行錯誤のあとも一応残しておきます。再現環境とデバッグの準備ができました。エラーが発生する箇所を調べます。
// gcc/gcc/tree-ssa-forwprop.c
static bool
simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2)
{
gimple *stmt1, *stmt2 = gsi_stmt (*gsi_p);
tree vuse = gimple_vuse (stmt2);
if (vuse == NULL)
return false;
stmt1 = SSA_NAME_DEF_STMT (vuse); //★★ここでエラー
// gcc/gcc/tree.h
/* Returns the statement which defines this SSA name. */
#define SSA_NAME_DEF_STMT(NODE) SSA_NAME_CHECK (NODE)->ssa_name.def_stmt
// gcc/gcc/tree-check.h
#define SSA_NAME_CHECK(t) TREE_CHECK (t, SSA_NAME)
// gcc/gcc/tree.h
/* When checking is enabled, errors will be generated if a tree node
is accessed incorrectly. The macros die with a fatal error. */
#if defined ENABLE_TREE_CHECKING && (GCC_VERSION >= 2007)
//★★enable-checking=yesだとこちらが有効になるので、エラーが発生する
#define TREE_CHECK(T, CODE) \
(tree_check ((T), __FILE__, __LINE__, __FUNCTION__, (CODE)))
...
#else /* not ENABLE_TREE_CHECKING, or not gcc */
...
//★★enable-checking=releaseだとこちらが有効になるので、エラーが発生しない
#define TREE_CHECK(T, CODE) (T)
// gcc/gcc/tree.h
//★★
// SSA_NAME_DEF_STMT (vuse)
// TREE_CHECK (vuse, SSA_NAME)
// tree_check (vuse, __FILE__, __LINE__, __FUNCTION__, SSA_NAME)
//
// vuseのTREE_CODEはVAR_DECL
inline tree
tree_check (tree __t, const char *__f, int __l, const char *__g, tree_code __c)
{
if (TREE_CODE (__t) != __c) //★★TREE_CODEがSSA_NAMEではないので、このチェックに引っかかる
tree_check_failed (__t, __f, __l, __g, __c, 0);
return __t;
}
正直、これを見ても「だから何??」ですよね。
GCC Internalsを見てもいまいち要領を得ませんが、変数への参照を表しているようです。エラーの原因となっているので、調べるしかありません。どこから来るのでしょうか?
// gcc/gcc/tree-ssa-forwprop.c
static bool
simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2)
{
//★★stmt2はイテレータgsi_pが指している先頭の要素
gimple *stmt1, *stmt2 = gsi_stmt (*gsi_p);
//★★vuseはgimple stmt2をgimple_statement_with_memory_opsにキャストしたときのvuseメンバ
tree vuse = gimple_vuse (stmt2);
if (vuse == NULL)
return false;
stmt1 = SSA_NAME_DEF_STMT (vuse); //★★ここでエラー
// gcc/gcc/gimple.h
/* Return the single VUSE operand of the statement G. */
static inline tree
gimple_vuse (const gimple *g)
{
const gimple_statement_with_memory_ops *mem_ops_stmt =
dyn_cast <const gimple_statement_with_memory_ops *> (g);
if (!mem_ops_stmt)
return NULL_TREE;
return mem_ops_stmt->vuse;
}
// gcc/gcc/gimple-iterator.h
/* Return the current stmt. */
static inline gimple *
gsi_stmt (gimple_stmt_iterator i)
{
return i.ptr;
}
このvuseメンバを設定するのはどこでしょうか?ソースコードから探すのは困難そうなので、watchpointで探しましょう。
$ gdb /path/to/build/_install/libexec/gcc/x86_64-unknown-elf/8.3.0/cc1 (gdb) r -quiet a.c -mtune=generic -march=x86-64 -g -O2 -Wall -std=c99 -o zzzzzzzz.s ...エラーが出ることを確認する... (gdb) b tree-ssa-forwprop.c:1246 Breakpoint 1 at 0x11360b5: file ../../gcc/tree-ssa-forwprop.c, line 1246. (gdb) r Breakpoint 1, simplify_builtin_call (gsi_p=0x7fffffffd680, callee2=0x7ffff74af300) at ../../gcc/tree-ssa-forwprop.c:1246 1246 stmt1 = SSA_NAME_DEF_STMT (vuse); (gdb) p *stmt2 $2 = {code = GIMPLE_CALL, no_warning = 0, visited = 0, nontemporal_move = 0, ... ★★code = GIMPLE_CALLなのでgcallにキャストしてもう一回ダンプ (gdb) p *(gcall *)stmt2 $3 = {<gimple_statement_with_memory_ops_base> = {<gimple_statement_with_ops_base> = {<gimple> = {code = GIMPLE_CALL, no_warning = 0, visited = 0, nontemporal_move = 0, plf = 0, modified = 0, has_volatile_ops = 0, pad = 0, subcode = 0, uid = 0, location = 2147483655, num_ops = 5, bb = 0x7ffff7475410, next = 0x7ffff7476118, prev = 0x7ffff7599b90}, use_ops = 0x7ffff75b14f8}, vdef = 0x7ffff7ffbf30, vuse = 0x7ffff7ffbf30}, ★★これ★★ call_used = {anything = 1, nonlocal = 0, ... ★★stmt2->vuseのアドレスを調べる (gdb) p &((gcall *)stmt2)->vuse $4 = (tree *) 0x7ffff75b30c8
エラーが発生するときのvuseは0x7ffff7ffbf30で、vuseを持っている変数 ((gcall *)stmt2)->vuseのアドレスは0x7ffff75b30c8です。デバッグ時、アドレスは毎回同じになることを利用して、先程調べたアドレスにwatchpointを設定し、何か値が書き込まれたら止めます。
★★stmt2->vuseを書き換える箇所を特定するためwatchpointを設定する (gdb) watch *(int *)0x7ffff75b30c8 (gdb) r ...memset系で止まるところは無視... Old value = 0 New value = -134234320 gimple_set_vuse (g=0x7ffff75b3090, vuse=0x7ffff7ffbf30) at ../../gcc/gimple.h:2084 2084 }
それらしき関数gimple_set_vuse() が見つかりました。vuseの値も0x7ffff7ffbf30で関数simplify_builtin_call() で観測した値と一致しており、別の用事で書き換えられたわけではなさそうです。
さらに追っていくと、vuseはcfun->gimple_df->vopが元になっていることがわかり、cfun->gimple_df->vopはcreate_vop_var() によって生成されていることがわかるのですが、そこで行き詰まってしまいます。GCCはエラーメッセージからエラーが発生した箇所はすぐにわかります。しかしエラーの原因はわからないことがほとんどです。GCCのデバッグの辛いところですね。
別のアプローチが必要そうです。
< | 2021 | > | ||||
<< | < | 04 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | 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 | - |
合計:
本日: