Binary Translation型のRISC-Vエミュレータを作っている。これまで、Binary Translationのための中間表現であるTCGは、かなりx86に近い構造になっていたが、より一般的な構造としてTCGをより細かな粒度に変換している。新しいTCGを使ってメモリアクセス命令を書き直していこうと思う。
まずはロード命令について考える。ロード命令については、以下のようなフローを取る。
- レジスタと即値を加算して仮想アドレスを作成する
- 仮想アドレスを用いてTLBを検索し、TLBがヒットするかを確認する
- TLBがヒットすれば、TLBの情報に基づいて物理アドレスを計算する
- 物理アドレスに基づいてメモリアクセスを行う
- (2. においてヒットしなければ、)ヘルパー関数を呼び出して仮想アドレスから物理アドレスを計算しメモリアクセスを行う
この方針に基づいてロード命令を実装した。以下のようになる。
let rs1_addr = get_rs1_addr!(inst.inst); let imm_const: u64 = ((inst.inst as i32) >> 20) as u64; let rd_addr = get_rd_addr!(inst.inst); 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(); let label_tlb_match = Rc::new(RefCell::new(TCGLabel::new())); let tcg_label_tlb_match = TCGOp::new_label(Rc::clone(&label_tlb_match)); let mut tcg_lists = vec![]; // Read Register tcg_lists.push(TCGOp::tcg_get_gpr(src_addr, rs1_addr)); // Extract TLB Index and offset if imm_const != 0 { tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, src_addr, src_addr, TCGv::new_imm(imm_const))); } tcg_lists.push(TCGOp::new_3op(TCGOpcode::AND_64BIT, vaddr_low12bit, src_addr, TCGv::new_imm(0xfff))); tcg_lists.push(TCGOp::new_3op(TCGOpcode::SRL_64BIT, vaddr_tlb_idx, src_addr, TCGv::new_imm(12))); tcg_lists.push(TCGOp::new_3op(TCGOpcode::AND_64BIT, vaddr_tlb_idx, vaddr_tlb_idx, TCGv::new_imm(0xfff))); tcg_lists.push(TCGOp::new_3op(TCGOpcode::SLL_64BIT, vaddr_tlb_idx, vaddr_tlb_idx, TCGv::new_imm(3))); // Make TLB Vaddr Index Address tcg_lists.push(TCGOp::new_1op(TCGOpcode::MOVE_STACK, stack_reg)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::ADD_TLBIDX_OFFSET, tlb_byte_addr, stack_reg)); // Relative Addr of TLB tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, vaddr_tlb_idx)); // Make VAddr upper bit for compare TLB value tcg_lists.push(TCGOp::new_3op(TCGOpcode::SRL_64BIT, src_addr, src_addr, TCGv::new_imm(24))); tcg_lists.push(TCGOp::new_2op(TCGOpcode::MEM_LOAD, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::new_2op_with_label(TCGOpcode::CMP_EQ, src_addr, tlb_byte_addr, Rc::clone(&label_tlb_match))); // if TLB not hit, jump helper function tcg_lists.push(TCGOp::new_helper_call_arg4(CALL_HELPER_IDX::CALL_LOAD64_IDX as usize, TCGv::new_reg(rd_addr as u64), TCGv::new_reg(rs1_addr as u64), TCGv::new_imm(imm_const), TCGv::new_imm(inst.addr))); let zero = Box::new(TCGv::new_reg(0 as u64)); let dummy_addr = Box::new(TCGv::new_imm(0)); let label_load_excp = Rc::new(RefCell::new(TCGLabel::new())); let tcg_label_load_excp = TCGOp::new_label(Rc::clone(&label_load_excp)); tcg_lists.push(TCGOp::new_4op(TCGOpcode::EQ_EAX_64BIT, src_addr, *zero, *dummy_addr, Rc::clone(&label_load_excp))); tcg_lists.push(TCGOp::new_0op(TCGOpcode::EXIT_TB, None)); // Extract lower 12bit address and add with TLB address tcg_lists.push(tcg_label_tlb_match); tcg_lists.push(TCGOp::new_1op(TCGOpcode::MOVE_STACK, stack_reg)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::ADD_TLBADDR_OFFSET, tlb_byte_addr, stack_reg)); // Relative Addr of TLB Paddr tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, vaddr_tlb_idx)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::MEM_LOAD, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, vaddr_low12bit)); tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, TCGv::new_imm(0x80000000))); tcg_lists.push(TCGOp::new_2op(TCGOpcode::ADD_MEM_OFFSET, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::MEM_LOAD, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::tcg_set_gpr(rd_addr, tlb_byte_addr)); tcg_lists.push(tcg_label_load_excp); self.tcg_temp_free(src_addr ); self.tcg_temp_free(vaddr_low12bit); self.tcg_temp_free(vaddr_tlb_idx ); self.tcg_temp_free(stack_reg ); self.tcg_temp_free(tlb_byte_addr ); return tcg_lists;
一つずつ噛み砕いていく。
- まず、レジスタを読み出してアクセスしたい仮想アドレスを計算する。
let rs1_addr = get_rs1_addr!(inst.inst); let imm_const: u64 = ((inst.inst as i32) >> 20) as u64; let rd_addr = get_rd_addr!(inst.inst); // レジスタ読み込みのためのTCGを挿入する tcg_lists.push(TCGOp::tcg_get_gpr(src_addr, rs1_addr)); // 即値オフセットが0でなければ即値を加算するTCGを挿入する if imm_const != 0 { tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, src_addr, src_addr, TCGv::new_imm(imm_const))); }
- TLBのアドレスを計算して、TLBへのアクセスを行う。
// 仮想アドレスの[23:12]ビットを抽出してしてTLBアクセスのアドレスを算出する // TLBのアドレスエントリは64ビットなので、アドレスとして算出するために最後に3ビット左にシフトする tcg_lists.push(TCGOp::new_3op(TCGOpcode::SRL_64BIT, vaddr_tlb_idx, src_addr, TCGv::new_imm(12))); tcg_lists.push(TCGOp::new_3op(TCGOpcode::AND_64BIT, vaddr_tlb_idx, vaddr_tlb_idx, TCGv::new_imm(0xfff))); tcg_lists.push(TCGOp::new_3op(TCGOpcode::SLL_64BIT, vaddr_tlb_idx, vaddr_tlb_idx, TCGv::new_imm(3))); // TLBの存在しているベースアドレスを計算し、上記のTLBオフセットを加算しアドレスを決定する tcg_lists.push(TCGOp::new_1op(TCGOpcode::MOVE_STACK, stack_reg)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::ADD_TLBIDX_OFFSET, tlb_byte_addr, stack_reg)); // Relative Addr of TLB tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, vaddr_tlb_idx));
- TLBアクセスを行い、TLBのアドレスと対象アドレスが一致すればTLBを用いてメモリアクセスを行うパスにジャンプ、そうでなければヘルパー関数へジャンプ
// Make VAddr upper bit for compare TLB value tcg_lists.push(TCGOp::new_3op(TCGOpcode::SRL_64BIT, src_addr, src_addr, TCGv::new_imm(24))); tcg_lists.push(TCGOp::new_2op(TCGOpcode::MEM_LOAD, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::new_2op_with_label(TCGOpcode::CMP_EQ, src_addr, tlb_byte_addr, Rc::clone(&label_tlb_match)));
- ヘルパー関数の実装:仮想アドレスから物理アドレスへの変換、TLBへのアドレス登録を行う
// if TLB not hit, jump helper function tcg_lists.push(TCGOp::new_helper_call_arg4(CALL_HELPER_IDX::CALL_LOAD64_IDX as usize, TCGv::new_reg(rd_addr as u64), TCGv::new_reg(rs1_addr as u64), TCGv::new_imm(imm_const), TCGv::new_imm(inst.addr))); let zero = Box::new(TCGv::new_reg(0 as u64)); let dummy_addr = Box::new(TCGv::new_imm(0)); let label_load_excp = Rc::new(RefCell::new(TCGLabel::new())); let tcg_label_load_excp = TCGOp::new_label(Rc::clone(&label_load_excp)); tcg_lists.push(TCGOp::new_4op(TCGOpcode::EQ_EAX_64BIT, src_addr, *zero, *dummy_addr, Rc::clone(&label_load_excp))); tcg_lists.push(TCGOp::new_0op(TCGOpcode::EXIT_TB, None));
// Extract lower 12bit address and add with TLB address tcg_lists.push(tcg_label_tlb_match); tcg_lists.push(TCGOp::new_1op(TCGOpcode::MOVE_STACK, stack_reg)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::ADD_TLBADDR_OFFSET, tlb_byte_addr, stack_reg)); // Relative Addr of TLB Paddr tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, vaddr_tlb_idx)); tcg_lists.push(TCGOp::new_2op(TCGOpcode::MEM_LOAD, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, vaddr_low12bit)); tcg_lists.push(TCGOp::new_3op(TCGOpcode::ADD_64BIT, tlb_byte_addr, tlb_byte_addr, TCGv::new_imm(0x80000000))); tcg_lists.push(TCGOp::new_2op(TCGOpcode::ADD_MEM_OFFSET, tlb_byte_addr, tlb_byte_addr));
- メモリアクセスを行い、結果をレジスタに格納する
tcg_lists.push(TCGOp::new_2op(TCGOpcode::MEM_LOAD, tlb_byte_addr, tlb_byte_addr)); tcg_lists.push(TCGOp::tcg_set_gpr(rd_addr, tlb_byte_addr));
結果的に生成されたx86命令は以下のようになった。
54: Guest PC Address = 80000130 <Convert_Virtual_Address. virtual_addr=0000000080000130 : vm_mode = 0, priv_mode = 3> converted physical address = 80000130 0000000080000130:0000000080000130 Hostcode 0080bf03 : ld t5, 8(ra) 00007FA8CF9C0000 488B9510000000 mov 0x10(%rbp),%rdx 00007FA8CF9C0007 4881C208000000 add $8,%rdx 00007FA8CF9C000E 488BDA mov %rdx,%rbx 00007FA8CF9C0011 4881E3FF0F0000 and $0xFFF,%rbx 00007FA8CF9C0018 488BCA mov %rdx,%rcx 00007FA8CF9C001B 48C1E90C shr $0xC,%rcx 00007FA8CF9C001F 4881E1FF0F0000 and $0xFFF,%rcx 00007FA8CF9C0026 48C1E103 shl $3,%rcx 00007FA8CF9C002A 488BF5 mov %rbp,%rsi 00007FA8CF9C002D 488BFE mov %rsi,%rdi 00007FA8CF9C0030 4881C7A0050000 add $0x5A0,%rdi 00007FA8CF9C0037 4803F9 add %rcx,%rdi 00007FA8CF9C003A 48C1EA18 shr $0x18,%rdx 00007FA8CF9C003E 488B3F mov (%rdi),%rdi 00007FA8CF9C0041 483BFA cmp %rdx,%rdi 00007FA8CF9C0044 0F844A000000 je 0x0000_7FA8_CF9C_0094 00007FA8CF9C004A 48BF90957FFEFF7F0000 movabs $0x7FFF_FE7F_9590,%rdi 00007FA8CF9C0054 48BE1E00000000000000 movabs $0x1E,%rsi 00007FA8CF9C005E 48BA0100000000000000 movabs $1,%rdx 00007FA8CF9C0068 48B90800000000000000 movabs $8,%rcx 00007FA8CF9C0072 49B83001008000000000 movabs $0x8000_0130,%r8 00007FA8CF9C007C FF9560040000 callq *0x460(%rbp) 00007FA8CF9C0082 483B8508000000 cmp 8(%rbp),%rax 00007FA8CF9C0089 0F840A000000 je 0x0000_7FA8_CF9C_0099 00007FA8CF9C008F E97BFF2F00 jmp 0x0000_7FA8_CFCC_000F 00007FA8CF9C0094 488BF5 mov %rbp,%rsi 00007FA8CF9C0097 488BFE mov %rsi,%rdi 00007FA8CF9C009A 4881C7A0850000 add $0x85A0,%rdi 00007FA8CF9C00A1 4803F9 add %rcx,%rdi 00007FA8CF9C00A4 488B3F mov (%rdi),%rdi 00007FA8CF9C00A7 4803FB add %rbx,%rdi 00007FA8CF9C00AA 4881C700000080 add $0xFFFF_FFFF_8000_0000,%rdi 00007FA8CF9C00B1 48B80000CDCFA87F0000 movabs $0x7FA8_CFCD_0000,%rax 00007FA8CF9C00BB 4803F8 add %rax,%rdi 00007FA8CF9C00BE 488B3F mov (%rdi),%rdi 00007FA8CF9C00C1 4889BDF8000000 mov %rdi,0xF8(%rbp) 00007FA8CF9C00C8 E942FF2F00 jmp 0x0000_7FA8_CFCC_000F