From 8a1e035d5024089ab92148e2b78a1276290c29ad Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Mon, 7 Oct 2024 18:01:47 -0400 Subject: [PATCH] YJIT: Fastpath for Module#name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Module#name shows up as a top C method callee in lobsters so probably common enough. It's also easy to substitute thanks to rb_mod_name() already having no GC yield points. klass = BasicObject 50_000_000.times { klass.name } Benchmark 1: /.rubies/post/bin/ruby --yjit mod_name.rb Time (mean ± σ): 1.433 s ± 0.010 s [User: 1.410 s, System: 0.010 s] Range (min … max): 1.421 s … 1.449 s 10 runs Benchmark 2: /.rubies/mstr/bin/ruby --yjit mod_name.rb Time (mean ± σ): 1.491 s ± 0.012 s [User: 1.468 s, System: 0.010 s] Range (min … max): 1.470 s … 1.511 s 10 runs Summary /.rubies/post/bin/ruby --yjit mod_name.rb ran 1.04 ± 0.01 times faster than /.rubies/mstr/bin/ruby --yjit mod_name.rb --- variable.c | 3 +++ yjit/bindgen/src/main.rs | 1 + yjit/src/codegen.rs | 28 ++++++++++++++++++++++++++++ yjit/src/cruby_bindings.inc.rs | 1 + 4 files changed, 33 insertions(+) diff --git a/variable.c b/variable.c index 4e55427ff57494..bcdf2fb0701f39 100644 --- a/variable.c +++ b/variable.c @@ -97,6 +97,8 @@ rb_namespace_p(VALUE obj) * to not be anonymous. *permanent is set to 1 * if +classpath+ has no anonymous components. There is no builtin * Ruby level APIs that can change a permanent +classpath+. + * + * YJIT needs this function to not allocate. */ static VALUE classname(VALUE klass, bool *permanent) @@ -127,6 +129,7 @@ rb_mod_name0(VALUE klass, bool *permanent) VALUE rb_mod_name(VALUE mod) { + // YJIT needs this function to not allocate. bool permanent; return classname(mod, &permanent); } diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index ea60dd23b91f08..28e7ac3e9c7a63 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -378,6 +378,7 @@ fn main() { .allowlist_function("rb_attr_get") .allowlist_function("rb_ivar_defined") .allowlist_function("rb_ivar_get") + .allowlist_function("rb_mod_name") // From internal/vm.h .allowlist_var("rb_vm_insns_count") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index d3aac992aa5c41..ee045d5d5bb393 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -5135,6 +5135,33 @@ fn jit_rb_mod_eqq( return true; } +// Substitution for rb_mod_name(). Returns the name of a module/class. +fn jit_rb_mod_name( + _jit: &mut JITState, + asm: &mut Assembler, + _ci: *const rb_callinfo, + _cme: *const rb_callable_method_entry_t, + _block: Option, + argc: i32, + _known_recv_class: Option, +) -> bool { + if argc != 0 { + return false; + } + + asm_comment!(asm, "Module#name"); + + // rb_mod_name() never allocates, so no preparation needed. + let name = asm.ccall(rb_mod_name as _, vec![asm.stack_opnd(0)]); + + let _ = asm.stack_pop(1); // pop self + // call-seq: mod.name -> string or nil + let ret = asm.stack_push(Type::Unknown); + asm.mov(ret, name); + + true +} + // Codegen for rb_obj_equal() // object identity comparison fn jit_rb_obj_equal( @@ -10373,6 +10400,7 @@ pub fn yjit_reg_method_codegen_fns() { yjit_reg_method(rb_mKernel, "eql?", jit_rb_obj_equal); yjit_reg_method(rb_cModule, "==", jit_rb_obj_equal); yjit_reg_method(rb_cModule, "===", jit_rb_mod_eqq); + yjit_reg_method(rb_cModule, "name", jit_rb_mod_name); yjit_reg_method(rb_cSymbol, "==", jit_rb_obj_equal); yjit_reg_method(rb_cSymbol, "===", jit_rb_obj_equal); yjit_reg_method(rb_cInteger, "==", jit_rb_int_equal); diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index ea4c14e512628b..147d30e2026076 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1036,6 +1036,7 @@ extern "C" { pub fn rb_str_buf_append(dst: VALUE, src: VALUE) -> VALUE; pub fn rb_str_dup(str_: VALUE) -> VALUE; pub fn rb_str_intern(str_: VALUE) -> VALUE; + pub fn rb_mod_name(mod_: VALUE) -> VALUE; pub fn rb_ivar_get(obj: VALUE, name: ID) -> VALUE; pub fn rb_ivar_defined(obj: VALUE, name: ID) -> VALUE; pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE;