目次: GCC
前回(2020年7月6日の日記参照)はエラー出力のコードを追いましたが、エラーの原因とは無関係でした。
問題解決のヒントはRTLのダンプファイルにあります。エラーを起こす282r.reloadの1つ前のパス281r.iraのダンプ(オプション --dump-rtl-allで出力できます)を見ると下記のような記述が見つかります。
... Popping a57(r107,l0) -- assign reg 26 Popping a58(r106,l0) -- assign reg 27 Popping a59(r105,l0) -- assign reg 64 ★64はv0レジスタの番号 Popping a60(r104,l0) -- assign reg 65 ★65はv1レジスタの番号 Popping a61(r165,l0) -- assign reg 5 ...
レジスタ番号にベクトルレジスタが出てきます。とても怪しいですね。このメッセージを出しているコードを追います。
// gcc/ira-color.c
/* Pop the coloring stack and assign hard registers to the popped
allocnos. */
static void
pop_allocnos_from_stack (void)
{
ira_allocno_t allocno;
enum reg_class aclass;
for (;allocno_stack_vec.length () != 0;)
{
allocno = allocno_stack_vec.pop ();
aclass = ALLOCNO_CLASS (allocno);
if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
{
fprintf (ira_dump_file, " Popping"); //★★Poppingはここで出力
ira_print_expanded_allocno (allocno);
fprintf (ira_dump_file, " -- ");
}
if (aclass == NO_REGS)
{
ALLOCNO_HARD_REGNO (allocno) = -1;
ALLOCNO_ASSIGNED_P (allocno) = true;
ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (allocno) == NULL);
ira_assert
(ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (allocno) == NULL);
if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
fprintf (ira_dump_file, "assign memory\n");
}
else if (assign_hard_reg (allocno, false)) //★★レジスタの割当をする
{
if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
fprintf (ira_dump_file, "assign reg %d\n",
ALLOCNO_HARD_REGNO (allocno)); //★★assign reg 64はここで出力
}
// gcc/ira-build.c
/* This recursive function outputs allocno A and if it is a cap the
function outputs its members. */
void
ira_print_expanded_allocno (ira_allocno_t a)
{
basic_block bb;
//★★Poppingの後ろのaxx(rxx, lxx) を出力している
fprintf (ira_dump_file, " a%d(r%d", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
fprintf (ira_dump_file, ",b%d", bb->index);
else
fprintf (ira_dump_file, ",l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop_num);
if (ALLOCNO_CAP_MEMBER (a) != NULL)
{
fprintf (ira_dump_file, ":");
ira_print_expanded_allocno (ALLOCNO_CAP_MEMBER (a));
}
fprintf (ira_dump_file, ")");
}
// gcc/ira-int.h
#define ALLOCNO_NUM(A) ((A)->num)
#define ALLOCNO_REGNO(A) ((A)->regno)
...
#define ALLOCNO_HARD_REGNO(A) ((A)->hard_regno)
...
いずれもira_allocno_tのメンバを参照する。 (例) Popping a59(r105,l0) -- assign reg 64 - a59 : num - r105 : regno - l0 : loop_num - reg 64: hard_regno
割り当てたレジスタ番号はallocno->hard_regnoに格納されているようです。割り当てる関数はメッセージ出力のすぐ上にあるassign_hard_reg() 関数が行います。
// gcc/ira-color.c
static bool
assign_hard_reg (ira_allocno_t a, bool retry_p)
{
HARD_REG_SET conflicting_regs[2], profitable_hard_regs;
int i, j, hard_regno, best_hard_regno, class_size;
...
best_hard_regno = -1;
...
/* We don't care about giving callee saved registers to allocnos no
living through calls because call clobbered registers are
allocated first (it is usual practice to put them first in
REG_ALLOC_ORDER). */
mode = ALLOCNO_MODE (a);
for (i = 0; i < class_size; i++)
{
hard_regno = ira_class_hard_regs[aclass][i];
#ifdef STACK_REGS
if (no_stack_reg_p
&& FIRST_STACK_REG <= hard_regno && hard_regno <= LAST_STACK_REG)
continue;
#endif
if (! check_hard_reg_p (a, hard_regno,
conflicting_regs, profitable_hard_regs)) //★★このチェックを通過すると、割り当てる候補になる
continue;
cost = costs[i];
full_cost = full_costs[i];
if (!HONOR_REG_ALLOC_ORDER)
{
...
}
if (min_cost > cost)
min_cost = cost;
if (min_full_cost > full_cost
|| (!HONOR_REG_ALLOC_ORDER && min_full_cost == full_cost
&& best_hard_regno > hard_regno))
{
min_full_cost = full_cost;
best_hard_regno = hard_regno; //★★どのハードウェアレジスタに割り当てるか決まる
ira_assert (hard_regno >= 0);
}
if (internal_flag_ira_verbose > 5 && ira_dump_file != NULL)
fprintf (ira_dump_file, "(%d=%d,%d) ", hard_regno, cost, full_cost);
}
...
if (! retry_p)
restore_costs_from_copies (a);
ALLOCNO_HARD_REGNO (a) = best_hard_regno; //★★allocno->hard_regnoに格納
ALLOCNO_ASSIGNED_P (a) = true;
if (best_hard_regno >= 0)
update_costs_from_copies (a, true, ! retry_p);
ira_assert (ALLOCNO_CLASS (a) == aclass);
/* We don't need updated costs anymore. */
ira_free_allocno_updated_costs (a);
return best_hard_regno >= 0; //★★best_hard_regnoに何か値が入っていれば成功
}
関数assign_hard_reg() は色んな複雑なことをやっていますが、レジスタ割当の決め手は最後のループです。ループ内では基本的に先頭のレジスタから使います(※)。ループの先頭にcheck_hard_reg_p() というチェックがあって、このチェックをパスしたレジスタが割当の候補になります。
(※)厳密にはどのレジスタから割当を試みるかは、ira_class_hard_regsで決まります。この配列を初期化するのはsetup_class_hard_regs() で、コードを見た感じだと順序を変更することもできるようです。今回、詳細は調べていません。
// gcc/ira-color.c
/* Return true if HARD_REGNO is ok for assigning to allocno A with
PROFITABLE_REGS and whose objects have CONFLICT_REGS. */
static inline bool
check_hard_reg_p (ira_allocno_t a, int hard_regno,
HARD_REG_SET *conflict_regs, HARD_REG_SET profitable_regs)
{
int j, nwords, nregs;
enum reg_class aclass;
machine_mode mode;
aclass = ALLOCNO_CLASS (a);
mode = ALLOCNO_MODE (a);
if (TEST_HARD_REG_BIT (ira_prohibited_class_mode_regs[aclass][mode],
hard_regno)) //★★このチェックを通過したら割当候補
return false;
/* Checking only profitable hard regs. */
if (! TEST_HARD_REG_BIT (profitable_regs, hard_regno)) //★★このチェックは基本的に通過するはず
return false;
...
// gcc/ira-int.h
#define ALLOCNO_MODE(A) ((A)->mode)
...
#define ALLOCNO_CLASS(A) ((A)->aclass)
...
ベクトルレジスタ(番号64以降)がcheck_hard_reg_p() に渡されるときのaclass, modeの値を見ておきます。今はどうでも良いんですが、次の章で使います。
ブレークに設定する条件はif hard_regno == 64 (gdb) p aclass $4 = ALL_REGS (gdb) p mode $5 = E_SFmode
チェックのキモはira_prohibited_class_mode_regsで、レジスタ番号が示す位置のビットがセットされていると、チェックに引っかかって、割り当て候補から外れます。この配列を初期化している箇所はsetup_prohibited_class_mode_regs() です。
// gcc/ira.c
static void
setup_prohibited_class_mode_regs (void)
ira_prohibited_class_mode_regs
{
int j, k, hard_regno, cl, last_hard_regno, count;
for (cl = (int) N_REG_CLASSES - 1; cl >= 0; cl--)
{
temp_hard_regset = reg_class_contents[cl] & ~no_unit_alloc_regs;
for (j = 0; j < NUM_MACHINE_MODES; j++)
{
count = 0;
last_hard_regno = -1;
CLEAR_HARD_REG_SET (ira_prohibited_class_mode_regs[cl][j]);
for (k = ira_class_hard_regs_num[cl] - 1; k >= 0; k--)
{
hard_regno = ira_class_hard_regs[cl][k];
if (!targetm.hard_regno_mode_ok (hard_regno, (machine_mode) j)) //★★hard_regno_mode_ok() がtrueであれば割当候補になる
SET_HARD_REG_BIT (ira_prohibited_class_mode_regs[cl][j],
hard_regno);
else if (in_hard_reg_set_p (temp_hard_regset,
(machine_mode) j, hard_regno))
{
last_hard_regno = hard_regno;
count++;
}
}
ira_class_singleton[cl][j] = (count == 1 ? last_hard_regno : -1);
}
}
}
// gcc/config/riscv/riscv.h
enum reg_class
{
NO_REGS, /* no registers in set */
SIBCALL_REGS, /* registers used by indirect sibcalls */
JALR_REGS, /* registers used by indirect calls */
GR_REGS, /* integer registers */
FP_REGS, /* floating-point registers */
VP_REGS, /* vector registers */
FRAME_REGS, /* arg pointer and frame pointer */
ALL_REGS, /* all registers */
LIM_REG_CLASSES /* max value + 1 */
};
#define N_REG_CLASSES (int) LIM_REG_CLASSES
配列の名前prohibitedの通り、この配列のビットがセットされているレジスタは割り当て「禁止」です。最初に配列のビットをクリアし(許可状態)、条件targetm.hard_regno_mode_ok() がtrueだったら変更せず(許可状態のまま)、falseだったらビットをセット(禁止状態)します。
次回は原因を修正します。
< | 2020 | > | ||||
<< | < | 07 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | 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.)