Skip to content

Commit 3c1ca50

Browse files
st0012k0kubun
authored andcommitted
ZJIT: Improve builtin function annotation collection
1 parent 85510fc commit 3c1ca50

File tree

2 files changed

+69
-32
lines changed

2 files changed

+69
-32
lines changed

zjit/src/cruby_methods.rs

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ use crate::hir_type::{types, Type};
1515

1616
pub struct Annotations {
1717
cfuncs: HashMap<*mut c_void, FnProperties>,
18-
// Builtin annotations by function pointer for fast runtime lookup
19-
// Uses Option to cache both hits and misses
20-
builtin_ptrs: HashMap<*mut c_void, Option<FnProperties>>,
21-
// Temporary name-based annotations used during initialization
22-
builtin_names: HashMap<&'static str, FnProperties>,
18+
builtin_funcs: HashMap<*mut c_void, FnProperties>,
2319
}
2420

2521
/// Runtime behaviors of C functions that implement a Ruby method
@@ -48,22 +44,9 @@ impl Annotations {
4844
}
4945

5046
/// Query about properties of a builtin function by its pointer
51-
/// If not found by pointer, checks by name and caches the result (including misses)
52-
pub fn get_builtin_properties(&mut self, bf: *const rb_builtin_function) -> Option<FnProperties> {
47+
pub fn get_builtin_properties(&self, bf: *const rb_builtin_function) -> Option<FnProperties> {
5348
let func_ptr = unsafe { (*bf).func_ptr as *mut c_void };
54-
55-
// First check if we already have it cached by pointer
56-
if let Some(&cached_result) = self.builtin_ptrs.get(&func_ptr) {
57-
return cached_result;
58-
}
59-
60-
// If not found, check by name and cache the result
61-
let name = unsafe { std::ffi::CStr::from_ptr((*bf).name).to_str().ok()? };
62-
let result = self.builtin_names.get(name).copied();
63-
64-
// Cache the result (both hits and misses)
65-
self.builtin_ptrs.insert(func_ptr, result);
66-
result
49+
self.builtin_funcs.get(&func_ptr).copied()
6750
}
6851
}
6952

@@ -83,11 +66,67 @@ fn annotate_c_method(props_map: &mut HashMap<*mut c_void, FnProperties>, class:
8366
props_map.insert(fn_ptr, props);
8467
}
8568

69+
/// Look up a method and find its builtin function pointer by parsing its ISEQ
70+
/// We currently only support methods with exactly one invokebuiltin instruction
71+
fn annotate_builtin_method(props_map: &mut HashMap<*mut c_void, FnProperties>, class: VALUE, method_name: &'static str, props: FnProperties) {
72+
unsafe {
73+
let method_id = rb_intern2(method_name.as_ptr().cast(), method_name.len().try_into().unwrap());
74+
let method = rb_method_entry_at(class, method_id);
75+
if method.is_null() {
76+
panic!("Method {}#{} not found", std::ffi::CStr::from_ptr(rb_class2name(class)).to_str().unwrap_or("?"), method_name);
77+
}
78+
79+
// Cast ME to CME - they have identical layout
80+
let cme = method.cast::<rb_callable_method_entry_t>();
81+
let def_type = get_cme_def_type(cme);
82+
83+
if def_type != VM_METHOD_TYPE_ISEQ {
84+
panic!("Method {}#{} is not an ISEQ method (type: {})",
85+
std::ffi::CStr::from_ptr(rb_class2name(class)).to_str().unwrap_or("?"),
86+
method_name, def_type);
87+
}
88+
89+
// Get the ISEQ from the method definition
90+
let iseq = get_def_iseq_ptr((*cme).def);
91+
if iseq.is_null() {
92+
panic!("Failed to get ISEQ for {}#{}",
93+
std::ffi::CStr::from_ptr(rb_class2name(class)).to_str().unwrap_or("?"),
94+
method_name);
95+
}
96+
97+
// Get the size of the ISEQ in instruction units
98+
let encoded_size = rb_iseq_encoded_size(iseq);
99+
100+
// Scan through the ISEQ to find invokebuiltin instructions
101+
let mut insn_idx: u32 = 0;
102+
while insn_idx < encoded_size {
103+
// Get the PC for this instruction index
104+
let pc = rb_iseq_pc_at_idx(iseq, insn_idx);
105+
106+
// Get the opcode using the proper decoder
107+
let opcode = rb_iseq_opcode_at_pc(iseq, pc);
108+
109+
if opcode == YARVINSN_invokebuiltin as i32 ||
110+
opcode == YARVINSN_opt_invokebuiltin_delegate as i32 ||
111+
opcode == YARVINSN_opt_invokebuiltin_delegate_leave as i32 {
112+
// The first operand is the builtin function pointer
113+
let bf_value = *pc.add(1);
114+
let bf_ptr = bf_value.as_ptr() as *const rb_builtin_function;
115+
let func_ptr = (*bf_ptr).func_ptr as *mut c_void;
116+
props_map.insert(func_ptr, props);
117+
}
118+
119+
// Move to the next instruction using the proper length
120+
insn_idx = insn_idx.saturating_add(rb_insn_len(VALUE(opcode as usize)).try_into().unwrap());
121+
}
122+
}
123+
}
124+
86125
/// Gather annotations. Run this right after boot since the annotations
87126
/// are about the stock versions of methods.
88127
pub fn init() -> Annotations {
89128
let cfuncs = &mut HashMap::new();
90-
let builtin_names = &mut HashMap::new();
129+
let builtin_funcs = &mut HashMap::new();
91130

92131
macro_rules! annotate {
93132
($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => {
@@ -100,18 +139,18 @@ pub fn init() -> Annotations {
100139
}
101140

102141
macro_rules! annotate_builtin {
103-
($name:literal, $return_type:expr) => {
104-
annotate_builtin!($name, $return_type, no_gc, leaf, elidable)
142+
($module:ident, $method_name:literal, $return_type:expr) => {
143+
annotate_builtin!($module, $method_name, $return_type, no_gc, leaf, elidable)
105144
};
106-
($name:literal, $return_type:expr, $($properties:ident),+) => {
145+
($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => {
107146
let mut props = FnProperties {
108147
no_gc: false,
109148
leaf: false,
110149
elidable: false,
111150
return_type: $return_type
112151
};
113152
$(props.$properties = true;)+
114-
builtin_names.insert($name, props);
153+
annotate_builtin_method(builtin_funcs, unsafe { $module }, $method_name, props);
115154
}
116155
}
117156

@@ -124,13 +163,11 @@ pub fn init() -> Annotations {
124163
annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable);
125164
annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable);
126165

127-
// Annotate builtin functions
128-
annotate_builtin!("rb_f_float", types::Flonum);
129-
annotate_builtin!("rb_f_float1", types::Flonum);
166+
annotate_builtin!(rb_mKernel, "Float", types::Flonum);
167+
annotate_builtin!(rb_mKernel, "Integer", types::Integer);
130168

131169
Annotations {
132170
cfuncs: std::mem::take(cfuncs),
133-
builtin_ptrs: HashMap::new(), // Cache queried built-in functions by pointer
134-
builtin_names: std::mem::take(builtin_names)
171+
builtin_funcs: std::mem::take(builtin_funcs),
135172
}
136173
}

zjit/src/state.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ impl ZJITState {
117117
&mut ZJITState::get_instance().invariants
118118
}
119119

120-
pub fn get_method_annotations() -> &'static mut cruby_methods::Annotations {
121-
&mut ZJITState::get_instance().method_annotations
120+
pub fn get_method_annotations() -> &'static cruby_methods::Annotations {
121+
&ZJITState::get_instance().method_annotations
122122
}
123123

124124
/// Return true if successful compilation should be asserted

0 commit comments

Comments
 (0)