目次: RISC-V
RISC-VではCPUが単精度浮動小数点F拡張や、倍精度浮動小数点D拡張の命令(flw, fldなど)に対応していても、mstatusのFSビットでFPUを有効にしていないと、命令実行時にIllegal Instruction例外が発生します。
QEMUはこのチェックをどこで行っているのかメモしておきます。
// qemu/target/riscv/translate.c
static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
{
/* check for compressed insn */
if (extract16(opcode, 0, 2) != 3) {
if (!has_ext(ctx, RVC)) {
gen_exception_illegal(ctx);
} else {
ctx->pc_succ_insn = ctx->base.pc_next + 2;
if (!decode_insn16(ctx, opcode)) {
/* fall back to old decoder */
decode_RV32_64C(ctx, opcode);
}
}
} else {
uint32_t opcode32 = opcode;
opcode32 = deposit32(opcode32, 16, 16,
translator_lduw(env, ctx->base.pc_next + 2));
ctx->pc_succ_insn = ctx->base.pc_next + 4;
if (!decode_insn32(ctx, opcode32)) { //★この関数がfalseを返す★
gen_exception_illegal(ctx);
}
}
}
static void gen_exception_illegal(DisasContext *ctx)
{
generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); //★Illegal Instruction例外★
}
関数decode_insn32() で命令をデコードしfalseが返ってきたら、例外を発生させます。このdecode_insn32() という関数は自動生成されており、ビルドディレクトリの下にあります。RV64向けなら下記のようなパスです。
// (build_dir)/libqemu-riscv64-softmmu.fa.p/decode-insn32.c.inc
static bool decode_insn32(DisasContext *ctx, uint32_t insn)
{
switch (insn & 0x0000007f) {
...
case 0x00000007:
/* ........ ........ ........ .0000111 */
switch ((insn >> 12) & 0x7) {
...
case 0x3:
/* ........ ........ .011.... .0000111 */
/* ../target/riscv/insn32.decode:199 */
decode_insn32_extract_i(ctx, &u.f_i, insn);
if (trans_fld(ctx, &u.f_i)) return true; //★このifが成立「しない」★
break;
...
static void decode_insn32_extract_i(DisasContext *ctx, arg_i *a, uint32_t insn)
{
a->imm = sextract32(insn, 20, 12);
a->rs1 = extract32(insn, 15, 5);
a->rd = extract32(insn, 7, 5);
}
// qemu/target/riscv/insn_trans/trans_rvd.c.inc
static bool trans_fld(DisasContext *ctx, arg_fld *a)
{
REQUIRE_FPU; //★このマクロ内でreturn false★
REQUIRE_EXT(ctx, RVD);
TCGv t0 = tcg_temp_new();
gen_get_gpr(t0, a->rs1);
tcg_gen_addi_tl(t0, t0, a->imm);
tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEQ);
mark_fs_dirty(ctx);
tcg_temp_free(t0);
return true;
}
// qemu/target/riscv/insn_trans/trans_rvf.c.inc
#define REQUIRE_FPU do {\
if (ctx->mstatus_fs == 0) \
return false; \
} while (0)
大体の仕組みは把握できましたので、実際にこの部分を通るかどうか見ましょう。
Illegal Instruction例外は実行中に何度も発生する例外ではありません。カーネル内で1度でも発生するとZephyrはログを出して止まります。すなわちQEMUも例外を発生させるのは1度だけです。
何度も通る処理だとブレーク条件が必要でややこしいですが、1度しか通らない処理なら単純にREQUIRE_FPUの行でブレークすれば良いです。条件ctx->mstatus_fs == 0が成立するかどうか簡単に確認できます。
$ gdb qemu/build/qemu-system-riscv64 (gdb) b trans_rvd.c.inc:23 Breakpoint 1 at 0x555555a858b8: file ../target/riscv/insn_trans/trans_rvd.c.inc, line 23. (gdb) r [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7ffff440c700 (LWP 1675592)] [New Thread 0x7ffff3a89700 (LWP 1675593)] *** Booting Zephyr OS build zephyr-v2.4.0-2328-g4f33cf942643 *** Running test suite test_sprintf =================================================================== START - test_sprintf_double [Switching to Thread 0x7ffff3a89700 (LWP 1675593)] Thread 3 "qemu-system-ris" hit Breakpoint 1, trans_fld (ctx=0x7ffff3a883d0, a=0x7ffff3a88290) at ../target/riscv/insn_trans/trans_rvd.c.inc:23 23 REQUIRE_FPU; (gdb) l 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 static bool trans_fld(DisasContext *ctx, arg_fld *a) 22 { 23 REQUIRE_FPU; //★ここで止めた★ 24 REQUIRE_EXT(ctx, RVD); 25 TCGv t0 = tcg_temp_new(); 26 gen_get_gpr(t0, a->rs1); 27 tcg_gen_addi_tl(t0, t0, a->imm); (gdb) p ctx->mstatus_fs $1 = 0
ちょっとしたメモのつもりが長くなってしまいました。まあいいや。
< | 2020 | > | ||||
<< | < | 12 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 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 | - | - |
合計:
本日: