compiler - Written in Rust from (almost) scratch - No runtime dependency on LLVM, JVM, .. - Includes parser, garbage collector, interpreter - Only x86-64 / Linux is supported - new! Supports RubyGems and Bundler - new! Trying to run ruby/spec
for lhs Load lhs & rhs %7 = %2 + %3 [Integer][Integer] mov rdi,QWORD PTR [rbp-0x40] mov rsi,r15 test rdi,0x1 je $deoptimize test rsi,0x1 je $deoptimize sub rdi,0x1 add rdi,rsi jo $deoptimize mov r15,rdi asm code for ADD
evil p a + b def evil $b.eval("a = 40") end ❯ monoruby sample2.rb 42 Compiler knows evil is not Kernel.#eval. and there are no method definitions inside it.
= 1 b = 2 evil(curse) p(a + b) tp = TracePoint.new(:line) do |tp| if tp.lineno == 8 tp.binding.eval("a = 40") end end tp.enable a = 1 b = 2 p a + b ❯ monoruby sample3.rb 42
= 1 b = 2 evil(curse) p(a + b) tp = TracePoint.new(:line) do |tp| if tp.lineno == 8 tp.binding.eval("a = 40") end end tp.enable a = 1 b = 2 p a + b TracePoint: You can do everything anywhere.
- if some assumptions are broken, we must immediately escape from JIT code, and bail out to the interpreter. - version in inline cache - receiver’s class in inline cache - constants - the local frame is not captured
checks - Guards - If check fails, go back to interpreter (deoptimization). - Some runtime overhead - version - receiver’s class - constant cache - frame capture
Point (binary patches) - Prepare memory space for patching in advance - No runtime penalty - If disaster occurs, rewrite all patch points. - re-definition of basic operations (i.e. Integer#+) - TracePoint