summaryrefslogtreecommitdiff
path: root/zjit/src/hir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'zjit/src/hir.rs')
-rw-r--r--zjit/src/hir.rs144
1 files changed, 139 insertions, 5 deletions
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index fbca1f4418..fca8c237fd 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -9,7 +9,7 @@ use crate::{
use std::{
cell::RefCell,
collections::{HashMap, HashSet, VecDeque},
- ffi::{c_int, c_void},
+ ffi::{c_int, c_void, CStr},
mem::{align_of, size_of},
ptr,
slice::Iter
@@ -477,6 +477,9 @@ pub enum Insn {
state: InsnId,
},
+ // Invoke a builtin function
+ InvokeBuiltin { bf: rb_builtin_function, args: Vec<InsnId>, state: InsnId },
+
/// Control flow instructions
Return { val: InsnId },
@@ -636,6 +639,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
}
Ok(())
}
+ Insn::InvokeBuiltin { bf, args, .. } => {
+ write!(f, "InvokeBuiltin {}", unsafe { CStr::from_ptr(bf.name) }.to_str().unwrap())?;
+ for arg in args {
+ write!(f, ", {arg}")?;
+ }
+ Ok(())
+ }
Insn::Return { val } => { write!(f, "Return {val}") }
Insn::FixnumAdd { left, right, .. } => { write!(f, "FixnumAdd {left}, {right}") },
Insn::FixnumSub { left, right, .. } => { write!(f, "FixnumSub {left}, {right}") },
@@ -1027,6 +1037,7 @@ impl Function {
args: args.iter().map(|arg| find!(*arg)).collect(),
state: *state,
},
+ InvokeBuiltin { bf, args, state } => InvokeBuiltin { bf: *bf, args: find_vec!(*args), state: *state },
ArraySet { array, idx, val } => ArraySet { array: find!(*array), idx: *idx, val: find!(*val) },
ArrayDup { val , state } => ArrayDup { val: find!(*val), state: *state },
&HashDup { val , state } => HashDup { val: find!(val), state },
@@ -1123,6 +1134,7 @@ impl Function {
Insn::SendWithoutBlock { .. } => types::BasicObject,
Insn::SendWithoutBlockDirect { .. } => types::BasicObject,
Insn::Send { .. } => types::BasicObject,
+ Insn::InvokeBuiltin { .. } => types::BasicObject,
Insn::Defined { .. } => types::BasicObject,
Insn::DefinedIvar { .. } => types::BasicObject,
Insn::GetConstantPath { .. } => types::BasicObject,
@@ -1727,6 +1739,10 @@ impl Function {
worklist.extend(args);
worklist.push_back(state);
}
+ Insn::InvokeBuiltin { args, state, .. } => {
+ worklist.extend(args);
+ worklist.push_back(state)
+ }
Insn::CCall { args, .. } => worklist.extend(args),
Insn::GetIvar { self_val, state, .. } | Insn::DefinedIvar { self_val, state, .. } => {
worklist.push_back(self_val);
@@ -2606,6 +2622,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let val = state.stack_pop()?;
fun.push_insn(block, Insn::SetIvar { self_val: self_param, id, val, state: exit_id });
}
+ YARVINSN_opt_reverse => {
+ // Reverse the order of the top N stack items.
+ let n = get_arg(pc, 0).as_usize();
+ for i in 0..n/2 {
+ let bottom = state.stack_topn(n - 1 - i)?;
+ let top = state.stack_topn(i)?;
+ state.stack_setn(i, bottom);
+ state.stack_setn(n - 1 - i, top);
+ }
+ }
YARVINSN_newrange => {
let flag = RangeType::from(get_arg(pc, 0).as_u32());
let high = state.stack_pop()?;
@@ -2614,6 +2640,35 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let insn_id = fun.push_insn(block, Insn::NewRange { low, high, flag, state: exit_id });
state.stack_push(insn_id);
}
+ YARVINSN_invokebuiltin => {
+ let bf: rb_builtin_function = unsafe { *get_arg(pc, 0).as_ptr() };
+
+ let mut args = vec![];
+ for _ in 0..bf.argc {
+ args.push(state.stack_pop()?);
+ }
+ args.push(self_param);
+ args.reverse();
+
+ let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
+ let insn_id = fun.push_insn(block, Insn::InvokeBuiltin { bf, args, state: exit_id });
+ state.stack_push(insn_id);
+ }
+ YARVINSN_opt_invokebuiltin_delegate |
+ YARVINSN_opt_invokebuiltin_delegate_leave => {
+ let bf: rb_builtin_function = unsafe { *get_arg(pc, 0).as_ptr() };
+ let index = get_arg(pc, 1).as_usize();
+ let argc = bf.argc as usize;
+
+ let mut args = vec![self_param];
+ for &local in state.locals().skip(index).take(argc) {
+ args.push(local);
+ }
+
+ let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
+ let insn_id = fun.push_insn(block, Insn::InvokeBuiltin { bf, args, state: exit_id });
+ state.stack_push(insn_id);
+ }
_ => {
// Unknown opcode; side-exit into the interpreter
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
@@ -2907,7 +2962,7 @@ mod tests {
#[track_caller]
fn assert_method_hir(method: &str, hir: Expect) {
- let iseq = crate::cruby::with_rubyvm(|| get_method_iseq(method));
+ let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method));
unsafe { crate::cruby::rb_zjit_profile_disable(iseq) };
let function = iseq_to_hir(iseq).unwrap();
assert_function_hir(function, hir);
@@ -2934,7 +2989,7 @@ mod tests {
#[track_caller]
fn assert_method_hir_with_opcodes(method: &str, opcodes: &[u32], hir: Expect) {
- let iseq = crate::cruby::with_rubyvm(|| get_method_iseq(method));
+ let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method));
for &opcode in opcodes {
assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize));
}
@@ -2956,7 +3011,7 @@ mod tests {
#[track_caller]
fn assert_compile_fails(method: &str, reason: ParseError) {
- let iseq = crate::cruby::with_rubyvm(|| get_method_iseq(method));
+ let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method));
unsafe { crate::cruby::rb_zjit_profile_disable(iseq) };
let result = iseq_to_hir(iseq);
assert!(result.is_err(), "Expected an error but succesfully compiled to HIR: {}", FunctionPrinter::without_snapshot(&result.unwrap()));
@@ -4165,6 +4220,47 @@ mod tests {
}
#[test]
+ fn opt_reverse() {
+ eval("
+ def reverse_odd
+ a, b, c = @a, @b, @c
+ [a, b, c]
+ end
+
+ def reverse_even
+ a, b, c, d = @a, @b, @c, @d
+ [a, b, c, d]
+ end
+ ");
+ assert_method_hir_with_opcode("reverse_odd", YARVINSN_opt_reverse, expect![[r#"
+ fn reverse_odd:
+ bb0(v0:BasicObject):
+ v1:NilClassExact = Const Value(nil)
+ v2:NilClassExact = Const Value(nil)
+ v3:NilClassExact = Const Value(nil)
+ v6:BasicObject = GetIvar v0, :@a
+ v8:BasicObject = GetIvar v0, :@b
+ v10:BasicObject = GetIvar v0, :@c
+ v12:ArrayExact = NewArray v6, v8, v10
+ Return v12
+ "#]]);
+ assert_method_hir_with_opcode("reverse_even", YARVINSN_opt_reverse, expect![[r#"
+ fn reverse_even:
+ bb0(v0:BasicObject):
+ v1:NilClassExact = Const Value(nil)
+ v2:NilClassExact = Const Value(nil)
+ v3:NilClassExact = Const Value(nil)
+ v4:NilClassExact = Const Value(nil)
+ v7:BasicObject = GetIvar v0, :@a
+ v9:BasicObject = GetIvar v0, :@b
+ v11:BasicObject = GetIvar v0, :@c
+ v13:BasicObject = GetIvar v0, :@d
+ v15:ArrayExact = NewArray v7, v9, v11, v13
+ Return v15
+ "#]]);
+ }
+
+ #[test]
fn test_branchnil() {
eval("
def test(x) = x&.itself
@@ -4180,6 +4276,44 @@ mod tests {
Return v10
"#]]);
}
+
+ #[test]
+ fn test_invokebuiltin_delegate_with_args() {
+ assert_method_hir_with_opcode("Float", YARVINSN_opt_invokebuiltin_delegate_leave, expect![[r#"
+ fn Float:
+ bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject, v3:BasicObject):
+ v6:BasicObject = InvokeBuiltin rb_f_float, v0, v1, v2
+ Jump bb1(v0, v1, v2, v3, v6)
+ bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject):
+ Return v12
+ "#]]);
+ }
+
+ #[test]
+ fn test_invokebuiltin_delegate_without_args() {
+ assert_method_hir_with_opcode("class", YARVINSN_opt_invokebuiltin_delegate_leave, expect![[r#"
+ fn class:
+ bb0(v0:BasicObject):
+ v3:BasicObject = InvokeBuiltin _bi20, v0
+ Jump bb1(v0, v3)
+ bb1(v5:BasicObject, v6:BasicObject):
+ Return v6
+ "#]]);
+ }
+
+ #[test]
+ fn test_invokebuiltin_with_args() {
+ let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("GC", "start"));
+ assert!(iseq_contains_opcode(iseq, YARVINSN_invokebuiltin), "iseq GC.start does not contain invokebuiltin");
+ let function = iseq_to_hir(iseq).unwrap();
+ assert_function_hir(function, expect![[r#"
+ fn start:
+ bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject, v3:BasicObject, v4:BasicObject):
+ v6:FalseClassExact = Const Value(false)
+ v8:BasicObject = InvokeBuiltin gc_start_internal, v0, v1, v2, v3, v6
+ Return v8
+ "#]]);
+ }
}
#[cfg(test)]
@@ -4190,7 +4324,7 @@ mod opt_tests {
#[track_caller]
fn assert_optimized_method_hir(method: &str, hir: Expect) {
- let iseq = crate::cruby::with_rubyvm(|| get_method_iseq(method));
+ let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method));
unsafe { crate::cruby::rb_zjit_profile_disable(iseq) };
let mut function = iseq_to_hir(iseq).unwrap();
function.optimize();