自作Binary Translationエミュレータの実装、浮動小数点レジスタへのロードストア命令の実装が完了したので、次は演算命令の実装だ。簡単なところから、FADD.D
(倍精度浮動小数点加算命令)から実装して行きたい。
さて、C++で記述されたsoftfloatをどのようにRustから呼び出すのかという話だが、昔はC++とRustをインタフェースする機能を使って実装していたが、いろいろ調べているとsoftfloatのRustラッパーがあったのでこれを使わせてもらうことにした。
Binary Translationにおいて、浮動小数点演算命令はヘルパー関数を呼び出すことによって実現する。
pub fn translate_fadd_d(inst: &InstrInfo) -> Vec<TCGOp> { let rs1_addr: usize = get_rs1_addr!(inst.inst) as usize; let rs2_addr: usize = get_rs2_addr!(inst.inst) as usize; let rd_addr: usize = get_rd_addr!(inst.inst) as usize; let rs1 = Box::new(TCGv::new_reg(rs1_addr as u64)); let rs2 = Box::new(TCGv::new_reg(rs2_addr as u64)); let rd = Box::new(TCGv::new_reg(rd_addr as u64)); let fadd_d = TCGOp::new_helper_call_arg3(CALL_HELPER_IDX::CALL_FADD_D_IDX as usize, *rd,*rs1, *rs2); vec![fadd_d] }
新たにヘルパー関数としてhelper_func_add_d()
を用意した。中でsoftfloat_wrapper
を使用している。
fn helper_func_fadd_d(emu: &mut EmuEnv, fd: u32, fs1: u32, fs2: u32) -> usize { println!("fadd(emu, {:}, {:}, 0x{:03x}) is called!", fd, fs1, fs2); let fs1_data = F64::from_bits(emu.m_fregs[fs1 as usize]); let fs2_data = F64::from_bits(emu.m_fregs[fs2 as usize]); let mut flag = ExceptionFlags::default(); flag.set(); let fd_data = fs1_data.add(fs2_data, RoundingMode::TiesToEven); flag.get(); let ret_flag = flag.bits(); emu.m_fregs[fd as usize] = fd_data.bits(); emu.m_csr.csrrw(CsrAddr::FFlags, ret_flag as i64); return 0; }
浮動小数点レジスタから値を読み込み、それをsoftfloat_wrapper
を使い専用のデータ型に変換する。そのうえで加算を行い、結果を浮動小数点レジスタに書き込むという仕組みだ。
フラグレジスタの取得が必要だが、これはExceptionFlags
を使って実現できるようだ。いったんデフォルト値をflags.set()
で設定し、演算後にflags.get()
ののちにflags.bits()
で取得できる(なぜこんなにややこしいかというとsoftfloatはフラグをグローバル変数に格納するからだ...理由が分かってしまう)。
このように実装した浮動小数点加算命令を実行してみる。rv64ud-p-fadd
テストパタンを利用した。
$ cargo run riscv-tests/isa/rv64ud-p-fadd
000000c0 : 08 fe 3c e5 ff 7f 00 00 48 be 03 00 00 00 00 00 000000d0 : 00 00 48 ba 00 00 00 00 00 00 00 00 48 b9 01 00 000000e0 : 00 00 00 00 00 00 ff 95 50 03 00 00 48 8b 85 20 000000f0 : 01 00 00 48 89 85 58 00 00 00 48 bf 08 fe 3c e5 reflect tb address = 0x7f12ab490000 fadd(emu, 3, 0, 0x001) is called! csrrw = 0000000000000001 helper_csrrw(emu, 11, 0, 0x001) is called! csrrw = 0000000000000000 x00 = 0000000000000000 x01 = 0000000000000000 x02 = 0000000000000000 x03 = 0000000000000003 x04 = 0000000000000000 x05 = 0000000000000108 x06 = 000000000000b109 x07 = 0000000000000000 x08 = 0000000000000000 x09 = 0000000000000000 x10 = c093480000000000 x11 = 0000000000000001 x12 = 0000000000000000 x13 = c093480000000000 x14 = 0000000000000000 x15 = 0000000000000000 x16 = 0000000000000000 x17 = 0000000000000000 x18 = 0000000000000000 x19 = 0000000000000000 x20 = 0000000000000000 x21 = 0000000000000000 x22 = 0000000000000000 x23 = 0000000000000000 x24 = 0000000000000000 x25 = 0000000000000000 x26 = 0000000000000000 x27 = 0000000000000000 x28 = 0000000000000000 x29 = 0000000000000000 x30 = 0000000000000000 x31 = 0000000000000000 f00 = c0934c6666666666 f01 = 3ff199999999999a f02 = 0000000000000000 f03 = c093480000000000 f04 = 0000000000000000 f05 = 0000000000000000 f06 = 0000000000000000 f07 = 0000000000000000 f08 = 0000000000000000 f09 = 0000000000000000 f10 = 0000000000000000 f11 = 0000000000000000 f12 = 0000000000000000 f13 = 0000000000000000 f14 = 0000000000000000 f15 = 0000000000000000 f16 = 0000000000000000 f17 = 0000000000000000 f18 = 0000000000000000 f19 = 0000000000000000 f20 = 0000000000000000 f21 = 0000000000000000 f22 = 0000000000000000 f23 = 0000000000000000 f24 = 0000000000000000 f25 = 0000000000000000 f26 = 0000000000000000 f27 = 0000000000000000 f28 = 0000000000000000 f29 = 0000000000000000 f30 = 0000000000000000 f31 = 0000000000000000 PC = 000000000000013c
テストパタン実行中にfadd(emu, 3, 0, 0x001) is called!
が呼び出され、softfloatが実行されたことが分かる。さらに浮動小数点レジスタも値が更新されているので演算自体も問題なさそうだ。上手く行った。
次は、これを加減乗除命令まで拡張していこう。