Ruby JIT Hacking Guide https://rubykaigi.org/2023/
Ruby JITHacking Guide@k0kubun
View Slide
@k0kubun
RJIT ERBHaml SlimI maintain
What's JIT?
Ruby JITsMJITRuby 2.6
Ruby JITsYJITRuby 3.1MJITRuby 2.6
Ruby JITsYJITRuby 3.1MJITRuby 2.6RJITRuby 3.3
RailsbenchSpeedup relative to 2.5 No JIT (s/s)00.511.52Ruby 2.5 Ruby 2.6 Ruby 2.7 Ruby 3.0 Ruby 3.1 Ruby 3.2 Ruby 3.31.931.851.54 1.591.21.251.081.051.071.261.241.121.091.051.071No JIT MJIT / RJIT YJIT
Ruby 3.2 YJIT
Ruby 3.3 YJIT
Using YJIT• Install Rust and then build Ruby• Use --yjit or export RUBY_YJIT_ENABLE=1
Ruby JIT Hacking Guide
Ruby Hacking Guide
Ruby JIT Hacking GuideJITJIT
How Ruby JIT worksRuby
How Ruby JIT works1+2RubyAbstract Syntax Tree
How Ruby JIT works1+2putobject 1putobject 2opt_plusleaveRubyAbstract Syntax TreeInstruction Sequence (Bytecode)
How Ruby JIT works1+2putobject 1putobject 2opt_plusleaveRubyAbstract Syntax TreeInstruction Sequence (Bytecode)Machine Code
x86_64 assembly
• Read asm comments•--yjit-dump-disasm•--rjit-dump-disasmx86_64 assembly
• mov: assignment instruction• esi: register for stack[0]x86_64 assemblystack[0] = 3
x86_64 assembly• mov: assignment instruction• edi: register for stack[1]stack[0] = 3stack[1] = 5
x86_64 assembly• add,sub: arithmetic instruction• rax: temporary registerstack[0] = 3stack[1] = 5temp = stack[0]temp -= 1temp += stack[1]
x86_64 assembly• jo: jump if overflow• rsi: register for stack[0]stack[0] = 3stack[1] = 5temp = stack[0]temp -= 1temp += stack[1]jump if overflowstack[0] = temp
x86_64 encoding
x86_64 encodingopv86: https://hikalium.github.io/opv86/
Calling a custom JITRuby 3.2 Ruby 3.3+1. Run Ruby with --mjit=pause --rjit=pause2. Override RubyVM::MJIT.compile RubyVM::RJIT#compile3. Call RubyVM::MJIT.resume RubyVM::RJIT.resume
Building JIT is fun
k0kubun/ruby-jit-challengeRuby JIT Challenge
Ruby JIT ChallengeHashtag: #ruby-jit-challengeSpeedup relative to No JIT (s/s)036912No JIT RJIT YJIT Ruby JIT11.086.313.751Fibonatti benchmark
Optimizing Ruby JIT
Side exitsside exit
Method redefinitionRedefinition HookInvalidate
Method redefinitionRedefinition HookInvalidateside exit
Constant redefinition
Register allocation: Stack0: rsi1: rdi4: r102: r83: r9VM stack
Register allocation: Local variables• Spill registers on C function calls• Binding• debug_inspector API
Polymorphic method cache
Splitting
Method inlining
Conclusion• Enjoy custom JIT development• Let's make YJIT the best Ruby JIT