aboutsummaryrefslogtreecommitdiffstats
path: root/yjit
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2023-11-02 09:15:48 -0700
committerGitHub <noreply@github.com>2023-11-02 09:15:48 -0700
commitad4f973ecd0a3481ff1abaa972d457e9f5b5fb4e (patch)
tree82c753f75cfd88d4c5df1583990607b484929b56 /yjit
parent5f130e2111db82eaf2c609e9cc7c000f9bac95be (diff)
downloadruby-ad4f973ecd0a3481ff1abaa972d457e9f5b5fb4e.tar.gz
YJIT: Always define method codegen table at boot (#8807)
Diffstat (limited to 'yjit')
-rw-r--r--yjit/src/codegen.rs181
-rw-r--r--yjit/src/yjit.rs7
2 files changed, 89 insertions, 99 deletions
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 39dc5f999a..ba2a23872e 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -5141,8 +5141,13 @@ fn jit_thread_s_current(
// Check if we know how to codegen for a particular cfunc method
fn lookup_cfunc_codegen(def: *const rb_method_definition_t) -> Option<MethodGenFn> {
let method_serial = unsafe { get_def_method_serial(def) };
+ let table = unsafe { METHOD_CODEGEN_TABLE.as_ref().unwrap() };
- CodegenGlobals::look_up_codegen_method(method_serial)
+ let option_ref = table.get(&method_serial);
+ match option_ref {
+ None => None,
+ Some(&mgf) => Some(mgf), // Deref
+ }
}
// Is anyone listening for :c_call and :c_return event currently?
@@ -8676,6 +8681,83 @@ type MethodGenFn = fn(
known_recv_class: *const VALUE,
) -> bool;
+/// Methods for generating code for hardcoded (usually C) methods
+static mut METHOD_CODEGEN_TABLE: Option<HashMap<usize, MethodGenFn>> = None;
+
+/// Register codegen functions for some Ruby core methods
+pub fn yjit_reg_method_codegen_fns() {
+ unsafe {
+ assert!(METHOD_CODEGEN_TABLE.is_none());
+ METHOD_CODEGEN_TABLE = Some(HashMap::default());
+
+ // Specialization for C methods. See yjit_reg_method() for details.
+ yjit_reg_method(rb_cBasicObject, "!", jit_rb_obj_not);
+
+ yjit_reg_method(rb_cNilClass, "nil?", jit_rb_true);
+ yjit_reg_method(rb_mKernel, "nil?", jit_rb_false);
+ yjit_reg_method(rb_mKernel, "is_a?", jit_rb_kernel_is_a);
+ yjit_reg_method(rb_mKernel, "kind_of?", jit_rb_kernel_is_a);
+ yjit_reg_method(rb_mKernel, "instance_of?", jit_rb_kernel_instance_of);
+
+ yjit_reg_method(rb_cBasicObject, "==", jit_rb_obj_equal);
+ yjit_reg_method(rb_cBasicObject, "equal?", jit_rb_obj_equal);
+ yjit_reg_method(rb_cBasicObject, "!=", jit_rb_obj_not_equal);
+ 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_cSymbol, "==", jit_rb_obj_equal);
+ yjit_reg_method(rb_cSymbol, "===", jit_rb_obj_equal);
+ yjit_reg_method(rb_cInteger, "==", jit_rb_int_equal);
+ yjit_reg_method(rb_cInteger, "===", jit_rb_int_equal);
+
+ yjit_reg_method(rb_cInteger, "/", jit_rb_int_div);
+ yjit_reg_method(rb_cInteger, "<<", jit_rb_int_lshift);
+ yjit_reg_method(rb_cInteger, "[]", jit_rb_int_aref);
+
+ yjit_reg_method(rb_cString, "empty?", jit_rb_str_empty_p);
+ yjit_reg_method(rb_cString, "to_s", jit_rb_str_to_s);
+ yjit_reg_method(rb_cString, "to_str", jit_rb_str_to_s);
+ yjit_reg_method(rb_cString, "bytesize", jit_rb_str_bytesize);
+ yjit_reg_method(rb_cString, "getbyte", jit_rb_str_getbyte);
+ yjit_reg_method(rb_cString, "<<", jit_rb_str_concat);
+ yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus);
+
+ yjit_reg_method(rb_cArray, "empty?", jit_rb_ary_empty_p);
+ yjit_reg_method(rb_cArray, "<<", jit_rb_ary_push);
+
+ yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to);
+ yjit_reg_method(rb_mKernel, "block_given?", jit_rb_f_block_given_p);
+
+ yjit_reg_method(rb_singleton_class(rb_cThread), "current", jit_thread_s_current);
+ }
+}
+
+// Register a specialized codegen function for a particular method. Note that
+// the if the function returns true, the code it generates runs without a
+// control frame and without interrupt checks. To avoid creating observable
+// behavior changes, the codegen function should only target simple code paths
+// that do not allocate and do not make method calls.
+fn yjit_reg_method(klass: VALUE, mid_str: &str, gen_fn: MethodGenFn) {
+ let id_string = std::ffi::CString::new(mid_str).expect("couldn't convert to CString!");
+ let mid = unsafe { rb_intern(id_string.as_ptr()) };
+ let me = unsafe { rb_method_entry_at(klass, mid) };
+
+ if me.is_null() {
+ panic!("undefined optimized method!: {mid_str}");
+ }
+
+ // For now, only cfuncs are supported
+ //RUBY_ASSERT(me && me->def);
+ //RUBY_ASSERT(me->def->type == VM_METHOD_TYPE_CFUNC);
+
+ let method_serial = unsafe {
+ let def = (*me).def;
+ get_def_method_serial(def)
+ };
+
+ unsafe { METHOD_CODEGEN_TABLE.as_mut().unwrap().insert(method_serial, gen_fn); }
+}
+
/// Global state needed for code generation
pub struct CodegenGlobals {
/// Inline code block (fast path)
@@ -8706,9 +8788,6 @@ pub struct CodegenGlobals {
/// For implementing global code invalidation
global_inval_patches: Vec<CodepagePatch>,
- // Methods for generating code for hardcoded (usually C) methods
- method_codegen_table: HashMap<usize, MethodGenFn>,
-
/// Page indexes for outlined code that are not associated to any ISEQ.
ocb_pages: Vec<usize>,
}
@@ -8792,7 +8871,7 @@ impl CodegenGlobals {
cb.mark_all_executable();
ocb.unwrap().mark_all_executable();
- let mut codegen_globals = CodegenGlobals {
+ let codegen_globals = CodegenGlobals {
inline_cb: cb,
outlined_cb: ocb,
leave_exit_code,
@@ -8802,97 +8881,15 @@ impl CodegenGlobals {
branch_stub_hit_trampoline,
entry_stub_hit_trampoline,
global_inval_patches: Vec::new(),
- method_codegen_table: HashMap::new(),
ocb_pages,
};
- // Register the method codegen functions
- codegen_globals.reg_method_codegen_fns();
-
// Initialize the codegen globals instance
unsafe {
CODEGEN_GLOBALS = Some(codegen_globals);
}
}
- // Register a specialized codegen function for a particular method. Note that
- // the if the function returns true, the code it generates runs without a
- // control frame and without interrupt checks. To avoid creating observable
- // behavior changes, the codegen function should only target simple code paths
- // that do not allocate and do not make method calls.
- fn yjit_reg_method(&mut self, klass: VALUE, mid_str: &str, gen_fn: MethodGenFn) {
- let id_string = std::ffi::CString::new(mid_str).expect("couldn't convert to CString!");
- let mid = unsafe { rb_intern(id_string.as_ptr()) };
- let me = unsafe { rb_method_entry_at(klass, mid) };
-
- if me.is_null() {
- panic!("undefined optimized method!");
- }
-
- // For now, only cfuncs are supported
- //RUBY_ASSERT(me && me->def);
- //RUBY_ASSERT(me->def->type == VM_METHOD_TYPE_CFUNC);
-
- let method_serial = unsafe {
- let def = (*me).def;
- get_def_method_serial(def)
- };
-
- self.method_codegen_table.insert(method_serial, gen_fn);
- }
-
- /// Register codegen functions for some Ruby core methods
- fn reg_method_codegen_fns(&mut self) {
- unsafe {
- // Specialization for C methods. See yjit_reg_method() for details.
- self.yjit_reg_method(rb_cBasicObject, "!", jit_rb_obj_not);
-
- self.yjit_reg_method(rb_cNilClass, "nil?", jit_rb_true);
- self.yjit_reg_method(rb_mKernel, "nil?", jit_rb_false);
- self.yjit_reg_method(rb_mKernel, "is_a?", jit_rb_kernel_is_a);
- self.yjit_reg_method(rb_mKernel, "kind_of?", jit_rb_kernel_is_a);
- self.yjit_reg_method(rb_mKernel, "instance_of?", jit_rb_kernel_instance_of);
-
- self.yjit_reg_method(rb_cBasicObject, "==", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cBasicObject, "equal?", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cBasicObject, "!=", jit_rb_obj_not_equal);
- self.yjit_reg_method(rb_mKernel, "eql?", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cModule, "==", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cModule, "===", jit_rb_mod_eqq);
- self.yjit_reg_method(rb_cSymbol, "==", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cSymbol, "===", jit_rb_obj_equal);
- self.yjit_reg_method(rb_cInteger, "==", jit_rb_int_equal);
- self.yjit_reg_method(rb_cInteger, "===", jit_rb_int_equal);
-
- self.yjit_reg_method(rb_cInteger, "/", jit_rb_int_div);
- self.yjit_reg_method(rb_cInteger, "<<", jit_rb_int_lshift);
- self.yjit_reg_method(rb_cInteger, "[]", jit_rb_int_aref);
-
- // rb_str_to_s() methods in string.c
- self.yjit_reg_method(rb_cString, "empty?", jit_rb_str_empty_p);
- self.yjit_reg_method(rb_cString, "to_s", jit_rb_str_to_s);
- self.yjit_reg_method(rb_cString, "to_str", jit_rb_str_to_s);
- self.yjit_reg_method(rb_cString, "bytesize", jit_rb_str_bytesize);
- self.yjit_reg_method(rb_cString, "getbyte", jit_rb_str_getbyte);
- self.yjit_reg_method(rb_cString, "<<", jit_rb_str_concat);
- self.yjit_reg_method(rb_cString, "+@", jit_rb_str_uplus);
-
- // rb_ary_empty_p() method in array.c
- self.yjit_reg_method(rb_cArray, "empty?", jit_rb_ary_empty_p);
- self.yjit_reg_method(rb_cArray, "<<", jit_rb_ary_push);
-
- self.yjit_reg_method(rb_mKernel, "respond_to?", jit_obj_respond_to);
- self.yjit_reg_method(rb_mKernel, "block_given?", jit_rb_f_block_given_p);
-
- // Thread.current
- self.yjit_reg_method(
- rb_singleton_class(rb_cThread),
- "current",
- jit_thread_s_current,
- );
- }
- }
-
/// Get a mutable reference to the codegen globals instance
pub fn get_instance() -> &'static mut CodegenGlobals {
unsafe { CODEGEN_GLOBALS.as_mut().unwrap() }
@@ -8952,16 +8949,6 @@ impl CodegenGlobals {
CodegenGlobals::get_instance().entry_stub_hit_trampoline
}
- pub fn look_up_codegen_method(method_serial: usize) -> Option<MethodGenFn> {
- let table = &CodegenGlobals::get_instance().method_codegen_table;
-
- let option_ref = table.get(&method_serial);
- match option_ref {
- None => None,
- Some(&mgf) => Some(mgf), // Deref
- }
- }
-
pub fn get_ocb_pages() -> &'static Vec<usize> {
&CodegenGlobals::get_instance().ocb_pages
}
diff --git a/yjit/src/yjit.rs b/yjit/src/yjit.rs
index 813918b4bc..1e7e01a834 100644
--- a/yjit/src/yjit.rs
+++ b/yjit/src/yjit.rs
@@ -29,9 +29,12 @@ pub fn yjit_enabled_p() -> bool {
/// This function is called from C code
#[no_mangle]
-pub extern "C" fn rb_yjit_init() {
+pub extern "C" fn rb_yjit_init(yjit_enabled: bool) {
+ // Register the method codegen functions. This must be done at boot.
+ yjit_reg_method_codegen_fns();
+
// If --yjit-disable, yjit_init() will not be called until RubyVM::YJIT.enable.
- if !get_option!(disable) {
+ if yjit_enabled && !get_option!(disable) {
yjit_init();
}
}