Skip to content

Commit 6e21f26

Browse files
committed
ZJIT: Add HIR for CCallWithFrame
1 parent 0ed63ea commit 6e21f26

2 files changed

Lines changed: 97 additions & 35 deletions

File tree

zjit/src/codegen.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
405405
&Insn::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))),
406406
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
407407
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfun, opnds!(args)),
408+
Insn::CallCFunc { cfun, args, cme, name: _, state } => gen_ccall_with_frame(jit, asm, *cfun, opnds!(args), *cme, &function.frame_state(*state)),
408409
Insn::CCallVariadic { cfun, recv, args, name: _, cme, state } => {
409410
gen_ccall_variadic(jit, asm, *cfun, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state))
410411
}
@@ -664,6 +665,41 @@ fn gen_patch_point(jit: &mut JITState, asm: &mut Assembler, invariant: &Invarian
664665
});
665666
}
666667

668+
/// Generate code for a C function call that pushes a frame
669+
fn gen_ccall_with_frame(jit: &mut JITState, asm: &mut Assembler, cfun: *const u8, args: Vec<Opnd>, cme: *const rb_callable_method_entry_t, state: &FrameState) -> lir::Opnd {
670+
gen_prepare_non_leaf_call(jit, asm, state);
671+
672+
gen_push_frame(asm, args.len(), state, ControlFrame {
673+
recv: args[0],
674+
iseq: None,
675+
cme,
676+
frame_type: VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL,
677+
});
678+
679+
asm_comment!(asm, "switch to new SP register");
680+
let sp_offset = (state.stack().len() - args.len() + VM_ENV_DATA_SIZE.as_usize()) * SIZEOF_VALUE;
681+
let new_sp = asm.add(SP, sp_offset.into());
682+
asm.mov(SP, new_sp);
683+
684+
asm_comment!(asm, "switch to new CFP");
685+
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
686+
asm.mov(CFP, new_cfp);
687+
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
688+
689+
let result = asm.ccall(cfun, args);
690+
691+
asm_comment!(asm, "pop C frame");
692+
let new_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
693+
asm.mov(CFP, new_cfp);
694+
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
695+
696+
asm_comment!(asm, "restore SP register for the caller");
697+
let new_sp = asm.sub(SP, sp_offset.into());
698+
asm.mov(SP, new_sp);
699+
700+
result
701+
}
702+
667703
/// Lowering for [`Insn::CCall`]. This is a low-level raw call that doesn't know
668704
/// anything about the callee, so handling for e.g. GC safety is dealt with elsewhere.
669705
fn gen_ccall(asm: &mut Assembler, cfun: *const u8, args: Vec<Opnd>) -> lir::Opnd {

zjit/src/hir.rs

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -644,10 +644,13 @@ pub enum Insn {
644644
IfTrue { val: InsnId, target: BranchEdge },
645645
IfFalse { val: InsnId, target: BranchEdge },
646646

647-
/// Call a C function
647+
/// Call a C function without pushing a frame
648648
/// `name` is for printing purposes only
649649
CCall { cfun: *const u8, args: Vec<InsnId>, name: ID, return_type: Type, elidable: bool },
650650

651+
/// Call a C function that pushes a frame
652+
CallCFunc {cfun: *const u8, args: Vec<InsnId>, cme: *const rb_callable_method_entry_t, name: ID, state: InsnId },
653+
651654
/// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv)
652655
/// This handles frame setup, argv creation, and frame teardown all in one
653656
CCallVariadic {
@@ -1027,6 +1030,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
10271030
}
10281031
Ok(())
10291032
},
1033+
Insn::CallCFunc { cfun, args, name, .. } => {
1034+
write!(f, "CallCFunc {}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfun))?;
1035+
for arg in args {
1036+
write!(f, ", {arg}")?;
1037+
}
1038+
Ok(())
1039+
},
10301040
Insn::CCallVariadic { cfun, recv, args, name, .. } => {
10311041
write!(f, "CCallVariadic {}@{:p}, {recv}", name.contents_lossy(), self.ptr_map.map_ptr(cfun))?;
10321042
for arg in args {
@@ -1546,6 +1556,7 @@ impl Function {
15461556
&ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state },
15471557
&ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) },
15481558
&CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun, args: find_vec!(args), name, return_type, elidable },
1559+
&CallCFunc { cfun, ref args, cme, name, state } => CallCFunc { cfun, args: find_vec!(args), cme, name, state: find!(state) },
15491560
&CCallVariadic { cfun, recv, ref args, cme, name, state } => CCallVariadic {
15501561
cfun, recv: find!(recv), args: find_vec!(args), cme, name, state
15511562
},
@@ -1646,6 +1657,7 @@ impl Function {
16461657
Insn::NewRangeFixnum { .. } => types::RangeExact,
16471658
Insn::ObjectAlloc { .. } => types::HeapObject,
16481659
Insn::ObjectAllocClass { class, .. } => Type::from_class(*class),
1660+
Insn::CallCFunc { .. } => types::BasicObject,
16491661
Insn::CCall { return_type, .. } => *return_type,
16501662
Insn::CCallVariadic { .. } => types::BasicObject,
16511663
Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type),
@@ -2296,35 +2308,37 @@ impl Function {
22962308
return Err(Some(method));
22972309
}
22982310

2299-
// Filter for a leaf and GC free function
2300-
use crate::cruby_methods::FnProperties;
2301-
let Some(FnProperties { leaf: true, no_gc: true, return_type, elidable }) =
2302-
ZJITState::get_method_annotations().get_cfunc_properties(method)
2303-
else {
2304-
fun.set_dynamic_send_reason(send_insn_id, SendWithoutBlockCfuncNotVariadic);
2305-
return Err(Some(method));
2306-
};
2307-
23082311
let ci_flags = unsafe { vm_ci_flag(call_info) };
2309-
// Filter for simple call sites (i.e. no splats etc.)
2310-
if ci_flags & VM_CALL_ARGS_SIMPLE != 0 {
2311-
gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, state);
23122312

2313-
if recv_class.instance_can_have_singleton_class() {
2314-
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state });
2315-
}
2316-
if let Some(profiled_type) = profiled_type {
2317-
// Guard receiver class
2318-
recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
2319-
}
2313+
if ci_flags & VM_CALL_ARGS_SIMPLE == 0 {
2314+
return Err(Some(method));
2315+
}
23202316

2321-
let cfun = unsafe { get_mct_func(cfunc) }.cast();
2322-
let mut cfunc_args = vec![recv];
2323-
cfunc_args.append(&mut args);
2317+
gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, state);
2318+
if recv_class.instance_can_have_singleton_class() {
2319+
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state });
2320+
}
2321+
if let Some(profiled_type) = profiled_type {
2322+
// Guard receiver class
2323+
recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
2324+
}
2325+
let cfun = unsafe { get_mct_func(cfunc) }.cast();
2326+
let mut cfunc_args = vec![recv];
2327+
cfunc_args.append(&mut args);
2328+
2329+
// Filter for a leaf and GC free function
2330+
use crate::cruby_methods::FnProperties;
2331+
// Filter for simple call sites (i.e. no splats etc.)
2332+
// Commit to the replacement. Put PatchPoint.
2333+
if let Some(FnProperties { leaf: true, no_gc: true, return_type, elidable }) = ZJITState::get_method_annotations().get_cfunc_properties(method) {
23242334
let ccall = fun.push_insn(block, Insn::CCall { cfun, args: cfunc_args, name: method_id, return_type, elidable });
23252335
fun.make_equal_to(send_insn_id, ccall);
2326-
return Ok(())
2336+
} else {
2337+
let ccall = fun.push_insn(block, Insn::CallCFunc { cfun, args: cfunc_args, cme: method, name: method_id, state });
2338+
fun.make_equal_to(send_insn_id, ccall);
23272339
}
2340+
2341+
return Ok(());
23282342
}
23292343
// Variadic method
23302344
-1 => {
@@ -2637,7 +2651,8 @@ impl Function {
26372651
worklist.extend(args);
26382652
worklist.push_back(state);
26392653
}
2640-
&Insn::InvokeBuiltin { ref args, state, .. }
2654+
&Insn::CallCFunc { ref args, state, .. }
2655+
| &Insn::InvokeBuiltin { ref args, state, .. }
26412656
| &Insn::InvokeBlock { ref args, state, .. } => {
26422657
worklist.extend(args);
26432658
worklist.push_back(state)
@@ -10864,8 +10879,10 @@ mod opt_tests {
1086410879
Jump bb2(v4)
1086510880
bb2(v6:BasicObject):
1086610881
v11:HashExact = NewHash
10867-
v13:BasicObject = SendWithoutBlock v11, :dup
10868-
v15:BasicObject = SendWithoutBlock v13, :freeze
10882+
PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010)
10883+
PatchPoint NoSingletonClass(Hash@0x1000)
10884+
v24:BasicObject = CallCFunc dup@0x1038, v11
10885+
v15:BasicObject = SendWithoutBlock v24, :freeze
1086910886
CheckInterrupts
1087010887
Return v15
1087110888
");
@@ -10955,8 +10972,10 @@ mod opt_tests {
1095510972
Jump bb2(v4)
1095610973
bb2(v6:BasicObject):
1095710974
v11:ArrayExact = NewArray
10958-
v13:BasicObject = SendWithoutBlock v11, :dup
10959-
v15:BasicObject = SendWithoutBlock v13, :freeze
10975+
PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010)
10976+
PatchPoint NoSingletonClass(Array@0x1000)
10977+
v24:BasicObject = CallCFunc dup@0x1038, v11
10978+
v15:BasicObject = SendWithoutBlock v24, :freeze
1096010979
CheckInterrupts
1096110980
Return v15
1096210981
");
@@ -11047,8 +11066,10 @@ mod opt_tests {
1104711066
bb2(v6:BasicObject):
1104811067
v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1104911068
v12:StringExact = StringCopy v10
11050-
v14:BasicObject = SendWithoutBlock v12, :dup
11051-
v16:BasicObject = SendWithoutBlock v14, :freeze
11069+
PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018)
11070+
PatchPoint NoSingletonClass(String@0x1008)
11071+
v25:BasicObject = CallCFunc dup@0x1040, v12
11072+
v16:BasicObject = SendWithoutBlock v25, :freeze
1105211073
CheckInterrupts
1105311074
Return v16
1105411075
");
@@ -11140,8 +11161,10 @@ mod opt_tests {
1114011161
bb2(v6:BasicObject):
1114111162
v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1114211163
v12:StringExact = StringCopy v10
11143-
v14:BasicObject = SendWithoutBlock v12, :dup
11144-
v16:BasicObject = SendWithoutBlock v14, :-@
11164+
PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018)
11165+
PatchPoint NoSingletonClass(String@0x1008)
11166+
v25:BasicObject = CallCFunc dup@0x1040, v12
11167+
v16:BasicObject = SendWithoutBlock v25, :-@
1114511168
CheckInterrupts
1114611169
Return v16
1114711170
");
@@ -11279,8 +11302,11 @@ mod opt_tests {
1127911302
bb2(v8:BasicObject, v9:BasicObject):
1128011303
v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1128111304
v25:BasicObject = GuardTypeNot v9, String
11282-
v26:BasicObject = SendWithoutBlock v9, :to_s
11283-
v17:String = AnyToString v9, str: v26
11305+
PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018)
11306+
PatchPoint NoSingletonClass(Array@0x1008)
11307+
v30:ArrayExact = GuardType v9, ArrayExact
11308+
v31:BasicObject = CallCFunc to_s@0x1040, v30
11309+
v17:String = AnyToString v9, str: v31
1128411310
v19:StringExact = StringConcat v13, v17
1128511311
CheckInterrupts
1128611312
Return v19

0 commit comments

Comments
 (0)