Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def stats_string
stats = self.stats

# Show counters independent from exit_* or dynamic_send_*
print_counters_with_prefix(prefix: 'not_optimized_cfuncs_', prompt: 'unoptimized sends to C functions', buf:, stats:, limit: 20)
print_counters_with_prefix(prefix: 'not_inlined_cfuncs_', prompt: 'not inlined C methods', buf:, stats:, limit: 20)

# Show fallback counters, ordered by the typical amount of fallbacks for the prefix at the time
print_counters_with_prefix(prefix: 'unspecialized_def_type_', prompt: 'not optimized method types', buf:, stats:, limit: 20)
Expand All @@ -172,6 +172,7 @@ def stats_string
:optimized_send_count,
:iseq_optimized_send_count,
:inline_cfunc_optimized_send_count,
:non_variadic_cfunc_optimized_send_count,
:variadic_cfunc_optimized_send_count,
], buf:, stats:, right_align: true, base: :send_count)
print_counters([
Expand Down
55 changes: 48 additions & 7 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,13 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(jit, asm, opnd!(val), *expected, &function.frame_state(*state)),
&Insn::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))),
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfun, opnds!(args)),
Insn::CCallVariadic { cfun, recv, args, name: _, cme, state } => {
gen_ccall_variadic(jit, asm, *cfun, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state))
Insn::CCall { cfunc, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, opnds!(args)),
// Give up CCallWithFrame for 7+ args since asm.ccall() doesn't support it.
Insn::CCallWithFrame { cd, state, args, .. } if args.len() > C_ARG_OPNDS.len() =>
gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::CCallWithFrameTooManyArgs),
Insn::CCallWithFrame { cfunc, args, cme, state, .. } => gen_ccall_with_frame(jit, asm, *cfunc, opnds!(args), *cme, &function.frame_state(*state)),
Insn::CCallVariadic { cfunc, recv, args, name: _, cme, state } => {
gen_ccall_variadic(jit, asm, *cfunc, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state))
}
Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id),
Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))),
Expand Down Expand Up @@ -664,19 +668,56 @@ fn gen_patch_point(jit: &mut JITState, asm: &mut Assembler, invariant: &Invarian
});
}

/// Generate code for a C function call that pushes a frame
fn gen_ccall_with_frame(jit: &mut JITState, asm: &mut Assembler, cfunc: *const u8, args: Vec<Opnd>, cme: *const rb_callable_method_entry_t, state: &FrameState) -> lir::Opnd {
gen_incr_counter(asm, Counter::non_variadic_cfunc_optimized_send_count);

gen_prepare_non_leaf_call(jit, asm, state);

gen_push_frame(asm, args.len(), state, ControlFrame {
recv: args[0],
iseq: None,
cme,
frame_type: VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL,
});

asm_comment!(asm, "switch to new SP register");
let sp_offset = (state.stack().len() - args.len() + VM_ENV_DATA_SIZE.as_usize()) * SIZEOF_VALUE;
let new_sp = asm.add(SP, sp_offset.into());
asm.mov(SP, new_sp);

asm_comment!(asm, "switch to new CFP");
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
asm.mov(CFP, new_cfp);
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);

let result = asm.ccall(cfunc, args);

asm_comment!(asm, "pop C frame");
let new_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
asm.mov(CFP, new_cfp);
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);

asm_comment!(asm, "restore SP register for the caller");
let new_sp = asm.sub(SP, sp_offset.into());
asm.mov(SP, new_sp);

result
}

/// Lowering for [`Insn::CCall`]. This is a low-level raw call that doesn't know
/// anything about the callee, so handling for e.g. GC safety is dealt with elsewhere.
fn gen_ccall(asm: &mut Assembler, cfun: *const u8, args: Vec<Opnd>) -> lir::Opnd {
fn gen_ccall(asm: &mut Assembler, cfunc: *const u8, args: Vec<Opnd>) -> lir::Opnd {
gen_incr_counter(asm, Counter::inline_cfunc_optimized_send_count);
asm.ccall(cfun, args)
asm.ccall(cfunc, args)
}

/// Generate code for a variadic C function call
/// func(int argc, VALUE *argv, VALUE recv)
fn gen_ccall_variadic(
jit: &mut JITState,
asm: &mut Assembler,
cfun: *const u8,
cfunc: *const u8,
recv: Opnd,
args: Vec<Opnd>,
cme: *const rb_callable_method_entry_t,
Expand Down Expand Up @@ -707,7 +748,7 @@ fn gen_ccall_variadic(
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);

let argv_ptr = gen_push_opnds(jit, asm, &args);
let result = asm.ccall(cfun, vec![args.len().into(), argv_ptr, recv]);
let result = asm.ccall(cfunc, vec![args.len().into(), argv_ptr, recv]);
gen_pop_opnds(asm, &args);

asm_comment!(asm, "pop C frame");
Expand Down
Loading