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
2 changes: 1 addition & 1 deletion prism/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1800,7 +1800,7 @@ nodes:
Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).

case true; when false; end
^^^^
^^^^
- name: conditions
type: node[]
kind: WhenNode
Expand Down
29 changes: 16 additions & 13 deletions ractor.c
Original file line number Diff line number Diff line change
Expand Up @@ -2280,12 +2280,16 @@ struct cross_ractor_require {
VALUE result;
VALUE exception;

// require
VALUE feature;
union {
struct {
VALUE feature;
} require;

// autoload
VALUE module;
ID name;
struct {
VALUE module;
ID name;
} autoload;
} as;

bool silent;
};
Expand All @@ -2294,8 +2298,7 @@ RUBY_REFERENCES(cross_ractor_require_refs) = {
RUBY_REF_EDGE(struct cross_ractor_require, port),
RUBY_REF_EDGE(struct cross_ractor_require, result),
RUBY_REF_EDGE(struct cross_ractor_require, exception),
RUBY_REF_EDGE(struct cross_ractor_require, feature),
RUBY_REF_EDGE(struct cross_ractor_require, module),
RUBY_REF_EDGE(struct cross_ractor_require, as.require.feature),
RUBY_REF_END
};

Expand All @@ -2322,10 +2325,10 @@ require_body(VALUE crr_obj)
if (crr->silent) {
int rb_require_internal_silent(VALUE fname);

RB_OBJ_WRITE(crr_obj, &crr->result, INT2NUM(rb_require_internal_silent(crr->feature)));
RB_OBJ_WRITE(crr_obj, &crr->result, INT2NUM(rb_require_internal_silent(crr->as.require.feature)));
}
else {
RB_OBJ_WRITE(crr_obj, &crr->result, rb_funcallv(Qnil, require, 1, &crr->feature));
RB_OBJ_WRITE(crr_obj, &crr->result, rb_funcallv(Qnil, require, 1, &crr->as.require.feature));
}

return Qnil;
Expand Down Expand Up @@ -2418,7 +2421,7 @@ rb_ractor_require(VALUE feature, bool silent)
FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);

// Convert feature to proper file path and make it shareable as fstring
RB_OBJ_WRITE(crr_obj, &crr->feature, rb_fstring(FilePathValue(feature)));
RB_OBJ_WRITE(crr_obj, &crr->as.require.feature, rb_fstring(FilePathValue(feature)));
RB_OBJ_WRITE(crr_obj, &crr->port, ractor_port_new(GET_RACTOR()));
crr->result = Qundef;
crr->exception = Qundef;
Expand Down Expand Up @@ -2459,7 +2462,7 @@ autoload_load_body(VALUE crr_obj)
struct cross_ractor_require *crr;
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);

RB_OBJ_WRITE(crr_obj, &crr->result, rb_autoload_load(crr->module, crr->name));
RB_OBJ_WRITE(crr_obj, &crr->result, rb_autoload_load(crr->as.autoload.module, crr->as.autoload.name));

return Qnil;
}
Expand All @@ -2476,8 +2479,8 @@ rb_ractor_autoload_load(VALUE module, ID name)
struct cross_ractor_require *crr;
VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);
RB_OBJ_WRITE(crr_obj, &crr->module, module);
RB_OBJ_WRITE(crr_obj, &crr->name, name);
RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
RB_OBJ_WRITE(crr_obj, &crr->as.autoload.name, name);
RB_OBJ_WRITE(crr_obj, &crr->port, ractor_port_new(GET_RACTOR()));
crr->result = Qundef;
crr->exception = Qundef;
Expand Down
38 changes: 38 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2710,6 +2710,44 @@ def test
}, insns: [:invokeblock]
end

def test_ccall_variadic_with_multiple_args
assert_compiles "[1, 2, 3]", %q{
def test
a = []
a.push(1, 2, 3)
a
end

test
test
}, insns: [:opt_send_without_block]
end

def test_ccall_variadic_with_no_args
assert_compiles "[1]", %q{
def test
a = [1]
a.push
end

test
test
}, insns: [:opt_send_without_block]
end

def test_ccall_variadic_with_no_args_causing_argument_error
assert_compiles ":error", %q{
def test
format
rescue ArgumentError
:error
end

test
test
}, insns: [:opt_send_without_block]
end

private

# Assert that every method call in `test_script` can be compiled by ZJIT
Expand Down
2 changes: 1 addition & 1 deletion tool/lib/leakchecker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def check_fd_leak(test_name)
}
unless fd_leaked.empty?
unless @@try_lsof == false
@@try_lsof |= system(*%W[lsof -a -d #{fd_leaked.minmax.uniq.join("-")} -p #$$], out: Test::Unit::Runner.output)
@@try_lsof |= system(*%W[lsof -w -a -d #{fd_leaked.minmax.uniq.join("-")} -p #$$], out: Test::Unit::Runner.output)
end
end
h.each {|fd, list|
Expand Down
17 changes: 17 additions & 0 deletions zjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval)
return result ? pushval : Qnil;
}

bool
rb_zjit_method_tracing_currently_enabled(void)
{
rb_event_flag_t tracing_events;
if (rb_multi_ractor_p()) {
tracing_events = ruby_vm_event_enabled_global_flags;
}
else {
// At the time of writing, events are never removed from
// ruby_vm_event_enabled_global_flags so always checking using it would
// mean we don't compile even after tracing is disabled.
tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
}

return tracing_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
}

bool
rb_zjit_insn_leaf(int insn, const VALUE *opes)
{
Expand Down
1 change: 1 addition & 0 deletions zjit/bindgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ fn main() {
.allowlist_function("rb_zjit_local_id")
.allowlist_function("rb_set_cfp_(pc|sp)")
.allowlist_function("rb_c_method_tracing_currently_enabled")
.allowlist_function("rb_zjit_method_tracing_currently_enabled")
.allowlist_function("rb_full_cfunc_return")
.allowlist_function("rb_assert_(iseq|cme)_handle")
.allowlist_function("rb_IMEMO_TYPE_P")
Expand Down
75 changes: 69 additions & 6 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,9 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&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::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))),
Insn::GetGlobal { id, state } => gen_getglobal(jit, asm, *id, &function.frame_state(*state)),
Expand Down Expand Up @@ -638,6 +641,52 @@ fn gen_ccall(asm: &mut Assembler, cfun: *const u8, args: Vec<Opnd>) -> lir::Opnd
asm.ccall(cfun, 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,
recv: Opnd,
args: Vec<Opnd>,
cme: *const rb_callable_method_entry_t,
state: &FrameState,
) -> lir::Opnd {
gen_prepare_non_leaf_call(jit, asm, state);

gen_push_frame(asm, args.len(), state, ControlFrame {
recv,
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 argv_ptr = gen_push_opnds(jit, asm, &args);
let result = asm.ccall(cfun, vec![args.len().into(), argv_ptr, recv]);
gen_pop_opnds(asm, &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
}

/// Emit an uncached instance variable lookup
fn gen_getivar(asm: &mut Assembler, recv: Opnd, id: ID) -> Opnd {
asm_ccall!(asm, rb_ivar_get, recv, id.0.into())
Expand Down Expand Up @@ -1054,7 +1103,7 @@ fn gen_send_without_block_direct(
// TODO: Lazily materialize caller frames on side exits or when needed
gen_push_frame(asm, args.len(), state, ControlFrame {
recv,
iseq,
iseq: Some(iseq),
cme,
frame_type: VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL,
});
Expand Down Expand Up @@ -1582,7 +1631,7 @@ fn gen_prepare_non_leaf_call(jit: &JITState, asm: &mut Assembler, state: &FrameS
/// Frame metadata written by gen_push_frame()
struct ControlFrame {
recv: Opnd,
iseq: IseqPtr,
iseq: Option<IseqPtr>,
cme: *const rb_callable_method_entry_t,
frame_type: u32,
}
Expand All @@ -1594,7 +1643,11 @@ fn gen_push_frame(asm: &mut Assembler, argc: usize, state: &FrameState, frame: C
// See vm_push_frame() for details
asm_comment!(asm, "push cme, specval, frame type");
// ep[-2]: cref of cme
let local_size = unsafe { get_iseq_body_local_table_size(frame.iseq) } as i32;
let local_size = if let Some(iseq) = frame.iseq {
(unsafe { get_iseq_body_local_table_size(iseq) }) as i32
} else {
0
};
let ep_offset = state.stack().len() as i32 + local_size - argc as i32 + VM_ENV_DATA_SIZE as i32 - 1;
asm.store(Opnd::mem(64, SP, (ep_offset - 2) * SIZEOF_VALUE_I32), VALUE::from(frame.cme).into());
// ep[-1]: block_handler or prev EP
Expand All @@ -1609,9 +1662,19 @@ fn gen_push_frame(asm: &mut Assembler, argc: usize, state: &FrameState, frame: C
}

asm_comment!(asm, "push callee control frame");
// cfp_opnd(RUBY_OFFSET_CFP_PC): written by the callee frame on side-exits or non-leaf calls
// cfp_opnd(RUBY_OFFSET_CFP_SP): written by the callee frame on side-exits or non-leaf calls
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_ISEQ), VALUE::from(frame.iseq).into());

if let Some(iseq) = frame.iseq {
// cfp_opnd(RUBY_OFFSET_CFP_PC): written by the callee frame on side-exits or non-leaf calls
// cfp_opnd(RUBY_OFFSET_CFP_SP): written by the callee frame on side-exits or non-leaf calls
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_ISEQ), VALUE::from(iseq).into());
} else {
// C frames don't have a PC and ISEQ
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_PC), 0.into());
let new_sp = asm.lea(Opnd::mem(64, SP, (ep_offset + 1) * SIZEOF_VALUE_I32));
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SP), new_sp);
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_ISEQ), 0.into());
}

asm.mov(cfp_opnd(RUBY_OFFSET_CFP_SELF), frame.recv);
let ep = asm.lea(Opnd::mem(64, SP, ep_offset * SIZEOF_VALUE_I32));
asm.mov(cfp_opnd(RUBY_OFFSET_CFP_EP), ep);
Expand Down
1 change: 1 addition & 0 deletions zjit/src/cruby_bindings.inc.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions zjit/src/cruby_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ pub fn init() -> Annotations {
annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable);
annotate!(rb_cBasicObject, "==", types::BoolExact, no_gc, leaf, elidable);
annotate!(rb_cBasicObject, "!", types::BoolExact, no_gc, leaf, elidable);
annotate!(rb_cBasicObject, "initialize", types::NilClass, no_gc, leaf, elidable);

annotate_builtin!(rb_mKernel, "Float", types::Float);
annotate_builtin!(rb_mKernel, "Integer", types::Integer);
Expand Down
Loading