Slide 1

Slide 1 text

あなたとJITASM, 今すぐアセンブ
 ル
 @s_isshiki1969 sisshiki1969 monochrome JIT

Slide 2

Slide 2 text

アセンブラ is 何 int main() { return 42; } main: push rbp mov rbp, rsp mov eax, 42 pop rbp ret 55 48 89 e5 b8 2a 00 00 00 5d c3 0f 1f 00 hoge.c hoge.s hoge.o これ アセンブリのテキストファイルを機械語へ変換

Slide 3

Slide 3 text

ダイナミックアセンブラ ● 「実行時に機械語を吐くプログラム」のためのライブラリ ● メモリ上に機械語を格納するバッファを確保し、そこへ機械語を生成して いく ● 応用例としてはJITコンパイラなど ● 例:Xbyak(C++、テンプレート)   DynASM(C、プリプロセッサ)   monoasm(Rust、手続きマクロ)

Slide 4

Slide 4 text

monoasm ● https://github.com/sisshiki1969/monoasm ● Rustで書かれたx86-64専用ダイナミック・アセンブラ ● ①ランタイム ②マクロ定義 で構成 ● Rubyの自作JITコンパイラ(monoruby)のために開発 ○ RubyKaigi 2024, 2025で発表 ● mov・四則演算・論理演算・比較・条件分岐・浮動小数点数演算 ● SIMD命令群は未対応

Slide 5

Slide 5 text

手続きマクロ(proc macro) マクロの中身をRustコードへ変換するRustプログラム #[proc_macro] pub fn monoasm(tokens: TokenStream) -> TokenStream { let stmts = parse_macro_input!(tokens as inst::Stmts); let base = stmts.base; let mut ts = quote!(let mut jit = #base;); ts.extend(stmts.contents.into_iter().map(compile)); quote!({ #ts }).into() } monoasm!(&mut jit, movq rax, [rdi + rsi * 8 + 16]; ); jit.enc_rexw_mr( &[0x8b], Reg::from(0), Rm::ind( Reg::from(7), Disp::from_disp(16), Scale::S1(3, Reg::from(6)), ), );

Slide 6

Slide 6 text

コード生成:インタプリタ monoasm! { &mut self.jit, movq r15, (self.dispatch.as_ptr()); movzxb rax, [r13 + (OPECODE)]; addq r13, 16; jmp [r15 + rax * 8]; }; r13: PC(現在処理中のバイトコードを指す) self.dispatch: ジャンプテーブルの先頭アドレス

Slide 7

Slide 7 text

コード生成:JITコンパイラ match kind { BinOpK::Add => { let overflow = self.jit.label(); match mode { OpMode::RR(_, _) => { monoasm!( &mut self.jit, subq R(lhs_r), 1; addq R(lhs_r), R(rhs_r); jo overflow; ); } OpMode::RI(_, i) | OpMode::IR(i, _) => { monoasm!( &mut self.jit, addq R(lhs_r), ((*i as i64) << 1); jo overflow; ); } } self.jit.select_page(1); monoasm!( &mut self.jit, overflow: movq rdi, (Value::symbol("_arith_overflow").id()); jmp deopt; ); self.jit.select_page(0); 複数のコードページを使い分ける

Slide 8

Slide 8 text

コード生成 match kind { BinOpK::Add => { let overflow = self.jit.label(); match mode { OpMode::RR(_, _) => { monoasm!( &mut self.jit, subq R(lhs_r), 1; addq R(lhs_r), R(rhs_r); jo overflow; ); } OpMode::RI(_, i) | OpMode::IR(i, _) => { monoasm!( &mut self.jit, addq R(lhs_r), ((*i as i64) << 1); jo overflow; ); } } self.jit.select_page(1); monoasm!( &mut self.jit, overflow: movq rdi, (Value::symbol("_arith_overflow").id()); jmp deopt; ); self.jit.select_page(0); ラベルを定義 ラベルを使用 ラベルを実アドレスにバインド let mask = 0x8000_0000_0000_0000u64 as i64; let imm = self.jit.const_i64(mask); monoasm!( &mut self.jit, xorps xmm(dst), [rip + imm]; ); データ領域にPC相対アクセス ()内にRustの式を書ける

Slide 9

Slide 9 text

Array#size fn array_size(bb: &mut BBContext, ir: &mut AsmIr, _: &JitContext, _: &Store, callsite: &CallSiteInfo, _: ClassId) -> bool { if !callsite.is_simple() { return false; } let dst = callsite.dst; ir.inline(move |gen, _, _| { monoasm! { &mut gen.jit, movq rax, [rdi + (RVALUE_OFFSET_ARY_CAPA)]; cmpq rax, (ARRAY_INLINE_CAPA); cmovgtq rax, [rdi + (RVALUE_OFFSET_HEAP_LEN)]; salq rax, 1; orq rax, 1; } }); bb.reg2acc_fixnum(ir, GP::Rax, dst); true }

Slide 10

Slide 10 text

Array#size monoasm! { &mut gen.jit, movq rax, [rdi + (RVALUE_OFFSET_ARY_CAPA)]; cmpq rax, (ARRAY_INLINE_CAPA); cmovgtq rax, [rdi + (RVALUE_OFFSET_HEAP_LEN)]; salq rax, 1; orq rax, 1; } [0] [1] [2] [3] [4] Capa <= 5 Capa Ptr Capa > 5 [0] [1] [2] ... [Len-1] Array object Array object Capa Len

Slide 11

Slide 11 text

Math#sqrt fn math_sqrt(bb: &mut BBContext, ir: &mut AsmIr, _: &JitContext, _: &Store, callsite: &CallSiteInfo, _: ClassId) -> bool { if !callsite.is_simple() { return false; } let CallSiteInfo { args, dst, .. } = *callsite; let deopt = ir.new_deopt(bb); let fsrc = bb.fetch_float_for_xmm(ir, args, deopt).enc(); if let Some(dst) = dst { let fret = bb.xmm_write_enc(dst); ir.inline(move |gen, _, _| { monoasm!( &mut gen.jit, sqrtsd xmm(fret), xmm(fsrc); ); }); } true }

Slide 12

Slide 12 text

JavaScript from “Building a baseline JIT for Lua automatically”, Blog of Haoran Xu Maglev

Slide 13

Slide 13 text

Python ● PEP 659 – Specializing Adaptive Interpreter ● PEP 744 – JIT Compilation ● Copy-and-patch compilation: a fast compilation algorithm for high-level languages and bytecode adaptive interpreter Baseline JIT tier-up deopt Interpreter specialize

Slide 14

Slide 14 text

Ruby ● YJIT (Lazy Basic Block Versioning) ● ZJIT ● monoruby (https://github.com/sisshiki1969/monoruby) Interpreter Optimizing JIT tier-up deopt

Slide 15

Slide 15 text

Optcarrot benchmark (~3000 frame)

Slide 16

Slide 16 text

目的:機械語を書く 手段:コンパイラを書く あなたとJITASM, 今すぐアセンブ
 ル