FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Binary Translation型エミュレータを作る(ストア命令の実装)

RustでBinary Translation型エミュレータを作っている。Binary Translation型エミュレータの特徴は、ゲストのバイナリ形式(RISC-Vなど)をホストのバイナリ形式(x86など)に実行時に変換するタイプのエミュレータである。今回はホストとしてはx86、ゲストとしてはRISC-Vを使用しており、RISC-Vの命令を等価なx86の命令に変換して実行することで、インタプリタ型のエミュレータ型と異なり高速実行を可能としている。

算術演算命令や分岐命令は簡単な実装が完了したので、次はメモリアクセス命令を実装してみる。まずはロード命令だ。ロード命令は基本的な流れとして、

  1. 汎用レジスタからベースとなるアドレスを取得する。
  2. ベースとなるアドレスに対して命令に付属されるオフセット値を加算しアドレスとする。
  3. 汎用レジスタからストアするデータを取得する。
  4. データを計算したアドレスに対してストアする。

私の実装したBinary Translationコンパイラの出力結果を見ながら、どのようにストア命令を実行しているのかをまとめる。この考え方はQEMUでも同じような方式で動いている。

まず、ゲストマシンはRISC-Vなので、以下のようなロード命令を考える。

SD    rs2,imm(rs1)       // 64ビットストア命令
SW   rs2,imm(rs1)       // 32ビットストア命令
SH   rs2,imm(rs1)       // 16ビットストア命令
SB   rs2,imm(rs1)       // 8ビットストア命令

それぞれの命令がどのようにx86コンパイルされるのかを見ていこう。

SD    x11,0x10(x10)        // 64ビットストア命令

0x4001c961b8:  48 8b 85 50 00 00 00     movq     0x50(%rbp), %rax
0x4001c961bf:  48 01 d0                 addq     %rdx, %rax
0x4001c961c2:  48 8b 8d 58 00 00 00     movq     0x58(%rbp), %rcx
0x4001c961c9:  48 89 88 10 00 00 00     movq     %rcx, 0x10(%rax)

最初のmovqが汎用レジスタからの値の取得に相当する。%rbpに対して0x50バイト加算した場所がx10の場所であり、そこから値を取得して%raxに格納する。次のaddqは、汎用レジスタの値に対してRISC-Vのバイナリが格納されているメモリ領域を加算し、物理メモリ上でロードすべき場所を計算する。

x86命令における3行目のmovqによりストアするデータを%rcxにロードする。ここではx11なのでオフセットが0x58に設定されている。ロードした%rcxの値を次のmovqで実際にメモリにストアする。今回はx10 + 0x10なので、movq %rcx, 0x10(%rax)となっている。

同じように、すべてのサイズに対してストア命令をコーディングする。

    sw      x12, 24(x10)
0x4001c961d0:  48 8b 85 50 00 00 00     movq     0x50(%rbp), %rax
0x4001c961d7:  48 01 d0                 addq     %rdx, %rax
0x4001c961da:  48 8b 8d 60 00 00 00     movq     0x60(%rbp), %rcx
0x4001c961e1:  89 88 18 00 00 00        movl     %ecx, 0x18(%rax)

    sh      x13, 34(x10)
0x4001c961fe:  48 8b 85 50 00 00 00     movq     0x50(%rbp), %rax
0x4001c96205:  48 01 d0                 addq     %rdx, %rax
0x4001c96208:  48 8b 8d 68 00 00 00     movq     0x68(%rbp), %rcx
0x4001c9620f:  66 89 88 22 00 00 00     movw     %cx, 0x22(%rax)

    sb      x14, 41(x10)
0x4001c9622e:  48 8b 85 50 00 00 00     movq     0x50(%rbp), %rax
0x4001c96235:  48 01 d0                 addq     %rdx, %rax
0x4001c96238:  48 8b 8d 70 00 00 00     movq     0x70(%rbp), %rcx
0x4001c9623f:  88 88 29 00 00 00        movb     %cl, 0x29(%rax)

以下のようなサンプルプログラムをコンパイルし、Binary Translation型エミュレータで実行した結果を示すと以下のようになった。

    .section    .text

_start:
    lui     x10, %hi(data_region)
    addi    x10, x10, %lo(data_region)

    ld      x11, 0(x10)
    lw      x12, 4(x10)
    lh      x13, 6(x10)
    lb      x14, 7(x10)

    lw      x16, 0(x10)
    lh      x17, 0(x10)
    lb      x18, 0(x10)

    lwu     x20, 4(x10)
    lhu     x21, 6(x10)
    lbu     x22, 7(x10)

    lwu     x20, 0(x10)
    lhu     x21, 0(x10)
    lbu     x22, 0(x10)

    sd      zero, 16(x10)
    sd      zero, 24(x10)
    sd      zero, 32(x10)
    sd      zero, 40(x10)

    sd      x11, 16(x10)

    sw      x12, 24(x10)
    sw      x12, 28(x10)

    sh      x13, 34(x10)
    sh      x13, 38(x10)

    sb      x14, 41(x10)
    sb      x14, 43(x10)
    sb      x14, 45(x10)
    sb      x14, 47(x10)

    ld      x25, 16(x10)
    ld      x26, 24(x10)
    ld      x27, 32(x10)
    ld      x28, 40(x10)

    ret

#    .section    .data
data_region:
    .word   0xdeadbeef
    .word   0x01234567
.rept   100
    nop
.endr
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000000
x04 = 0000000000000000  x05 = 0000000000000000  x06 = 0000000000000000  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 0000000000000084  x11 = 01234567deadbeef
x12 = 0000000001234567  x13 = 0000000000000123  x14 = 0000000000000001  x15 = 0000000000000000
x16 = ffffffffdeadbeef  x17 = ffffffffffffbeef  x18 = ffffffffffffffef  x19 = 0000000000000000
x20 = 00000000deadbeef  x21 = 000000000000beef  x22 = 00000000000000ef  x23 = 0000000000000000
x24 = 0000000000000000  x25 = 01234567deadbeef  x26 = 0123456701234567  x27 = 0123000001230000
x28 = 0100010001000100  x29 = 0000000000000000  x30 = 0000000000000000  x31 = 0000000000000000

想定通りの結果だ。所望のメモリアドレスにストアされた値を、ロード命令でロードできていることを確認した。

f:id:msyksphinz:20200903233212p:plain