(ソースコードメモ)Qemu-aarch64

#はじめに
Qemuでアプリケーションを動かすコードをAARCH64で追ってみる。Qemuは、大まかに

- 共通コード
- [TCG (Tiny Code Generator)](https://wiki.qemu.org/Documentation/TCG)
- アーキ依存コード

からなる。ここでは、CPUの処理中心に追う。なお、メモリ管理は省いている。

#ソースコードを追ってみる。
## mainからcpu_loopまで
main関数でcpu_loopが呼ばれる。ここは、すべてのアーキで共通コードとなる。

- [main](https://github.com/qemu/qemu/blob/v4.2.0/linux-user/main.c#L617)

## cpu_loopからTranalatorOps構造体まで
ここから、アーキ依存コードとなる。cpu_loopから、TranslatorOps構造体まで追ってみる。
cpu_loopから、cpu_execに行きその中で、tcgが実行される。以下は、cpu_exec内のコードである

```C++
            tb = tb_find(cpu, last_tb, tb_exit, cflags);
            cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit);
```


- [cpu_loop](https://github.com/qemu/qemu/blob/v4.2.0/linux-user/aarch64/cpu_loop.c#L75)
   - [cpu_exec](https://github.com/qemu/qemu/blob/v4.2.0/accel/tcg/cpu-exec.c#L661) 
      - [tb_find](https://github.com/qemu/qemu/blob/v4.2.0/accel/tcg/cpu-exec.c#L395)
          - [TranslationBlock *tb_gen_code](https://github.com/qemu/qemu/blob/v4.2.0/accel/tcg/translate-all.c#L1664)

## TranslatorOps構造体からdisas_sve関数まで


- [gen_intermediate_code](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/translate.c#L11332)によるTranslateOps構造体の設定

```C++
/* generate intermediate code for basic block 'tb'.  */
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns)
{
    DisasContext dc;
    const TranslatorOps *ops = &arm_translator_ops;

    if (FIELD_EX32(tb->flags, TBFLAG_A32, THUMB)) {
        ops = &thumb_translator_ops;
    }
#ifdef TARGET_AARCH64
    if (FIELD_EX32(tb->flags, TBFLAG_ANY, AARCH64_STATE)) {
        ops = &aarch64_translator_ops;
    }
#endif

    translator_loop(ops, &dc.base, cpu, tb, max_insns);
}
```

- [aarch64_translator_ops構造体] (https://github.com/qemu/qemu/blob/v4.2.0/target/arm/translate-a64.c#L14353)

```C++
const TranslatorOps aarch64_translator_ops = {
    .init_disas_context = aarch64_tr_init_disas_context,
    .tb_start           = aarch64_tr_tb_start,
    .insn_start         = aarch64_tr_insn_start,
    .breakpoint_check   = aarch64_tr_breakpoint_check,
    .translate_insn     = aarch64_tr_translate_insn,
    .tb_stop            = aarch64_tr_tb_stop,
    .disas_log          = aarch64_tr_disas_log,
};
```
- [aarch64_tr_translate_insn](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/translate-a64.c#L14246)
   - [disas_a64_insn](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/translate-a64.c#L14361)

ここから、disas_sveが呼ばれる。(0x2の分岐処理)

```C++
/* C3.1 A64 instruction index by encoding */
static void disas_a64_insn(CPUARMState *env, DisasContext *s)
{
    uint32_t insn;

    s->pc_curr = s->base.pc_next;
    insn = arm_ldl_code(env, s->base.pc_next, s->sctlr_b);
    s->insn = insn;
    s->base.pc_next += 4;

    s->fp_access_checked = false;

    if (dc_isar_feature(aa64_bti, s)) {
        if (s->base.num_insns == 1) {
            /*
             * At the first insn of the TB, compute s->guarded_page.
             * We delayed computing this until successfully reading
             * the first insn of the TB, above.  This (mostly) ensures
             * that the softmmu tlb entry has been populated, and the
             * page table GP bit is available.
             *
             * Note that we need to compute this even if btype == 0,
             * because this value is used for BR instructions later
             * where ENV is not available.
             */
            s->guarded_page = is_guarded_page(env, s);

            /* First insn can have btype set to non-zero.  */
            tcg_debug_assert(s->btype >= 0);

            /*
             * Note that the Branch Target Exception has fairly high
             * priority -- below debugging exceptions but above most
             * everything else.  This allows us to handle this now
             * instead of waiting until the insn is otherwise decoded.
             */
            if (s->btype != 0
                && s->guarded_page
                && !btype_destination_ok(insn, s->bt, s->btype)) {
                gen_exception_insn(s, s->pc_curr, EXCP_UDEF,
                                   syn_btitrap(s->btype),
                                   default_exception_el(s));
                return;
            }
        } else {
            /* Not the first insn: btype must be 0.  */
            tcg_debug_assert(s->btype == 0);
        }
    }

    switch (extract32(insn, 25, 4)) {
    case 0x0: case 0x1: case 0x3: /* UNALLOCATED */
        unallocated_encoding(s);
        break;
    case 0x2:
        if (!dc_isar_feature(aa64_sve, s) || !disas_sve(s, insn)) {
            unallocated_encoding(s);
        }
        break;
    case 0x8: case 0x9: /* Data processing - immediate */
        disas_data_proc_imm(s, insn);
        break;
    case 0xa: case 0xb: /* Branch, exception generation and system insns */
        disas_b_exc_sys(s, insn);
        break;
    case 0x4:
    case 0x6:
    case 0xc:
    case 0xe:      /* Loads and stores */
        disas_ldst(s, insn);
        break;
    case 0x5:
    case 0xd:      /* Data processing - register */
        disas_data_proc_reg(s, insn);
        break;
    case 0x7:
    case 0xf:      /* Data processing - SIMD and floating point */
        disas_data_proc_simd_fp(s, insn);
        break;
    default:
        assert(FALSE); /* all 15 cases should be handled above */
        break;
    }

    /* if we allocated any temporaries, free them here */
    free_tmp_a64(s);

    /*
     * After execution of most insns, btype is reset to 0.
     * Note that we set btype == -1 when the insn sets btype.
     */
    if (s->btype > 0 && s->base.is_jmp != DISAS_NORETURN) {
        reset_btype(s);
    }
}
```

## disas_sveの生成コードなど
[`target/arm/sve.decode`](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/sve.decode)を[`scripts/decodetree.py`](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/sve.decode)で処理して、`build/aarch64-linux-user/target/arm/decode-sve.inc.c`を生成する。(のでビルドしないとコードが見られない。)
`sve.decode`の書き方は、[この仕様書](https://github.com/qemu/qemu/blob/v4.2.0/docs/devel/decodetree.rst)にある。[テストセット](https://github.com/qemu/qemu/tree/v4.2.0/tests/decode)も参考になる。なお、`sve.decode`を記載するためには、ARMのSVE資料等も必要になる(参考資料参照)。
また、disas_sveの追加に関しては、この[Qemuパッチ](https://lists.gnu.org/archive/html/qemu-devel/2018-05/msg04507.html)も参考になる。
余談だが、decodetree.pyスクリプトは、[QemuにSVE命令セットを導入する際にLinaroが作成した。](https://github.com/qemu/qemu/commit/568ae7efae7e4b90e213049efb8b6e4e12b47ca3)ほかの命令セットRISCV/OpenRISC等でも使われている。

#参考資料
## Qemu
- [README](https://github.com/qemu/qemu/blob/v4.2.0/docs/arm-cpu-features.rst)
- [(Wiki)Features/ARM/SVE](https://wiki.qemu.org/Features/ARM/SVE)
- ソースコード
   - [リリース履歴](https://github.com/qemu/qemu/releases)
   - ARM64関連コード
      - linux-user/aarch64
         - [cpu_loop](https://github.com/qemu/qemu/blob/v4.2.0/linux-user/aarch64/cpu_loop.c#L75)
         - [accel/tcg/cpu-exec.c](https://github.com/qemu/qemu/blob/v4.2.0/accel/tcg/cpu-exec.c#L661) 
      - target/arm
         - [monitor.c](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/monitor.c#L101)
         - [cpu64.c aarch64_max_initfn](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/cpu64.c#L602) SVEのフラグの設定など(-cpu maxの場合)
         - [sve.decode](https://github.com/qemu/qemu/blob/v4.2.0/target/arm/sve.decode)
    - 共通コード
       - linux-user
          - [main.c](https://github.com/qemu/qemu/blob/v4.2.0/linux-user/main.c)
    - TCG関連コード
       - accel/tcg
           - [cpu-exec.c](https://github.com/qemu/qemu/blob/v4.2.0/accel/tcg/cpu-exec.c#L661) 

## ARM社
- [ARM Architecture Reference Manual Supplement - The Scalable Vector Extension (SVE), for ARMv8-A](https://developer.arm.com/docs/ddi0584/latest/arm-architecture-reference-manual-supplement-the-scalable-vector-extension-sve-for-armv8-a)
   - 画面の`by clicking the following link`をクリックすると、PDF以外の添付資料がZIPでダウンロードできる。命令セットのニーモニック等は、`SVE_xml\encodingindex.xml`を参照のこと