Skip to content

Commit 0b5cc0d

Browse files
committed
ZJIT: Annotate builtin functions
1 parent 5a7be72 commit 0b5cc0d

3 files changed

Lines changed: 98 additions & 5 deletions

File tree

zjit/src/codegen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
337337
gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), opnd!(self_val), opnds!(args))?,
338338
Insn::SendWithoutBlockDirect { cme, iseq, self_val, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(self_val), opnds!(args), &function.frame_state(*state))?,
339339
Insn::InvokeBuiltin { bf, args, state } => gen_invokebuiltin(asm, &function.frame_state(*state), bf, opnds!(args))?,
340+
Insn::BuiltinCall { bf, args, state, return_type: _ } => gen_invokebuiltin(asm, &function.frame_state(*state), bf, opnds!(args))?,
340341
Insn::Return { val } => return Some(gen_return(asm, opnd!(val))?),
341342
Insn::FixnumAdd { left, right, state } => gen_fixnum_add(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state))?,
342343
Insn::FixnumSub { left, right, state } => gen_fixnum_sub(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state))?,

zjit/src/cruby_methods.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::hir_type::{types, Type};
1515

1616
pub struct Annotations {
1717
cfuncs: HashMap<*mut c_void, FnProperties>,
18+
builtins: HashMap<&'static str, FnProperties>,
1819
}
1920

2021
/// Runtime behaviors of C functions that implement a Ruby method
@@ -41,6 +42,11 @@ impl Annotations {
4142
};
4243
self.cfuncs.get(&fn_ptr).copied()
4344
}
45+
46+
/// Query about properties of a builtin function
47+
pub fn get_builtin_properties(&self, name: &str) -> Option<FnProperties> {
48+
self.builtins.get(&name).copied()
49+
}
4450
}
4551

4652
fn annotate_c_method(props_map: &mut HashMap<*mut c_void, FnProperties>, class: VALUE, method_name: &'static str, props: FnProperties) {
@@ -63,6 +69,7 @@ fn annotate_c_method(props_map: &mut HashMap<*mut c_void, FnProperties>, class:
6369
/// are about the stock versions of methods.
6470
pub fn init() -> Annotations {
6571
let cfuncs = &mut HashMap::new();
72+
let builtins = &mut HashMap::new();
6673

6774
macro_rules! annotate {
6875
($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => {
@@ -74,6 +81,22 @@ pub fn init() -> Annotations {
7481
}
7582
}
7683

84+
macro_rules! annotate_builtin {
85+
($name:literal, $return_type:expr) => {
86+
annotate_builtin!($name, $return_type, no_gc, leaf, elidable)
87+
};
88+
($name:literal, $return_type:expr, $($properties:ident),+) => {
89+
let mut props = FnProperties {
90+
no_gc: false,
91+
leaf: false,
92+
elidable: false,
93+
return_type: $return_type
94+
};
95+
$(props.$properties = true;)+
96+
builtins.insert($name, props);
97+
}
98+
}
99+
77100
annotate!(rb_mKernel, "itself", types::BasicObject, no_gc, leaf, elidable);
78101
annotate!(rb_cString, "bytesize", types::Fixnum, no_gc, leaf);
79102
annotate!(rb_cModule, "name", types::StringExact.union(types::NilClass), no_gc, leaf, elidable);
@@ -83,7 +106,12 @@ pub fn init() -> Annotations {
83106
annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable);
84107
annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable);
85108

109+
// Annotate builtin functions
110+
annotate_builtin!("rb_f_float", types::Flonum);
111+
annotate_builtin!("rb_f_float1", types::Flonum);
112+
86113
Annotations {
87-
cfuncs: std::mem::take(cfuncs)
114+
cfuncs: std::mem::take(cfuncs),
115+
builtins: std::mem::take(builtins)
88116
}
89117
}

zjit/src/hir.rs

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,13 @@ pub enum Insn {
523523

524524
// Invoke a builtin function
525525
InvokeBuiltin { bf: rb_builtin_function, args: Vec<InsnId>, state: InsnId },
526+
/// Call a builtin function with known return type
527+
BuiltinCall {
528+
bf: rb_builtin_function,
529+
args: Vec<InsnId>,
530+
state: InsnId,
531+
return_type: Type,
532+
},
526533

527534
/// Control flow instructions
528535
Return { val: InsnId },
@@ -705,6 +712,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
705712
}
706713
Ok(())
707714
}
715+
Insn::BuiltinCall { bf, args, .. } => {
716+
write!(f, "BuiltinCall {}", unsafe { CStr::from_ptr(bf.name) }.to_str().unwrap())?;
717+
for arg in args {
718+
write!(f, ", {arg}")?;
719+
}
720+
Ok(())
721+
}
708722
Insn::Return { val } => { write!(f, "Return {val}") }
709723
Insn::FixnumAdd { left, right, .. } => { write!(f, "FixnumAdd {left}, {right}") },
710724
Insn::FixnumSub { left, right, .. } => { write!(f, "FixnumSub {left}, {right}") },
@@ -1160,6 +1174,7 @@ impl Function {
11601174
state,
11611175
},
11621176
&InvokeBuiltin { bf, ref args, state } => InvokeBuiltin { bf: bf, args: find_vec!(args), state },
1177+
&BuiltinCall { bf, ref args, state, return_type } => BuiltinCall { bf: bf, args: find_vec!(args), state, return_type },
11631178
&ArrayDup { val, state } => ArrayDup { val: find!(val), state },
11641179
&HashDup { val, state } => HashDup { val: find!(val), state },
11651180
&CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun, args: find_vec!(args), name, return_type, elidable },
@@ -1257,6 +1272,7 @@ impl Function {
12571272
Insn::SendWithoutBlockDirect { .. } => types::BasicObject,
12581273
Insn::Send { .. } => types::BasicObject,
12591274
Insn::InvokeBuiltin { .. } => types::BasicObject,
1275+
Insn::BuiltinCall { return_type, .. } => *return_type,
12601276
Insn::Defined { .. } => types::BasicObject,
12611277
Insn::DefinedIvar { .. } => types::BasicObject,
12621278
Insn::GetConstantPath { .. } => types::BasicObject,
@@ -1915,6 +1931,10 @@ impl Function {
19151931
worklist.extend(args);
19161932
worklist.push_back(state)
19171933
}
1934+
&Insn::BuiltinCall { ref args, state, .. } => {
1935+
worklist.extend(args);
1936+
worklist.push_back(state)
1937+
}
19181938
&Insn::CCall { ref args, .. } => worklist.extend(args),
19191939
&Insn::GetIvar { self_val, state, .. } | &Insn::DefinedIvar { self_val, state, .. } => {
19201940
worklist.push_back(self_val);
@@ -3115,7 +3135,21 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
31153135
args.reverse();
31163136

31173137
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3118-
let insn_id = fun.push_insn(block, Insn::InvokeBuiltin { bf, args, state: exit_id });
3138+
3139+
// Check if this builtin is annotated
3140+
let builtin_name = unsafe { CStr::from_ptr(bf.name).to_str().unwrap() };
3141+
let insn_id = if let Some(props) = ZJITState::get_method_annotations().get_builtin_properties(builtin_name) {
3142+
// Use BuiltinCall with known return type
3143+
fun.push_insn(block, Insn::BuiltinCall {
3144+
bf,
3145+
args,
3146+
state: exit_id,
3147+
return_type: props.return_type,
3148+
})
3149+
} else {
3150+
// Fall back to InvokeBuiltin for unannotated builtins
3151+
fun.push_insn(block, Insn::InvokeBuiltin { bf, args, state: exit_id })
3152+
};
31193153
state.stack_push(insn_id);
31203154
}
31213155
YARVINSN_opt_invokebuiltin_delegate |
@@ -3130,7 +3164,21 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
31303164
}
31313165

31323166
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3133-
let insn_id = fun.push_insn(block, Insn::InvokeBuiltin { bf, args, state: exit_id });
3167+
3168+
// Check if this builtin is annotated
3169+
let builtin_name = unsafe { CStr::from_ptr(bf.name).to_str().unwrap() };
3170+
let insn_id = if let Some(props) = ZJITState::get_method_annotations().get_builtin_properties(builtin_name) {
3171+
// Use BuiltinCall with known return type
3172+
fun.push_insn(block, Insn::BuiltinCall {
3173+
bf,
3174+
args,
3175+
state: exit_id,
3176+
return_type: props.return_type,
3177+
})
3178+
} else {
3179+
// Fall back to InvokeBuiltin for unannotated builtins
3180+
fun.push_insn(block, Insn::InvokeBuiltin { bf, args, state: exit_id })
3181+
};
31343182
state.stack_push(insn_id);
31353183
}
31363184
YARVINSN_objtostring => {
@@ -4970,12 +5018,28 @@ mod tests {
49705018

49715019
#[test]
49725020
fn test_invokebuiltin_delegate_with_args() {
5021+
// Using an unannotated builtin to test InvokeBuiltin generation
5022+
let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("GC", "start"));
5023+
assert!(iseq_contains_opcode(iseq, YARVINSN_invokebuiltin), "iseq GC.start does not contain invokebuiltin");
5024+
let function = iseq_to_hir(iseq).unwrap();
5025+
assert_function_hir(function, expect![[r#"
5026+
fn start@<internal:gc>:36:
5027+
bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject, v3:BasicObject, v4:BasicObject):
5028+
v6:FalseClass = Const Value(false)
5029+
v8:BasicObject = InvokeBuiltin gc_start_internal, v0, v1, v2, v3, v6
5030+
Return v8
5031+
"#]]);
5032+
}
5033+
5034+
#[test]
5035+
fn test_invokebuiltin_delegate_annotated() {
5036+
// Test that Float now generates BuiltinCall with correct type
49735037
assert_method_hir_with_opcode("Float", YARVINSN_opt_invokebuiltin_delegate_leave, expect![[r#"
49745038
fn Float@<internal:kernel>:197:
49755039
bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject, v3:BasicObject):
4976-
v6:BasicObject = InvokeBuiltin rb_f_float, v0, v1, v2
5040+
v6:Flonum = BuiltinCall rb_f_float, v0, v1, v2
49775041
Jump bb1(v0, v1, v2, v3, v6)
4978-
bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject):
5042+
bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:Flonum):
49795043
Return v12
49805044
"#]]);
49815045
}

0 commit comments

Comments
 (0)