$30 off During Our Annual Pro Sale. View Details »

Exit(ing) through the YJIT

Exit(ing) through the YJIT

When optimizing code for the YJIT compiler it can be difficult to figure out what code is exiting and why. While working on tracing exits in a Ruby codebase, I found myself wishing we had a tool to reveal the exact line that was causing exits to occur. We set to work on building that functionality into Ruby and now we are able to see every side-exit and why. In this talk we’ll learn about side-exits and how we built a tracer for them. We’ll explore the original implementation, how we rewrote it in Rust, and lastly why it’s so important to always ask "can I make what I built even better?"

Eileen M. Uchitelle

December 01, 2022
Tweet

More Decks by Eileen M. Uchitelle

Other Decks in Programming

Transcript

  1. def call_method(arg) if arg == "hello" puts arg else puts

    "bye" end end YJIT only compiles code that is run
  2. def call_method(arg) if arg == "hello" puts arg else puts

    "bye" end end call_method("hello") YJIT only compiles code that is run
  3. def call_method(arg) if arg == "hello" puts arg else puts

    "bye" end end call_method("hello") YJIT only compiles code that is run
  4. Pre-requisites • autoconf • make • bison • GCC or

    Clang • libyaml • [email protected] • Rust (for YJIT) • Ruby
  5. ar_demo.rb ActiveRecord::Base.establish_connection( adapter: "sqlite3", database: ":memory:") ActiveRecord::Schema.define do create_table :posts,

    force: true do |t| end end class Post < ActiveRecord::Base end class BugTest < ActiveSupport::TestCase def test_create Post.create! end end
  6. ar_demo.rb [...] class Post < ActiveRecord::Base def call_method(options) one, two,

    three = options end end class BugTest < ActiveSupport::TestCase def test_create post = Post.create! post.call_method("hello") end end
  7. yjit/src/codegen.rs /// Maps a YARV opcode to a code generation

    function (if supported) fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> { let VALUE(opcode) = opcode; let opcode = opcode as ruby_vminsn_type; assert!(opcode < VM_INSTRUCTION_SIZE); match opcode { [...] YARVINSN_send => Some(gen_send), YARVINSN_invokeblock => Some(gen_invokeblock), YARVINSN_invokesuper => Some(gen_invokesuper), YARVINSN_leave => Some(gen_leave), [...]
  8. yjit/src/codegen.rs /// Maps a YARV opcode to a code generation

    function (if supported) fn get_gen_fn(opcode: VALUE) -> Option<InsnGenFn> { let VALUE(opcode) = opcode; let opcode = opcode as ruby_vminsn_type; assert!(opcode < VM_INSTRUCTION_SIZE); match opcode { [...] YARVINSN_send => Some(gen_send), YARVINSN_invokeblock => Some(gen_invokeblock), YARVINSN_invokesuper => Some(gen_invokesuper), YARVINSN_leave => Some(gen_leave), [...]