diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index 85c4302737a9b2..ea9e60b1b48770 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -186,6 +186,10 @@ jobs: - run: make install + # setup/directories set MAKEFLAGS=-j4 for macOS, which randomly fails sqlite3.gem builds + - name: Unset MAKEFLAGS + run: echo "MAKEFLAGS=" >> "$GITHUB_ENV" + - name: Checkout ruby-bench uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: diff --git a/class.c b/class.c index a4249425a13a93..04c66ce8d54962 100644 --- a/class.c +++ b/class.c @@ -690,7 +690,7 @@ rb_class_classext_free_subclasses(rb_classext_t *ext, VALUE klass, bool replacin } VM_ASSERT( rb_ns_subclasses_ref_count(anchor->ns_subclasses) > 0, - "ns_subclasses refcount (%p) %ld", anchor->ns_subclasses, rb_ns_subclasses_ref_count(anchor->ns_subclasses)); + "ns_subclasses refcount (%p) %d", anchor->ns_subclasses, rb_ns_subclasses_ref_count(anchor->ns_subclasses)); st_delete(tbl, &ns_id, NULL); rb_ns_subclasses_ref_dec(anchor->ns_subclasses); xfree(anchor); diff --git a/internal/class.h b/internal/class.h index 138620dd6f0469..f182211705e1f5 100644 --- a/internal/class.h +++ b/internal/class.h @@ -28,29 +28,29 @@ #endif struct rb_ns_subclasses { - long refcount; + rb_atomic_t refcount; struct st_table *tbl; }; typedef struct rb_ns_subclasses rb_ns_subclasses_t; -static inline long +static inline rb_atomic_t rb_ns_subclasses_ref_count(rb_ns_subclasses_t *ns_sub) { - return ns_sub->refcount; + return ATOMIC_LOAD_RELAXED(ns_sub->refcount); } static inline rb_ns_subclasses_t * rb_ns_subclasses_ref_inc(rb_ns_subclasses_t *ns_sub) { - ns_sub->refcount++; + RUBY_ATOMIC_FETCH_ADD(ns_sub->refcount, 1); return ns_sub; } static inline void rb_ns_subclasses_ref_dec(rb_ns_subclasses_t *ns_sub) { - ns_sub->refcount--; - if (ns_sub->refcount == 0) { + rb_atomic_t was = RUBY_ATOMIC_FETCH_SUB(ns_sub->refcount, 1); + if (was == 1) { st_free_table(ns_sub->tbl); xfree(ns_sub); } diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index f40817e7a1ef54..cb05751da16c16 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -887,4 +887,19 @@ def test_method_table_assignment_just_after_class_init class C; end end; end + + def test_subclasses_refcount_in_ractors + assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + rs = [] + 8.times do + rs << Ractor.new do + 5_000.times do + Class.new + end + end + end + rs.each(&:join) + end; + end end diff --git a/vm.c b/vm.c index f0aebf08a38694..5b706dc6c5e227 100644 --- a/vm.c +++ b/vm.c @@ -3445,7 +3445,7 @@ size_t rb_vm_memsize_workqueue(struct ccan_list_head *workqueue); // vm_trace.c static enum rb_id_table_iterator_result vm_memsize_constant_cache_i(ID id, VALUE ics, void *size) { - *((size_t *) size) += rb_st_memsize((st_table *) ics); + *((size_t *) size) += rb_set_memsize((set_table *) ics); return ID_TABLE_CONTINUE; } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index e1ec5e63ec9653..63dcaba8a33bf5 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -5497,12 +5497,6 @@ vm_invoke_proc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, return vm_invoke_block(ec, reg_cfp, calling, ci, is_lambda, block_handler); } -enum rb_block_handler_type -rb_vm_block_handler_type(VALUE block_handler) -{ - return vm_block_handler_type(block_handler); -} - static inline VALUE vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_callinfo *ci, @@ -6065,10 +6059,30 @@ vm_define_method(const rb_execution_context_t *ec, VALUE obj, ID id, VALUE iseqv } } +// Return the untagged block handler: +// * If it's an ISEQ or an IFUNC, fetch it from its rb_captured_block +// * If it's a PROC or SYMBOL, return it as is +static VALUE +rb_vm_untag_block_handler(VALUE block_handler) +{ + switch (vm_block_handler_type(block_handler)) { + case block_handler_type_iseq: + case block_handler_type_ifunc: { + struct rb_captured_block *captured = VM_TAGGED_PTR_REF(block_handler, 0x03); + return captured->code.val; + } + case block_handler_type_proc: + case block_handler_type_symbol: + return block_handler; + default: + rb_bug("rb_vm_untag_block_handler: unreachable"); + } +} + VALUE -rb_vm_get_block_handler(rb_control_frame_t *reg_cfp) +rb_vm_get_untagged_block_handler(rb_control_frame_t *reg_cfp) { - return VM_CF_BLOCK_HANDLER(reg_cfp); + return rb_vm_untag_block_handler(VM_CF_BLOCK_HANDLER(reg_cfp)); } static VALUE diff --git a/zjit.c b/zjit.c index 72e6fe14241ef4..d1f192801a2ec8 100644 --- a/zjit.c +++ b/zjit.c @@ -302,8 +302,7 @@ rb_zjit_class_has_default_allocator(VALUE klass) } -VALUE rb_vm_get_block_handler(rb_control_frame_t *reg_cfp); -enum rb_block_handler_type rb_vm_block_handler_type(VALUE block_handler); +VALUE rb_vm_get_untagged_block_handler(rb_control_frame_t *reg_cfp); // Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them. VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self); diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index bbb3b54d6c873c..95209375dcfd71 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -399,8 +399,7 @@ fn main() { .allowlist_function("rb_yarv_str_eql_internal") .allowlist_function("rb_str_neq_internal") .allowlist_function("rb_yarv_ary_entry_internal") - .allowlist_function("rb_vm_get_block_handler") - .allowlist_function("rb_vm_block_handler_type") + .allowlist_function("rb_vm_get_untagged_block_handler") .allowlist_function("rb_FL_TEST") .allowlist_function("rb_FL_TEST_RAW") .allowlist_function("rb_RB_TYPE_P") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 6a7707dd5a5a17..f90c4605a26050 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -2163,7 +2163,8 @@ fn gen_function_stub(cb: &mut CodeBlock, iseq_call: IseqCallRef) -> Result Result { let (mut asm, scratch_reg) = Assembler::new_with_scratch_reg(); asm_comment!(asm, "function_stub_hit trampoline"); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index dc9d0d144c1259..86239588292adb 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -571,11 +571,6 @@ pub struct rb_captured_block__bindgen_ty_1 { pub val: __BindgenUnionField, pub bindgen_union_field: u64, } -pub const block_handler_type_iseq: rb_block_handler_type = 0; -pub const block_handler_type_ifunc: rb_block_handler_type = 1; -pub const block_handler_type_symbol: rb_block_handler_type = 2; -pub const block_handler_type_proc: rb_block_handler_type = 3; -pub type rb_block_handler_type = u32; pub const block_type_iseq: rb_block_type = 0; pub const block_type_ifunc: rb_block_type = 1; pub const block_type_symbol: rb_block_type = 2; @@ -1339,8 +1334,7 @@ unsafe extern "C" { pub fn rb_zjit_class_initialized_p(klass: VALUE) -> bool; pub fn rb_zjit_class_get_alloc_func(klass: VALUE) -> rb_alloc_func_t; pub fn rb_zjit_class_has_default_allocator(klass: VALUE) -> bool; - pub fn rb_vm_get_block_handler(reg_cfp: *mut rb_control_frame_t) -> VALUE; - pub fn rb_vm_block_handler_type(block_handler: VALUE) -> rb_block_handler_type; + pub fn rb_vm_get_untagged_block_handler(reg_cfp: *mut rb_control_frame_t) -> VALUE; pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_iseq_pc_at_idx(iseq: *const rb_iseq_t, insn_idx: u32) -> *mut VALUE; pub fn rb_iseq_opcode_at_pc(iseq: *const rb_iseq_t, pc: *const VALUE) -> ::std::os::raw::c_int; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index e0a2e0fdff1802..07ffe4b00a7f5e 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4421,10 +4421,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let summary = TypeDistributionSummary::new(&self_type_distribution); if summary.is_monomorphic() { let obj = summary.bucket(0).class(); - let bh_type = unsafe { rb_vm_block_handler_type(obj) }; - if bh_type == block_handler_type_iseq { + if unsafe { rb_IMEMO_TYPE_P(obj, imemo_iseq) == 1 } { fun.push_insn(block, Insn::IncrCounter(Counter::invokeblock_handler_monomorphic_iseq)); - } else if bh_type == block_handler_type_ifunc { + } else if unsafe { rb_IMEMO_TYPE_P(obj, imemo_ifunc) == 1 } { fun.push_insn(block, Insn::IncrCounter(Counter::invokeblock_handler_monomorphic_ifunc)); } else { fun.push_insn(block, Insn::IncrCounter(Counter::invokeblock_handler_monomorphic_other)); diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index c58999668e59cf..3366fe8e58db17 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -45,7 +45,7 @@ impl Profiler { } fn peek_at_block_handler(&self) -> VALUE { - unsafe { rb_vm_get_block_handler(self.cfp) } + unsafe { rb_vm_get_untagged_block_handler(self.cfp) } } } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index fbfac7b42990ee..e1d7c692ed52e0 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -331,8 +331,6 @@ pub enum CompileError { IseqStackTooLarge, ExceptionHandler, OutOfMemory, - RegisterSpillOnAlloc, - RegisterSpillOnCCall, ParseError(ParseError), JitToJitOptional, } @@ -347,8 +345,6 @@ pub fn exit_counter_for_compile_error(compile_error: &CompileError) -> Counter { IseqStackTooLarge => compile_error_iseq_stack_too_large, ExceptionHandler => compile_error_exception_handler, OutOfMemory => compile_error_out_of_memory, - RegisterSpillOnAlloc => compile_error_register_spill_on_alloc, - RegisterSpillOnCCall => compile_error_register_spill_on_ccall, JitToJitOptional => compile_error_jit_to_jit_optional, ParseError(parse_error) => match parse_error { StackUnderflow(_) => compile_error_parse_stack_underflow,