FPGA開発日記

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

Binary Translation型エミュレータを作る(Bitmapsを使ってレジスタアロケータを書き直す)

Binary Translation型のRISC-Vエミュレータを作っている。現在のTCGは、レジスタを直接指定するのではなくアロケートする仕組みになっている。このアロケーション処理をもう少し簡単に記述できるようにするため、Rustのビットマップの機能を使ってみた。

Rustのビットマップは以下のようなものだ。

docs.rs

レジスタの数だけビットマップを持っておき、アロケートすると順番にレジスタを消費する。

f:id:msyksphinz:20201031122431p:plain

まず、Bitmapは初期値ですべてのビットをTrueに設定しておく。これはBitmapがfind_first()という最初のTrueのビットを探す関数しか持っていないため、最初にすべてTrueにしておき順番に消費するという流れになっている。

    pub fn new() -> TranslateRiscv {
        let mut trans = TranslateRiscv {
            reg_bitmap: Bitmap::new()
        };
        for idx in 0..5 {
            trans.reg_bitmap.set(idx, true);
        }
        trans
    }

まず、レジスタを消費するためにtcg_temp_new()を実行する。

        let src_addr       = self.tcg_temp_new();
        let vaddr_low12bit = self.tcg_temp_new();
        let vaddr_tlb_idx  = self.tcg_temp_new();
        let stack_reg      = self.tcg_temp_new();
        let tlb_byte_addr  = self.tcg_temp_new();

これによりBitmapがアサインされ自動的に使用するレジスタが決定される。

    pub fn tcg_temp_new(&mut self) -> TCGv {
        let new_idx = match self.reg_bitmap.first_index() {
            Some(idx) => {
                self.reg_bitmap.set(idx, false);
                idx
            }
            None => panic!("New temporaries not found."),
        };
        let new_v = TCGv::new_temp(new_idx as u64);
        new_v
    }

一方でtcg_temp_free()を実行するとそのレジスタは解放される。

        self.tcg_temp_free(vaddr_low12bit);
        self.tcg_temp_free(vaddr_tlb_idx );
        self.tcg_temp_free(tlb_byte_addr );
        self.tcg_temp_free(rs2_data);

以下のようにBitmapを解放する。

    pub fn tcg_temp_free(&mut self, idx: TCGv) {
        self.reg_bitmap.set(idx.value as usize, true);
    }

x86においてどのレジスタを使用するかは、以下の関数で決定する。

    fn convert_x86_reg(temp: u64) -> X86TargetRM {
        return match temp {
            0 => X86TargetRM::RDX,
            1 => X86TargetRM::RBX,
            2 => X86TargetRM::RCX,
            3 => X86TargetRM::RSI,
            4 => X86TargetRM::RDI,
            5 => X86TargetRM::SIB,
            _ => panic!("Not supported yet")
        }
    }

これでx86の可能な限りのレジスタアサインすることができるようになった。また、TCG自体はレジスタの存在を気にする必要がなくなった。