diff options
| -rw-r--r-- | zjit/src/cruby_methods.rs | 97 | ||||
| -rw-r--r-- | zjit/src/state.rs | 4 |
2 files changed, 69 insertions, 32 deletions
diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 4851671a5b..08c2cee7b7 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -15,11 +15,7 @@ use crate::hir_type::{types, Type}; pub struct Annotations { cfuncs: HashMap<*mut c_void, FnProperties>, - // Builtin annotations by function pointer for fast runtime lookup - // Uses Option to cache both hits and misses - builtin_ptrs: HashMap<*mut c_void, Option<FnProperties>>, - // Temporary name-based annotations used during initialization - builtin_names: HashMap<&'static str, FnProperties>, + builtin_funcs: HashMap<*mut c_void, FnProperties>, } /// Runtime behaviors of C functions that implement a Ruby method @@ -48,22 +44,9 @@ impl Annotations { } /// Query about properties of a builtin function by its pointer - /// If not found by pointer, checks by name and caches the result (including misses) - pub fn get_builtin_properties(&mut self, bf: *const rb_builtin_function) -> Option<FnProperties> { + pub fn get_builtin_properties(&self, bf: *const rb_builtin_function) -> Option<FnProperties> { let func_ptr = unsafe { (*bf).func_ptr as *mut c_void }; - - // First check if we already have it cached by pointer - if let Some(&cached_result) = self.builtin_ptrs.get(&func_ptr) { - return cached_result; - } - - // If not found, check by name and cache the result - let name = unsafe { std::ffi::CStr::from_ptr((*bf).name).to_str().ok()? }; - let result = self.builtin_names.get(name).copied(); - - // Cache the result (both hits and misses) - self.builtin_ptrs.insert(func_ptr, result); - result + self.builtin_funcs.get(&func_ptr).copied() } } @@ -83,11 +66,67 @@ fn annotate_c_method(props_map: &mut HashMap<*mut c_void, FnProperties>, class: props_map.insert(fn_ptr, props); } +/// Look up a method and find its builtin function pointer by parsing its ISEQ +/// We currently only support methods with exactly one invokebuiltin instruction +fn annotate_builtin_method(props_map: &mut HashMap<*mut c_void, FnProperties>, class: VALUE, method_name: &'static str, props: FnProperties) { + unsafe { + let method_id = rb_intern2(method_name.as_ptr().cast(), method_name.len().try_into().unwrap()); + let method = rb_method_entry_at(class, method_id); + if method.is_null() { + panic!("Method {}#{} not found", std::ffi::CStr::from_ptr(rb_class2name(class)).to_str().unwrap_or("?"), method_name); + } + + // Cast ME to CME - they have identical layout + let cme = method.cast::<rb_callable_method_entry_t>(); + let def_type = get_cme_def_type(cme); + + if def_type != VM_METHOD_TYPE_ISEQ { + panic!("Method {}#{} is not an ISEQ method (type: {})", + std::ffi::CStr::from_ptr(rb_class2name(class)).to_str().unwrap_or("?"), + method_name, def_type); + } + + // Get the ISEQ from the method definition + let iseq = get_def_iseq_ptr((*cme).def); + if iseq.is_null() { + panic!("Failed to get ISEQ for {}#{}", + std::ffi::CStr::from_ptr(rb_class2name(class)).to_str().unwrap_or("?"), + method_name); + } + + // Get the size of the ISEQ in instruction units + let encoded_size = rb_iseq_encoded_size(iseq); + + // Scan through the ISEQ to find invokebuiltin instructions + let mut insn_idx: u32 = 0; + while insn_idx < encoded_size { + // Get the PC for this instruction index + let pc = rb_iseq_pc_at_idx(iseq, insn_idx); + + // Get the opcode using the proper decoder + let opcode = rb_iseq_opcode_at_pc(iseq, pc); + + if opcode == YARVINSN_invokebuiltin as i32 || + opcode == YARVINSN_opt_invokebuiltin_delegate as i32 || + opcode == YARVINSN_opt_invokebuiltin_delegate_leave as i32 { + // The first operand is the builtin function pointer + let bf_value = *pc.add(1); + let bf_ptr = bf_value.as_ptr() as *const rb_builtin_function; + let func_ptr = (*bf_ptr).func_ptr as *mut c_void; + props_map.insert(func_ptr, props); + } + + // Move to the next instruction using the proper length + insn_idx = insn_idx.saturating_add(rb_insn_len(VALUE(opcode as usize)).try_into().unwrap()); + } + } +} + /// Gather annotations. Run this right after boot since the annotations /// are about the stock versions of methods. pub fn init() -> Annotations { let cfuncs = &mut HashMap::new(); - let builtin_names = &mut HashMap::new(); + let builtin_funcs = &mut HashMap::new(); macro_rules! annotate { ($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => { @@ -100,10 +139,10 @@ pub fn init() -> Annotations { } macro_rules! annotate_builtin { - ($name:literal, $return_type:expr) => { - annotate_builtin!($name, $return_type, no_gc, leaf, elidable) + ($module:ident, $method_name:literal, $return_type:expr) => { + annotate_builtin!($module, $method_name, $return_type, no_gc, leaf, elidable) }; - ($name:literal, $return_type:expr, $($properties:ident),+) => { + ($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => { let mut props = FnProperties { no_gc: false, leaf: false, @@ -111,7 +150,7 @@ pub fn init() -> Annotations { return_type: $return_type }; $(props.$properties = true;)+ - builtin_names.insert($name, props); + annotate_builtin_method(builtin_funcs, unsafe { $module }, $method_name, props); } } @@ -124,13 +163,11 @@ pub fn init() -> Annotations { annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable); annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable); - // Annotate builtin functions - annotate_builtin!("rb_f_float", types::Flonum); - annotate_builtin!("rb_f_float1", types::Flonum); + annotate_builtin!(rb_mKernel, "Float", types::Flonum); + annotate_builtin!(rb_mKernel, "Integer", types::Integer); Annotations { cfuncs: std::mem::take(cfuncs), - builtin_ptrs: HashMap::new(), // Cache queried built-in functions by pointer - builtin_names: std::mem::take(builtin_names) + builtin_funcs: std::mem::take(builtin_funcs), } } diff --git a/zjit/src/state.rs b/zjit/src/state.rs index b572e150eb..79be91fd85 100644 --- a/zjit/src/state.rs +++ b/zjit/src/state.rs @@ -117,8 +117,8 @@ impl ZJITState { &mut ZJITState::get_instance().invariants } - pub fn get_method_annotations() -> &'static mut cruby_methods::Annotations { - &mut ZJITState::get_instance().method_annotations + pub fn get_method_annotations() -> &'static cruby_methods::Annotations { + &ZJITState::get_instance().method_annotations } /// Return true if successful compilation should be asserted |
