@@ -15,11 +15,7 @@ use crate::hir_type::{types, Type};
1515
1616pub 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.
88127pub 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}
0 commit comments