From db7a9a1d6d65413791b91df56d8ad3868457fc56 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 6 Jul 2026 13:59:59 +0900 Subject: [PATCH 01/20] Dynamically determine max shape capacity SHAPE_MAX_CAPACITY needed to be below or equal to the maximum capacity allocatable by the GC otherwise it would crash. This commit decouples that by adding back max_capacity in the rb_shape_tree_t and adding a new GC API function rb_gc_impl_max_allocation_size to report the maximum size allocatable by the GC. --- gc.c | 13 +++++++++++-- gc/default/default.c | 10 ++++++++-- gc/gc_impl.h | 1 + gc/mmtk/mmtk.c | 10 ++++++++-- gc/wbcheck/wbcheck.c | 10 ++++++++-- internal/gc.h | 1 + shape.c | 15 ++++++++++----- shape.h | 16 +++++++--------- 8 files changed, 54 insertions(+), 22 deletions(-) diff --git a/gc.c b/gc.c index fe4ecc8d37a484..7e5e9f0962ae65 100644 --- a/gc.c +++ b/gc.c @@ -625,6 +625,7 @@ typedef struct gc_function_map { size_t (*obj_slot_size)(VALUE obj); size_t (*size_slot_size)(void *objspace_ptr, size_t size); bool (*size_allocatable_p)(size_t size); + size_t (*max_allocation_size)(void); // Malloc void *(*malloc)(void *objspace_ptr, size_t size, bool gc_allowed); void *(*calloc)(void *objspace_ptr, size_t size, bool gc_allowed); @@ -803,6 +804,7 @@ ruby_modular_gc_init(void) load_modular_gc_func(obj_slot_size); load_modular_gc_func(size_slot_size); load_modular_gc_func(size_allocatable_p); + load_modular_gc_func(max_allocation_size); // Malloc load_modular_gc_func(malloc); load_modular_gc_func(calloc); @@ -890,6 +892,7 @@ ruby_modular_gc_init(void) # define rb_gc_impl_obj_slot_size rb_gc_functions.obj_slot_size # define rb_gc_impl_size_slot_size rb_gc_functions.size_slot_size # define rb_gc_impl_size_allocatable_p rb_gc_functions.size_allocatable_p +# define rb_gc_impl_max_allocation_size rb_gc_functions.max_allocation_size // Malloc # define rb_gc_impl_malloc rb_gc_functions.malloc # define rb_gc_impl_calloc rb_gc_functions.calloc @@ -1106,8 +1109,8 @@ rb_class_allocate_instance(VALUE klass) VALUE obj; // Directly start as COMPLEX if we know we're over the limit. - RUBY_ASSERT(SHAPE_MAX_CAPACITY > 0); - if (RB_UNLIKELY(index_tbl_num_entries > SHAPE_MAX_CAPACITY)) { + RUBY_ASSERT(rb_shape_max_capacity() > 0); + if (RB_UNLIKELY(index_tbl_num_entries > rb_shape_max_capacity())) { obj = class_allocate_complex_instance(klass, index_tbl_num_entries); } else { @@ -4009,6 +4012,12 @@ rb_gc_size_allocatable_p(size_t size) return rb_gc_impl_size_allocatable_p(size); } +size_t +rb_gc_max_allocation_size(void) +{ + return rb_gc_impl_max_allocation_size(); +} + static enum rb_id_table_iterator_result update_id_table(VALUE *value, void *data, int existing) { diff --git a/gc/default/default.c b/gc/default/default.c index b39a1bd85592fd..4cc0a78d947d30 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -2458,10 +2458,16 @@ heap_slot_size(unsigned char pool_id) return pool_slot_sizes[pool_id] - RVALUE_OVERHEAD; } +size_t +rb_gc_impl_max_allocation_size(void) +{ + return heap_slot_size(HEAP_COUNT - 1); +} + bool rb_gc_impl_size_allocatable_p(size_t size) { - return size + RVALUE_OVERHEAD <= pool_slot_sizes[HEAP_COUNT - 1]; + return size <= rb_gc_impl_max_allocation_size(); } static const size_t ALLOCATED_COUNT_STEP = 1024; @@ -10332,7 +10338,7 @@ rb_gc_impl_init(void) rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_SIZE")), SIZET2NUM(HEAP_PAGE_BITMAP_SIZE)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_SIZE")), SIZET2NUM(HEAP_PAGE_SIZE)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_COUNT")), LONG2FIX(HEAP_COUNT)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(heap_slot_size(HEAP_COUNT - 1))); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), SIZET2NUM(rb_gc_impl_max_allocation_size())); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), LONG2FIX(RVALUE_OLD_AGE)); if (RB_BUG_INSTEAD_OF_RB_MEMERROR+0) { rb_hash_aset(gc_constants, ID2SYM(rb_intern("RB_BUG_INSTEAD_OF_RB_MEMERROR")), Qtrue); diff --git a/gc/gc_impl.h b/gc/gc_impl.h index 892831c8c175a8..5a8d3e2965a2f2 100644 --- a/gc/gc_impl.h +++ b/gc/gc_impl.h @@ -59,6 +59,7 @@ GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE k GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj); GC_IMPL_FN size_t rb_gc_impl_size_slot_size(void *objspace_ptr, size_t size); GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size); +GC_IMPL_FN size_t rb_gc_impl_max_allocation_size(void); // Malloc /* * BEWARE: These functions may or may not run under GVL. diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 3600a3d2e36ce8..14369b8bc33a8c 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -670,7 +670,7 @@ rb_gc_impl_init(void) rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(SIZEOF_VALUE >= 8 ? 64 : 32)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic))); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MMTK_MAX_OBJ_SIZE)); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), SIZET2NUM(rb_gc_impl_max_allocation_size())); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_COUNT")), LONG2FIX(MMTK_HEAP_COUNT)); // TODO: correctly set RVALUE_OLD_AGE when we have generational GC support rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), INT2FIX(0)); @@ -969,7 +969,13 @@ rb_gc_impl_size_slot_size(void *objspace_ptr, size_t size) bool rb_gc_impl_size_allocatable_p(size_t size) { - return size <= MMTK_MAX_OBJ_SIZE; + return size <= rb_gc_impl_max_allocation_size(); +} + +size_t +rb_gc_impl_max_allocation_size(void) +{ + return MMTK_MAX_OBJ_SIZE; } // Malloc diff --git a/gc/wbcheck/wbcheck.c b/gc/wbcheck/wbcheck.c index 5da87f5762e7c5..622a893a176f3b 100644 --- a/gc/wbcheck/wbcheck.c +++ b/gc/wbcheck/wbcheck.c @@ -518,7 +518,7 @@ rb_gc_impl_init(void) rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(sizeof(struct RBasic) + sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]))); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic))); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0)); - rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MAX_HEAP_SIZE)); + rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), SIZET2NUM(rb_gc_impl_max_allocation_size())); rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_COUNT")), LONG2FIX(HEAP_COUNT)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(HEAP_COUNT)); rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), INT2FIX(3)); @@ -1127,7 +1127,13 @@ bool rb_gc_impl_size_allocatable_p(size_t size) { // Only allow sizes up to the largest heap size - return size <= MAX_HEAP_SIZE; + return size <= rb_gc_impl_max_allocation_size(); +} + +size_t +rb_gc_impl_max_allocation_size(void) +{ + return MAX_HEAP_SIZE; } // Malloc diff --git a/internal/gc.h b/internal/gc.h index 6d1df9363fe5e1..5a2e76d8ef6ad3 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -206,6 +206,7 @@ void rb_gc_ractor_cache_free(void *cache); bool rb_gc_size_allocatable_p(size_t size); size_t rb_gc_size_slot_size(size_t size); +size_t rb_gc_max_allocation_size(void); void rb_gc_mark_and_move(VALUE *ptr); diff --git a/shape.c b/shape.c index 875b62099b7dbc..bb4bb437d74e08 100644 --- a/shape.c +++ b/shape.c @@ -516,7 +516,7 @@ shape_grow_capa(attr_index_t current_capa) { size_t next_size = rb_obj_embedded_size(current_capa + 1); if (UNLIKELY(!rb_gc_size_allocatable_p(next_size))) { - return SHAPE_MAX_CAPACITY; + return rb_shape_max_capacity(); } attr_index_t next_capa = rb_shape_capacity_for_slot_size(rb_gc_size_slot_size(next_size)); @@ -700,7 +700,7 @@ rb_shape_transition_object_id(shape_id_t original_shape_id) bool dont_care; rb_shape_t *shape = NULL; - if (LIKELY(original_shape->next_field_index < SHAPE_MAX_CAPACITY)) { + if (LIKELY(original_shape->next_field_index < rb_shape_max_capacity())) { shape = get_next_shape_internal(original_shape, id_object_id, SHAPE_OBJ_ID, &dont_care, true); } if (!shape) { @@ -765,8 +765,9 @@ shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE klass, ID id } #endif - RUBY_ASSERT(SHAPE_MAX_CAPACITY > 0); - if (UNLIKELY(shape->next_field_index >= SHAPE_MAX_CAPACITY)) { + RUBY_ASSERT(SHAPE_ID_CAPACITY_MAX > 0); + RUBY_ASSERT(rb_shape_max_capacity() > 0); + if (UNLIKELY(shape->next_field_index >= rb_shape_max_capacity())) { return NULL; } @@ -1558,6 +1559,10 @@ rb_shape_find_by_id(VALUE mod, VALUE id) void Init_default_shapes(void) { + attr_index_t max_capacity = (attr_index_t)((rb_gc_max_allocation_size() - sizeof(struct RBasic)) / sizeof(VALUE)); + if (max_capacity > SHAPE_ID_CAPACITY_MAX) max_capacity = SHAPE_ID_CAPACITY_MAX; + rb_shape_tree.max_capacity = max_capacity; + #ifdef HAVE_MMAP size_t shape_list_mmap_size = rb_size_mul_or_raise(SHAPE_BUFFER_SIZE, sizeof(rb_shape_t), rb_eRuntimeError); rb_shape_tree.shape_list = (rb_shape_t *)mmap(NULL, shape_list_mmap_size, @@ -1651,7 +1656,7 @@ Init_shape(void) rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS)); rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS)); - rb_define_const(rb_cShape, "SHAPE_MAX_FIELDS", INT2NUM(SHAPE_MAX_CAPACITY)); + rb_define_const(rb_cShape, "SHAPE_MAX_FIELDS", INT2NUM(rb_shape_max_capacity())); rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t))); rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t))); rb_define_const(rb_cShape, "SHAPE_BUFFER_SIZE", INT2NUM(sizeof(rb_shape_t) * SHAPE_BUFFER_SIZE)); diff --git a/shape.h b/shape.h index ab126b5a89d0f8..763a43fddd703d 100644 --- a/shape.h +++ b/shape.h @@ -89,18 +89,9 @@ typedef uint32_t redblack_id_t; #define SHAPE_MAX_VARIATIONS 8 -// TODO: Fix this so that the max capacity does not depend on the CPU architecture -#if RBASIC_SHAPE_ID_FIELD -# define SHAPE_MAX_CAPACITY 125 -#else -# define SHAPE_MAX_CAPACITY 126 -#endif - #define INVALID_SHAPE_ID (SHAPE_BUFFER_SIZE - 1) #define ATTR_INDEX_NOT_SET ((attr_index_t)-1) -STATIC_ASSERT(shape_max_capacity, SHAPE_MAX_CAPACITY <= SHAPE_ID_CAPACITY_MAX); - #define ROOT_SHAPE_ID 0x0 #define ROOT_SHAPE_WITH_OBJ_ID 0x1 #define ROOT_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_COMPLEX) @@ -134,6 +125,7 @@ enum shape_flags { typedef struct { rb_shape_t *shape_list; + attr_index_t max_capacity; } rb_shape_tree_t; RUBY_SYMBOL_EXPORT_BEGIN @@ -143,6 +135,12 @@ RUBY_SYMBOL_EXPORT_END size_t rb_shapes_cache_size(void); size_t rb_shapes_count(void); +static inline attr_index_t +rb_shape_max_capacity(void) +{ + return rb_shape_tree.max_capacity; +} + static inline shape_id_t RBASIC_SHAPE_ID(VALUE obj) { From a8b69568bc49e000938efa6f3b129bae8c1ef772 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 02:08:37 +0000 Subject: [PATCH 02/20] Bump taiki-e/install-action Bumps the github-actions group with 1 update in the / directory: [taiki-e/install-action](https://github.com/taiki-e/install-action). Updates `taiki-e/install-action` from 2.82.9 to 2.82.10 - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/4684b8405694ae9dd42c9f39ba901a70ae83f4a3...50414676f9f5d50a65992c6dd2ed02641263226c) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.82.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/zjit-macos.yml | 2 +- .github/workflows/zjit-ubuntu.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index f82baa081ab6cc..82cef518c3ea51 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -98,7 +98,7 @@ jobs: rustup install ${{ matrix.rust_version }} --profile minimal rustup default ${{ matrix.rust_version }} - - uses: taiki-e/install-action@4684b8405694ae9dd42c9f39ba901a70ae83f4a3 # v2.82.9 + - uses: taiki-e/install-action@50414676f9f5d50a65992c6dd2ed02641263226c # v2.82.10 with: tool: nextest@0.9 if: ${{ matrix.test_task == 'zjit-check' }} diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 396397052e0758..81aa9dd4beaca7 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -141,7 +141,7 @@ jobs: ruby-version: '3.1' bundler: none - - uses: taiki-e/install-action@4684b8405694ae9dd42c9f39ba901a70ae83f4a3 # v2.82.9 + - uses: taiki-e/install-action@50414676f9f5d50a65992c6dd2ed02641263226c # v2.82.10 with: tool: nextest@0.9 if: ${{ matrix.test_task == 'zjit-check' }} From 86a6b79e5d384357dcc3befdbd0dbeca9c77b3ca Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 6 Jul 2026 21:30:56 +0900 Subject: [PATCH 03/20] Decouple Test_StrSetLen from slot size Array#join now pre-calculates the length, so the string created in the tests were embedded anyways. --- test/-ext-/string/test_set_len.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/-ext-/string/test_set_len.rb b/test/-ext-/string/test_set_len.rb index 41e14a293ac3c5..1ffa6e77341767 100644 --- a/test/-ext-/string/test_set_len.rb +++ b/test/-ext-/string/test_set_len.rb @@ -4,8 +4,7 @@ class Test_StrSetLen < Test::Unit::TestCase def setup - # Make string long enough so that it is not embedded - @range_end = ("0".ord + GC.stat_heap(0, :slot_size)).chr + @range_end = ("0".ord + 40).chr @s0 = [*"0"..@range_end].join("").freeze @s1 = Bug::String.new(@s0) end From 4ad5d89714acfb263799453f8b8095757a61990b Mon Sep 17 00:00:00 2001 From: YO4 Date: Tue, 7 Jul 2026 07:05:39 +0900 Subject: [PATCH 04/20] remove unused rb_gc_vm_context->lock Follow up to GH-PR #16914 --- gc.c | 1 - gc/gc.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/gc.c b/gc.c index 7e5e9f0962ae65..c61f080884e623 100644 --- a/gc.c +++ b/gc.c @@ -194,7 +194,6 @@ rb_gc_get_ractor_newobj_cache(void) void rb_gc_initialize_vm_context(struct rb_gc_vm_context *context) { - rb_native_mutex_initialize(&context->lock); context->ec = GET_EC(); } diff --git a/gc/gc.h b/gc/gc.h index 2a20d65c56cf1d..d8c351c7e92754 100644 --- a/gc/gc.h +++ b/gc/gc.h @@ -38,8 +38,6 @@ struct rb_gc_obj_suffix { #endif struct rb_gc_vm_context { - rb_nativethread_lock_t lock; - struct rb_execution_context_struct *ec; }; From 291d0d95ea0ea1df8e102f36f1c4f093196bc24b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 08:17:48 +0200 Subject: [PATCH 05/20] onig_reg_copy_body: fix copying of exact_end Followup: https://github.com/ruby/ruby/pull/17671 --- regcomp.c | 2 +- test/ruby/test_regexp.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/regcomp.c b/regcomp.c index ec41dbe4a02fdc..d4188087ea377f 100644 --- a/regcomp.c +++ b/regcomp.c @@ -5615,7 +5615,7 @@ onig_reg_copy_body(regex_t* nreg, regex_t* oreg) size_t exact_size = oreg->exact_end - oreg->exact; if (COPY_FAILED(exact, exact_size)) goto err; - (nreg)->exact_end = (oreg)->exact + exact_size; + (nreg)->exact_end = (nreg)->exact + exact_size; } if (IS_NOT_NULL(oreg->p)) { diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 250956dd3a009d..0518f5828e6c4e 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -740,6 +740,7 @@ def test_initialize assert_equal(/foo/, assert_warning(/ignored/) {Regexp.new(/foo/, Regexp::IGNORECASE)}) assert_equal(/foo/, assert_no_warning(/ignored/) {Regexp.new(/foo/)}) assert_equal(/foo/, assert_no_warning(/ignored/) {Regexp.new(/foo/, timeout: nil)}) + assert_equal(/foo/, Regexp.new(Regexp.new("foo"))) arg_encoding_none = //n.options # ARG_ENCODING_NONE is implementation defined value From b7856ca0f2f2cb57e2d29cd9737e14935f5ce145 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 7 Jul 2026 02:10:51 -0500 Subject: [PATCH 06/20] [DOC] Update Set Methods for Deleting documentation Co-authored-by: Jeremy Evans --- set.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/set.c b/set.c index 3d88d8d2ca10ae..1f65e1445a1459 100644 --- a/set.c +++ b/set.c @@ -804,7 +804,7 @@ set_i_add_p(VALUE set, VALUE item) * call-seq: * delete(object) -> self * - * Removes the given +object+ from +self+, if +self+ includes the object; + * Removes the given +object+ from +self+ if +self+ includes the object; * returns +self+: * * set = Set[0, 'zero', :zero] @@ -2472,21 +2472,23 @@ rb_set_size(VALUE set) * === Methods for Deleting * * - #clear: - * Removes all elements in the set; returns +self+. + * Removes all elements from +self+; returns +self+. * - #delete: - * Removes a given object from the set; returns +self+. + * Removes the given object from +self+ if +self+ includes the object; returns +self+. * - #delete?: - * If the given object is an element in the set, - * removes it and returns +self+; otherwise, returns +nil+. - * - #subtract: - * Removes each given object from the set; returns +self+. - * - #delete_if - Removes elements specified by a given block. - * - #select! (aliased as #filter!): - * Removes elements not specified by a given block. + * Like #delete, but returns +nil+ if the object is not in +self+. + * - #delete_if: + * Calls the block with each element in +self+; + * removes the element if the block returns a truthy value. * - #keep_if: - * Removes elements not specified by a given block. + * Calls the block with each element in +self+, + * deleting the element if the block returns +false+ or +nil+; returns +self+. * - #reject! - * Removes elements specified by a given block. + * Like #delete_if, but returns +nil+ if no changes were made. + * - #select! (aliased as #filter!): + * Like #keep_if, but returns +nil+ if no changes were made. + * - #subtract: + * Deletes from +self+ every element found in the given enumerable; returns +self+: * * === Methods for Converting * From bcbf54feb4756a45dc5dc7408f5774c5afea1622 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 7 Jul 2026 02:11:56 -0500 Subject: [PATCH 07/20] [DOC] Update Set Methods for Converting documentation Co-authored-by: Jeremy Evans --- set.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/set.c b/set.c index 1f65e1445a1459..d7d35470680419 100644 --- a/set.c +++ b/set.c @@ -2502,19 +2502,19 @@ rb_set_size(VALUE set) * as determined by the given block. * - #flatten: * Returns a new set that is a recursive flattening of +self+. - * - #flatten!: - * Replaces each nested set in +self+ with the elements from that set. + * - #flatten!: Like #flatten, but if any changes were made + * replaces +self+ with the result and returns +self+. * - #inspect (aliased as #to_s): - * Returns a string displaying the elements. + * Returns a string representation of +self+. * - #join: - * Returns a string containing all elements, converted to strings - * as needed, and joined by the given record separator. + * Returns the string formed by joining the string-converted elements of +self+ + * with the given separator. * - #to_a: - * Returns an array containing all set elements. + * Returns an array containing the elements of +self+. * - #to_set: - * Returns +self+ if given no arguments and no block; - * with a block given, returns a new set consisting of block - * return values. + * With a block given, creates and returns a new set; + * calls the block with each element of +self+, + * and adds the block's returns value to the new set. * * === Methods for Iterating * From a1515f149edd85c77bdf53cfb6710f0b262cd6ca Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 7 Jul 2026 02:13:58 -0500 Subject: [PATCH 08/20] [DOC] Remove Set Methods for Iterating documentation section This section only had one method (#each), so move that method to the "Other Methods" section. --- set.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/set.c b/set.c index d7d35470680419..9a9a7636f87367 100644 --- a/set.c +++ b/set.c @@ -2516,15 +2516,12 @@ rb_set_size(VALUE set) * calls the block with each element of +self+, * and adds the block's returns value to the new set. * - * === Methods for Iterating - * - * - #each: - * Calls the block with each successive element; returns +self+. - * * === Other Methods * * - #compare_by_identity: * Sets +self+ to compare by object identity (rather than by object content). + * - #each: + * Calls the block with each successive element of +self+; returns +self+. * - #reset: * Resets the internal state; useful if an element * has been modified while an element in the set. From 0fd945f3de57af984cb1cfb14dfe144ece61951c Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 08:22:01 +0200 Subject: [PATCH 09/20] Stop including onigmo.h in rregexp.h Followup: https://github.com/ruby/ruby/pull/17671 [Bug #22184] `onigmo.h` contains a lot of declarations that can easily conflict with third party code, hence it's better to keep it opaque. That however requires a few hacks. --- depend | 10 ------ enc/depend | 22 ------------- ext/-test-/RUBY_ALIGNOF/depend | 1 - ext/-test-/arith_seq/beg_len_step/depend | 1 - ext/-test-/arith_seq/extract/depend | 1 - ext/-test-/array/concat/depend | 1 - ext/-test-/array/resize/depend | 1 - ext/-test-/bignum/depend | 7 ---- ext/-test-/bug-14834/depend | 1 - ext/-test-/bug-3571/depend | 1 - ext/-test-/bug-5832/depend | 1 - ext/-test-/bug_reporter/depend | 1 - ext/-test-/class/depend | 2 -- ext/-test-/debug/depend | 3 -- ext/-test-/dln/empty/depend | 1 - ext/-test-/econv/depend | 1 - ext/-test-/ensure_and_callcc/depend | 1 - ext/-test-/enumerator_kw/depend | 1 - ext/-test-/eval/depend | 1 - ext/-test-/exception/depend | 3 -- ext/-test-/fatal/depend | 3 -- ext/-test-/file/depend | 1 - ext/-test-/float/depend | 2 -- ext/-test-/funcall/depend | 1 - ext/-test-/gvl/call_without_gvl/depend | 1 - ext/-test-/hash/depend | 2 -- ext/-test-/integer/depend | 3 -- ext/-test-/iseq_load/depend | 1 - ext/-test-/iter/depend | 3 -- ext/-test-/load/dot.dot/depend | 1 - ext/-test-/load/protect/depend | 1 - .../load/resolve_symbol_resolver/depend | 1 - ext/-test-/load/resolve_symbol_target/depend | 1 - ext/-test-/load/stringify_symbols/depend | 1 - ext/-test-/load/stringify_target/depend | 1 - ext/-test-/marshal/compat/depend | 1 - ext/-test-/marshal/internal_ivar/depend | 1 - ext/-test-/marshal/usr/depend | 1 - ext/-test-/memory_view/depend | 1 - ext/-test-/method/depend | 2 -- ext/-test-/notimplement/depend | 1 - ext/-test-/num2int/depend | 1 - ext/-test-/path_to_class/depend | 1 - ext/-test-/popen_deadlock/depend | 1 - ext/-test-/postponed_job/depend | 1 - ext/-test-/proc/depend | 3 -- ext/-test-/random/depend | 3 -- ext/-test-/rational/depend | 1 - ext/-test-/rb_call_super_kw/depend | 1 - ext/-test-/recursion/depend | 1 - ext/-test-/regexp/depend | 2 -- ext/-test-/sanitizers/depend | 1 - ext/-test-/scan_args/depend | 1 - ext/-test-/st/foreach/depend | 1 - ext/-test-/st/numhash/depend | 1 - ext/-test-/st/update/depend | 1 - ext/-test-/string/depend | 7 ---- ext/-test-/struct/depend | 5 --- ext/-test-/symbol/depend | 2 -- ext/-test-/thread/id/depend | 1 - ext/-test-/thread/instrumentation/depend | 1 - ext/-test-/thread/lock_native_thread/depend | 1 - ext/-test-/time/depend | 2 -- ext/-test-/tracepoint/depend | 2 -- ext/-test-/typeddata/depend | 1 - ext/-test-/vm/depend | 1 - ext/continuation/depend | 1 - ext/date/depend | 1 - ext/digest/bubblebabble/depend | 1 - ext/digest/depend | 1 - ext/digest/md5/depend | 2 -- ext/digest/rmd160/depend | 2 -- ext/digest/sha1/depend | 2 -- ext/digest/sha2/depend | 2 -- ext/fcntl/depend | 1 - ext/rbconfig/sizeof/depend | 2 -- ext/ripper/depend | 1 - include/ruby/internal/core/rregexp.h | 32 +++++++++---------- re.c | 11 +++++-- 79 files changed, 24 insertions(+), 169 deletions(-) diff --git a/depend b/depend index 66264ed2dfaa28..c3252dd87e821b 100644 --- a/depend +++ b/depend @@ -3002,7 +3002,6 @@ debug_counter.$(OBJEXT): {$(VPATH)}internal/variable.h debug_counter.$(OBJEXT): {$(VPATH)}internal/warning_push.h debug_counter.$(OBJEXT): {$(VPATH)}internal/xmalloc.h debug_counter.$(OBJEXT): {$(VPATH)}missing.h -debug_counter.$(OBJEXT): {$(VPATH)}onigmo.h debug_counter.$(OBJEXT): {$(VPATH)}st.h debug_counter.$(OBJEXT): {$(VPATH)}subst.h debug_counter.$(OBJEXT): {$(VPATH)}thread_native.h @@ -3386,7 +3385,6 @@ dln.$(OBJEXT): {$(VPATH)}internal/variable.h dln.$(OBJEXT): {$(VPATH)}internal/warning_push.h dln.$(OBJEXT): {$(VPATH)}internal/xmalloc.h dln.$(OBJEXT): {$(VPATH)}missing.h -dln.$(OBJEXT): {$(VPATH)}onigmo.h dln.$(OBJEXT): {$(VPATH)}st.h dln.$(OBJEXT): {$(VPATH)}subst.h dln_find.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -3546,7 +3544,6 @@ dln_find.$(OBJEXT): {$(VPATH)}internal/variable.h dln_find.$(OBJEXT): {$(VPATH)}internal/warning_push.h dln_find.$(OBJEXT): {$(VPATH)}internal/xmalloc.h dln_find.$(OBJEXT): {$(VPATH)}missing.h -dln_find.$(OBJEXT): {$(VPATH)}onigmo.h dln_find.$(OBJEXT): {$(VPATH)}st.h dln_find.$(OBJEXT): {$(VPATH)}subst.h dmydln.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -3705,7 +3702,6 @@ dmydln.$(OBJEXT): {$(VPATH)}internal/variable.h dmydln.$(OBJEXT): {$(VPATH)}internal/warning_push.h dmydln.$(OBJEXT): {$(VPATH)}internal/xmalloc.h dmydln.$(OBJEXT): {$(VPATH)}missing.h -dmydln.$(OBJEXT): {$(VPATH)}onigmo.h dmydln.$(OBJEXT): {$(VPATH)}st.h dmydln.$(OBJEXT): {$(VPATH)}subst.h dmyenc.$(OBJEXT): {$(VPATH)}dmyenc.c @@ -7107,7 +7103,6 @@ inits.$(OBJEXT): {$(VPATH)}internal/variable.h inits.$(OBJEXT): {$(VPATH)}internal/warning_push.h inits.$(OBJEXT): {$(VPATH)}internal/xmalloc.h inits.$(OBJEXT): {$(VPATH)}missing.h -inits.$(OBJEXT): {$(VPATH)}onigmo.h inits.$(OBJEXT): {$(VPATH)}prelude.rbinc inits.$(OBJEXT): {$(VPATH)}st.h inits.$(OBJEXT): {$(VPATH)}subst.h @@ -8524,7 +8519,6 @@ loadpath.$(OBJEXT): {$(VPATH)}internal/warning_push.h loadpath.$(OBJEXT): {$(VPATH)}internal/xmalloc.h loadpath.$(OBJEXT): {$(VPATH)}loadpath.c loadpath.$(OBJEXT): {$(VPATH)}missing.h -loadpath.$(OBJEXT): {$(VPATH)}onigmo.h loadpath.$(OBJEXT): {$(VPATH)}st.h loadpath.$(OBJEXT): {$(VPATH)}subst.h loadpath.$(OBJEXT): {$(VPATH)}verconf.h @@ -8862,7 +8856,6 @@ main.$(OBJEXT): {$(VPATH)}internal/warning_push.h main.$(OBJEXT): {$(VPATH)}internal/xmalloc.h main.$(OBJEXT): {$(VPATH)}main.c main.$(OBJEXT): {$(VPATH)}missing.h -main.$(OBJEXT): {$(VPATH)}onigmo.h main.$(OBJEXT): {$(VPATH)}st.h main.$(OBJEXT): {$(VPATH)}subst.h main.$(OBJEXT): {$(VPATH)}vm_debug.h @@ -9269,7 +9262,6 @@ math.$(OBJEXT): {$(VPATH)}internal/warning_push.h math.$(OBJEXT): {$(VPATH)}internal/xmalloc.h math.$(OBJEXT): {$(VPATH)}math.c math.$(OBJEXT): {$(VPATH)}missing.h -math.$(OBJEXT): {$(VPATH)}onigmo.h math.$(OBJEXT): {$(VPATH)}shape.h math.$(OBJEXT): {$(VPATH)}st.h math.$(OBJEXT): {$(VPATH)}subst.h @@ -16660,7 +16652,6 @@ setproctitle.$(OBJEXT): {$(VPATH)}internal/variable.h setproctitle.$(OBJEXT): {$(VPATH)}internal/warning_push.h setproctitle.$(OBJEXT): {$(VPATH)}internal/xmalloc.h setproctitle.$(OBJEXT): {$(VPATH)}missing.h -setproctitle.$(OBJEXT): {$(VPATH)}onigmo.h setproctitle.$(OBJEXT): {$(VPATH)}setproctitle.c setproctitle.$(OBJEXT): {$(VPATH)}st.h setproctitle.$(OBJEXT): {$(VPATH)}subst.h @@ -19272,7 +19263,6 @@ util.$(OBJEXT): {$(VPATH)}internal/variable.h util.$(OBJEXT): {$(VPATH)}internal/warning_push.h util.$(OBJEXT): {$(VPATH)}internal/xmalloc.h util.$(OBJEXT): {$(VPATH)}missing.h -util.$(OBJEXT): {$(VPATH)}onigmo.h util.$(OBJEXT): {$(VPATH)}ruby_atomic.h util.$(OBJEXT): {$(VPATH)}st.h util.$(OBJEXT): {$(VPATH)}subst.h diff --git a/enc/depend b/enc/depend index c1a73bc4a2f843..4bf97dc880f195 100644 --- a/enc/depend +++ b/enc/depend @@ -5243,7 +5243,6 @@ enc/trans/big5.$(OBJEXT): internal/variable.h enc/trans/big5.$(OBJEXT): internal/warning_push.h enc/trans/big5.$(OBJEXT): internal/xmalloc.h enc/trans/big5.$(OBJEXT): missing.h -enc/trans/big5.$(OBJEXT): onigmo.h enc/trans/big5.$(OBJEXT): st.h enc/trans/big5.$(OBJEXT): subst.h enc/trans/cesu_8.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -5404,7 +5403,6 @@ enc/trans/cesu_8.$(OBJEXT): internal/variable.h enc/trans/cesu_8.$(OBJEXT): internal/warning_push.h enc/trans/cesu_8.$(OBJEXT): internal/xmalloc.h enc/trans/cesu_8.$(OBJEXT): missing.h -enc/trans/cesu_8.$(OBJEXT): onigmo.h enc/trans/cesu_8.$(OBJEXT): st.h enc/trans/cesu_8.$(OBJEXT): subst.h enc/trans/chinese.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -5565,7 +5563,6 @@ enc/trans/chinese.$(OBJEXT): internal/variable.h enc/trans/chinese.$(OBJEXT): internal/warning_push.h enc/trans/chinese.$(OBJEXT): internal/xmalloc.h enc/trans/chinese.$(OBJEXT): missing.h -enc/trans/chinese.$(OBJEXT): onigmo.h enc/trans/chinese.$(OBJEXT): st.h enc/trans/chinese.$(OBJEXT): subst.h enc/trans/ebcdic.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -5726,7 +5723,6 @@ enc/trans/ebcdic.$(OBJEXT): internal/variable.h enc/trans/ebcdic.$(OBJEXT): internal/warning_push.h enc/trans/ebcdic.$(OBJEXT): internal/xmalloc.h enc/trans/ebcdic.$(OBJEXT): missing.h -enc/trans/ebcdic.$(OBJEXT): onigmo.h enc/trans/ebcdic.$(OBJEXT): st.h enc/trans/ebcdic.$(OBJEXT): subst.h enc/trans/emoji.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -5887,7 +5883,6 @@ enc/trans/emoji.$(OBJEXT): internal/variable.h enc/trans/emoji.$(OBJEXT): internal/warning_push.h enc/trans/emoji.$(OBJEXT): internal/xmalloc.h enc/trans/emoji.$(OBJEXT): missing.h -enc/trans/emoji.$(OBJEXT): onigmo.h enc/trans/emoji.$(OBJEXT): st.h enc/trans/emoji.$(OBJEXT): subst.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -6048,7 +6043,6 @@ enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/variable.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/warning_push.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): internal/xmalloc.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): missing.h -enc/trans/emoji_iso2022_kddi.$(OBJEXT): onigmo.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): st.h enc/trans/emoji_iso2022_kddi.$(OBJEXT): subst.h enc/trans/emoji_sjis_docomo.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -6209,7 +6203,6 @@ enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/variable.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/warning_push.h enc/trans/emoji_sjis_docomo.$(OBJEXT): internal/xmalloc.h enc/trans/emoji_sjis_docomo.$(OBJEXT): missing.h -enc/trans/emoji_sjis_docomo.$(OBJEXT): onigmo.h enc/trans/emoji_sjis_docomo.$(OBJEXT): st.h enc/trans/emoji_sjis_docomo.$(OBJEXT): subst.h enc/trans/emoji_sjis_kddi.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -6370,7 +6363,6 @@ enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/variable.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/warning_push.h enc/trans/emoji_sjis_kddi.$(OBJEXT): internal/xmalloc.h enc/trans/emoji_sjis_kddi.$(OBJEXT): missing.h -enc/trans/emoji_sjis_kddi.$(OBJEXT): onigmo.h enc/trans/emoji_sjis_kddi.$(OBJEXT): st.h enc/trans/emoji_sjis_kddi.$(OBJEXT): subst.h enc/trans/emoji_sjis_softbank.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -6531,7 +6523,6 @@ enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/variable.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/warning_push.h enc/trans/emoji_sjis_softbank.$(OBJEXT): internal/xmalloc.h enc/trans/emoji_sjis_softbank.$(OBJEXT): missing.h -enc/trans/emoji_sjis_softbank.$(OBJEXT): onigmo.h enc/trans/emoji_sjis_softbank.$(OBJEXT): st.h enc/trans/emoji_sjis_softbank.$(OBJEXT): subst.h enc/trans/escape.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -6692,7 +6683,6 @@ enc/trans/escape.$(OBJEXT): internal/variable.h enc/trans/escape.$(OBJEXT): internal/warning_push.h enc/trans/escape.$(OBJEXT): internal/xmalloc.h enc/trans/escape.$(OBJEXT): missing.h -enc/trans/escape.$(OBJEXT): onigmo.h enc/trans/escape.$(OBJEXT): st.h enc/trans/escape.$(OBJEXT): subst.h enc/trans/gb18030.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -6853,7 +6843,6 @@ enc/trans/gb18030.$(OBJEXT): internal/variable.h enc/trans/gb18030.$(OBJEXT): internal/warning_push.h enc/trans/gb18030.$(OBJEXT): internal/xmalloc.h enc/trans/gb18030.$(OBJEXT): missing.h -enc/trans/gb18030.$(OBJEXT): onigmo.h enc/trans/gb18030.$(OBJEXT): st.h enc/trans/gb18030.$(OBJEXT): subst.h enc/trans/gbk.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7014,7 +7003,6 @@ enc/trans/gbk.$(OBJEXT): internal/variable.h enc/trans/gbk.$(OBJEXT): internal/warning_push.h enc/trans/gbk.$(OBJEXT): internal/xmalloc.h enc/trans/gbk.$(OBJEXT): missing.h -enc/trans/gbk.$(OBJEXT): onigmo.h enc/trans/gbk.$(OBJEXT): st.h enc/trans/gbk.$(OBJEXT): subst.h enc/trans/iso2022.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7176,7 +7164,6 @@ enc/trans/iso2022.$(OBJEXT): internal/variable.h enc/trans/iso2022.$(OBJEXT): internal/warning_push.h enc/trans/iso2022.$(OBJEXT): internal/xmalloc.h enc/trans/iso2022.$(OBJEXT): missing.h -enc/trans/iso2022.$(OBJEXT): onigmo.h enc/trans/iso2022.$(OBJEXT): st.h enc/trans/iso2022.$(OBJEXT): subst.h enc/trans/japanese.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7337,7 +7324,6 @@ enc/trans/japanese.$(OBJEXT): internal/variable.h enc/trans/japanese.$(OBJEXT): internal/warning_push.h enc/trans/japanese.$(OBJEXT): internal/xmalloc.h enc/trans/japanese.$(OBJEXT): missing.h -enc/trans/japanese.$(OBJEXT): onigmo.h enc/trans/japanese.$(OBJEXT): st.h enc/trans/japanese.$(OBJEXT): subst.h enc/trans/japanese_euc.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7498,7 +7484,6 @@ enc/trans/japanese_euc.$(OBJEXT): internal/variable.h enc/trans/japanese_euc.$(OBJEXT): internal/warning_push.h enc/trans/japanese_euc.$(OBJEXT): internal/xmalloc.h enc/trans/japanese_euc.$(OBJEXT): missing.h -enc/trans/japanese_euc.$(OBJEXT): onigmo.h enc/trans/japanese_euc.$(OBJEXT): st.h enc/trans/japanese_euc.$(OBJEXT): subst.h enc/trans/japanese_sjis.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7659,7 +7644,6 @@ enc/trans/japanese_sjis.$(OBJEXT): internal/variable.h enc/trans/japanese_sjis.$(OBJEXT): internal/warning_push.h enc/trans/japanese_sjis.$(OBJEXT): internal/xmalloc.h enc/trans/japanese_sjis.$(OBJEXT): missing.h -enc/trans/japanese_sjis.$(OBJEXT): onigmo.h enc/trans/japanese_sjis.$(OBJEXT): st.h enc/trans/japanese_sjis.$(OBJEXT): subst.h enc/trans/korean.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7820,7 +7804,6 @@ enc/trans/korean.$(OBJEXT): internal/variable.h enc/trans/korean.$(OBJEXT): internal/warning_push.h enc/trans/korean.$(OBJEXT): internal/xmalloc.h enc/trans/korean.$(OBJEXT): missing.h -enc/trans/korean.$(OBJEXT): onigmo.h enc/trans/korean.$(OBJEXT): st.h enc/trans/korean.$(OBJEXT): subst.h enc/trans/newline.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -7980,7 +7963,6 @@ enc/trans/newline.$(OBJEXT): internal/variable.h enc/trans/newline.$(OBJEXT): internal/warning_push.h enc/trans/newline.$(OBJEXT): internal/xmalloc.h enc/trans/newline.$(OBJEXT): missing.h -enc/trans/newline.$(OBJEXT): onigmo.h enc/trans/newline.$(OBJEXT): st.h enc/trans/newline.$(OBJEXT): subst.h enc/trans/single_byte.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -8141,7 +8123,6 @@ enc/trans/single_byte.$(OBJEXT): internal/variable.h enc/trans/single_byte.$(OBJEXT): internal/warning_push.h enc/trans/single_byte.$(OBJEXT): internal/xmalloc.h enc/trans/single_byte.$(OBJEXT): missing.h -enc/trans/single_byte.$(OBJEXT): onigmo.h enc/trans/single_byte.$(OBJEXT): st.h enc/trans/single_byte.$(OBJEXT): subst.h enc/trans/transdb.$(OBJEXT): $(hdrdir)/ruby.h @@ -8302,7 +8283,6 @@ enc/trans/transdb.$(OBJEXT): internal/variable.h enc/trans/transdb.$(OBJEXT): internal/warning_push.h enc/trans/transdb.$(OBJEXT): internal/xmalloc.h enc/trans/transdb.$(OBJEXT): missing.h -enc/trans/transdb.$(OBJEXT): onigmo.h enc/trans/transdb.$(OBJEXT): st.h enc/trans/transdb.$(OBJEXT): subst.h enc/trans/transdb.$(OBJEXT): transdb.h @@ -8464,7 +8444,6 @@ enc/trans/utf8_mac.$(OBJEXT): internal/variable.h enc/trans/utf8_mac.$(OBJEXT): internal/warning_push.h enc/trans/utf8_mac.$(OBJEXT): internal/xmalloc.h enc/trans/utf8_mac.$(OBJEXT): missing.h -enc/trans/utf8_mac.$(OBJEXT): onigmo.h enc/trans/utf8_mac.$(OBJEXT): st.h enc/trans/utf8_mac.$(OBJEXT): subst.h enc/trans/utf_16_32.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -8625,7 +8604,6 @@ enc/trans/utf_16_32.$(OBJEXT): internal/variable.h enc/trans/utf_16_32.$(OBJEXT): internal/warning_push.h enc/trans/utf_16_32.$(OBJEXT): internal/xmalloc.h enc/trans/utf_16_32.$(OBJEXT): missing.h -enc/trans/utf_16_32.$(OBJEXT): onigmo.h enc/trans/utf_16_32.$(OBJEXT): st.h enc/trans/utf_16_32.$(OBJEXT): subst.h enc/unicode.$(OBJEXT): $(UNICODE_HDR_DIR)/casefold.h diff --git a/ext/-test-/RUBY_ALIGNOF/depend b/ext/-test-/RUBY_ALIGNOF/depend index 4fc0e775dcad74..103d20b33c8de5 100644 --- a/ext/-test-/RUBY_ALIGNOF/depend +++ b/ext/-test-/RUBY_ALIGNOF/depend @@ -156,7 +156,6 @@ c.o: $(hdrdir)/ruby/internal/variable.h c.o: $(hdrdir)/ruby/internal/warning_push.h c.o: $(hdrdir)/ruby/internal/xmalloc.h c.o: $(hdrdir)/ruby/missing.h -c.o: $(hdrdir)/ruby/onigmo.h c.o: $(hdrdir)/ruby/ruby.h c.o: $(hdrdir)/ruby/st.h c.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/arith_seq/beg_len_step/depend b/ext/-test-/arith_seq/beg_len_step/depend index e84aada9e065c3..098c8ff1f0f0fb 100644 --- a/ext/-test-/arith_seq/beg_len_step/depend +++ b/ext/-test-/arith_seq/beg_len_step/depend @@ -155,7 +155,6 @@ beg_len_step.o: $(hdrdir)/ruby/internal/variable.h beg_len_step.o: $(hdrdir)/ruby/internal/warning_push.h beg_len_step.o: $(hdrdir)/ruby/internal/xmalloc.h beg_len_step.o: $(hdrdir)/ruby/missing.h -beg_len_step.o: $(hdrdir)/ruby/onigmo.h beg_len_step.o: $(hdrdir)/ruby/ruby.h beg_len_step.o: $(hdrdir)/ruby/st.h beg_len_step.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/arith_seq/extract/depend b/ext/-test-/arith_seq/extract/depend index 69d2fa9ddd7c9f..5c07cea4b4b1dd 100644 --- a/ext/-test-/arith_seq/extract/depend +++ b/ext/-test-/arith_seq/extract/depend @@ -155,7 +155,6 @@ extract.o: $(hdrdir)/ruby/internal/variable.h extract.o: $(hdrdir)/ruby/internal/warning_push.h extract.o: $(hdrdir)/ruby/internal/xmalloc.h extract.o: $(hdrdir)/ruby/missing.h -extract.o: $(hdrdir)/ruby/onigmo.h extract.o: $(hdrdir)/ruby/ruby.h extract.o: $(hdrdir)/ruby/st.h extract.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/array/concat/depend b/ext/-test-/array/concat/depend index 7fa8248d4dd93d..8edf45465f707d 100644 --- a/ext/-test-/array/concat/depend +++ b/ext/-test-/array/concat/depend @@ -156,7 +156,6 @@ to_ary_concat.o: $(hdrdir)/ruby/internal/variable.h to_ary_concat.o: $(hdrdir)/ruby/internal/warning_push.h to_ary_concat.o: $(hdrdir)/ruby/internal/xmalloc.h to_ary_concat.o: $(hdrdir)/ruby/missing.h -to_ary_concat.o: $(hdrdir)/ruby/onigmo.h to_ary_concat.o: $(hdrdir)/ruby/ruby.h to_ary_concat.o: $(hdrdir)/ruby/st.h to_ary_concat.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/array/resize/depend b/ext/-test-/array/resize/depend index d2ef9bcee43f11..e6a228b43d779c 100644 --- a/ext/-test-/array/resize/depend +++ b/ext/-test-/array/resize/depend @@ -155,7 +155,6 @@ resize.o: $(hdrdir)/ruby/internal/variable.h resize.o: $(hdrdir)/ruby/internal/warning_push.h resize.o: $(hdrdir)/ruby/internal/xmalloc.h resize.o: $(hdrdir)/ruby/missing.h -resize.o: $(hdrdir)/ruby/onigmo.h resize.o: $(hdrdir)/ruby/ruby.h resize.o: $(hdrdir)/ruby/st.h resize.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/bignum/depend b/ext/-test-/bignum/depend index 81c80d73a8c6af..82972f10327311 100644 --- a/ext/-test-/bignum/depend +++ b/ext/-test-/bignum/depend @@ -156,7 +156,6 @@ big2str.o: $(hdrdir)/ruby/internal/variable.h big2str.o: $(hdrdir)/ruby/internal/warning_push.h big2str.o: $(hdrdir)/ruby/internal/xmalloc.h big2str.o: $(hdrdir)/ruby/missing.h -big2str.o: $(hdrdir)/ruby/onigmo.h big2str.o: $(hdrdir)/ruby/ruby.h big2str.o: $(hdrdir)/ruby/st.h big2str.o: $(hdrdir)/ruby/subst.h @@ -320,7 +319,6 @@ bigzero.o: $(hdrdir)/ruby/internal/variable.h bigzero.o: $(hdrdir)/ruby/internal/warning_push.h bigzero.o: $(hdrdir)/ruby/internal/xmalloc.h bigzero.o: $(hdrdir)/ruby/missing.h -bigzero.o: $(hdrdir)/ruby/onigmo.h bigzero.o: $(hdrdir)/ruby/ruby.h bigzero.o: $(hdrdir)/ruby/st.h bigzero.o: $(hdrdir)/ruby/subst.h @@ -484,7 +482,6 @@ div.o: $(hdrdir)/ruby/internal/variable.h div.o: $(hdrdir)/ruby/internal/warning_push.h div.o: $(hdrdir)/ruby/internal/xmalloc.h div.o: $(hdrdir)/ruby/missing.h -div.o: $(hdrdir)/ruby/onigmo.h div.o: $(hdrdir)/ruby/ruby.h div.o: $(hdrdir)/ruby/st.h div.o: $(hdrdir)/ruby/subst.h @@ -648,7 +645,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -810,7 +806,6 @@ intpack.o: $(hdrdir)/ruby/internal/variable.h intpack.o: $(hdrdir)/ruby/internal/warning_push.h intpack.o: $(hdrdir)/ruby/internal/xmalloc.h intpack.o: $(hdrdir)/ruby/missing.h -intpack.o: $(hdrdir)/ruby/onigmo.h intpack.o: $(hdrdir)/ruby/ruby.h intpack.o: $(hdrdir)/ruby/st.h intpack.o: $(hdrdir)/ruby/subst.h @@ -974,7 +969,6 @@ mul.o: $(hdrdir)/ruby/internal/variable.h mul.o: $(hdrdir)/ruby/internal/warning_push.h mul.o: $(hdrdir)/ruby/internal/xmalloc.h mul.o: $(hdrdir)/ruby/missing.h -mul.o: $(hdrdir)/ruby/onigmo.h mul.o: $(hdrdir)/ruby/ruby.h mul.o: $(hdrdir)/ruby/st.h mul.o: $(hdrdir)/ruby/subst.h @@ -1138,7 +1132,6 @@ str2big.o: $(hdrdir)/ruby/internal/variable.h str2big.o: $(hdrdir)/ruby/internal/warning_push.h str2big.o: $(hdrdir)/ruby/internal/xmalloc.h str2big.o: $(hdrdir)/ruby/missing.h -str2big.o: $(hdrdir)/ruby/onigmo.h str2big.o: $(hdrdir)/ruby/ruby.h str2big.o: $(hdrdir)/ruby/st.h str2big.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/bug-14834/depend b/ext/-test-/bug-14834/depend index 660de6d5cf5369..f83939d559f63c 100644 --- a/ext/-test-/bug-14834/depend +++ b/ext/-test-/bug-14834/depend @@ -156,7 +156,6 @@ bug-14834.o: $(hdrdir)/ruby/internal/variable.h bug-14834.o: $(hdrdir)/ruby/internal/warning_push.h bug-14834.o: $(hdrdir)/ruby/internal/xmalloc.h bug-14834.o: $(hdrdir)/ruby/missing.h -bug-14834.o: $(hdrdir)/ruby/onigmo.h bug-14834.o: $(hdrdir)/ruby/ruby.h bug-14834.o: $(hdrdir)/ruby/st.h bug-14834.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/bug-3571/depend b/ext/-test-/bug-3571/depend index b1452bdf5ec8dd..69c970b6f25703 100644 --- a/ext/-test-/bug-3571/depend +++ b/ext/-test-/bug-3571/depend @@ -156,7 +156,6 @@ bug.o: $(hdrdir)/ruby/internal/variable.h bug.o: $(hdrdir)/ruby/internal/warning_push.h bug.o: $(hdrdir)/ruby/internal/xmalloc.h bug.o: $(hdrdir)/ruby/missing.h -bug.o: $(hdrdir)/ruby/onigmo.h bug.o: $(hdrdir)/ruby/ruby.h bug.o: $(hdrdir)/ruby/st.h bug.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/bug-5832/depend b/ext/-test-/bug-5832/depend index b1452bdf5ec8dd..69c970b6f25703 100644 --- a/ext/-test-/bug-5832/depend +++ b/ext/-test-/bug-5832/depend @@ -156,7 +156,6 @@ bug.o: $(hdrdir)/ruby/internal/variable.h bug.o: $(hdrdir)/ruby/internal/warning_push.h bug.o: $(hdrdir)/ruby/internal/xmalloc.h bug.o: $(hdrdir)/ruby/missing.h -bug.o: $(hdrdir)/ruby/onigmo.h bug.o: $(hdrdir)/ruby/ruby.h bug.o: $(hdrdir)/ruby/st.h bug.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/bug_reporter/depend b/ext/-test-/bug_reporter/depend index 14275ab9820c07..e9993c3295505e 100644 --- a/ext/-test-/bug_reporter/depend +++ b/ext/-test-/bug_reporter/depend @@ -156,7 +156,6 @@ bug_reporter.o: $(hdrdir)/ruby/internal/variable.h bug_reporter.o: $(hdrdir)/ruby/internal/warning_push.h bug_reporter.o: $(hdrdir)/ruby/internal/xmalloc.h bug_reporter.o: $(hdrdir)/ruby/missing.h -bug_reporter.o: $(hdrdir)/ruby/onigmo.h bug_reporter.o: $(hdrdir)/ruby/ruby.h bug_reporter.o: $(hdrdir)/ruby/st.h bug_reporter.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/class/depend b/ext/-test-/class/depend index 91204b36b89fd0..557206cefb41f4 100644 --- a/ext/-test-/class/depend +++ b/ext/-test-/class/depend @@ -155,7 +155,6 @@ class2name.o: $(hdrdir)/ruby/internal/variable.h class2name.o: $(hdrdir)/ruby/internal/warning_push.h class2name.o: $(hdrdir)/ruby/internal/xmalloc.h class2name.o: $(hdrdir)/ruby/missing.h -class2name.o: $(hdrdir)/ruby/onigmo.h class2name.o: $(hdrdir)/ruby/ruby.h class2name.o: $(hdrdir)/ruby/st.h class2name.o: $(hdrdir)/ruby/subst.h @@ -317,7 +316,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/debug/depend b/ext/-test-/debug/depend index f9d50e25140357..4ae0378ef2072d 100644 --- a/ext/-test-/debug/depend +++ b/ext/-test-/debug/depend @@ -156,7 +156,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ inspector.o: $(hdrdir)/ruby/internal/variable.h inspector.o: $(hdrdir)/ruby/internal/warning_push.h inspector.o: $(hdrdir)/ruby/internal/xmalloc.h inspector.o: $(hdrdir)/ruby/missing.h -inspector.o: $(hdrdir)/ruby/onigmo.h inspector.o: $(hdrdir)/ruby/ruby.h inspector.o: $(hdrdir)/ruby/st.h inspector.o: $(hdrdir)/ruby/subst.h @@ -480,7 +478,6 @@ profile_frames.o: $(hdrdir)/ruby/internal/variable.h profile_frames.o: $(hdrdir)/ruby/internal/warning_push.h profile_frames.o: $(hdrdir)/ruby/internal/xmalloc.h profile_frames.o: $(hdrdir)/ruby/missing.h -profile_frames.o: $(hdrdir)/ruby/onigmo.h profile_frames.o: $(hdrdir)/ruby/ruby.h profile_frames.o: $(hdrdir)/ruby/st.h profile_frames.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/dln/empty/depend b/ext/-test-/dln/empty/depend index 2e20de2d4ce906..58f15085983101 100644 --- a/ext/-test-/dln/empty/depend +++ b/ext/-test-/dln/empty/depend @@ -156,7 +156,6 @@ empty.o: $(hdrdir)/ruby/internal/variable.h empty.o: $(hdrdir)/ruby/internal/warning_push.h empty.o: $(hdrdir)/ruby/internal/xmalloc.h empty.o: $(hdrdir)/ruby/missing.h -empty.o: $(hdrdir)/ruby/onigmo.h empty.o: $(hdrdir)/ruby/ruby.h empty.o: $(hdrdir)/ruby/st.h empty.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/econv/depend b/ext/-test-/econv/depend index 02d7ee451f85a0..3a5bc9c6590a1b 100644 --- a/ext/-test-/econv/depend +++ b/ext/-test-/econv/depend @@ -329,7 +329,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/ensure_and_callcc/depend b/ext/-test-/ensure_and_callcc/depend index ddf2e0e300e384..54431847a687bf 100644 --- a/ext/-test-/ensure_and_callcc/depend +++ b/ext/-test-/ensure_and_callcc/depend @@ -156,7 +156,6 @@ ensure_and_callcc.o: $(hdrdir)/ruby/internal/variable.h ensure_and_callcc.o: $(hdrdir)/ruby/internal/warning_push.h ensure_and_callcc.o: $(hdrdir)/ruby/internal/xmalloc.h ensure_and_callcc.o: $(hdrdir)/ruby/missing.h -ensure_and_callcc.o: $(hdrdir)/ruby/onigmo.h ensure_and_callcc.o: $(hdrdir)/ruby/ruby.h ensure_and_callcc.o: $(hdrdir)/ruby/st.h ensure_and_callcc.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/enumerator_kw/depend b/ext/-test-/enumerator_kw/depend index e70dbb47792381..b6d2f0a9989788 100644 --- a/ext/-test-/enumerator_kw/depend +++ b/ext/-test-/enumerator_kw/depend @@ -156,7 +156,6 @@ enumerator_kw.o: $(hdrdir)/ruby/internal/variable.h enumerator_kw.o: $(hdrdir)/ruby/internal/warning_push.h enumerator_kw.o: $(hdrdir)/ruby/internal/xmalloc.h enumerator_kw.o: $(hdrdir)/ruby/missing.h -enumerator_kw.o: $(hdrdir)/ruby/onigmo.h enumerator_kw.o: $(hdrdir)/ruby/ruby.h enumerator_kw.o: $(hdrdir)/ruby/st.h enumerator_kw.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/eval/depend b/ext/-test-/eval/depend index 672ce96f2e8e03..03a1c7d7ef9bee 100644 --- a/ext/-test-/eval/depend +++ b/ext/-test-/eval/depend @@ -155,7 +155,6 @@ eval.o: $(hdrdir)/ruby/internal/variable.h eval.o: $(hdrdir)/ruby/internal/warning_push.h eval.o: $(hdrdir)/ruby/internal/xmalloc.h eval.o: $(hdrdir)/ruby/missing.h -eval.o: $(hdrdir)/ruby/onigmo.h eval.o: $(hdrdir)/ruby/ruby.h eval.o: $(hdrdir)/ruby/st.h eval.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/exception/depend b/ext/-test-/exception/depend index 9a7fc1b73bd5d9..690e5ad3776231 100644 --- a/ext/-test-/exception/depend +++ b/ext/-test-/exception/depend @@ -155,7 +155,6 @@ dataerror.o: $(hdrdir)/ruby/internal/variable.h dataerror.o: $(hdrdir)/ruby/internal/warning_push.h dataerror.o: $(hdrdir)/ruby/internal/xmalloc.h dataerror.o: $(hdrdir)/ruby/missing.h -dataerror.o: $(hdrdir)/ruby/onigmo.h dataerror.o: $(hdrdir)/ruby/ruby.h dataerror.o: $(hdrdir)/ruby/st.h dataerror.o: $(hdrdir)/ruby/subst.h @@ -490,7 +489,6 @@ ensured.o: $(hdrdir)/ruby/internal/variable.h ensured.o: $(hdrdir)/ruby/internal/warning_push.h ensured.o: $(hdrdir)/ruby/internal/xmalloc.h ensured.o: $(hdrdir)/ruby/missing.h -ensured.o: $(hdrdir)/ruby/onigmo.h ensured.o: $(hdrdir)/ruby/ruby.h ensured.o: $(hdrdir)/ruby/st.h ensured.o: $(hdrdir)/ruby/subst.h @@ -652,7 +650,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/fatal/depend b/ext/-test-/fatal/depend index 1b1d32c776f320..306bc9099c2dbd 100644 --- a/ext/-test-/fatal/depend +++ b/ext/-test-/fatal/depend @@ -157,7 +157,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -319,7 +318,6 @@ invalid.o: $(hdrdir)/ruby/internal/variable.h invalid.o: $(hdrdir)/ruby/internal/warning_push.h invalid.o: $(hdrdir)/ruby/internal/xmalloc.h invalid.o: $(hdrdir)/ruby/missing.h -invalid.o: $(hdrdir)/ruby/onigmo.h invalid.o: $(hdrdir)/ruby/ruby.h invalid.o: $(hdrdir)/ruby/st.h invalid.o: $(hdrdir)/ruby/subst.h @@ -481,7 +479,6 @@ rb_fatal.o: $(hdrdir)/ruby/internal/variable.h rb_fatal.o: $(hdrdir)/ruby/internal/warning_push.h rb_fatal.o: $(hdrdir)/ruby/internal/xmalloc.h rb_fatal.o: $(hdrdir)/ruby/missing.h -rb_fatal.o: $(hdrdir)/ruby/onigmo.h rb_fatal.o: $(hdrdir)/ruby/ruby.h rb_fatal.o: $(hdrdir)/ruby/st.h rb_fatal.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/file/depend b/ext/-test-/file/depend index f28f52b2d58248..fe320f3d44a98f 100644 --- a/ext/-test-/file/depend +++ b/ext/-test-/file/depend @@ -329,7 +329,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/float/depend b/ext/-test-/float/depend index ba9b88c9cfcf34..334ed33c3b2299 100644 --- a/ext/-test-/float/depend +++ b/ext/-test-/float/depend @@ -159,7 +159,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -321,7 +320,6 @@ nextafter.o: $(hdrdir)/ruby/internal/variable.h nextafter.o: $(hdrdir)/ruby/internal/warning_push.h nextafter.o: $(hdrdir)/ruby/internal/xmalloc.h nextafter.o: $(hdrdir)/ruby/missing.h -nextafter.o: $(hdrdir)/ruby/onigmo.h nextafter.o: $(hdrdir)/ruby/ruby.h nextafter.o: $(hdrdir)/ruby/st.h nextafter.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/funcall/depend b/ext/-test-/funcall/depend index 1cc94db86d1131..e54370306f47ca 100644 --- a/ext/-test-/funcall/depend +++ b/ext/-test-/funcall/depend @@ -156,7 +156,6 @@ funcall.o: $(hdrdir)/ruby/internal/variable.h funcall.o: $(hdrdir)/ruby/internal/warning_push.h funcall.o: $(hdrdir)/ruby/internal/xmalloc.h funcall.o: $(hdrdir)/ruby/missing.h -funcall.o: $(hdrdir)/ruby/onigmo.h funcall.o: $(hdrdir)/ruby/ruby.h funcall.o: $(hdrdir)/ruby/st.h funcall.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/gvl/call_without_gvl/depend b/ext/-test-/gvl/call_without_gvl/depend index 1f5a9da0bce843..236d1e1d3b6148 100644 --- a/ext/-test-/gvl/call_without_gvl/depend +++ b/ext/-test-/gvl/call_without_gvl/depend @@ -155,7 +155,6 @@ call_without_gvl.o: $(hdrdir)/ruby/internal/variable.h call_without_gvl.o: $(hdrdir)/ruby/internal/warning_push.h call_without_gvl.o: $(hdrdir)/ruby/internal/xmalloc.h call_without_gvl.o: $(hdrdir)/ruby/missing.h -call_without_gvl.o: $(hdrdir)/ruby/onigmo.h call_without_gvl.o: $(hdrdir)/ruby/ruby.h call_without_gvl.o: $(hdrdir)/ruby/st.h call_without_gvl.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/hash/depend b/ext/-test-/hash/depend index 8628dd37eacdd6..416b93f9de37b1 100644 --- a/ext/-test-/hash/depend +++ b/ext/-test-/hash/depend @@ -156,7 +156,6 @@ delete.o: $(hdrdir)/ruby/internal/variable.h delete.o: $(hdrdir)/ruby/internal/warning_push.h delete.o: $(hdrdir)/ruby/internal/xmalloc.h delete.o: $(hdrdir)/ruby/missing.h -delete.o: $(hdrdir)/ruby/onigmo.h delete.o: $(hdrdir)/ruby/ruby.h delete.o: $(hdrdir)/ruby/st.h delete.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/integer/depend b/ext/-test-/integer/depend index 648dc31358144d..d0589b5e5dd8a8 100644 --- a/ext/-test-/integer/depend +++ b/ext/-test-/integer/depend @@ -156,7 +156,6 @@ core_ext.o: $(hdrdir)/ruby/internal/variable.h core_ext.o: $(hdrdir)/ruby/internal/warning_push.h core_ext.o: $(hdrdir)/ruby/internal/xmalloc.h core_ext.o: $(hdrdir)/ruby/missing.h -core_ext.o: $(hdrdir)/ruby/onigmo.h core_ext.o: $(hdrdir)/ruby/ruby.h core_ext.o: $(hdrdir)/ruby/st.h core_ext.o: $(hdrdir)/ruby/subst.h @@ -329,7 +328,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -491,7 +489,6 @@ my_integer.o: $(hdrdir)/ruby/internal/variable.h my_integer.o: $(hdrdir)/ruby/internal/warning_push.h my_integer.o: $(hdrdir)/ruby/internal/xmalloc.h my_integer.o: $(hdrdir)/ruby/missing.h -my_integer.o: $(hdrdir)/ruby/onigmo.h my_integer.o: $(hdrdir)/ruby/ruby.h my_integer.o: $(hdrdir)/ruby/st.h my_integer.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/iseq_load/depend b/ext/-test-/iseq_load/depend index df3711943edc5e..9361ddb93844ce 100644 --- a/ext/-test-/iseq_load/depend +++ b/ext/-test-/iseq_load/depend @@ -156,7 +156,6 @@ iseq_load.o: $(hdrdir)/ruby/internal/variable.h iseq_load.o: $(hdrdir)/ruby/internal/warning_push.h iseq_load.o: $(hdrdir)/ruby/internal/xmalloc.h iseq_load.o: $(hdrdir)/ruby/missing.h -iseq_load.o: $(hdrdir)/ruby/onigmo.h iseq_load.o: $(hdrdir)/ruby/ruby.h iseq_load.o: $(hdrdir)/ruby/st.h iseq_load.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/iter/depend b/ext/-test-/iter/depend index 5b49c7b315494c..161947382cd91b 100644 --- a/ext/-test-/iter/depend +++ b/ext/-test-/iter/depend @@ -156,7 +156,6 @@ break.o: $(hdrdir)/ruby/internal/variable.h break.o: $(hdrdir)/ruby/internal/warning_push.h break.o: $(hdrdir)/ruby/internal/xmalloc.h break.o: $(hdrdir)/ruby/missing.h -break.o: $(hdrdir)/ruby/onigmo.h break.o: $(hdrdir)/ruby/ruby.h break.o: $(hdrdir)/ruby/st.h break.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -480,7 +478,6 @@ yield.o: $(hdrdir)/ruby/internal/variable.h yield.o: $(hdrdir)/ruby/internal/warning_push.h yield.o: $(hdrdir)/ruby/internal/xmalloc.h yield.o: $(hdrdir)/ruby/missing.h -yield.o: $(hdrdir)/ruby/onigmo.h yield.o: $(hdrdir)/ruby/ruby.h yield.o: $(hdrdir)/ruby/st.h yield.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/load/dot.dot/depend b/ext/-test-/load/dot.dot/depend index cf4680c63ee20f..339837d1832ad7 100644 --- a/ext/-test-/load/dot.dot/depend +++ b/ext/-test-/load/dot.dot/depend @@ -156,7 +156,6 @@ dot.dot.o: $(hdrdir)/ruby/internal/variable.h dot.dot.o: $(hdrdir)/ruby/internal/warning_push.h dot.dot.o: $(hdrdir)/ruby/internal/xmalloc.h dot.dot.o: $(hdrdir)/ruby/missing.h -dot.dot.o: $(hdrdir)/ruby/onigmo.h dot.dot.o: $(hdrdir)/ruby/ruby.h dot.dot.o: $(hdrdir)/ruby/st.h dot.dot.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/load/protect/depend b/ext/-test-/load/protect/depend index 780ba382baf52e..c76c6f88ed9591 100644 --- a/ext/-test-/load/protect/depend +++ b/ext/-test-/load/protect/depend @@ -156,7 +156,6 @@ protect.o: $(hdrdir)/ruby/internal/variable.h protect.o: $(hdrdir)/ruby/internal/warning_push.h protect.o: $(hdrdir)/ruby/internal/xmalloc.h protect.o: $(hdrdir)/ruby/missing.h -protect.o: $(hdrdir)/ruby/onigmo.h protect.o: $(hdrdir)/ruby/ruby.h protect.o: $(hdrdir)/ruby/st.h protect.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/load/resolve_symbol_resolver/depend b/ext/-test-/load/resolve_symbol_resolver/depend index d6107a6cf05568..f422898b691af7 100644 --- a/ext/-test-/load/resolve_symbol_resolver/depend +++ b/ext/-test-/load/resolve_symbol_resolver/depend @@ -156,7 +156,6 @@ resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/variable.h resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/warning_push.h resolve_symbol_resolver.o: $(hdrdir)/ruby/internal/xmalloc.h resolve_symbol_resolver.o: $(hdrdir)/ruby/missing.h -resolve_symbol_resolver.o: $(hdrdir)/ruby/onigmo.h resolve_symbol_resolver.o: $(hdrdir)/ruby/ruby.h resolve_symbol_resolver.o: $(hdrdir)/ruby/st.h resolve_symbol_resolver.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/load/resolve_symbol_target/depend b/ext/-test-/load/resolve_symbol_target/depend index 52f922db422ec0..aa0b5327bee023 100644 --- a/ext/-test-/load/resolve_symbol_target/depend +++ b/ext/-test-/load/resolve_symbol_target/depend @@ -156,7 +156,6 @@ resolve_symbol_target.o: $(hdrdir)/ruby/internal/variable.h resolve_symbol_target.o: $(hdrdir)/ruby/internal/warning_push.h resolve_symbol_target.o: $(hdrdir)/ruby/internal/xmalloc.h resolve_symbol_target.o: $(hdrdir)/ruby/missing.h -resolve_symbol_target.o: $(hdrdir)/ruby/onigmo.h resolve_symbol_target.o: $(hdrdir)/ruby/ruby.h resolve_symbol_target.o: $(hdrdir)/ruby/st.h resolve_symbol_target.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/load/stringify_symbols/depend b/ext/-test-/load/stringify_symbols/depend index f6621b0127ed34..2d4d79a7b7a21a 100644 --- a/ext/-test-/load/stringify_symbols/depend +++ b/ext/-test-/load/stringify_symbols/depend @@ -156,7 +156,6 @@ stringify_symbols.o: $(hdrdir)/ruby/internal/variable.h stringify_symbols.o: $(hdrdir)/ruby/internal/warning_push.h stringify_symbols.o: $(hdrdir)/ruby/internal/xmalloc.h stringify_symbols.o: $(hdrdir)/ruby/missing.h -stringify_symbols.o: $(hdrdir)/ruby/onigmo.h stringify_symbols.o: $(hdrdir)/ruby/ruby.h stringify_symbols.o: $(hdrdir)/ruby/st.h stringify_symbols.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/load/stringify_target/depend b/ext/-test-/load/stringify_target/depend index d453d873229786..c66575d4e4fbc5 100644 --- a/ext/-test-/load/stringify_target/depend +++ b/ext/-test-/load/stringify_target/depend @@ -156,7 +156,6 @@ stringify_target.o: $(hdrdir)/ruby/internal/variable.h stringify_target.o: $(hdrdir)/ruby/internal/warning_push.h stringify_target.o: $(hdrdir)/ruby/internal/xmalloc.h stringify_target.o: $(hdrdir)/ruby/missing.h -stringify_target.o: $(hdrdir)/ruby/onigmo.h stringify_target.o: $(hdrdir)/ruby/ruby.h stringify_target.o: $(hdrdir)/ruby/st.h stringify_target.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/marshal/compat/depend b/ext/-test-/marshal/compat/depend index 0b2d1ce777f75b..36b9235c230de1 100644 --- a/ext/-test-/marshal/compat/depend +++ b/ext/-test-/marshal/compat/depend @@ -156,7 +156,6 @@ usrcompat.o: $(hdrdir)/ruby/internal/variable.h usrcompat.o: $(hdrdir)/ruby/internal/warning_push.h usrcompat.o: $(hdrdir)/ruby/internal/xmalloc.h usrcompat.o: $(hdrdir)/ruby/missing.h -usrcompat.o: $(hdrdir)/ruby/onigmo.h usrcompat.o: $(hdrdir)/ruby/ruby.h usrcompat.o: $(hdrdir)/ruby/st.h usrcompat.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/marshal/internal_ivar/depend b/ext/-test-/marshal/internal_ivar/depend index 198637c52f29a1..a2e093d809b941 100644 --- a/ext/-test-/marshal/internal_ivar/depend +++ b/ext/-test-/marshal/internal_ivar/depend @@ -156,7 +156,6 @@ internal_ivar.o: $(hdrdir)/ruby/internal/variable.h internal_ivar.o: $(hdrdir)/ruby/internal/warning_push.h internal_ivar.o: $(hdrdir)/ruby/internal/xmalloc.h internal_ivar.o: $(hdrdir)/ruby/missing.h -internal_ivar.o: $(hdrdir)/ruby/onigmo.h internal_ivar.o: $(hdrdir)/ruby/ruby.h internal_ivar.o: $(hdrdir)/ruby/st.h internal_ivar.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/marshal/usr/depend b/ext/-test-/marshal/usr/depend index 99cc51180de996..5ffb8c58de24be 100644 --- a/ext/-test-/marshal/usr/depend +++ b/ext/-test-/marshal/usr/depend @@ -156,7 +156,6 @@ usrmarshal.o: $(hdrdir)/ruby/internal/variable.h usrmarshal.o: $(hdrdir)/ruby/internal/warning_push.h usrmarshal.o: $(hdrdir)/ruby/internal/xmalloc.h usrmarshal.o: $(hdrdir)/ruby/missing.h -usrmarshal.o: $(hdrdir)/ruby/onigmo.h usrmarshal.o: $(hdrdir)/ruby/ruby.h usrmarshal.o: $(hdrdir)/ruby/st.h usrmarshal.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/memory_view/depend b/ext/-test-/memory_view/depend index f1a8b2404851af..a6ffd76f459669 100644 --- a/ext/-test-/memory_view/depend +++ b/ext/-test-/memory_view/depend @@ -157,7 +157,6 @@ memory_view.o: $(hdrdir)/ruby/internal/warning_push.h memory_view.o: $(hdrdir)/ruby/internal/xmalloc.h memory_view.o: $(hdrdir)/ruby/memory_view.h memory_view.o: $(hdrdir)/ruby/missing.h -memory_view.o: $(hdrdir)/ruby/onigmo.h memory_view.o: $(hdrdir)/ruby/ruby.h memory_view.o: $(hdrdir)/ruby/st.h memory_view.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/method/depend b/ext/-test-/method/depend index eab03eb4bff625..95745b3daea8eb 100644 --- a/ext/-test-/method/depend +++ b/ext/-test-/method/depend @@ -156,7 +156,6 @@ arity.o: $(hdrdir)/ruby/internal/variable.h arity.o: $(hdrdir)/ruby/internal/warning_push.h arity.o: $(hdrdir)/ruby/internal/xmalloc.h arity.o: $(hdrdir)/ruby/missing.h -arity.o: $(hdrdir)/ruby/onigmo.h arity.o: $(hdrdir)/ruby/ruby.h arity.o: $(hdrdir)/ruby/st.h arity.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/notimplement/depend b/ext/-test-/notimplement/depend index b1452bdf5ec8dd..69c970b6f25703 100644 --- a/ext/-test-/notimplement/depend +++ b/ext/-test-/notimplement/depend @@ -156,7 +156,6 @@ bug.o: $(hdrdir)/ruby/internal/variable.h bug.o: $(hdrdir)/ruby/internal/warning_push.h bug.o: $(hdrdir)/ruby/internal/xmalloc.h bug.o: $(hdrdir)/ruby/missing.h -bug.o: $(hdrdir)/ruby/onigmo.h bug.o: $(hdrdir)/ruby/ruby.h bug.o: $(hdrdir)/ruby/st.h bug.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/num2int/depend b/ext/-test-/num2int/depend index ccd33727bf2055..75536363ac650d 100644 --- a/ext/-test-/num2int/depend +++ b/ext/-test-/num2int/depend @@ -156,7 +156,6 @@ num2int.o: $(hdrdir)/ruby/internal/variable.h num2int.o: $(hdrdir)/ruby/internal/warning_push.h num2int.o: $(hdrdir)/ruby/internal/xmalloc.h num2int.o: $(hdrdir)/ruby/missing.h -num2int.o: $(hdrdir)/ruby/onigmo.h num2int.o: $(hdrdir)/ruby/ruby.h num2int.o: $(hdrdir)/ruby/st.h num2int.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/path_to_class/depend b/ext/-test-/path_to_class/depend index 47c93be2fc7534..e535058e092935 100644 --- a/ext/-test-/path_to_class/depend +++ b/ext/-test-/path_to_class/depend @@ -156,7 +156,6 @@ path_to_class.o: $(hdrdir)/ruby/internal/variable.h path_to_class.o: $(hdrdir)/ruby/internal/warning_push.h path_to_class.o: $(hdrdir)/ruby/internal/xmalloc.h path_to_class.o: $(hdrdir)/ruby/missing.h -path_to_class.o: $(hdrdir)/ruby/onigmo.h path_to_class.o: $(hdrdir)/ruby/ruby.h path_to_class.o: $(hdrdir)/ruby/st.h path_to_class.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/popen_deadlock/depend b/ext/-test-/popen_deadlock/depend index faa103fec4d28f..0b8932e8b8a191 100644 --- a/ext/-test-/popen_deadlock/depend +++ b/ext/-test-/popen_deadlock/depend @@ -156,7 +156,6 @@ infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/variable.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/warning_push.h infinite_loop_dlsym.o: $(hdrdir)/ruby/internal/xmalloc.h infinite_loop_dlsym.o: $(hdrdir)/ruby/missing.h -infinite_loop_dlsym.o: $(hdrdir)/ruby/onigmo.h infinite_loop_dlsym.o: $(hdrdir)/ruby/ruby.h infinite_loop_dlsym.o: $(hdrdir)/ruby/st.h infinite_loop_dlsym.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/postponed_job/depend b/ext/-test-/postponed_job/depend index d8898c732a0fe1..ff567e39215ce5 100644 --- a/ext/-test-/postponed_job/depend +++ b/ext/-test-/postponed_job/depend @@ -157,7 +157,6 @@ postponed_job.o: $(hdrdir)/ruby/internal/variable.h postponed_job.o: $(hdrdir)/ruby/internal/warning_push.h postponed_job.o: $(hdrdir)/ruby/internal/xmalloc.h postponed_job.o: $(hdrdir)/ruby/missing.h -postponed_job.o: $(hdrdir)/ruby/onigmo.h postponed_job.o: $(hdrdir)/ruby/ruby.h postponed_job.o: $(hdrdir)/ruby/st.h postponed_job.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/proc/depend b/ext/-test-/proc/depend index 4dd1aa7fb4307b..97834db0a2d422 100644 --- a/ext/-test-/proc/depend +++ b/ext/-test-/proc/depend @@ -156,7 +156,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ receiver.o: $(hdrdir)/ruby/internal/variable.h receiver.o: $(hdrdir)/ruby/internal/warning_push.h receiver.o: $(hdrdir)/ruby/internal/xmalloc.h receiver.o: $(hdrdir)/ruby/missing.h -receiver.o: $(hdrdir)/ruby/onigmo.h receiver.o: $(hdrdir)/ruby/ruby.h receiver.o: $(hdrdir)/ruby/st.h receiver.o: $(hdrdir)/ruby/subst.h @@ -480,7 +478,6 @@ super.o: $(hdrdir)/ruby/internal/variable.h super.o: $(hdrdir)/ruby/internal/warning_push.h super.o: $(hdrdir)/ruby/internal/xmalloc.h super.o: $(hdrdir)/ruby/missing.h -super.o: $(hdrdir)/ruby/onigmo.h super.o: $(hdrdir)/ruby/ruby.h super.o: $(hdrdir)/ruby/st.h super.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/random/depend b/ext/-test-/random/depend index 1d738ff50f3c53..380c30fbe4a355 100644 --- a/ext/-test-/random/depend +++ b/ext/-test-/random/depend @@ -155,7 +155,6 @@ bad_version.o: $(hdrdir)/ruby/internal/variable.h bad_version.o: $(hdrdir)/ruby/internal/warning_push.h bad_version.o: $(hdrdir)/ruby/internal/xmalloc.h bad_version.o: $(hdrdir)/ruby/missing.h -bad_version.o: $(hdrdir)/ruby/onigmo.h bad_version.o: $(hdrdir)/ruby/random.h bad_version.o: $(hdrdir)/ruby/ruby.h bad_version.o: $(hdrdir)/ruby/st.h @@ -318,7 +317,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -479,7 +477,6 @@ loop.o: $(hdrdir)/ruby/internal/variable.h loop.o: $(hdrdir)/ruby/internal/warning_push.h loop.o: $(hdrdir)/ruby/internal/xmalloc.h loop.o: $(hdrdir)/ruby/missing.h -loop.o: $(hdrdir)/ruby/onigmo.h loop.o: $(hdrdir)/ruby/random.h loop.o: $(hdrdir)/ruby/ruby.h loop.o: $(hdrdir)/ruby/st.h diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend index fd8df5e4f57273..d949fca66bcc88 100644 --- a/ext/-test-/rational/depend +++ b/ext/-test-/rational/depend @@ -160,7 +160,6 @@ rat.o: $(hdrdir)/ruby/internal/variable.h rat.o: $(hdrdir)/ruby/internal/warning_push.h rat.o: $(hdrdir)/ruby/internal/xmalloc.h rat.o: $(hdrdir)/ruby/missing.h -rat.o: $(hdrdir)/ruby/onigmo.h rat.o: $(hdrdir)/ruby/ruby.h rat.o: $(hdrdir)/ruby/st.h rat.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/rb_call_super_kw/depend b/ext/-test-/rb_call_super_kw/depend index 1ffafebd98ff4a..bf34323ca7ddc8 100644 --- a/ext/-test-/rb_call_super_kw/depend +++ b/ext/-test-/rb_call_super_kw/depend @@ -156,7 +156,6 @@ rb_call_super_kw.o: $(hdrdir)/ruby/internal/variable.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/warning_push.h rb_call_super_kw.o: $(hdrdir)/ruby/internal/xmalloc.h rb_call_super_kw.o: $(hdrdir)/ruby/missing.h -rb_call_super_kw.o: $(hdrdir)/ruby/onigmo.h rb_call_super_kw.o: $(hdrdir)/ruby/ruby.h rb_call_super_kw.o: $(hdrdir)/ruby/st.h rb_call_super_kw.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/recursion/depend b/ext/-test-/recursion/depend index e4fc781dfe4fad..b6487eb4dfa624 100644 --- a/ext/-test-/recursion/depend +++ b/ext/-test-/recursion/depend @@ -156,7 +156,6 @@ recursion.o: $(hdrdir)/ruby/internal/variable.h recursion.o: $(hdrdir)/ruby/internal/warning_push.h recursion.o: $(hdrdir)/ruby/internal/xmalloc.h recursion.o: $(hdrdir)/ruby/missing.h -recursion.o: $(hdrdir)/ruby/onigmo.h recursion.o: $(hdrdir)/ruby/ruby.h recursion.o: $(hdrdir)/ruby/st.h recursion.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/regexp/depend b/ext/-test-/regexp/depend index e656d00c57cd5c..6e2b24ac27ec1c 100644 --- a/ext/-test-/regexp/depend +++ b/ext/-test-/regexp/depend @@ -156,7 +156,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ new.o: $(hdrdir)/ruby/internal/variable.h new.o: $(hdrdir)/ruby/internal/warning_push.h new.o: $(hdrdir)/ruby/internal/xmalloc.h new.o: $(hdrdir)/ruby/missing.h -new.o: $(hdrdir)/ruby/onigmo.h new.o: $(hdrdir)/ruby/ruby.h new.o: $(hdrdir)/ruby/st.h new.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/sanitizers/depend b/ext/-test-/sanitizers/depend index 69d302381db754..0e6e632803307e 100644 --- a/ext/-test-/sanitizers/depend +++ b/ext/-test-/sanitizers/depend @@ -155,7 +155,6 @@ sanitizers.o: $(hdrdir)/ruby/internal/variable.h sanitizers.o: $(hdrdir)/ruby/internal/warning_push.h sanitizers.o: $(hdrdir)/ruby/internal/xmalloc.h sanitizers.o: $(hdrdir)/ruby/missing.h -sanitizers.o: $(hdrdir)/ruby/onigmo.h sanitizers.o: $(hdrdir)/ruby/ruby.h sanitizers.o: $(hdrdir)/ruby/st.h sanitizers.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/scan_args/depend b/ext/-test-/scan_args/depend index 2a28e043c4da0c..ca0fc1923860b0 100644 --- a/ext/-test-/scan_args/depend +++ b/ext/-test-/scan_args/depend @@ -156,7 +156,6 @@ scan_args.o: $(hdrdir)/ruby/internal/variable.h scan_args.o: $(hdrdir)/ruby/internal/warning_push.h scan_args.o: $(hdrdir)/ruby/internal/xmalloc.h scan_args.o: $(hdrdir)/ruby/missing.h -scan_args.o: $(hdrdir)/ruby/onigmo.h scan_args.o: $(hdrdir)/ruby/ruby.h scan_args.o: $(hdrdir)/ruby/st.h scan_args.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/st/foreach/depend b/ext/-test-/st/foreach/depend index a95ecda94f9bd9..29aab2bb29511f 100644 --- a/ext/-test-/st/foreach/depend +++ b/ext/-test-/st/foreach/depend @@ -156,7 +156,6 @@ foreach.o: $(hdrdir)/ruby/internal/variable.h foreach.o: $(hdrdir)/ruby/internal/warning_push.h foreach.o: $(hdrdir)/ruby/internal/xmalloc.h foreach.o: $(hdrdir)/ruby/missing.h -foreach.o: $(hdrdir)/ruby/onigmo.h foreach.o: $(hdrdir)/ruby/ruby.h foreach.o: $(hdrdir)/ruby/st.h foreach.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/st/numhash/depend b/ext/-test-/st/numhash/depend index c9e87bcd0ac5f9..18320d55f56805 100644 --- a/ext/-test-/st/numhash/depend +++ b/ext/-test-/st/numhash/depend @@ -156,7 +156,6 @@ numhash.o: $(hdrdir)/ruby/internal/variable.h numhash.o: $(hdrdir)/ruby/internal/warning_push.h numhash.o: $(hdrdir)/ruby/internal/xmalloc.h numhash.o: $(hdrdir)/ruby/missing.h -numhash.o: $(hdrdir)/ruby/onigmo.h numhash.o: $(hdrdir)/ruby/ruby.h numhash.o: $(hdrdir)/ruby/st.h numhash.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/st/update/depend b/ext/-test-/st/update/depend index d275a3526a7f88..247f0efd6b39b6 100644 --- a/ext/-test-/st/update/depend +++ b/ext/-test-/st/update/depend @@ -156,7 +156,6 @@ update.o: $(hdrdir)/ruby/internal/variable.h update.o: $(hdrdir)/ruby/internal/warning_push.h update.o: $(hdrdir)/ruby/internal/xmalloc.h update.o: $(hdrdir)/ruby/missing.h -update.o: $(hdrdir)/ruby/onigmo.h update.o: $(hdrdir)/ruby/ruby.h update.o: $(hdrdir)/ruby/st.h update.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/string/depend b/ext/-test-/string/depend index 74519141c08f9b..478ae3b82b7500 100644 --- a/ext/-test-/string/depend +++ b/ext/-test-/string/depend @@ -842,7 +842,6 @@ ellipsize.o: $(hdrdir)/ruby/internal/variable.h ellipsize.o: $(hdrdir)/ruby/internal/warning_push.h ellipsize.o: $(hdrdir)/ruby/internal/xmalloc.h ellipsize.o: $(hdrdir)/ruby/missing.h -ellipsize.o: $(hdrdir)/ruby/onigmo.h ellipsize.o: $(hdrdir)/ruby/ruby.h ellipsize.o: $(hdrdir)/ruby/st.h ellipsize.o: $(hdrdir)/ruby/subst.h @@ -1699,7 +1698,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -1861,7 +1859,6 @@ modify.o: $(hdrdir)/ruby/internal/variable.h modify.o: $(hdrdir)/ruby/internal/warning_push.h modify.o: $(hdrdir)/ruby/internal/xmalloc.h modify.o: $(hdrdir)/ruby/missing.h -modify.o: $(hdrdir)/ruby/onigmo.h modify.o: $(hdrdir)/ruby/ruby.h modify.o: $(hdrdir)/ruby/st.h modify.o: $(hdrdir)/ruby/subst.h @@ -2196,7 +2193,6 @@ nofree.o: $(hdrdir)/ruby/internal/variable.h nofree.o: $(hdrdir)/ruby/internal/warning_push.h nofree.o: $(hdrdir)/ruby/internal/xmalloc.h nofree.o: $(hdrdir)/ruby/missing.h -nofree.o: $(hdrdir)/ruby/onigmo.h nofree.o: $(hdrdir)/ruby/ruby.h nofree.o: $(hdrdir)/ruby/st.h nofree.o: $(hdrdir)/ruby/subst.h @@ -2705,7 +2701,6 @@ rb_interned_str.o: $(hdrdir)/ruby/internal/variable.h rb_interned_str.o: $(hdrdir)/ruby/internal/warning_push.h rb_interned_str.o: $(hdrdir)/ruby/internal/xmalloc.h rb_interned_str.o: $(hdrdir)/ruby/missing.h -rb_interned_str.o: $(hdrdir)/ruby/onigmo.h rb_interned_str.o: $(hdrdir)/ruby/ruby.h rb_interned_str.o: $(hdrdir)/ruby/st.h rb_interned_str.o: $(hdrdir)/ruby/subst.h @@ -2867,7 +2862,6 @@ rb_str_dup.o: $(hdrdir)/ruby/internal/variable.h rb_str_dup.o: $(hdrdir)/ruby/internal/warning_push.h rb_str_dup.o: $(hdrdir)/ruby/internal/xmalloc.h rb_str_dup.o: $(hdrdir)/ruby/missing.h -rb_str_dup.o: $(hdrdir)/ruby/onigmo.h rb_str_dup.o: $(hdrdir)/ruby/ruby.h rb_str_dup.o: $(hdrdir)/ruby/st.h rb_str_dup.o: $(hdrdir)/ruby/subst.h @@ -3029,7 +3023,6 @@ set_len.o: $(hdrdir)/ruby/internal/variable.h set_len.o: $(hdrdir)/ruby/internal/warning_push.h set_len.o: $(hdrdir)/ruby/internal/xmalloc.h set_len.o: $(hdrdir)/ruby/missing.h -set_len.o: $(hdrdir)/ruby/onigmo.h set_len.o: $(hdrdir)/ruby/ruby.h set_len.o: $(hdrdir)/ruby/st.h set_len.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/struct/depend b/ext/-test-/struct/depend index 8f8cf15788dc32..e2638e4cdf5115 100644 --- a/ext/-test-/struct/depend +++ b/ext/-test-/struct/depend @@ -156,7 +156,6 @@ data.o: $(hdrdir)/ruby/internal/variable.h data.o: $(hdrdir)/ruby/internal/warning_push.h data.o: $(hdrdir)/ruby/internal/xmalloc.h data.o: $(hdrdir)/ruby/missing.h -data.o: $(hdrdir)/ruby/onigmo.h data.o: $(hdrdir)/ruby/ruby.h data.o: $(hdrdir)/ruby/st.h data.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ duplicate.o: $(hdrdir)/ruby/internal/variable.h duplicate.o: $(hdrdir)/ruby/internal/warning_push.h duplicate.o: $(hdrdir)/ruby/internal/xmalloc.h duplicate.o: $(hdrdir)/ruby/missing.h -duplicate.o: $(hdrdir)/ruby/onigmo.h duplicate.o: $(hdrdir)/ruby/ruby.h duplicate.o: $(hdrdir)/ruby/st.h duplicate.o: $(hdrdir)/ruby/subst.h @@ -480,7 +478,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -642,7 +639,6 @@ len.o: $(hdrdir)/ruby/internal/variable.h len.o: $(hdrdir)/ruby/internal/warning_push.h len.o: $(hdrdir)/ruby/internal/xmalloc.h len.o: $(hdrdir)/ruby/missing.h -len.o: $(hdrdir)/ruby/onigmo.h len.o: $(hdrdir)/ruby/ruby.h len.o: $(hdrdir)/ruby/st.h len.o: $(hdrdir)/ruby/subst.h @@ -804,7 +800,6 @@ member.o: $(hdrdir)/ruby/internal/variable.h member.o: $(hdrdir)/ruby/internal/warning_push.h member.o: $(hdrdir)/ruby/internal/xmalloc.h member.o: $(hdrdir)/ruby/missing.h -member.o: $(hdrdir)/ruby/onigmo.h member.o: $(hdrdir)/ruby/ruby.h member.o: $(hdrdir)/ruby/st.h member.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/symbol/depend b/ext/-test-/symbol/depend index a07567a5e38427..b1d8e1aab6fd3c 100644 --- a/ext/-test-/symbol/depend +++ b/ext/-test-/symbol/depend @@ -156,7 +156,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ type.o: $(hdrdir)/ruby/internal/variable.h type.o: $(hdrdir)/ruby/internal/warning_push.h type.o: $(hdrdir)/ruby/internal/xmalloc.h type.o: $(hdrdir)/ruby/missing.h -type.o: $(hdrdir)/ruby/onigmo.h type.o: $(hdrdir)/ruby/ruby.h type.o: $(hdrdir)/ruby/st.h type.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/thread/id/depend b/ext/-test-/thread/id/depend index 6f4a912aa8e6ae..6b76b31ddcc3b7 100644 --- a/ext/-test-/thread/id/depend +++ b/ext/-test-/thread/id/depend @@ -156,7 +156,6 @@ id.o: $(hdrdir)/ruby/internal/variable.h id.o: $(hdrdir)/ruby/internal/warning_push.h id.o: $(hdrdir)/ruby/internal/xmalloc.h id.o: $(hdrdir)/ruby/missing.h -id.o: $(hdrdir)/ruby/onigmo.h id.o: $(hdrdir)/ruby/ruby.h id.o: $(hdrdir)/ruby/st.h id.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/thread/instrumentation/depend b/ext/-test-/thread/instrumentation/depend index 176d637786d728..63e1c7e44fb63f 100644 --- a/ext/-test-/thread/instrumentation/depend +++ b/ext/-test-/thread/instrumentation/depend @@ -156,7 +156,6 @@ instrumentation.o: $(hdrdir)/ruby/internal/variable.h instrumentation.o: $(hdrdir)/ruby/internal/warning_push.h instrumentation.o: $(hdrdir)/ruby/internal/xmalloc.h instrumentation.o: $(hdrdir)/ruby/missing.h -instrumentation.o: $(hdrdir)/ruby/onigmo.h instrumentation.o: $(hdrdir)/ruby/ruby.h instrumentation.o: $(hdrdir)/ruby/st.h instrumentation.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/thread/lock_native_thread/depend b/ext/-test-/thread/lock_native_thread/depend index 067dacb5fc7bf7..a32843e531494a 100644 --- a/ext/-test-/thread/lock_native_thread/depend +++ b/ext/-test-/thread/lock_native_thread/depend @@ -155,7 +155,6 @@ lock_native_thread.o: $(hdrdir)/ruby/internal/variable.h lock_native_thread.o: $(hdrdir)/ruby/internal/warning_push.h lock_native_thread.o: $(hdrdir)/ruby/internal/xmalloc.h lock_native_thread.o: $(hdrdir)/ruby/missing.h -lock_native_thread.o: $(hdrdir)/ruby/onigmo.h lock_native_thread.o: $(hdrdir)/ruby/ruby.h lock_native_thread.o: $(hdrdir)/ruby/st.h lock_native_thread.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/time/depend b/ext/-test-/time/depend index 2ac5a85445ab87..e5b05f311358e8 100644 --- a/ext/-test-/time/depend +++ b/ext/-test-/time/depend @@ -156,7 +156,6 @@ init.o: $(hdrdir)/ruby/internal/variable.h init.o: $(hdrdir)/ruby/internal/warning_push.h init.o: $(hdrdir)/ruby/internal/xmalloc.h init.o: $(hdrdir)/ruby/missing.h -init.o: $(hdrdir)/ruby/onigmo.h init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h @@ -484,7 +483,6 @@ new.o: $(hdrdir)/ruby/internal/variable.h new.o: $(hdrdir)/ruby/internal/warning_push.h new.o: $(hdrdir)/ruby/internal/xmalloc.h new.o: $(hdrdir)/ruby/missing.h -new.o: $(hdrdir)/ruby/onigmo.h new.o: $(hdrdir)/ruby/ruby.h new.o: $(hdrdir)/ruby/st.h new.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/tracepoint/depend b/ext/-test-/tracepoint/depend index b1256d31b7aa73..014ba83b16af6a 100644 --- a/ext/-test-/tracepoint/depend +++ b/ext/-test-/tracepoint/depend @@ -156,7 +156,6 @@ gc_hook.o: $(hdrdir)/ruby/internal/variable.h gc_hook.o: $(hdrdir)/ruby/internal/warning_push.h gc_hook.o: $(hdrdir)/ruby/internal/xmalloc.h gc_hook.o: $(hdrdir)/ruby/missing.h -gc_hook.o: $(hdrdir)/ruby/onigmo.h gc_hook.o: $(hdrdir)/ruby/ruby.h gc_hook.o: $(hdrdir)/ruby/st.h gc_hook.o: $(hdrdir)/ruby/subst.h @@ -318,7 +317,6 @@ tracepoint.o: $(hdrdir)/ruby/internal/variable.h tracepoint.o: $(hdrdir)/ruby/internal/warning_push.h tracepoint.o: $(hdrdir)/ruby/internal/xmalloc.h tracepoint.o: $(hdrdir)/ruby/missing.h -tracepoint.o: $(hdrdir)/ruby/onigmo.h tracepoint.o: $(hdrdir)/ruby/ruby.h tracepoint.o: $(hdrdir)/ruby/st.h tracepoint.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/typeddata/depend b/ext/-test-/typeddata/depend index 3bcce0e2870fb2..b9b4915eeed590 100644 --- a/ext/-test-/typeddata/depend +++ b/ext/-test-/typeddata/depend @@ -156,7 +156,6 @@ typeddata.o: $(hdrdir)/ruby/internal/variable.h typeddata.o: $(hdrdir)/ruby/internal/warning_push.h typeddata.o: $(hdrdir)/ruby/internal/xmalloc.h typeddata.o: $(hdrdir)/ruby/missing.h -typeddata.o: $(hdrdir)/ruby/onigmo.h typeddata.o: $(hdrdir)/ruby/ruby.h typeddata.o: $(hdrdir)/ruby/st.h typeddata.o: $(hdrdir)/ruby/subst.h diff --git a/ext/-test-/vm/depend b/ext/-test-/vm/depend index e6d60f5864820a..9313f2ee36fa19 100644 --- a/ext/-test-/vm/depend +++ b/ext/-test-/vm/depend @@ -155,7 +155,6 @@ at_exit.o: $(hdrdir)/ruby/internal/variable.h at_exit.o: $(hdrdir)/ruby/internal/warning_push.h at_exit.o: $(hdrdir)/ruby/internal/xmalloc.h at_exit.o: $(hdrdir)/ruby/missing.h -at_exit.o: $(hdrdir)/ruby/onigmo.h at_exit.o: $(hdrdir)/ruby/ruby.h at_exit.o: $(hdrdir)/ruby/st.h at_exit.o: $(hdrdir)/ruby/subst.h diff --git a/ext/continuation/depend b/ext/continuation/depend index 4724fad81c9af5..b395d3d4d7a1bb 100644 --- a/ext/continuation/depend +++ b/ext/continuation/depend @@ -155,7 +155,6 @@ continuation.o: $(hdrdir)/ruby/internal/variable.h continuation.o: $(hdrdir)/ruby/internal/warning_push.h continuation.o: $(hdrdir)/ruby/internal/xmalloc.h continuation.o: $(hdrdir)/ruby/missing.h -continuation.o: $(hdrdir)/ruby/onigmo.h continuation.o: $(hdrdir)/ruby/ruby.h continuation.o: $(hdrdir)/ruby/st.h continuation.o: $(hdrdir)/ruby/subst.h diff --git a/ext/date/depend b/ext/date/depend index da8bd9207f1629..4fb78149a16509 100644 --- a/ext/date/depend +++ b/ext/date/depend @@ -508,7 +508,6 @@ date_strftime.o: $(hdrdir)/ruby/internal/variable.h date_strftime.o: $(hdrdir)/ruby/internal/warning_push.h date_strftime.o: $(hdrdir)/ruby/internal/xmalloc.h date_strftime.o: $(hdrdir)/ruby/missing.h -date_strftime.o: $(hdrdir)/ruby/onigmo.h date_strftime.o: $(hdrdir)/ruby/ruby.h date_strftime.o: $(hdrdir)/ruby/st.h date_strftime.o: $(hdrdir)/ruby/subst.h diff --git a/ext/digest/bubblebabble/depend b/ext/digest/bubblebabble/depend index c6e887024d5194..02d88e960c112a 100644 --- a/ext/digest/bubblebabble/depend +++ b/ext/digest/bubblebabble/depend @@ -156,7 +156,6 @@ bubblebabble.o: $(hdrdir)/ruby/internal/variable.h bubblebabble.o: $(hdrdir)/ruby/internal/warning_push.h bubblebabble.o: $(hdrdir)/ruby/internal/xmalloc.h bubblebabble.o: $(hdrdir)/ruby/missing.h -bubblebabble.o: $(hdrdir)/ruby/onigmo.h bubblebabble.o: $(hdrdir)/ruby/ruby.h bubblebabble.o: $(hdrdir)/ruby/st.h bubblebabble.o: $(hdrdir)/ruby/subst.h diff --git a/ext/digest/depend b/ext/digest/depend index 345d8ca2d45215..3eabb1d1d6a570 100644 --- a/ext/digest/depend +++ b/ext/digest/depend @@ -156,7 +156,6 @@ digest.o: $(hdrdir)/ruby/internal/variable.h digest.o: $(hdrdir)/ruby/internal/warning_push.h digest.o: $(hdrdir)/ruby/internal/xmalloc.h digest.o: $(hdrdir)/ruby/missing.h -digest.o: $(hdrdir)/ruby/onigmo.h digest.o: $(hdrdir)/ruby/ruby.h digest.o: $(hdrdir)/ruby/st.h digest.o: $(hdrdir)/ruby/subst.h diff --git a/ext/digest/md5/depend b/ext/digest/md5/depend index f40e4eebed75f0..d1c25c28c89aa7 100644 --- a/ext/digest/md5/depend +++ b/ext/digest/md5/depend @@ -159,7 +159,6 @@ md5.o: $(hdrdir)/ruby/internal/variable.h md5.o: $(hdrdir)/ruby/internal/warning_push.h md5.o: $(hdrdir)/ruby/internal/xmalloc.h md5.o: $(hdrdir)/ruby/missing.h -md5.o: $(hdrdir)/ruby/onigmo.h md5.o: $(hdrdir)/ruby/ruby.h md5.o: $(hdrdir)/ruby/st.h md5.o: $(hdrdir)/ruby/subst.h @@ -323,7 +322,6 @@ md5init.o: $(hdrdir)/ruby/internal/variable.h md5init.o: $(hdrdir)/ruby/internal/warning_push.h md5init.o: $(hdrdir)/ruby/internal/xmalloc.h md5init.o: $(hdrdir)/ruby/missing.h -md5init.o: $(hdrdir)/ruby/onigmo.h md5init.o: $(hdrdir)/ruby/ruby.h md5init.o: $(hdrdir)/ruby/st.h md5init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/digest/rmd160/depend b/ext/digest/rmd160/depend index 0f7f026bea4760..aec484f7b3ee38 100644 --- a/ext/digest/rmd160/depend +++ b/ext/digest/rmd160/depend @@ -159,7 +159,6 @@ rmd160.o: $(hdrdir)/ruby/internal/variable.h rmd160.o: $(hdrdir)/ruby/internal/warning_push.h rmd160.o: $(hdrdir)/ruby/internal/xmalloc.h rmd160.o: $(hdrdir)/ruby/missing.h -rmd160.o: $(hdrdir)/ruby/onigmo.h rmd160.o: $(hdrdir)/ruby/ruby.h rmd160.o: $(hdrdir)/ruby/st.h rmd160.o: $(hdrdir)/ruby/subst.h @@ -323,7 +322,6 @@ rmd160init.o: $(hdrdir)/ruby/internal/variable.h rmd160init.o: $(hdrdir)/ruby/internal/warning_push.h rmd160init.o: $(hdrdir)/ruby/internal/xmalloc.h rmd160init.o: $(hdrdir)/ruby/missing.h -rmd160init.o: $(hdrdir)/ruby/onigmo.h rmd160init.o: $(hdrdir)/ruby/ruby.h rmd160init.o: $(hdrdir)/ruby/st.h rmd160init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/digest/sha1/depend b/ext/digest/sha1/depend index c5ea09758a4928..e6bd9d8f731cd9 100644 --- a/ext/digest/sha1/depend +++ b/ext/digest/sha1/depend @@ -159,7 +159,6 @@ sha1.o: $(hdrdir)/ruby/internal/variable.h sha1.o: $(hdrdir)/ruby/internal/warning_push.h sha1.o: $(hdrdir)/ruby/internal/xmalloc.h sha1.o: $(hdrdir)/ruby/missing.h -sha1.o: $(hdrdir)/ruby/onigmo.h sha1.o: $(hdrdir)/ruby/ruby.h sha1.o: $(hdrdir)/ruby/st.h sha1.o: $(hdrdir)/ruby/subst.h @@ -323,7 +322,6 @@ sha1init.o: $(hdrdir)/ruby/internal/variable.h sha1init.o: $(hdrdir)/ruby/internal/warning_push.h sha1init.o: $(hdrdir)/ruby/internal/xmalloc.h sha1init.o: $(hdrdir)/ruby/missing.h -sha1init.o: $(hdrdir)/ruby/onigmo.h sha1init.o: $(hdrdir)/ruby/ruby.h sha1init.o: $(hdrdir)/ruby/st.h sha1init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/digest/sha2/depend b/ext/digest/sha2/depend index 6621a5fee378b5..2b74776b3e097c 100644 --- a/ext/digest/sha2/depend +++ b/ext/digest/sha2/depend @@ -159,7 +159,6 @@ sha2.o: $(hdrdir)/ruby/internal/variable.h sha2.o: $(hdrdir)/ruby/internal/warning_push.h sha2.o: $(hdrdir)/ruby/internal/xmalloc.h sha2.o: $(hdrdir)/ruby/missing.h -sha2.o: $(hdrdir)/ruby/onigmo.h sha2.o: $(hdrdir)/ruby/ruby.h sha2.o: $(hdrdir)/ruby/st.h sha2.o: $(hdrdir)/ruby/subst.h @@ -323,7 +322,6 @@ sha2init.o: $(hdrdir)/ruby/internal/variable.h sha2init.o: $(hdrdir)/ruby/internal/warning_push.h sha2init.o: $(hdrdir)/ruby/internal/xmalloc.h sha2init.o: $(hdrdir)/ruby/missing.h -sha2init.o: $(hdrdir)/ruby/onigmo.h sha2init.o: $(hdrdir)/ruby/ruby.h sha2init.o: $(hdrdir)/ruby/st.h sha2init.o: $(hdrdir)/ruby/subst.h diff --git a/ext/fcntl/depend b/ext/fcntl/depend index 0f02a81f9d330c..57ea0f21068068 100644 --- a/ext/fcntl/depend +++ b/ext/fcntl/depend @@ -156,7 +156,6 @@ fcntl.o: $(hdrdir)/ruby/internal/variable.h fcntl.o: $(hdrdir)/ruby/internal/warning_push.h fcntl.o: $(hdrdir)/ruby/internal/xmalloc.h fcntl.o: $(hdrdir)/ruby/missing.h -fcntl.o: $(hdrdir)/ruby/onigmo.h fcntl.o: $(hdrdir)/ruby/ruby.h fcntl.o: $(hdrdir)/ruby/st.h fcntl.o: $(hdrdir)/ruby/subst.h diff --git a/ext/rbconfig/sizeof/depend b/ext/rbconfig/sizeof/depend index c9a9d59cae789e..7e6c950769f759 100644 --- a/ext/rbconfig/sizeof/depend +++ b/ext/rbconfig/sizeof/depend @@ -170,7 +170,6 @@ limits.o: $(hdrdir)/ruby/internal/variable.h limits.o: $(hdrdir)/ruby/internal/warning_push.h limits.o: $(hdrdir)/ruby/internal/xmalloc.h limits.o: $(hdrdir)/ruby/missing.h -limits.o: $(hdrdir)/ruby/onigmo.h limits.o: $(hdrdir)/ruby/ruby.h limits.o: $(hdrdir)/ruby/st.h limits.o: $(hdrdir)/ruby/subst.h @@ -331,7 +330,6 @@ sizes.o: $(hdrdir)/ruby/internal/variable.h sizes.o: $(hdrdir)/ruby/internal/warning_push.h sizes.o: $(hdrdir)/ruby/internal/xmalloc.h sizes.o: $(hdrdir)/ruby/missing.h -sizes.o: $(hdrdir)/ruby/onigmo.h sizes.o: $(hdrdir)/ruby/ruby.h sizes.o: $(hdrdir)/ruby/st.h sizes.o: $(hdrdir)/ruby/subst.h diff --git a/ext/ripper/depend b/ext/ripper/depend index 6bac9d2135be88..db83378a1d53db 100644 --- a/ext/ripper/depend +++ b/ext/ripper/depend @@ -209,7 +209,6 @@ eventids1.o: $(hdrdir)/ruby/internal/variable.h eventids1.o: $(hdrdir)/ruby/internal/warning_push.h eventids1.o: $(hdrdir)/ruby/internal/xmalloc.h eventids1.o: $(hdrdir)/ruby/missing.h -eventids1.o: $(hdrdir)/ruby/onigmo.h eventids1.o: $(hdrdir)/ruby/ruby.h eventids1.o: $(hdrdir)/ruby/st.h eventids1.o: $(hdrdir)/ruby/subst.h diff --git a/include/ruby/internal/core/rregexp.h b/include/ruby/internal/core/rregexp.h index 063f8695f7d868..664fa77a582075 100644 --- a/include/ruby/internal/core/rregexp.h +++ b/include/ruby/internal/core/rregexp.h @@ -27,7 +27,6 @@ #include "ruby/internal/core/rstring.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" -#include "ruby/onigmo.h" /** * Convenient casting macro. @@ -37,13 +36,6 @@ */ #define RREGEXP(obj) RBIMPL_CAST((struct RRegexp *)(obj)) -/** - * Convenient accessor macro. - * - * @param obj An object, which is in fact an ::RRegexp. - * @return The passed object's pattern buffer. - */ -#define RREGEXP_PTR(obj) (&RREGEXP(obj)->pattern) /** @cond INTERNAL_MACRO */ #define RREGEXP_SRC RREGEXP_SRC #define RREGEXP_SRC_PTR RREGEXP_SRC_PTR @@ -79,16 +71,24 @@ struct RRegexp { * catastrophic effects. Just leave it. */ unsigned long usecnt; - - /** - * The pattern buffer. This is a quasi-opaque struct that holds compiled - * intermediate representation of the regular expression. - * - * @note Compilation of a regexp could be delayed until actual match. - */ - struct re_pattern_buffer pattern; /* a.k.a. OnigRegexType, defined in onigmo.h */ }; +RBIMPL_ATTR_PURE_UNLESS_DEBUG() +RBIMPL_ATTR_ARTIFICIAL() +/** + * Convenient getter function. + * + * @param[in] rexp The regular expression in question. + * @return The pattern buffer of the regular expression. + * @pre `rexp` must be of ::RRegexp. + */ +static inline struct re_pattern_buffer * +RREGEXP_PTR(VALUE rexp) +{ + RBIMPL_ASSERT_TYPE(rexp, RUBY_T_REGEXP); + return (struct re_pattern_buffer *)(((struct RRegexp *)rexp) + 1); +} + RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** diff --git a/re.c b/re.c index 9fbdb68c50c668..4c566f478fc906 100644 --- a/re.c +++ b/re.c @@ -32,6 +32,11 @@ #include "ruby/util.h" #include "ractor_core.h" +struct RRegexp_and_re_pattern_buffer { + struct RRegexp re; + struct re_pattern_buffer pattern; /* a.k.a. OnigRegexType, defined in onigmo.h */ +}; + /* Flags of RRegexp * * 4: KCODE_FIXED @@ -3502,10 +3507,10 @@ rb_reg_initialize_str(VALUE obj, VALUE str, int options, onig_errmsg_buffer err, VALUE rb_reg_s_alloc(VALUE klass) { - NEWOBJ_OF(re, struct RRegexp, klass, T_REGEXP, sizeof(struct RRegexp)); + NEWOBJ_OF(re, struct RRegexp, klass, T_REGEXP, sizeof(struct RRegexp_and_re_pattern_buffer)); - MEMZERO(RREGEXP_PTR(re), struct re_pattern_buffer, 1); - RB_OBJ_WRITE(re, &re->src, 0); + MEMZERO(RREGEXP_PTR((VALUE)re), struct re_pattern_buffer, 1); + RB_OBJ_WRITE((VALUE)re, &re->src, 0); re->usecnt = 0; return (VALUE)re; From d9b4ab5b40615c06dd93991af4f3787f1a3dd5b8 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sun, 7 Jun 2026 15:01:40 +0900 Subject: [PATCH 10/20] Inline GC fastpath in ZJIT for newarray instruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements support in ZJIT for inlining GC fast path. The GC fast path is defined in gc_fastpath.rs for both the default GC and MMTk. For the default GC, the fast path uses the bump pointer allocator that was implemented in https://github.com/ruby/ruby/pull/17201. If the bump pointer region is full, it will bail out and fall back to the slow path. This GC fast path is used by the newarray instruction in ZJIT in the empty array case. We can see significant improvements in the following microbenchmark, which allocates 100 million empty arrays: def run(max) i = 0 while i < max a = [] i += 1 end end 30.times { run(2) } run(100_000_000) This microbenchmark runs 1.5x faster on my machine: Benchmark 1: master Time (mean ± σ): 583.3 ms ± 10.1 ms [User: 569.3 ms, System: 12.0 ms] Range (min … max): 571.9 ms … 600.0 ms 10 runs Benchmark 2: branch Time (mean ± σ): 389.5 ms ± 8.2 ms [User: 375.0 ms, System: 12.1 ms] Range (min … max): 380.2 ms … 405.3 ms 10 runs Summary branch ran 1.50 ± 0.04 times faster than master --- common.mk | 2 +- depend | 3 + gc.c | 13 ++ gc/default/default.c | 340 ++++++++++++++++++++--------- gc/default/zjit_fastpath.h | 21 ++ gc/gc_impl.h | 37 ++++ gc/mmtk/mmtk.c | 75 ++++++- gc/mmtk/zjit_fastpath.h | 36 ++++ gc/wbcheck/wbcheck.c | 7 + internal/gc.h | 3 + zjit.c | 10 +- zjit/bindgen/src/main.rs | 13 ++ zjit/src/codegen.rs | 19 +- zjit/src/codegen/gc_fastpath.rs | 367 ++++++++++++++++++++++++++++++++ zjit/src/cruby.rs | 2 + zjit/src/cruby_bindings.inc.rs | 78 +++++++ 16 files changed, 915 insertions(+), 111 deletions(-) create mode 100644 gc/default/zjit_fastpath.h create mode 100644 gc/mmtk/zjit_fastpath.h create mode 100644 zjit/src/codegen/gc_fastpath.rs diff --git a/common.mk b/common.mk index 07ece19ee98373..e7908cc0e54567 100644 --- a/common.mk +++ b/common.mk @@ -698,7 +698,7 @@ clean-local:: clean-runnable -$(Q)$(RMALL) dump_ast$(BUILD_EXEEXT)* -$(Q)$(RMALL) target -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine target \ - $(PRISM_BUILD_DIR)/*/ $(PRISM_BUILD_DIR) tmp \ + $(PRISM_BUILD_DIR)/*/ $(PRISM_BUILD_DIR) gc/default gc tmp \ 2> $(NULL) || $(NULLCMD) bin/clean-runnable:: PHONY diff --git a/depend b/depend index c3252dd87e821b..f4a6a4a6f10fd1 100644 --- a/depend +++ b/depend @@ -5932,6 +5932,7 @@ gc.$(OBJEXT): $(hdrdir)/ruby.h gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h gc.$(OBJEXT): $(hdrdir)/ruby/version.h gc.$(OBJEXT): $(top_srcdir)/gc/default/default.c +gc.$(OBJEXT): $(top_srcdir)/gc/default/zjit_fastpath.h gc.$(OBJEXT): $(top_srcdir)/gc/gc.h gc.$(OBJEXT): $(top_srcdir)/gc/gc_impl.h gc.$(OBJEXT): $(top_srcdir)/internal/array.h @@ -21707,6 +21708,8 @@ zjit.$(OBJEXT): {$(VPATH)}prism_xallocator.h zjit.$(OBJEXT): {$(VPATH)}probes.dmyh zjit.$(OBJEXT): {$(VPATH)}probes.h zjit.$(OBJEXT): {$(VPATH)}probes_helper.h +zjit.$(OBJEXT): {$(VPATH)}ractor.h +zjit.$(OBJEXT): {$(VPATH)}ractor_core.h zjit.$(OBJEXT): {$(VPATH)}ruby_assert.h zjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h zjit.$(OBJEXT): {$(VPATH)}rubyparser.h diff --git a/gc.c b/gc.c index c61f080884e623..c44b808c701ec7 100644 --- a/gc.c +++ b/gc.c @@ -621,6 +621,7 @@ typedef struct gc_function_map { struct rb_gc_vm_context *(*get_vm_context)(void *objspace_ptr); // Object allocation VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size, size_t *actual_alloc_size); + bool (*zjit_new_obj_fastpath)(void *objspace_ptr, size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath); size_t (*obj_slot_size)(VALUE obj); size_t (*size_slot_size)(void *objspace_ptr, size_t size); bool (*size_allocatable_p)(size_t size); @@ -800,6 +801,7 @@ ruby_modular_gc_init(void) load_modular_gc_func(get_vm_context); // Object allocation load_modular_gc_func(new_obj); + load_modular_gc_func(zjit_new_obj_fastpath); load_modular_gc_func(obj_slot_size); load_modular_gc_func(size_slot_size); load_modular_gc_func(size_allocatable_p); @@ -888,6 +890,7 @@ ruby_modular_gc_init(void) # define rb_gc_impl_get_vm_context rb_gc_functions.get_vm_context // Object allocation # define rb_gc_impl_new_obj rb_gc_functions.new_obj +# define rb_gc_impl_zjit_new_obj_fastpath rb_gc_functions.zjit_new_obj_fastpath # define rb_gc_impl_obj_slot_size rb_gc_functions.obj_slot_size # define rb_gc_impl_size_slot_size rb_gc_functions.size_slot_size # define rb_gc_impl_size_allocatable_p rb_gc_functions.size_allocatable_p @@ -3713,6 +3716,16 @@ rb_gc_ractor_cache_free(void *cache) rb_gc_impl_ractor_cache_free(rb_gc_get_objspace(), cache); } +bool +rb_gc_zjit_new_obj_fastpath(size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath) +{ +#if RACTOR_CHECK_MODE || defined(RUBY_ASAN_ENABLED) + return false; +#else + return rb_gc_impl_zjit_new_obj_fastpath(rb_gc_get_objspace(), alloc_size, flags, klass, fastpath); +#endif +} + void rb_gc_register_mark_object(VALUE obj) { diff --git a/gc/default/default.c b/gc/default/default.c index 4cc0a78d947d30..7d0eb0ede4e4d5 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -1,6 +1,7 @@ #include "ruby/internal/config.h" #include +#include #ifndef _WIN32 # include @@ -73,6 +74,10 @@ rb_hrtime_sub(rb_hrtime_t a, rb_hrtime_t b) #include "probes.h" +#if USE_ZJIT +# include "gc/default/zjit_fastpath.h" +#endif + #ifdef BUILDING_MODULAR_GC # define RB_DEBUG_COUNTER_INC(_name) ((void)0) # define RB_DEBUG_COUNTER_INC_IF(_name, cond) (!!(cond)) @@ -233,16 +238,15 @@ static RB_THREAD_LOCAL_SPECIFIER int malloc_increase_local; #endif typedef struct ractor_newobj_heap_cache { - uintptr_t cursor; - uintptr_t cursor_end; struct free_region *next_region; struct heap_page *using_page; - size_t allocated_objects_count; + uintptr_t region_end; } rb_ractor_newobj_heap_cache_t; typedef struct ractor_newobj_cache { size_t incremental_mark_step_allocated_slots; rb_ractor_newobj_heap_cache_t heap_caches[HEAP_COUNT]; + struct gc_bump_pointer_heap bump_heaps[]; } rb_ractor_newobj_cache_t; typedef struct { @@ -1757,11 +1761,31 @@ calloc1(size_t n) return calloc(1, n); } +static void +gc_ractor_jit_cursor_sync(void *c, void *data) +{ + rb_ractor_newobj_cache_t *gc_cache = c; + bool tracing = (bool)(uintptr_t)data; + + for (size_t heap_idx = 0; heap_idx < HEAP_COUNT; heap_idx++) { + struct gc_bump_pointer_heap *bump = &gc_cache->bump_heaps[heap_idx]; + bump->jit_cursor_end = tracing ? bump->cursor : bump->cursor_end; + } +} + void rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event) { rb_objspace_t *objspace = objspace_ptr; + rb_event_flag_t old_events = objspace->hook_events; objspace->hook_events = event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK; + + bool was_tracing = old_events & RUBY_INTERNAL_EVENT_NEWOBJ; + bool now_tracing = objspace->hook_events & RUBY_INTERNAL_EVENT_NEWOBJ; + if (was_tracing != now_tracing) { + rb_gc_ractor_newobj_cache_foreach(gc_ractor_jit_cursor_sync, + (void *)(uintptr_t)now_tracing); + } } unsigned long long @@ -2470,22 +2494,48 @@ rb_gc_impl_size_allocatable_p(size_t size) return size <= rb_gc_impl_max_allocation_size(); } -static const size_t ALLOCATED_COUNT_STEP = 1024; +static inline void +gc_bump_flush_alloc_count(struct gc_bump_pointer_heap *bump, rb_heap_t *heap) +{ + if (bump->slot_size == 0) return; + + size_t n = (bump->cursor - bump->region_start) / bump->slot_size; + if (n > 0) { + RUBY_ATOMIC_SIZE_ADD(heap->total_allocated_objects, n); + bump->region_start = bump->cursor; + } +} + static void -ractor_cache_flush_count(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache) +ractor_cache_flush_count(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache) { for (int heap_idx = 0; heap_idx < HEAP_COUNT; heap_idx++) { - rb_ractor_newobj_heap_cache_t *heap_cache = &cache->heap_caches[heap_idx]; + gc_bump_flush_alloc_count(&gc_cache->bump_heaps[heap_idx], &heaps[heap_idx]); + } +} - rb_heap_t *heap = &heaps[heap_idx]; - RUBY_ATOMIC_SIZE_ADD(heap->total_allocated_objects, heap_cache->allocated_objects_count); - heap_cache->allocated_objects_count = 0; +static inline void +ractor_cache_open_window(rb_objspace_t *objspace, struct gc_bump_pointer_heap *bump, + rb_ractor_newobj_heap_cache_t *heap_cache) +{ + uintptr_t end = heap_cache->region_end; + + if (RB_UNLIKELY(is_incremental_marking(objspace))) { + uintptr_t window_end = bump->cursor + INCREMENTAL_MARK_STEP_ALLOCATIONS * bump->slot_size; + if (window_end < end) end = window_end; } + + bump->cursor_end = end; + bump->jit_cursor_end = + RB_UNLIKELY(objspace->hook_events & RUBY_INTERNAL_EVENT_NEWOBJ) ? bump->cursor : end; } static inline bool -ractor_cache_advance_region(rb_ractor_newobj_heap_cache_t *heap_cache) +ractor_cache_advance_region(rb_objspace_t *objspace, struct gc_bump_pointer_heap *bump, + rb_ractor_newobj_heap_cache_t *heap_cache, rb_heap_t *heap) { + gc_bump_flush_alloc_count(bump, heap); + struct free_region *region = heap_cache->next_region; if (region == NULL) { return false; @@ -2493,51 +2543,42 @@ ractor_cache_advance_region(rb_ractor_newobj_heap_cache_t *heap_cache) rb_asan_unpoison_object((VALUE)region, false); GC_ASSERT(RB_TYPE_P((VALUE)region, T_NONE)); - heap_cache->cursor = (uintptr_t)region; - heap_cache->cursor_end = region->end; + bump->cursor = (uintptr_t)region; + bump->region_start = (uintptr_t)region; + heap_cache->region_end = region->end; heap_cache->next_region = region->next; rb_asan_poison_object((VALUE)region); + ractor_cache_open_window(objspace, bump, heap_cache); + return true; } static inline VALUE -ractor_cache_allocate_slot(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, +ractor_cache_allocate_slot(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx) { - rb_ractor_newobj_heap_cache_t *heap_cache = &cache->heap_caches[heap_idx]; + struct gc_bump_pointer_heap *bump = &gc_cache->bump_heaps[heap_idx]; - uintptr_t cursor = heap_cache->cursor; - if (RB_UNLIKELY(cursor >= heap_cache->cursor_end)) { - if (!ractor_cache_advance_region(heap_cache)) { + uintptr_t cursor = bump->cursor; + if (RB_UNLIKELY(cursor + bump->slot_size > bump->cursor_end)) { + if (RB_UNLIKELY(is_incremental_marking(objspace))) { return Qfalse; } - cursor = heap_cache->cursor; - } - if (RB_UNLIKELY(is_incremental_marking(objspace))) { - // Not allowed to allocate without running an incremental marking step - if (cache->incremental_mark_step_allocated_slots >= INCREMENTAL_MARK_STEP_ALLOCATIONS) { + rb_ractor_newobj_heap_cache_t *heap_cache = &gc_cache->heap_caches[heap_idx]; + if (!ractor_cache_advance_region(objspace, bump, heap_cache, &heaps[heap_idx])) { return Qfalse; } - - cache->incremental_mark_step_allocated_slots++; + cursor = bump->cursor; } VALUE obj = (VALUE)cursor; rb_asan_unpoison_object(obj, true); - heap_cache->cursor = cursor + pool_slot_sizes[heap_idx]; - - heap_cache->allocated_objects_count++; - rb_heap_t *heap = &heaps[heap_idx]; - if (heap_cache->allocated_objects_count >= ALLOCATED_COUNT_STEP) { - RUBY_ATOMIC_SIZE_ADD(heap->total_allocated_objects, heap_cache->allocated_objects_count); - heap_cache->allocated_objects_count = 0; - } + bump->cursor = cursor + bump->slot_size; #if RGENGC_CHECK_MODE GC_ASSERT(rb_gc_impl_obj_slot_size(obj) == heap_slot_size(heap_idx)); - // zero clear MEMZERO((char *)obj, char, heap_slot_size(heap_idx)); #endif return obj; @@ -2563,14 +2604,15 @@ heap_next_free_page(rb_objspace_t *objspace, rb_heap_t *heap) } static inline void -ractor_cache_set_page(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx, +ractor_cache_set_page(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx, struct heap_page *page) { gc_report(3, objspace, "ractor_set_cache: Using page %p\n", (void *)page->body); - rb_ractor_newobj_heap_cache_t *heap_cache = &cache->heap_caches[heap_idx]; + struct gc_bump_pointer_heap *bump = &gc_cache->bump_heaps[heap_idx]; + rb_ractor_newobj_heap_cache_t *heap_cache = &gc_cache->heap_caches[heap_idx]; - GC_ASSERT(heap_cache->cursor >= heap_cache->cursor_end); + GC_ASSERT(bump->cursor + bump->slot_size > bump->cursor_end); GC_ASSERT(heap_cache->next_region == NULL); GC_ASSERT(page->free_slots != 0); GC_ASSERT(page->free_region != NULL); @@ -2580,11 +2622,14 @@ ractor_cache_set_page(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, struct free_region *region = page->free_region; rb_asan_unpoison_object((VALUE)region, false); GC_ASSERT(RB_TYPE_P((VALUE)region, T_NONE)); - heap_cache->cursor = (uintptr_t)region; - heap_cache->cursor_end = region->end; + bump->cursor = (uintptr_t)region; + bump->region_start = (uintptr_t)region; + heap_cache->region_end = region->end; heap_cache->next_region = region->next; rb_asan_poison_object((VALUE)region); + ractor_cache_open_window(objspace, bump, heap_cache); + page->free_slots = 0; page->free_region = NULL; } @@ -2621,11 +2666,50 @@ rb_gc_impl_size_slot_size(void *objspace_ptr, size_t size) return heap_slot_size((unsigned char)heap_idx_for_size(size)); } -NOINLINE(static VALUE newobj_cache_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx, bool vm_locked)); +bool +rb_gc_impl_zjit_new_obj_fastpath(void *objspace_ptr, size_t alloc_size, VALUE flags, VALUE klass, + struct rb_gc_zjit_fastpath *fastpath) +{ +#if USE_ZJIT + size_t heap_idx = 0; + size_t slot_size = 0; + for (; pool_slot_sizes[heap_idx] != 0; heap_idx++) { + if (alloc_size <= pool_slot_sizes[heap_idx]) { + slot_size = pool_slot_sizes[heap_idx]; + break; + } + } + if (slot_size == 0) return false; + + size_t bump_base = offsetof(rb_ractor_newobj_cache_t, bump_heaps) + + heap_idx * sizeof(struct gc_bump_pointer_heap); + + struct rb_gc_zjit_default_new_obj_fastpath default_fastpath = { + bump_base + offsetof(struct gc_bump_pointer_heap, cursor), + bump_base + offsetof(struct gc_bump_pointer_heap, jit_cursor_end), + slot_size, + flags, + klass + }; + + memset(fastpath, 0, sizeof(*fastpath)); + fastpath->kind = RB_GC_ZJIT_FASTPATH_DEFAULT; + memcpy(fastpath->data.words, &default_fastpath, sizeof(default_fastpath)); + + return true; +#else + return false; +#endif +} + +NOINLINE(static VALUE newobj_bump_pointer_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx, bool vm_locked)); static VALUE -newobj_cache_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx, bool vm_locked) +newobj_bump_pointer_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx, bool vm_locked) { + struct gc_bump_pointer_heap *bump = &gc_cache->bump_heaps[heap_idx]; + rb_ractor_newobj_cache_t *cache = gc_cache; + rb_ractor_newobj_heap_cache_t *heap_cache = &cache->heap_caches[heap_idx]; rb_heap_t *heap = &heaps[heap_idx]; VALUE obj = Qfalse; @@ -2638,21 +2722,57 @@ newobj_cache_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size } { + if (RB_UNLIKELY(during_gc || ruby_gc_stressful)) { + if (during_gc) { + dont_gc_on(); + during_gc = 0; + if (rb_memerror_reentered()) { + rb_memerror(); + } + rb_bug("object allocation during garbage collection phase"); + } + + if (ruby_gc_stressful) { + if (!garbage_collect(objspace, GPR_FLAG_NEWOBJ)) { + rb_memerror(); + } + } + } + if (is_incremental_marking(objspace)) { - gc_continue(objspace, heap); - cache->incremental_mark_step_allocated_slots = 0; + if (bump->slot_size > 0) { + cache->incremental_mark_step_allocated_slots += + (bump->cursor - bump->region_start) / bump->slot_size; + } + gc_bump_flush_alloc_count(bump, heap); - // Retry allocation after resetting incremental_mark_step_allocated_slots - obj = ractor_cache_allocate_slot(objspace, cache, heap_idx); + if (cache->incremental_mark_step_allocated_slots >= INCREMENTAL_MARK_STEP_ALLOCATIONS) { + gc_continue(objspace, heap); + cache->incremental_mark_step_allocated_slots = 0; + } + + if (bump->cursor + bump->slot_size <= heap_cache->region_end) { + ractor_cache_open_window(objspace, bump, heap_cache); + obj = ractor_cache_allocate_slot(objspace, gc_cache, heap_idx); + } + } + + if (obj == Qfalse) { + if (ractor_cache_advance_region(objspace, bump, heap_cache, heap)) { + obj = ractor_cache_allocate_slot(objspace, gc_cache, heap_idx); + } } if (obj == Qfalse) { - // Get next free page (possibly running GC) struct heap_page *page = heap_next_free_page(objspace, heap); - ractor_cache_set_page(objspace, cache, heap_idx, page); + ractor_cache_set_page(objspace, gc_cache, heap_idx, page); - // Retry allocation after moving to new page - obj = ractor_cache_allocate_slot(objspace, cache, heap_idx); + obj = ractor_cache_allocate_slot(objspace, gc_cache, heap_idx); + } + + if (RB_UNLIKELY(ruby_gc_stressful)) { + bump->cursor_end = bump->cursor; + bump->jit_cursor_end = bump->cursor; } } @@ -2667,45 +2787,28 @@ newobj_cache_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size } static VALUE -newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx, bool vm_locked) +newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx, bool vm_locked) { - VALUE obj = ractor_cache_allocate_slot(objspace, cache, heap_idx); + VALUE obj = ractor_cache_allocate_slot(objspace, gc_cache, heap_idx); if (RB_UNLIKELY(obj == Qfalse)) { - obj = newobj_cache_miss(objspace, cache, heap_idx, vm_locked); + obj = newobj_bump_pointer_miss(objspace, gc_cache, heap_idx, vm_locked); } return obj; } -ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx)); +ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, int wb_protected, size_t heap_idx)); static inline VALUE -newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx) +newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, int wb_protected, size_t heap_idx) { VALUE obj; unsigned int lev; lev = RB_GC_CR_LOCK(); { - if (RB_UNLIKELY(during_gc || ruby_gc_stressful)) { - if (during_gc) { - dont_gc_on(); - during_gc = 0; - if (rb_memerror_reentered()) { - rb_memerror(); - } - rb_bug("object allocation during garbage collection phase"); - } - - if (ruby_gc_stressful) { - if (!garbage_collect(objspace, GPR_FLAG_NEWOBJ)) { - rb_memerror(); - } - } - } - - obj = newobj_alloc(objspace, cache, heap_idx, true); + obj = newobj_alloc(objspace, gc_cache, heap_idx, true); newobj_init(klass, flags, wb_protected, objspace, obj); } RB_GC_CR_UNLOCK(lev); @@ -2714,20 +2817,20 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_new } NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, - rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx)); + rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx)); NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, - rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx)); + rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx)); static VALUE -newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) +newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx) { - return newobj_slowpath(klass, flags, objspace, cache, TRUE, heap_idx); + return newobj_slowpath(klass, flags, objspace, gc_cache, TRUE, heap_idx); } static VALUE -newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) +newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *gc_cache, size_t heap_idx) { - return newobj_slowpath(klass, flags, objspace, cache, FALSE, heap_idx); + return newobj_slowpath(klass, flags, objspace, gc_cache, FALSE, heap_idx); } VALUE @@ -2748,19 +2851,19 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags size_t heap_idx = heap_idx_for_size(alloc_size); *actual_alloc_size = heap_slot_size((unsigned char)heap_idx); - rb_ractor_newobj_cache_t *cache = (rb_ractor_newobj_cache_t *)cache_ptr; + rb_ractor_newobj_cache_t *gc_cache = (rb_ractor_newobj_cache_t *)cache_ptr; if (!RB_UNLIKELY(during_gc || ruby_gc_stressful) && wb_protected) { - obj = newobj_alloc(objspace, cache, heap_idx, false); + obj = newobj_alloc(objspace, gc_cache, heap_idx, false); newobj_init(klass, flags, wb_protected, objspace, obj); } else { RB_DEBUG_COUNTER_INC(obj_newobj_slowpath); obj = wb_protected ? - newobj_slowpath_wb_protected(klass, flags, objspace, cache, heap_idx) : - newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, heap_idx); + newobj_slowpath_wb_protected(klass, flags, objspace, gc_cache, heap_idx) : + newobj_slowpath_wb_unprotected(klass, flags, objspace, gc_cache, heap_idx); } return obj; @@ -3481,7 +3584,6 @@ heap_page_alloc_slot_from_region(struct heap_page *free_page) asan_unlock_freelist(free_page); if (new_start < region_end) { - /* Shrink the region: rewrite its header one slot forward. */ VALUE next_start = (VALUE)new_start; rb_asan_unpoison_object(next_start, false); struct free_region *new_region = (struct free_region *)new_start; @@ -3492,7 +3594,6 @@ heap_page_alloc_slot_from_region(struct heap_page *free_page) free_page->free_region = new_region; } else { - /* The region held a single slot. */ free_page->free_region = next; } asan_lock_freelist(free_page); @@ -3517,7 +3618,6 @@ try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, uintptr_t dest_slot = heap_page_alloc_slot_from_region(free_page); if (dest_slot == 0) { - /* if we can't get a slot then the page must be full */ return false; } VALUE dest = (VALUE)dest_slot; @@ -4009,16 +4109,17 @@ gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode) } static void -heap_page_flush_cache_regions(struct heap_page *page, rb_ractor_newobj_heap_cache_t *heap_cache) +heap_page_flush_cache_regions(struct heap_page *page, struct gc_bump_pointer_heap *bump, + rb_ractor_newobj_heap_cache_t *heap_cache) { struct free_region *chain = heap_cache->next_region; - if (heap_cache->cursor < heap_cache->cursor_end) { - VALUE start = (VALUE)heap_cache->cursor; + if (bump->cursor < heap_cache->region_end) { + VALUE start = (VALUE)bump->cursor; rb_asan_unpoison_object(start, false); struct free_region *remnant = (struct free_region *)start; remnant->flags = 0; - remnant->end = heap_cache->cursor_end; + remnant->end = heap_cache->region_end; remnant->next = chain; rb_asan_poison_object(start); chain = remnant; @@ -4075,30 +4176,45 @@ static int compare_pinned_slots(const void *left, const void *right, void *d); static void gc_ractor_newobj_cache_clear(void *c, void *data) { - rb_objspace_t *objspace = rb_gc_get_objspace(); - rb_ractor_newobj_cache_t *newobj_cache = c; + rb_objspace_t *objspace = data; + rb_ractor_newobj_cache_t *gc_cache = c; + rb_ractor_newobj_cache_t *newobj_cache = gc_cache; newobj_cache->incremental_mark_step_allocated_slots = 0; for (size_t heap_idx = 0; heap_idx < HEAP_COUNT; heap_idx++) { - + struct gc_bump_pointer_heap *bump = &gc_cache->bump_heaps[heap_idx]; rb_ractor_newobj_heap_cache_t *cache = &newobj_cache->heap_caches[heap_idx]; rb_heap_t *heap = &heaps[heap_idx]; - RUBY_ATOMIC_SIZE_ADD(heap->total_allocated_objects, cache->allocated_objects_count); - cache->allocated_objects_count = 0; + gc_bump_flush_alloc_count(bump, heap); struct heap_page *page = cache->using_page; - RUBY_DEBUG_LOG("ractor using_page:%p cursor:%p", (void *)page, (void *)cache->cursor); + RUBY_DEBUG_LOG("ractor using_page:%p cursor:%p", (void *)page, (void *)bump->cursor); if (page) { - heap_page_flush_cache_regions(page, cache); + heap_page_flush_cache_regions(page, bump, cache); } cache->using_page = NULL; - cache->cursor = 0; - cache->cursor_end = 0; cache->next_region = NULL; + cache->region_end = 0; + bump->cursor = 0; + bump->cursor_end = 0; + bump->jit_cursor_end = 0; + bump->region_start = 0; + } +} + +static void +gc_ractor_newobj_cache_exhaust(void *c, void *data) +{ + rb_ractor_newobj_cache_t *gc_cache = c; + + for (size_t heap_idx = 0; heap_idx < HEAP_COUNT; heap_idx++) { + struct gc_bump_pointer_heap *bump = &gc_cache->bump_heaps[heap_idx]; + bump->cursor_end = bump->cursor; + bump->jit_cursor_end = bump->cursor; } } @@ -4189,7 +4305,7 @@ gc_sweep_start(rb_objspace_t *objspace) } } - rb_gc_ractor_newobj_cache_foreach(gc_ractor_newobj_cache_clear, NULL); + rb_gc_ractor_newobj_cache_foreach(gc_ractor_newobj_cache_clear, objspace); } static void @@ -6241,6 +6357,10 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark) mark_roots(objspace, NULL); + if (is_incremental_marking(objspace)) { + rb_gc_ractor_newobj_cache_foreach(gc_ractor_newobj_cache_exhaust, NULL); + } + gc_report(1, objspace, "gc_marks_start: (%s) end, stack in %"PRIdSIZE"\n", full_mark ? "full" : "minor", mark_stack_size(&objspace->mark_stack)); } @@ -6698,17 +6818,25 @@ rb_gc_impl_ractor_cache_alloc(void *objspace_ptr, void *ractor) objspace->live_ractor_cache_count++; - return calloc1(sizeof(rb_ractor_newobj_cache_t)); + rb_ractor_newobj_cache_t *gc_cache = + calloc1(sizeof(rb_ractor_newobj_cache_t) + + sizeof(struct gc_bump_pointer_heap) * (HEAP_COUNT + 1)); + + for (size_t i = 0; i < HEAP_COUNT; i++) { + gc_cache->bump_heaps[i].slot_size = pool_slot_sizes[i]; + } + return gc_cache; } void -rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache) +rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache_ptr) { rb_objspace_t *objspace = objspace_ptr; + rb_ractor_newobj_cache_t *gc_cache = cache_ptr; objspace->live_ractor_cache_count--; - gc_ractor_newobj_cache_clear(cache, NULL); - free(cache); + gc_ractor_newobj_cache_clear(gc_cache, objspace); + free(gc_cache); } static void @@ -7245,6 +7373,8 @@ gc_sweeping_enter(rb_objspace_t *objspace) if (MEASURE_GC) { gc_clock_start(&objspace->profile.sweeping_start_time); } + + rb_gc_initialize_vm_context(&objspace->vm_context); } static void @@ -8334,6 +8464,10 @@ rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag) objspace->flags.gc_stressful = RTEST(flag); objspace->gc_stress_mode = flag; + + if (objspace->flags.gc_stressful) { + rb_gc_ractor_newobj_cache_foreach(gc_ractor_newobj_cache_exhaust, NULL); + } } static int @@ -10204,7 +10338,7 @@ rb_gc_impl_after_fork(void *objspace_ptr, rb_pid_t pid) objspace->fork_vm_lock_lev = 0; if (pid == 0) { /* child process */ - rb_gc_ractor_newobj_cache_foreach(gc_ractor_newobj_cache_clear, NULL); + rb_gc_ractor_newobj_cache_foreach(gc_ractor_newobj_cache_clear, objspace); } } diff --git a/gc/default/zjit_fastpath.h b/gc/default/zjit_fastpath.h new file mode 100644 index 00000000000000..08dd00928f50f3 --- /dev/null +++ b/gc/default/zjit_fastpath.h @@ -0,0 +1,21 @@ +#ifndef RUBY_GC_DEFAULT_ZJIT_FASTPATH_H +#define RUBY_GC_DEFAULT_ZJIT_FASTPATH_H + +#include + +#include "gc/gc_impl.h" +#include "ruby/internal/static_assert.h" +#include "ruby/ruby.h" + +struct rb_gc_zjit_default_new_obj_fastpath { + size_t cursor_offset; + size_t jit_cursor_end_offset; + size_t slot_size; + VALUE flags; + VALUE klass; +}; + +RBIMPL_STATIC_ASSERT(zjit_default_fastpath_fits, + sizeof(struct rb_gc_zjit_default_new_obj_fastpath) <= sizeof(union rb_gc_zjit_fastpath_data)); + +#endif /* RUBY_GC_DEFAULT_ZJIT_FASTPATH_H */ diff --git a/gc/gc_impl.h b/gc/gc_impl.h index 5a8d3e2965a2f2..5aa8fee1503a79 100644 --- a/gc/gc_impl.h +++ b/gc/gc_impl.h @@ -10,6 +10,33 @@ */ #include "ruby/ruby.h" +#include +#include + +struct gc_bump_pointer_heap { + uintptr_t cursor; + uintptr_t cursor_end; + uintptr_t jit_cursor_end; + uintptr_t region_start; + size_t slot_size; +}; + +enum rb_gc_zjit_fastpath_kind { + RB_GC_ZJIT_FASTPATH_DEFAULT = 1, + RB_GC_ZJIT_FASTPATH_MMTK = 2, +}; + +#define RB_GC_ZJIT_FASTPATH_DATA_WORDS 19 + +union rb_gc_zjit_fastpath_data { + uintptr_t words[RB_GC_ZJIT_FASTPATH_DATA_WORDS]; +}; + +struct rb_gc_zjit_fastpath { + enum rb_gc_zjit_fastpath_kind kind; + union rb_gc_zjit_fastpath_data data; +}; + #ifndef RB_GC_OBJECT_METADATA_ENTRY_DEFINED # define RB_GC_OBJECT_METADATA_ENTRY_DEFINED struct rb_gc_object_metadata_entry { @@ -56,6 +83,16 @@ GC_IMPL_FN void rb_gc_impl_config_set(void *objspace_ptr, VALUE hash); GC_IMPL_FN struct rb_gc_vm_context *rb_gc_impl_get_vm_context(void *objspace_ptr); // Object allocation GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size, size_t *actual_alloc_size); +/* This is an (optional) function that allows the GC implementation to return + * metadata for ZJIT's fast path object allocator. Returns `true` if ZJIT can + * use the fast path allocator, `false` otherwise. + * + * The GC is provided `alloc_size`, `flags`, and `class` which describe the + * object to be allocated. The GC can use this information to perform + * precomputation and fill `fastpath` with GC-specific metadata. ZJIT owns the + * generated instruction sequence; see zjit/src/gc_fastpath.rs. + */ +GC_IMPL_FN bool rb_gc_impl_zjit_new_obj_fastpath(void *objspace_ptr, size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath); GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj); GC_IMPL_FN size_t rb_gc_impl_size_slot_size(void *objspace_ptr, size_t size); GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size); diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 14369b8bc33a8c..6c43c89b5952f0 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -1,5 +1,6 @@ #include #include +#include #include "ruby/assert.h" #include "ruby/atomic.h" @@ -9,6 +10,10 @@ #include "gc/gc_impl.h" #include "gc/mmtk/mmtk.h" +#if USE_ZJIT +# include "gc/mmtk/zjit_fastpath.h" +#endif + #include "ccan/list/list.h" #include "darray.h" @@ -104,6 +109,8 @@ RB_THREAD_LOCAL_SPECIFIER VALUE marking_parent_object; #include +#define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0 + static inline VALUE rb_mmtk_call_object_closure(VALUE obj, bool pin); static void @@ -643,6 +650,20 @@ rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache_ptr) objspace->live_ractor_cache_count--; mmtk_destroy_mutator(cache->mutator); + + free(cache); +} + +static bool +zjit_mmtk_gc_stress_p(void *objspace_ptr) +{ + return ((struct objspace *)objspace_ptr)->gc_stress; +} + +static bool +zjit_mmtk_newobj_tracing_p(void) +{ + return rb_gc_event_hook_required_p(RUBY_INTERNAL_EVENT_NEWOBJ); } void rb_gc_impl_set_params(void *objspace_ptr) { } @@ -663,6 +684,59 @@ static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = { }; #endif +bool +rb_gc_impl_zjit_new_obj_fastpath(void *objspace_ptr, size_t alloc_size, VALUE flags, VALUE klass, + struct rb_gc_zjit_fastpath *fastpath) +{ +#if USE_ZJIT && RB_GC_OBJ_SUFFIX_SIZE == 0 + struct objspace *objspace = objspace_ptr; + + size_t payload_size = alloc_size; + for (int i = 0; i < MMTK_HEAP_COUNT; i++) { + if (payload_size == heap_sizes[i]) break; + if (payload_size < heap_sizes[i]) { + payload_size = heap_sizes[i]; + break; + } + } + + if (payload_size > MMTK_MAX_OBJ_SIZE) return false; + + size_t total_size = payload_size + sizeof(VALUE) + RB_GC_OBJ_SUFFIX_SIZE; + size_t value_size_shift = sizeof(VALUE) == 8 ? 3 : 2; + + struct rb_gc_zjit_mmtk_new_obj_fastpath mmtk_fastpath = { + objspace, + offsetof(struct objspace, total_allocated_objects), + offsetof(struct MMTk_ractor_cache, mutator), + offsetof(struct MMTk_ractor_cache, bump_pointer), + offsetof(struct MMTk_ractor_cache, obj_free_parallel_buf), + offsetof(struct MMTk_ractor_cache, obj_free_parallel_count), + offsetof(MMTk_BumpPointer, cursor), + offsetof(MMTk_BumpPointer, limit), + MMTk_MIN_OBJ_ALIGN, + payload_size, + total_size, + MMTK_ALLOCATION_SEMANTICS_DEFAULT, + (uintptr_t)zjit_mmtk_gc_stress_p, + (uintptr_t)zjit_mmtk_newobj_tracing_p, + (uintptr_t)mmtk_post_alloc, + OBJ_FREE_BUF_CAPACITY - 1, + value_size_shift, + flags, + klass + }; + + memset(fastpath, 0, sizeof(*fastpath)); + fastpath->kind = RB_GC_ZJIT_FASTPATH_MMTK; + memcpy(fastpath->data.words, &mmtk_fastpath, sizeof(mmtk_fastpath)); + + return true; +#else + return false; +#endif +} + void rb_gc_impl_init(void) { @@ -898,7 +972,6 @@ mmtk_post_alloc_fast_immix(struct objspace *objspace, struct MMTk_ractor_cache * VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size, size_t *actual_alloc_size) { -#define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0 struct objspace *objspace = objspace_ptr; struct MMTk_ractor_cache *ractor_cache = cache_ptr; diff --git a/gc/mmtk/zjit_fastpath.h b/gc/mmtk/zjit_fastpath.h new file mode 100644 index 00000000000000..b2a106ba19cd55 --- /dev/null +++ b/gc/mmtk/zjit_fastpath.h @@ -0,0 +1,36 @@ +#ifndef RUBY_GC_MMTK_ZJIT_FASTPATH_H +#define RUBY_GC_MMTK_ZJIT_FASTPATH_H + +#include +#include + +#include "gc/gc_impl.h" +#include "ruby/internal/static_assert.h" +#include "ruby/ruby.h" + +struct rb_gc_zjit_mmtk_new_obj_fastpath { + const void *objspace; + size_t objspace_total_allocated_objects_offset; + size_t ractor_cache_mutator_offset; + size_t ractor_cache_bump_pointer_offset; + size_t ractor_cache_obj_free_parallel_buf_offset; + size_t ractor_cache_obj_free_parallel_count_offset; + size_t bump_pointer_cursor_offset; + size_t bump_pointer_limit_offset; + size_t min_obj_align; + size_t payload_size; + size_t total_alloc_size; + uint32_t allocation_semantics_default; + uintptr_t gc_stress_p_func; + uintptr_t newobj_tracing_p_func; + uintptr_t post_alloc_func; + size_t obj_free_buf_capacity_minus_one; + size_t value_size_shift; + VALUE flags; + VALUE klass; +}; + +RBIMPL_STATIC_ASSERT(zjit_mmtk_fastpath_fits, + sizeof(struct rb_gc_zjit_mmtk_new_obj_fastpath) <= sizeof(union rb_gc_zjit_fastpath_data)); + +#endif /* RUBY_GC_MMTK_ZJIT_FASTPATH_H */ diff --git a/gc/wbcheck/wbcheck.c b/gc/wbcheck/wbcheck.c index 622a893a176f3b..86ab19f87ecc5c 100644 --- a/gc/wbcheck/wbcheck.c +++ b/gc/wbcheck/wbcheck.c @@ -498,6 +498,13 @@ rb_gc_impl_ractor_cache_alloc(void *objspace_ptr, void *ractor) return NULL; } +bool +rb_gc_impl_zjit_new_obj_fastpath(void *objspace_ptr, size_t alloc_size, VALUE flags, VALUE klass, + struct rb_gc_zjit_fastpath *fastpath) +{ + return false; +} + void rb_gc_impl_set_params(void *objspace_ptr) { diff --git a/internal/gc.h b/internal/gc.h index 5a2e76d8ef6ad3..8a3dbd1884b4bf 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -16,6 +16,8 @@ #include "ruby/ruby.h" /* for rb_event_flag_t */ #include "vm_core.h" /* for GET_EC() */ +struct rb_gc_zjit_fastpath; + #ifndef USE_MODULAR_GC # define USE_MODULAR_GC 0 #endif @@ -203,6 +205,7 @@ rb_execution_context_t *rb_gc_get_ec(void); void *rb_gc_ractor_cache_alloc(rb_ractor_t *ractor); void rb_gc_ractor_cache_free(void *cache); +bool rb_gc_zjit_new_obj_fastpath(size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath); bool rb_gc_size_allocatable_p(size_t size); size_t rb_gc_size_slot_size(size_t size); diff --git a/zjit.c b/zjit.c index 082a9038066723..2953a694fd3c83 100644 --- a/zjit.c +++ b/zjit.c @@ -23,6 +23,7 @@ #include "iseq.h" #include "ruby/debug.h" #include "internal/cont.h" +#include "ractor_core.h" // This build config impacts the pointer tagging scheme and we only want to // support one scheme for simplicity. @@ -30,7 +31,8 @@ STATIC_ASSERT(pointer_tagging_scheme, USE_FLONUM); enum zjit_struct_offsets { ISEQ_BODY_OFFSET_PARAM = offsetof(struct rb_iseq_constant_body, param), - ISEQ_BODY_OFFSET_OUTER_VARIABLES = offsetof(struct rb_iseq_constant_body, outer_variables) + ISEQ_BODY_OFFSET_OUTER_VARIABLES = offsetof(struct rb_iseq_constant_body, outer_variables), + RUBY_OFFSET_THREAD_RACTOR = offsetof(rb_thread_t, ractor), }; // Special JITFrame used by all C method calls. We don't control the native @@ -166,6 +168,12 @@ rb_zjit_singleton_class_p(VALUE klass) return RCLASS_SINGLETON_P(klass); } +size_t +rb_zjit_offset_ractor_newobj_cache(void) +{ + return offsetof(rb_ractor_t, newobj_cache); +} + VALUE rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval) { diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 55f784bd03965e..4bad82b4e1e9c8 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -53,6 +53,10 @@ fn main() { .header(src_root.join(c_file).to_str().unwrap()) .header(src_root.join("jit.c").to_str().unwrap()) + .header(src_root.join("gc/gc_impl.h").to_str().unwrap()) + .header(src_root.join("gc/default/zjit_fastpath.h").to_str().unwrap()) + .header(src_root.join("gc/mmtk/zjit_fastpath.h").to_str().unwrap()) + // Don't want to copy over C comment .generate_comments(false) @@ -86,6 +90,15 @@ fn main() { // This struct is public to Ruby C extensions .allowlist_type("RBasic") + .allowlist_type("RArray") + .allowlist_type("gc_bump_pointer_heap") + .allowlist_type("rb_gc_zjit_fastpath_kind") + .allowlist_type("rb_gc_zjit_fastpath") + .allowlist_type("rb_gc_zjit_fastpath_data") + .allowlist_type("rb_gc_zjit_default_new_obj_fastpath") + .allowlist_type("rb_gc_zjit_mmtk_new_obj_fastpath") + .allowlist_var("RB_GC_ZJIT_FASTPATH_.*") + .allowlist_type("ruby_rstring_flags") // This function prints info about a value and is useful for debugging diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 511fa9d34c2a13..4df06236bbc0b3 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -2,6 +2,8 @@ #![allow(clippy::let_and_return)] +mod gc_fastpath; + use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::ffi::{c_int, c_long, c_void}; @@ -1965,7 +1967,7 @@ fn gen_array_dup( /// Compile a new array instruction fn gen_new_array( - jit: &JITState, + jit: &mut JITState, asm: &mut Assembler, elements: Vec, state: &FrameState, @@ -1974,12 +1976,19 @@ fn gen_new_array( let num: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long"); - if elements.is_empty() { - asm_ccall!(asm, rb_ec_ary_new_from_values, EC, 0i64.into(), Opnd::UImm(0)) - } else { + if !elements.is_empty() { let argv = gen_push_opnds(jit, asm, &elements); - asm_ccall!(asm, rb_ec_ary_new_from_values, EC, num.into(), argv) + return asm_ccall!(asm, rb_ec_ary_new_from_values, EC, num.into(), argv); } + + let alloc_size = std::mem::size_of::(); + + let flags = (RUBY_T_ARRAY as u64) | (RARRAY_EMBED_FLAG as u64); + let klass = unsafe { rb_cArray }; + + gc_fastpath::gc_fast_path_new_obj(jit, asm, alloc_size, flags, klass, |asm| { + asm_ccall!(asm, rb_ec_ary_new_from_values, EC, 0i64.into(), Opnd::UImm(0)) + }) } /// Adjust potentially-negative index by the given length, returning the adjusted index. If still negative, diff --git a/zjit/src/codegen/gc_fastpath.rs b/zjit/src/codegen/gc_fastpath.rs new file mode 100644 index 00000000000000..ad109440d14d8b --- /dev/null +++ b/zjit/src/codegen/gc_fastpath.rs @@ -0,0 +1,367 @@ +use std::ffi::c_void; + +use crate::backend::lir::{self, Assembler, EC, Opnd, Target, asm_comment}; +use crate::cruby::{ + RB_GC_ZJIT_FASTPATH_DEFAULT, RB_GC_ZJIT_FASTPATH_MMTK, + RUBY_OFFSET_EC_THREAD_PTR, RUBY_OFFSET_RBASIC_FLAGS, RUBY_OFFSET_RBASIC_KLASS, + RUBY_OFFSET_THREAD_RACTOR, VALUE, VALUE_BITS, rb_zjit_offset_ractor_newobj_cache, +}; +use super::JITState; + +#[repr(C)] +#[derive(Clone, Copy)] +struct RbGcZjitDefaultNewObjFastpath { + cursor_offset: usize, + jit_cursor_end_offset: usize, + slot_size: usize, + flags: VALUE, + klass: VALUE, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct RbGcZjitMmtkNewObjFastpath { + objspace: *const c_void, + objspace_total_allocated_objects_offset: usize, + ractor_cache_mutator_offset: usize, + ractor_cache_bump_pointer_offset: usize, + ractor_cache_obj_free_parallel_buf_offset: usize, + ractor_cache_obj_free_parallel_count_offset: usize, + bump_pointer_cursor_offset: usize, + bump_pointer_limit_offset: usize, + min_obj_align: usize, + payload_size: usize, + total_alloc_size: usize, + allocation_semantics_default: u32, + gc_stress_p_func: usize, + newobj_tracing_p_func: usize, + post_alloc_func: usize, + obj_free_buf_capacity_minus_one: usize, + value_size_shift: usize, + flags: VALUE, + klass: VALUE, +} + +#[repr(C)] +union RbGcZjitFastpathData { + default_gc: RbGcZjitDefaultNewObjFastpath, + mmtk: RbGcZjitMmtkNewObjFastpath, +} + +#[repr(C)] +struct RbGcZjitFastpath { + kind: u32, + data: RbGcZjitFastpathData, +} + +unsafe extern "C" { + fn rb_gc_zjit_new_obj_fastpath( + alloc_size: usize, + flags: VALUE, + klass: VALUE, + fastpath: *mut RbGcZjitFastpath, + ) -> bool; +} + +enum PreparedNewObjFastpath { + Default(RbGcZjitDefaultNewObjFastpath), + Mmtk(RbGcZjitMmtkNewObjFastpath), +} + +pub(super) fn gc_fast_path_new_obj( + jit: &mut JITState, + asm: &mut Assembler, + alloc_size: usize, + flags: u64, + klass: VALUE, + slow_path: impl Fn(&mut Assembler) -> lir::Opnd, +) -> lir::Opnd { + let Some(fastpath) = prepare_new_obj_fastpath(alloc_size, flags, klass) else { + return slow_path(asm); + }; + + asm_comment!(asm, "GC inline allocation"); + + let hir_block_id = asm.current_block().hir_block_id; + let rpo_idx = asm.current_block().rpo_index; + + let result_block = asm.new_block(hir_block_id, false, rpo_idx); + let miss_block = asm.new_block(hir_block_id, false, rpo_idx); + + let result_edge = |v: Opnd| Target::Block(Box::new(lir::BranchEdge { target: result_block, args: vec![v] })); + + let obj = emit_new_obj_fastpath(jit, asm, &fastpath, miss_block) + .expect("validated GC fastpath must return an object"); + asm.jmp(result_edge(obj)); + + asm.set_current_block(miss_block); + let label = jit.get_label(asm, miss_block, hir_block_id); + asm.write_label(label); + let obj = slow_path(asm); + asm.jmp(result_edge(obj)); + + asm.set_current_block(result_block); + let label = jit.get_label(asm, result_block, hir_block_id); + asm.write_label(label); + let param = asm.new_block_param(VALUE_BITS); + asm.current_block().add_parameter(param); + param +} + +fn prepare_new_obj_fastpath(alloc_size: usize, flags: u64, klass: VALUE) -> Option { + let mut fastpath: RbGcZjitFastpath = unsafe { std::mem::zeroed() }; + let has_fastpath = unsafe { + rb_gc_zjit_new_obj_fastpath(alloc_size, VALUE(flags as usize), klass, &mut fastpath) + }; + + if !has_fastpath { + return None; + } + + match fastpath.kind { + RB_GC_ZJIT_FASTPATH_DEFAULT => { + let fastpath = unsafe { fastpath.data.default_gc }; + Some(PreparedNewObjFastpath::Default(fastpath)) + } + RB_GC_ZJIT_FASTPATH_MMTK => { + let fastpath = unsafe { fastpath.data.mmtk }; + if fastpath.objspace.is_null() + || fastpath.gc_stress_p_func == 0 + || fastpath.newobj_tracing_p_func == 0 + || fastpath.post_alloc_func == 0 + || fastpath.min_obj_align == 0 + || !fastpath.min_obj_align.is_power_of_two() + { + return None; + } + + Some(PreparedNewObjFastpath::Mmtk(fastpath)) + } + _ => None, + } +} + +fn emit_new_obj_fastpath( + jit: &mut JITState, + asm: &mut Assembler, + prepared: &PreparedNewObjFastpath, + miss_block: lir::BlockId, +) -> Option { + let miss = Target::Block(Box::new(lir::BranchEdge { + target: miss_block, + args: vec![], + })); + + match prepared { + PreparedNewObjFastpath::Default(fastpath) => { + emit_default_new_obj_fastpath(jit, asm, fastpath, &miss) + } + PreparedNewObjFastpath::Mmtk(fastpath) => { + emit_mmtk_new_obj_fastpath(jit, asm, fastpath, &miss) + } + } +} + +/* This function implements the GC fast path for the default GC. It implements + * the fast path defined in function ractor_cache_allocate_slot (but not the + * medium path in ractor_cache_advance_region). It also implements newobj_init + * to write the flags and klass in the object. */ +fn emit_default_new_obj_fastpath( + jit: &mut JITState, + asm: &mut Assembler, + fastpath: &RbGcZjitDefaultNewObjFastpath, + miss: &Target, +) -> Option { + let cursor_offset: i32 = fastpath.cursor_offset.try_into().ok()?; + let jit_cursor_end_offset: i32 = fastpath.jit_cursor_end_offset.try_into().ok()?; + let slot_size: u64 = fastpath.slot_size.try_into().ok()?; + + let thread = asm.load(Opnd::mem(64, EC, RUBY_OFFSET_EC_THREAD_PTR as i32)); + let ractor = asm.load(Opnd::mem(64, thread, RUBY_OFFSET_THREAD_RACTOR as i32)); + let ractor_newobj_cache_offset: i32 = unsafe { rb_zjit_offset_ractor_newobj_cache() } + .try_into() + .expect("ractor newobj cache offset fits in i32"); + let gc_cache = asm.load(Opnd::mem(64, ractor, ractor_newobj_cache_offset)); + + let cursor = asm.load(Opnd::mem(64, gc_cache, cursor_offset)); + let cursor_end = asm.load(Opnd::mem(64, gc_cache, jit_cursor_end_offset)); + + let new_cursor = asm.add(cursor, Opnd::UImm(slot_size)); + asm.cmp(cursor_end, new_cursor); + asm.jl(jit, miss.clone()); + + asm.store(Opnd::mem(64, gc_cache, cursor_offset), new_cursor); + asm.store( + Opnd::mem(VALUE_BITS, cursor, RUBY_OFFSET_RBASIC_FLAGS), + fastpath.flags.into(), + ); + asm.store( + Opnd::mem(VALUE_BITS, cursor, RUBY_OFFSET_RBASIC_KLASS), + fastpath.klass.into(), + ); + + Some(cursor) +} + +/* This function implements the GC fast path for MMTk. It implements the fast + * path defined in function rb_mmtk_alloc_fast_path, as well as writing the + * flags and klass into the object. */ +fn emit_mmtk_new_obj_fastpath( + jit: &mut JITState, + asm: &mut Assembler, + fastpath: &RbGcZjitMmtkNewObjFastpath, + miss: &Target, +) -> Option { + let objspace_total_allocated_objects_offset: i32 = fastpath + .objspace_total_allocated_objects_offset + .try_into() + .ok()?; + let ractor_cache_mutator_offset: i32 = fastpath.ractor_cache_mutator_offset.try_into().ok()?; + let ractor_cache_bump_pointer_offset: i32 = fastpath + .ractor_cache_bump_pointer_offset + .try_into() + .ok()?; + let ractor_cache_obj_free_parallel_buf_offset: u64 = fastpath + .ractor_cache_obj_free_parallel_buf_offset + .try_into() + .ok()?; + let ractor_cache_obj_free_parallel_count_offset: i32 = fastpath + .ractor_cache_obj_free_parallel_count_offset + .try_into() + .ok()?; + let bump_pointer_cursor_offset: i32 = fastpath.bump_pointer_cursor_offset.try_into().ok()?; + let bump_pointer_limit_offset: i32 = fastpath.bump_pointer_limit_offset.try_into().ok()?; + let payload_size: u64 = fastpath.payload_size.try_into().ok()?; + let total_alloc_size: u64 = fastpath.total_alloc_size.try_into().ok()?; + let obj_free_buf_capacity_minus_one: u64 = fastpath + .obj_free_buf_capacity_minus_one + .try_into() + .ok()?; + let value_size_shift: u64 = fastpath.value_size_shift.try_into().ok()?; + let newobj_tracing_p_func = (fastpath.newobj_tracing_p_func != 0) + .then_some(fastpath.newobj_tracing_p_func as *const u8)?; + let gc_stress_p_func = (fastpath.gc_stress_p_func != 0) + .then_some(fastpath.gc_stress_p_func as *const u8)?; + let post_alloc_func = (fastpath.post_alloc_func != 0) + .then_some(fastpath.post_alloc_func as *const u8)?; + + let event_hook = asm.ccall(newobj_tracing_p_func, vec![]); + asm.test(event_hook, event_hook); + asm.jnz(jit, miss.clone()); + + let objspace_const = Opnd::const_ptr(fastpath.objspace); + let gc_stress = asm.ccall(gc_stress_p_func, vec![objspace_const]); + asm.test(gc_stress, gc_stress); + asm.jnz(jit, miss.clone()); + + let objspace = asm.load(objspace_const); + let thread = asm.load(Opnd::mem(64, EC, RUBY_OFFSET_EC_THREAD_PTR as i32)); + let ractor = asm.load(Opnd::mem(64, thread, RUBY_OFFSET_THREAD_RACTOR as i32)); + let ractor_newobj_cache_offset: i32 = unsafe { rb_zjit_offset_ractor_newobj_cache() } + .try_into() + .expect("ractor newobj cache offset fits in i32"); + let ractor_cache = asm.load(Opnd::mem(64, ractor, ractor_newobj_cache_offset)); + + let bump_pointer = asm.load(Opnd::mem( + 64, + ractor_cache, + ractor_cache_bump_pointer_offset, + )); + asm.test(bump_pointer, bump_pointer); + asm.jz(jit, miss.clone()); + + let obj_free_count = asm.load(Opnd::mem( + 64, + ractor_cache, + ractor_cache_obj_free_parallel_count_offset, + )); + asm.cmp(obj_free_count, Opnd::UImm(obj_free_buf_capacity_minus_one)); + asm.jge(jit, miss.clone()); + + let cursor = asm.load(Opnd::mem( + 64, + bump_pointer, + bump_pointer_cursor_offset, + )); + let align_mask: i64 = (fastpath.min_obj_align - 1).try_into().ok()?; + let adjusted = asm.add(cursor, Opnd::UImm(align_mask as u64)); + let aligned = asm.and(adjusted, Opnd::Imm(!align_mask)); + let new_cursor = asm.add(aligned, Opnd::UImm(total_alloc_size)); + let limit = asm.load(Opnd::mem( + 64, + bump_pointer, + bump_pointer_limit_offset, + )); + asm.cmp(limit, new_cursor); + asm.jl(jit, miss.clone()); + + asm.store( + Opnd::mem( + 64, + bump_pointer, + bump_pointer_cursor_offset, + ), + new_cursor, + ); + + let value_size: u64 = std::mem::size_of::().try_into().ok()?; + let obj = asm.add(aligned, Opnd::UImm(value_size)); + asm.store(Opnd::mem(VALUE_BITS, aligned, 0), Opnd::UImm(payload_size)); + asm.store( + Opnd::mem(VALUE_BITS, obj, RUBY_OFFSET_RBASIC_FLAGS), + fastpath.flags.into(), + ); + asm.store( + Opnd::mem(VALUE_BITS, obj, RUBY_OFFSET_RBASIC_KLASS), + fastpath.klass.into(), + ); + + let mutator = asm.load(Opnd::mem( + 64, + ractor_cache, + ractor_cache_mutator_offset, + )); + asm.ccall( + post_alloc_func, + vec![ + mutator, + obj, + Opnd::UImm(total_alloc_size), + Opnd::UImm(u64::from(fastpath.allocation_semantics_default)), + ], + ); + + let obj_free_index = asm.lshift(obj_free_count, Opnd::UImm(value_size_shift)); + let obj_free_buf = asm.add( + ractor_cache, + Opnd::UImm(ractor_cache_obj_free_parallel_buf_offset), + ); + let obj_free_slot = asm.add(obj_free_buf, obj_free_index); + asm.store(Opnd::mem(64, obj_free_slot, 0), obj); + let new_obj_free_count = asm.add(obj_free_count, Opnd::UImm(1)); + asm.store( + Opnd::mem( + 64, + ractor_cache, + ractor_cache_obj_free_parallel_count_offset, + ), + new_obj_free_count, + ); + + let total_allocated_objects = asm.load(Opnd::mem( + 64, + objspace, + objspace_total_allocated_objects_offset, + )); + let new_total_allocated_objects = asm.add(total_allocated_objects, Opnd::UImm(1)); + asm.store( + Opnd::mem( + 64, + objspace, + objspace_total_allocated_objects_offset, + ), + new_total_allocated_objects, + ); + + Some(obj) +} diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index b798fe45d8bbf9..cc5bc35f8c8d10 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -122,6 +122,8 @@ unsafe extern "C" { ci: *const rb_callinfo, ) -> *const rb_callable_method_entry_t; + pub fn rb_zjit_offset_ractor_newobj_cache() -> usize; + // Floats within range will be encoded without creating objects in the heap. // (Range is 0x3000000000000001 to 0x4fffffffffffffff (1.7272337110188893E-77 to 2.3158417847463237E+77). pub fn rb_float_new(d: f64) -> VALUE; diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index a2f71ef8fb6f6f..69e5a2b6fe0e01 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -237,6 +237,7 @@ pub const SHAPE_ID_NUM_BITS: u32 = 32; pub const ZJIT_STACK_MAP_VREG_TAG: u32 = 8; pub const ZJIT_STACK_MAP_SHIFT: u32 = 8; pub const ZJIT_JIT_RETURN_C_FRAME: u32 = 1; +pub const RB_GC_ZJIT_FASTPATH_DATA_WORDS: u32 = 19; pub type rb_alloc_func_t = ::std::option::Option VALUE>; pub const RUBY_Qfalse: ruby_special_consts = 0; pub const RUBY_Qnil: ruby_special_consts = 4; @@ -340,6 +341,29 @@ pub const RARRAY_EMBED_LEN_MASK: ruby_rarray_flags = 4161536; pub type ruby_rarray_flags = u32; pub const RARRAY_EMBED_LEN_SHIFT: ruby_rarray_consts = 15; pub type ruby_rarray_consts = u32; +#[repr(C)] +pub struct RArray { + pub basic: RBasic, + pub as_: RArray__bindgen_ty_1, +} +#[repr(C)] +pub struct RArray__bindgen_ty_1 { + pub heap: __BindgenUnionField, + pub ary: __BindgenUnionField<[VALUE; 1usize]>, + pub bindgen_union_field: [u64; 3usize], +} +#[repr(C)] +pub struct RArray__bindgen_ty_1__bindgen_ty_1 { + pub len: ::std::os::raw::c_long, + pub aux: RArray__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1, + pub ptr: *const VALUE, +} +#[repr(C)] +pub struct RArray__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 { + pub capa: __BindgenUnionField<::std::os::raw::c_long>, + pub shared_root: __BindgenUnionField, + pub bindgen_union_field: u64, +} pub const RMODULE_IS_REFINEMENT: ruby_rmodule_flags = 8192; pub type ruby_rmodule_flags = u32; pub const ROBJECT_HEAP: ruby_robject_flags = 65536; @@ -1925,6 +1949,7 @@ pub struct zjit_jit_frame { } pub const ISEQ_BODY_OFFSET_PARAM: zjit_struct_offsets = 16; pub const ISEQ_BODY_OFFSET_OUTER_VARIABLES: zjit_struct_offsets = 288; +pub const RUBY_OFFSET_THREAD_RACTOR: zjit_struct_offsets = 24; pub type zjit_struct_offsets = u32; pub const ROBJECT_OFFSET_AS_HEAP_FIELDS: jit_bindgen_constants = 16; pub const ROBJECT_OFFSET_AS_ARY: jit_bindgen_constants = 16; @@ -1943,6 +1968,59 @@ pub type rb_iseq_param_keyword_struct = rb_iseq_constant_body_rb_iseq_parameters_rb_iseq_param_keyword; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct gc_bump_pointer_heap { + pub cursor: usize, + pub cursor_end: usize, + pub jit_cursor_end: usize, + pub region_start: usize, + pub slot_size: usize, +} +pub const RB_GC_ZJIT_FASTPATH_DEFAULT: rb_gc_zjit_fastpath_kind = 1; +pub const RB_GC_ZJIT_FASTPATH_MMTK: rb_gc_zjit_fastpath_kind = 2; +pub type rb_gc_zjit_fastpath_kind = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub union rb_gc_zjit_fastpath_data { + pub words: [usize; 19usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct rb_gc_zjit_fastpath { + pub kind: rb_gc_zjit_fastpath_kind, + pub data: rb_gc_zjit_fastpath_data, +} +#[repr(C)] +pub struct rb_gc_zjit_default_new_obj_fastpath { + pub cursor_offset: usize, + pub jit_cursor_end_offset: usize, + pub slot_size: usize, + pub flags: VALUE, + pub klass: VALUE, +} +#[repr(C)] +pub struct rb_gc_zjit_mmtk_new_obj_fastpath { + pub objspace: *const ::std::os::raw::c_void, + pub objspace_total_allocated_objects_offset: usize, + pub ractor_cache_mutator_offset: usize, + pub ractor_cache_bump_pointer_offset: usize, + pub ractor_cache_obj_free_parallel_buf_offset: usize, + pub ractor_cache_obj_free_parallel_count_offset: usize, + pub bump_pointer_cursor_offset: usize, + pub bump_pointer_limit_offset: usize, + pub min_obj_align: usize, + pub payload_size: usize, + pub total_alloc_size: usize, + pub allocation_semantics_default: u32, + pub gc_stress_p_func: usize, + pub newobj_tracing_p_func: usize, + pub post_alloc_func: usize, + pub obj_free_buf_capacity_minus_one: usize, + pub value_size_shift: usize, + pub flags: VALUE, + pub klass: VALUE, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct succ_index_table { pub _address: u8, } From b17078a0de38cbfe3192be3467e7d8faba124f43 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 7 Jul 2026 18:39:52 +0900 Subject: [PATCH 11/20] Suppress unused-function warnings --- gc.c | 1 + process.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/gc.c b/gc.c index c44b808c701ec7..624e3d66104335 100644 --- a/gc.c +++ b/gc.c @@ -3720,6 +3720,7 @@ bool rb_gc_zjit_new_obj_fastpath(size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath) { #if RACTOR_CHECK_MODE || defined(RUBY_ASAN_ENABLED) + (void)rb_gc_impl_zjit_new_obj_fastpath; return false; #else return rb_gc_impl_zjit_new_obj_fastpath(rb_gc_get_objspace(), alloc_size, flags, klass, fastpath); diff --git a/process.c b/process.c index dc7199cba21c94..ebc73ff07ae6bc 100644 --- a/process.c +++ b/process.c @@ -3852,6 +3852,7 @@ getresgid(rb_gid_t *rgid, rb_gid_t *egid, rb_gid_t *sgid) #define HAVE_GETRESGID #endif +#if !defined(RUBY_ASAN_ENABLED) static int has_privilege(void) { @@ -3914,6 +3915,7 @@ has_privilege(void) return 0; } #endif +#endif struct child_handler_disabler_state { From 89e7b08a9fa347e7b9b3d54f656a4e3654198e05 Mon Sep 17 00:00:00 2001 From: Kunshan Wang Date: Tue, 7 Jul 2026 18:07:22 +0800 Subject: [PATCH 12/20] eBPF-based timeline visualization tool (#17190) --- depend | 4 + gc.c | 43 ++++- gc/default/default.c | 17 +- probes.d | 117 +++++++++++++ thread_pthread.c | 7 + tool/timeline/README.md | 214 +++++++++++++++++++++++ tool/timeline/capture.bt | 9 + tool/timeline/capture.rb | 119 +++++++++++++ tool/timeline/lib/converter.rb | 58 +++++++ tool/timeline/lib/converter_defs.rb | 133 +++++++++++++++ tool/timeline/lib/tracepoint.rb | 20 +++ tool/timeline/lib/tracepoint_defs.rb | 105 ++++++++++++ tool/timeline/visualize.rb | 246 +++++++++++++++++++++++++++ vm_sync.c | 9 + 14 files changed, 1095 insertions(+), 6 deletions(-) create mode 100644 tool/timeline/README.md create mode 100644 tool/timeline/capture.bt create mode 100755 tool/timeline/capture.rb create mode 100644 tool/timeline/lib/converter.rb create mode 100644 tool/timeline/lib/converter_defs.rb create mode 100644 tool/timeline/lib/tracepoint.rb create mode 100644 tool/timeline/lib/tracepoint_defs.rb create mode 100755 tool/timeline/visualize.rb diff --git a/depend b/depend index f4a6a4a6f10fd1..b375948348d28a 100644 --- a/depend +++ b/depend @@ -18642,6 +18642,8 @@ thread.$(OBJEXT): {$(VPATH)}onigmo.h thread.$(OBJEXT): {$(VPATH)}oniguruma.h thread.$(OBJEXT): {$(VPATH)}prism_compile.h thread.$(OBJEXT): {$(VPATH)}prism_xallocator.h +thread.$(OBJEXT): {$(VPATH)}probes.dmyh +thread.$(OBJEXT): {$(VPATH)}probes.h thread.$(OBJEXT): {$(VPATH)}ractor.h thread.$(OBJEXT): {$(VPATH)}ractor_core.h thread.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -20710,6 +20712,8 @@ vm_sync.$(OBJEXT): {$(VPATH)}missing.h vm_sync.$(OBJEXT): {$(VPATH)}node.h vm_sync.$(OBJEXT): {$(VPATH)}onigmo.h vm_sync.$(OBJEXT): {$(VPATH)}oniguruma.h +vm_sync.$(OBJEXT): {$(VPATH)}probes.dmyh +vm_sync.$(OBJEXT): {$(VPATH)}probes.h vm_sync.$(OBJEXT): {$(VPATH)}ractor.h vm_sync.$(OBJEXT): {$(VPATH)}ractor_core.h vm_sync.$(OBJEXT): {$(VPATH)}ruby_assert.h diff --git a/gc.c b/gc.c index 624e3d66104335..8266f11e8a4a6e 100644 --- a/gc.c +++ b/gc.c @@ -1054,6 +1054,10 @@ rb_newobj(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape gc_newobj_hook(obj); } + if (RUBY_DTRACE_GC_OBJ_NEW_ENABLED()) { + RUBY_DTRACE_GC_OBJ_NEW((void*)obj, flags); + } + #if RGENGC_CHECK_MODE # ifndef GC_DEBUG_SLOT_FILL_SPECIAL_VALUE # define GC_DEBUG_SLOT_FILL_SPECIAL_VALUE 255 @@ -1539,7 +1543,13 @@ rb_gc_obj_free(void *objspace, VALUE obj) RB_DEBUG_COUNTER_INC(obj_free); - switch (BUILTIN_TYPE(obj)) { + enum ruby_value_type builtin_type = BUILTIN_TYPE(obj); + + if (RUBY_DTRACE_GC_OBJ_FREE_ENABLED()) { + RUBY_DTRACE_GC_OBJ_FREE((void*)obj, RBASIC(obj)->flags); + } + + switch (builtin_type) { case T_NIL: case T_FIXNUM: case T_TRUE: @@ -1550,7 +1560,7 @@ rb_gc_obj_free(void *objspace, VALUE obj) break; } - switch (BUILTIN_TYPE(obj)) { + switch (builtin_type) { case T_OBJECT: if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { if (rb_obj_shape_complex_p(obj)) { @@ -5435,6 +5445,10 @@ static void *ruby_xmalloc_body(size_t size); void * ruby_xmalloc(size_t size) { + if (RUBY_DTRACE_GC_XMALLOC_ENABLED()) { + RUBY_DTRACE_GC_XMALLOC(1, size); + } + return handle_malloc_failure(ruby_xmalloc_body(size)); } @@ -5477,6 +5491,10 @@ static void *ruby_xmalloc2_body(size_t n, size_t size); void * ruby_xmalloc2(size_t n, size_t size) { + if (RUBY_DTRACE_GC_XMALLOC_ENABLED()) { + RUBY_DTRACE_GC_XMALLOC(n, size); + } + return handle_malloc_failure(ruby_xmalloc2_body(n, size)); } @@ -5491,6 +5509,10 @@ static void *ruby_xcalloc_body(size_t n, size_t size); void * ruby_xcalloc(size_t n, size_t size) { + if (RUBY_DTRACE_GC_XCALLOC_ENABLED()) { + RUBY_DTRACE_GC_XCALLOC(n, size); + } + return handle_malloc_failure(ruby_xcalloc_body(n, size)); } @@ -5554,9 +5576,20 @@ ruby_xrealloc2(void *ptr, size_t n, size_t size) #ifdef ruby_xfree_sized #undef ruby_xfree_sized #endif + +static bool g_nofree = false; + void ruby_xfree_sized(void *x, size_t size) { + if (g_nofree) { + return; + } + + if (RUBY_DTRACE_GC_XFREE_ENABLED()) { + RUBY_DTRACE_GC_XFREE(x, size); + } + if (LIKELY(x)) { /* It's possible for a C extension's pthread destructor function set by pthread_key_create * to be called after ruby_vm_destruct and attempt to free memory. Fall back to mimfree in @@ -5820,6 +5853,12 @@ rb_gc_checking_shareable(void) void Init_GC(void) { + const char* nofree_str = getenv("RUBY_NO_FREE"); + if (nofree_str && strcmp(nofree_str, "yes") == 0) { + fprintf(stderr, "WARNING: Enabling no-free mode! xfree() will never free anything!\n"); + g_nofree = true; + } + #undef rb_intern rb_gc_register_address(&id2ref_value); diff --git a/gc/default/default.c b/gc/default/default.c index 7d0eb0ede4e4d5..5cc14e22ddfae2 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -74,6 +74,9 @@ rb_hrtime_sub(rb_hrtime_t a, rb_hrtime_t b) #include "probes.h" +#define RUBY_DTRACE_GC_HOOK(name, ...) \ + do {if (RUBY_DTRACE_GC_##name##_ENABLED()) RUBY_DTRACE_GC_##name(__VA_ARGS__);} while (0) + #if USE_ZJIT # include "gc/default/zjit_fastpath.h" #endif @@ -4414,6 +4417,8 @@ gc_sweep_step(rb_objspace_t *objspace, rb_heap_t *heap) gc_sweep_page(objspace, heap, &ctx); int free_slots = ctx.freed_slots + ctx.empty_slots; + RUBY_DTRACE_GC_HOOK(SWEEP_PAGE, ctx.page->slot_size, ctx.final_slots, ctx.freed_slots, ctx.empty_slots); + heap->sweeping_page = ccan_list_next(&heap->pages, sweep_page, page_node); if (free_slots == sweep_page->total_slots) { @@ -5175,12 +5180,13 @@ gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count) } gc_mark_children(objspace, obj); + popped_count++; + if (incremental) { if (RGENGC_CHECK_MODE && !RVALUE_MARKING(objspace, obj)) { rb_bug("gc_mark_stacked_objects: incremental, but marking bit is 0"); } CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj); - popped_count++; if (popped_count + (objspace->marked_slots - marked_slots_at_the_beginning) > count) { break; @@ -5191,6 +5197,8 @@ gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count) } } + RUBY_DTRACE_GC_HOOK(MARK_STACKED_OBJECTS, popped_count); + if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace); if (is_mark_stack_empty(mstack)) { @@ -7247,6 +7255,8 @@ gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_ { *lock_lev = RB_GC_VM_LOCK(); + RUBY_DTRACE_GC_HOOK(ENTER, event); + if (objspace->profile.run) { switch (event) { case gc_enter_event_start: @@ -7294,6 +7304,8 @@ gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_l { GC_ASSERT(during_gc != 0); + RUBY_DTRACE_GC_HOOK(EXIT, event); + rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_EXIT); if (objspace->profile.gc_pause_start_time) { @@ -9300,9 +9312,6 @@ gc_prof_timer_stop(rb_objspace_t *objspace) } } -#define RUBY_DTRACE_GC_HOOK(name) \ - do {if (RUBY_DTRACE_GC_##name##_ENABLED()) RUBY_DTRACE_GC_##name();} while (0) - static inline void gc_prof_mark_timer_start(rb_objspace_t *objspace) { diff --git a/probes.d b/probes.d index 0762a2d25f3e6d..abf0d50e7f1c26 100644 --- a/probes.d +++ b/probes.d @@ -214,6 +214,123 @@ provider ruby { Fired at the end of a sweep phase. */ probe gc__sweep__end(); + + /* + ruby:::gc-enter(event); + + Fired when the `gc_enter` function in default.c is called. + + * `event` the `event` argument passed to gc_enter + */ + probe gc__enter(int event); + + /* + ruby:::gc-exit(event); + + Fired when the `gc_exit` function in default.c is called. + + * `event` the `event` argument passed to gc_exit + */ + probe gc__exit(int event); + + /* + ruby:::gc-mark-stacked-objects(popped_count); + + Fired for every invocation of `gc_mark_stacked_objects` in default.c. + + * `popped_count` the number of objects popped from the mark stack in that invocation + */ + probe gc__mark_stacked_objects(int popped_count); + + /* + ruby:::gc-sweep_page(slot_size, final_slots, freed_slots, empty_slots); + + Fired for every page swept in `gc_sweep_step` in default.c. + + * `slot_size` is the slot size of the page. + * Other arguments are from the `gc_sweep_context` struct. + */ + probe gc__sweep_page(int slot_size, int final_slots, int freed_slots, int empty_slots); + + /* + ruby:::gc-obj_new(); + + Fired when an object is allocated + + * `obj` the pointer to the allocated object + * `flags` the initial flags of the object + */ + probe gc__obj_new(void *obj, int flags); + + /* + ruby:::gc-obj_free(); + + Fired when finalizing an object with `rb_gc_obj_free`. + + * `obj` the pointer to the finalized object + * `flags` the flags of the object when it is finalized + */ + probe gc__obj_free(void *obj, int flags); + + /* + ruby:::gc-xmalloc(n, size); + + Fired when allocating memory with `ruby_xmalloc` or `ruby_xmalloc2`. + + * `n` the number of elements. For `ruby_xmalloc` it is 1. + * `size` the size of each element + */ + probe gc__xmalloc(int n, int size); + + /* + ruby:::gc-xcalloc(); + + Fired when allocating memory with `ruby_xcalloc`. + + * `n` the number of elements. For `ruby_xmalloc` it is 1. + * `size` the size of each element + */ + probe gc__xcalloc(int n, int size); + + /* + ruby:::gc-xfree(ptr, size); + + Fired when de-allocating memory with `ruby_xfree` or `ruby_xfree_sized`. + + * `ptr` the pointer to the object + * `size` the size of the object. 0 if called with `xfree` + */ + probe gc__xfree(void *obj, int size); + + /* + ruby:::gvl-acquire(); + + Fired the global VM lock is acquired + */ + probe gvl__acquire(); + + /* + ruby:::gvl-release(); + + Fired the global VM lock is release + */ + probe gvl__release(); + + /* + ruby:::rts-set_running(); + + Fired when setting the running thread of a Ractor (`rb_thread_sched::running`). + + This probe is mainly used to identify the duration in which a thread occupies a Ractor. If the + `old_thread` is NULL and the `new_thread` is not NULL, it means the `new_thread` is scheduled + onto a Ractor. If the `old_thread` is not NULL but the `new_thread` is NULL, it means the + `old_thread` is de-scheduled form a Ractor. + + * `sched` the `rb_thread_sched` instance + * `old_thread` the old thread running on the ractor + * `new_thread` the new thread running on the ractor + */ + probe rts__set_running(void *sched, void *old_thread, void *new_thread); }; #pragma D attributes Stable/Evolving/Common provider ruby provider diff --git a/thread_pthread.c b/thread_pthread.c index f819a0030e2d3b..e1f3f7e668d9ba 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -40,6 +40,8 @@ #include #include +#include "probes.h" + #if defined __APPLE__ # include #endif @@ -689,6 +691,11 @@ thread_sched_set_running(struct rb_thread_sched *sched, rb_thread_t *th) RUBY_DEBUG_LOG("th:%u->th:%u", rb_th_serial(sched->running), rb_th_serial(th)); VM_ASSERT(sched->running != th); + if (RUBY_DTRACE_RTS_SET_RUNNING_ENABLED()) { + rb_thread_t *old = sched->running; + RUBY_DTRACE_RTS_SET_RUNNING(sched, old, th); + } + sched->running = th; } diff --git a/tool/timeline/README.md b/tool/timeline/README.md new file mode 100644 index 00000000000000..df752faecf71a3 --- /dev/null +++ b/tool/timeline/README.md @@ -0,0 +1,214 @@ +# eBPF-based timeline visualization tool + +This directory contains timeline visualization tool based on [eBPF] and [bpftrace]. It sets up +probes that attach to the Userspace Statically Defined Tracepoints (USDT) in the Ruby executable or +library, records events during the execution of a Ruby program, and outputs a log file in the [Trace +Event Format] which can be visualized on a timeline in the [Perfetto UI] web frontend. + +This tool is primarily intended for analyzing garbage collection performance, but it can also +visualize other events, such as the acquisition of the global VM lock, and can be extended. + +[eBPF]: https://ebpf.io/what-is-ebpf/ +[bpftrace]: https://bpftrace.org/ +[Trace Event Format]: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit?usp=sharing +[Perfetto UI]: https://www.ui.perfetto.dev/ + +## How to use? + +### Prepare and build + +You need to run the tool on a Linux distribution, and you need the following command line tools. + +- `bpftrace`: The `capture.rb` script uses `bpftrace` to capture events. +- `dtrace` from [SystemTap]: CRuby uses the `dtrace` command line tool during build time to + generate USDT trace points. Because `bpftrace` can only work with SystemTap's USDT format, you + need to install the `dtrace` command line tool from SystemTap, not the [`dtrace` tool from + Oracle][dtrace-oracle] + + CAUTION: Ubuntu 26.04 provides both the `dtrace` from SystemTap (package name is + `systemtap-sdt-dev`) and the `dtrace` from Oracle (package name is `dtrace`), and they can + coexist (installed as `/usr/bin/dtrace` and `/usr/sbin/dtrace`, respectively). Make sure CRuby + is using the one from SystemTap. + +[SystemTap]: https://sourceware.org/systemtap/ +[dtrace-oracle]: https://github.com/oracle/dtrace + +On Ubuntu, you can use the following commands: + +```shell +sudo apt install bpftrace systemtap-sdt-dev +sudo apt remove dtrace +``` + +Build the `ruby` executable. Make sure the `ruby` executable is built with USDT trace points. If +the `configure` command detectes the `dtrace` command line tool, it should be enabled by default. If +not, add `--with-dtrace` to the `configure` command. You can use the `readelf -n` command to check +if the trace points exist. It should show `stapstd` entries with `Provider: ruby`. + +```shell +$ readelf -n /path/to/ruby +Displaying notes found in: .note.stapsdt + Owner Data size Description + stapsdt 0x00000045 NT_STAPSDT (SystemTap probe descriptors) + Provider: ruby + Name: array__create + Location: 0x000000000006ba92, Base: 0x00000000007abe48, Semaphore: 0x000000000090a658 + Arguments: -4@$0 8@-16(%rbp) -4@%eax +... +``` + +### Capture and visualize + +Open one terminal and run + +```shell +/path/to/capture.rb -r /path/to/ruby +``` + +The `-r` option points to the `ruby` executable. You will be prompted to enter the sudo password. +Then you will see output on the terminal: + +``` +Attaching 'begin' probe +Trying to attach probe: usdt:/path/to/ruby:ruby:gc__exit +Trying to attach probe: usdt:/path/to/ruby:ruby:gc__enter +Trying to attach probe: usdt:/path/to/ruby:ruby:gc__sweep__end +Trying to attach probe: usdt:/path/to/ruby:ruby:gc__sweep__begin +Trying to attach probe: usdt:/path/to/ruby:ruby:gc__mark__end +Trying to attach probe: usdt:/path/to/ruby:ruby:gc__mark__begin +Attached 7 probes +====RUBY_TRACING_LOG_START==== +``` + +Then open another terminal and run a Ruby program using *the same* `ruby` executable specified +above. Be careful if you have multiple Ruby builds in your filesystem. + +```shell +/path/to/ruby some_script.rb +``` + +Go back to the first terminal. If everything goes well, you should see output from `capture.rb` in +the CSV format like this: + +``` +... +GCEnterExit,B,18498,2093498438581,1 +gc_sweep,B,18498,2093498439826 +gc_sweep,E,18498,2093498444465 +GCEnterExit,E,18498,2093498445798,1 +GCEnterExit,B,18498,2093498629067,1 +gc_sweep,B,18498,2093498630379 +gc_sweep,E,18498,2093498682380 +GCEnterExit,E,18498,2093498683848,1 +GCEnterExit,B,18498,2093501215787,3 +GCEnterExit,E,18498,2093501641157,3 +``` + +Then press CTRL+C to interrupt the `capture.rb` script. (More precisely, it interrupts the +underlying `bpftrace` program which `capture.rb` invoked.) + +If everything went as expected, we repeat the `capture.rb`, but pipe the output into a log file. + +```shell +/path/to/capture.rb -r /path/to/ruby > running_some_script.log +``` + +Then use the other terminal to run the script again + +```shell +/path/to/ruby some_script.rb +``` + +Go back to the first terminal and use CTRL+C to interrupt `capture.rb`. You should see the standard +output captured in the `running_some_script.log` file. + +Then use the `visualize.rb` script to convert the log file into a JSON file. + +```shell +/path/to/visualize.rb running_some_script.log +``` + +It should generate a file named `running_some_script.log.json.gz`. + +Open a browser and go to . Click "open trace file" and select the +`running_some_script.log.json.gz` file. Then you will be able to see the timeline. Duration +events, such as `GCEnterExit`, `gc_mark` and `gc_sweep`, are displayed as bars, and instant events +are displayed as arrows. Some events, such as `GCEnterExit` and `gc_mark`, have arguments. If you +click an event, the arguments will be shown on the bottom part of the window. + +### Enabling additional probes + +The supported USDT trace points are organized into groups, and the default group is enabled by +default. To enable additional groups of trace points, use the `-g` option of `capture.rb`. For +example, if you want to monitor the number of objects swept during sweeping, you can enable the +`sweep_details` group. + +```shell +/path/to/capture.rb -r /path/to/ruby -g sweep_details > running_some_script_with_sweep_details.log +``` + +There is no additional options needed for the `visualize.rb` script. Just run it as usual. + +See `lib/tracepoint_defs.rb` for a complete list of groups and their trace points. + +### Working with GC modules + +The default GC in CRuby can be compiled as a GC module. If DTrace support is enabled when building +`ruby`, the USDT trace points will be automatically built into the GC module of the default GC. + +When capturing, add the `-m` option to specify the path of the GC module. + +```shell +/path/to/capture.rb -r /path/to/ruby -m /path/to/librubygc.default.so > running_some_script.log +``` + +Then run `ruby` with the GC module. + +```shell +RUBY_GC_LIBRARY=default /path/to/ruby some_script.rb +``` + +Then visualize in the usual way. + +## How to extend? + +You can introduce more trace points by editing the `probes.d` file. + +Then you can insert the trace points into the program in the form of + +```c +RUBY_DTRACE_XXX_XXX_XXX(/* parameters */) +``` + +To capture and visualize those events, you need to edit the script `lib/tracepoint_defs.rb`. It +will be read by both the `capture.rb` and the `visualize.rb` scripts. After adding the definition +of your added trace points, the events and the arguments will be displayed on the timeline. + +In rare cases, you can hack `visualize.rb` directly to do post-processing on each event. For +example, you can combine the numbers of objects marked in multiple invocations of +`gc_mark_stacked_objects` and add it as one argument of the currnet `GCEnterExit` event in the +timeline. + +### Performance concerns + +USDT trace points are no-op instructions and have negligible overhead when not attached. Therefore, +it is safe to insert the trace points even in the hot paths of hot functions. + +However, when a trace point is attached by `bpftrace` (and therefore our capturing tool), it will +introduce an overhead whenever it is fired. Different trace points are also fired at different +frequencies. For example, `gc_enter` and `gc_exit` events are much less frequent than `obj_new` and +`obj_free`. So you should avoid enabling high-frequency trace points which you are not interested +in in order not to reduce the fidelity of the measurement. You can do this by grouping the trace +points by freqneucy in `lib/tracepoint_defs.rb` so that you can selectively enable the trace points +you are interested in with the `-g` option of `capture.rb`. + +## Acknowledgement + +This timeline tracing tool is inspired by [a similar tracing tool][mmtk-timeline] from the [Memory +Management Toolkit (MMTK)][mmtk] project. The methodology was developed by Claire Huang, Steve +Blackburn, and Zixian Cai, and is described in details in the paper *[Improving Garbage Collection +Observability with Performance Tracing][HBC23]*. + +[mmtk-timeline]: https://github.com/mmtk/mmtk-core/tree/master/tools/tracing/timeline +[mmtk]: https://www.mmtk.io/ +[HBC23]: https://doi.org/10.1145/3617651.3622986 diff --git a/tool/timeline/capture.bt b/tool/timeline/capture.bt new file mode 100644 index 00000000000000..f3059e22715b76 --- /dev/null +++ b/tool/timeline/capture.bt @@ -0,0 +1,9 @@ +BEGIN { + printf("====RUBY_TRACING_LOG_START====\n"); +} + +% usdt_entries.each do |entry| +usdt:<%=entry.file%>:ruby:<%=entry.probe_name%> { + printf("<%=entry.probe_name%>,%d,%d,%lu<%=",%lu"*entry.nargs%>\n", pid, tid, nsecs<%=(0...entry.nargs).map{|i| ", arg#{i}"}.join%>); +} +% end diff --git a/tool/timeline/capture.rb b/tool/timeline/capture.rb new file mode 100755 index 00000000000000..f43ae6ac4dbe82 --- /dev/null +++ b/tool/timeline/capture.rb @@ -0,0 +1,119 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'erb' +require 'optparse' +require 'pathname' + +require_relative 'lib/tracepoint_defs' + +EXTRA_GROUPS = RubyTimelineTool::USDT_DEFS.keys - RubyTimelineTool::DEFAULT_GROUPS + +options = {} + +OptionParser.new do |parser| + parser.accept(Pathname) do |value| + Pathname.new(value) + end + parser.on('-r', '--ruby-bin RUBY', Pathname, 'Path to the ruby executable') + parser.on('-m', '--gc-module-so GC_MODULE', Pathname, 'Path to the GC module .so') + parser.on('-e', '--erb-debug', TrueClass, 'Print the Ruby script generated by ERB. For debugging.') + parser.on('-p', '--print-script', TrueClass, 'Print the content of the bpftrace script') + parser.on('-d', '--dry-run', TrueClass, 'Dry run. Print the bpftrace command to be executed, but do not actually execute it.') + parser.on('-g', '--extra-groups=', "Enable extra USDT groups, separated by comma. Available groups: #{EXTRA_GROUPS.join(', ')}") +end.parse!(into: options) + +[:'ruby-bin'].each do |k| + if !options.include?(k) + raise "Option --#{k} is required" + end +end + +ruby_bin = options[:'ruby-bin'] +if !ruby_bin.file? + raise "Ruby executable '#{ruby_bin}' does not exist" +end + +gc_module_so = options[:'gc-module-so'] + +gc_module_kind = case +when gc_module_so.nil? + nil +when m = /librubygc\.(\w+)/.match(gc_module_so.basename.to_s) + m[1] +else + raise "Unexpected gc module so name: #{gc_module_so}" +end + +script_path = Pathname(__dir__) / 'capture.bt' +script = File.read(script_path) +template = ERB.new(script, trim_mode: '%<>') +template.location = [script_path.to_s, 1] + +if options[:'erb-debug'] + $stderr.puts template.src +end + +extra_groups_opt = options[:'extra-groups'] + +selected_groups = RubyTimelineTool::DEFAULT_GROUPS.dup + +if !extra_groups_opt.nil? + extra_groups_opt.split(',').each do |group| + if !EXTRA_GROUPS.include?(group) + raise "Unknown USDT group #{group}" + end + + selected_groups << group + end +end + +selected_groups.uniq! + +$stderr.puts "Selected groups: #{selected_groups.join(',')}" + +usdts = RubyTimelineTool::USDT_DEFS.values_at(*selected_groups).flatten + +ScriptUsdtEntry = Struct.new('ScriptUsdtEntry', :file, :probe_name, :nargs) + +usdt_entries = [] + +usdts.each do |t| + file = case [gc_module_kind, t.where] + in nil, 'ruby' then ruby_bin + in nil, 'default' then ruby_bin + in x, 'ruby' then ruby_bin + in x, y if x == y then gc_module_so + else nil + end + + if file.nil? + @stderr.puts "Skipped probe '#{probe_name}' which is for '#{location}'. Current GC module: '#{gc_module_kind}'." + next + end + + nargs = t.args.length + + usdt_entries << ScriptUsdtEntry.new(file, t.probe_name, nargs) +end + +content = template.result_with_hash({ + usdt_entries:, +}) + +if options[:'print-script'] + $stderr.puts content +end + +IO.write('capture.out.bt', content) + +command_line = ['sudo', 'bpftrace', '-v', '--unsafe', 'capture.out.bt'] +$stderr.puts 'Command to execute:' +$stderr.puts command_line.map { |s| "'#{s}'" }.join(' ') + +if options[:'dry-run'] + $stderr.puts 'Dry run. Exit...' + exit 0 +end + +exec(*command_line) diff --git a/tool/timeline/lib/converter.rb b/tool/timeline/lib/converter.rb new file mode 100644 index 00000000000000..49c3fbe6e9c03f --- /dev/null +++ b/tool/timeline/lib/converter.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module RubyTimelineTool + class Converter + def convert_arg(_raw_value) + raise 'Not implemented' + end + end + + class EnumConverter < Converter + def initialize(hash) + super() + @hash = hash + @ihash = hash.invert + end + + def convert_arg(raw_value) + value = raw_value.to_i + @ihash[value] + end + + def [](key) + @hash[key] + end + end + + class FlagsConverter < Converter + def initialize(hash) + super() + @hash = hash + end + + def convert_arg(raw_value) + value = raw_value.to_i + # Output {foo: true, bar: false, ...} + @hash.transform_values do |bit_value| + (value & bit_value) != 0 + end + end + + def [](key) + @hash[key] + end + end + + def self.convert_arg(raw_value, converter) + case + when converter.is_a?(Converter) + converter.convert_arg(raw_value) + when converter.is_a?(Symbol) + raw_value.send(converter) + when converter.respond_to?(:call) + converter.call(raw_value) + else + raise "Unexpected converter #{@converter.inspect}" + end + end +end diff --git a/tool/timeline/lib/converter_defs.rb b/tool/timeline/lib/converter_defs.rb new file mode 100644 index 00000000000000..eeb61c9aee57c9 --- /dev/null +++ b/tool/timeline/lib/converter_defs.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require_relative 'converter' + +module RubyTimelineTool + # Keep in sync with `enum gc_enter_event` in `gc/default/default.c`. + GCEnterEvent = EnumConverter.new({ + start: 0, + continue: 1, + rest: 2, + finalizer: 3, + }) + + # Keep in sync with `enum ruby_value_type` in `include/ruby/internal/value_type.h`. + RubyBuiltinType = EnumConverter.new({ + RUBY_T_NONE: 0x00, # /**< Non-object (swept etc.) */: + RUBY_T_OBJECT: 0x01, # /**< @see struct ::RObject */ + RUBY_T_CLASS: 0x02, # /**< @see struct ::RClass and ::rb_cClass */ + RUBY_T_MODULE: 0x03, # /**< @see struct ::RClass and ::rb_cModule */ + RUBY_T_FLOAT: 0x04, # /**< @see struct ::RFloat */ + RUBY_T_STRING: 0x05, # /**< @see struct ::RString */ + RUBY_T_REGEXP: 0x06, # /**< @see struct ::RRegexp */ + RUBY_T_ARRAY: 0x07, # /**< @see struct ::RArray */ + RUBY_T_HASH: 0x08, # /**< @see struct ::RHash */ + RUBY_T_STRUCT: 0x09, # /**< @see struct ::RStruct */ + RUBY_T_BIGNUM: 0x0a, # /**< @see struct ::RBignum */ + RUBY_T_FILE: 0x0b, # /**< @see struct ::RFile */ + RUBY_T_DATA: 0x0c, # /**< @see struct ::RTypedData */ + RUBY_T_MATCH: 0x0d, # /**< @see struct ::RMatch */ + RUBY_T_COMPLEX: 0x0e, # /**< @see struct ::RComplex */ + RUBY_T_RATIONAL: 0x0f, # /**< @see struct ::RRational */: + RUBY_T_NIL: 0x11, # /**< @see ::RUBY_Qnil */ + RUBY_T_TRUE: 0x12, # /**< @see ::RUBY_Qtrue */ + RUBY_T_FALSE: 0x13, # /**< @see ::RUBY_Qfalse */ + RUBY_T_SYMBOL: 0x14, # /**< @see struct ::RSymbol */ + RUBY_T_FIXNUM: 0x15, # /**< Integers formerly known as Fixnums. */ + RUBY_T_UNDEF: 0x16, # /**< @see ::RUBY_Qundef */: + RUBY_T_IMEMO: 0x1a, # /**< @see struct ::RIMemo */ + RUBY_T_NODE: 0x1b, # /**< @see struct ::RNode */ + RUBY_T_ICLASS: 0x1c, # /**< Hidden classes known as IClasses. */ + RUBY_T_ZOMBIE: 0x1d, # /**< @see struct ::RZombie */ + RUBY_T_MOVED: 0x1e, # /**< @see struct ::RMoved */ + }) + + # Keep in sync with `enum ruby_value_type` in `include/ruby/internal/value_type.h`. + RUBY_T_MASK = 0x1f + + # Keep in sync with `enum ruby_fl_type` in `include/ruby/internal/fl_type.h`. + RubyFlType = FlagsConverter.new({ + RUBY_FL_PROMOTED: (1 << 5), + RUBY_FL_UNUSED6: (1 << 6), + RUBY_FL_FINALIZE: (1 << 7), + RUBY_FL_SHAREABLE: (1 << 8), + RUBY_FL_WEAK_REFERENCE: (1 << 9), + RUBY_FL_UNUSED10: (1 << 10), + RUBY_FL_FREEZE: (1 << 11), + }) + + # Keep in sync with `enum ruby_fl_ushift` in `include/ruby/internal/fl_type.h`. + RUBY_FL_USHIFT = 12 + + # Keep in sync with `include/ruby/internal/fl_type.h` + def self.FL_USER_N(n) + 1 << (RUBY_FL_USHIFT + n) + end + + # Keep in sync with `enum imemo_type` in `internal/imemo.h`. + ImemoType = EnumConverter.new({ + imemo_env: 0, + imemo_cref: 1, # /*!< class reference */ + imemo_svar: 2, # /*!< special variable */ + imemo_throw_data: 3, + imemo_ifunc: 4, # /*!< iterator function */ + imemo_memo: 5, + imemo_ment: 6, + imemo_iseq: 7, + imemo_tmpbuf: 8, + imemo_cvar_entry: 9, + imemo_callinfo: 10, + imemo_callcache: 11, + imemo_constcache: 12, + imemo_fields: 13, + imemo_subclasses: 14, + imemo_cdhash: 15, + }) + + # Keep in sync with `IMEMO_MASK` in `internal/imemo.h`. + IMEMO_MASK = 0x0f + + # Keep in sync with both `internal/string.h` and `include/ruby/internal/core/rstring.h`. + StringFlags = FlagsConverter.new({ + STR_SHARED: FL_USER_N(0), + RSTRING_NOEMBED: FL_USER_N(1), + STR_CHILLED_LITERAL: FL_USER_N(2), + STR_CHILLED_SYMBOL_TO_S: FL_USER_N(3), + RSTRING_FSTR: FL_USER_N(17), + }) + + # Keep in sync with both `internal/array.h` and `include/ruby/internal/core/rarray.h`. + ArrayFlags = FlagsConverter.new({ + RARRAY_SHARED_FLAG: FL_USER_N(0), + RARRAY_EMBED_FLAG: FL_USER_N(1), + RARRAY_SHARED_ROOT_FLAG: FL_USER_N(12), + RARRAY_PTR_IN_USE_FLAG: FL_USER_N(14), + }) + + RubyFlags = proc do |raw_value| + value = raw_value.to_i + builtin_type = RubyBuiltinType.convert_arg(value & RUBY_T_MASK) + result = { + raw_value: value, + raw_value_binary: value.to_s(2), + builtin_type:, + } + + if builtin_type == :RUBY_T_IMEMO + result[:imemo_type] = ImemoType.convert_arg((value >> RUBY_FL_USHIFT) & IMEMO_MASK) + end + + result[:flags] = RubyFlType.convert_arg(value) + + case builtin_type + when :RUBY_T_STRING + result[:string_args] = StringFlags.convert_arg(value) + when :RUBY_T_ARRAY + result[:array_args] = ArrayFlags.convert_arg(value) + + # TODO: Decode the flags for more types of interest. + end + + result + end +end diff --git a/tool/timeline/lib/tracepoint.rb b/tool/timeline/lib/tracepoint.rb new file mode 100644 index 00000000000000..e24ae6283de6cb --- /dev/null +++ b/tool/timeline/lib/tracepoint.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module RubyTimelineTool + class TracePoint + def initialize(probe_name, where, vis_name, ph, args: nil, visualize: true) + @probe_name = probe_name + @where = where + @vis_name = vis_name + @ph = ph + @args = args || {} + @visualize = visualize + end + + attr_reader :probe_name, :where, :vis_name, :ph, :args, :visualize + end + + def self.tp(*args, **kwargs) + TracePoint.new(*args, **kwargs) + end +end diff --git a/tool/timeline/lib/tracepoint_defs.rb b/tool/timeline/lib/tracepoint_defs.rb new file mode 100644 index 00000000000000..db0868ff20dc8c --- /dev/null +++ b/tool/timeline/lib/tracepoint_defs.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require_relative 'tracepoint' +require_relative 'converter_defs' + +module RubyTimelineTool + # All USDT trace points. + # + # It maps each group to an array of trace points. The `default` group is always enabled, and + # other groups can be enabled using the `-g` command line option of `capture.rb`. + # + # `tp(...)` defines a trace point. It has four compulsory arguments. + # + # 1. The USDT probe name. It is the `xxx` in `probe xxx()` in `probes.d`, and it is also the + # `Name:` field of the output of `readelf -n`. The `capture.rb` tool assumes the "provider + # (the `Provider:` field of `readelf -n`) of the USDT is `ruby`, and we don't need to specify + # it here. + # 2. The place the probe is defined. Possible values are: + # - `ruby`: It is part of the Ruby runtime, and will always be compiled into the `ruby` + # executable. + # - `default`: It is part of the default GC (`default.c`). It will be compiled into the + # `ruby` executable and the default GC module if modular GC is enabled. + # 3. The event name in the output timeline. + # 4. The event type, as specified by the Trace Event Format. Common types include + # - 'B' and 'E': The beginning and the end of a duration event. + # - 'i': An instant event. + # - 'c': A counter event. + # + # It can also have a special value 'meta' (not specified in the Trace Event Format) which + # means it will not be added to the output JSON file, but will still be available for + # `visualize.rb` for post-processing. + # + # For more information about the Trace Event Format, see: + # https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit?usp=sharing + # + # `tp(...)` also has some optional keyword argument. + # + # - `args`: It is used by the `capture.rb` script to set up the arguments of the USDT probes, + # and used by `visualize.rb` to convert the argument values from string (read from the log) to + # JSON values. It has the form: + # + # ```ruby + # args: {arg1: converter1, arg2: converter2, ...} + # ``` + # + # The order of the key-value pairs must match the order of the arguments of the USDT trace + # points (as defined in `probes.d`). + # + # Each converter can be one of the following + # + # - An instance of `Converter`. + # - A symbol, such as `:to_i`, to be sent to the argument string. + # - An object that responds to `call`. + # + # There are some converters defined in `converter_defs.rb`. + USDT_DEFS = { + 'default' => [ + # The default group visualizes the duration of each GC, each marking and each sweeping event. + tp('gc__mark__begin', 'default', 'gc_mark', 'B'), + tp('gc__mark__end', 'default', 'gc_mark', 'E'), + tp('gc__sweep__begin', 'default', 'gc_sweep', 'B'), + tp('gc__sweep__end', 'default', 'gc_sweep', 'E'), + tp('gc__enter', 'default', 'GCEnterExit', 'B', args: {event: GCEnterEvent}), + tp('gc__exit', 'default', 'GCEnterExit', 'E', args: {event: GCEnterEvent}), + ].freeze, + 'mark_details' => [ + # This group collects more detals of marking events, such as the number of objects visited. + tp('gc__mark_stacked_objects', 'default', 'gc_mark_stacked_objects', 'meta', args: {popped_count: :to_i}), + ].freeze, + 'sweep_details' => [ + # This group collects more detals of sweeping events, such as the number of objects swept. + tp('gc__sweep_page', 'default', 'gc_sweep_page', 'i', args: {slot_size: :to_i, final_slots: :to_i, freed_slots: :to_i, empty_slots: :to_i}), + ].freeze, + 'obj_new' => [ + # This group traces the creation of GC-managed objects. + tp('gc__obj_new', 'ruby', 'gc_obj_new', 'i', args: {obj: :to_i, flags: RubyFlags}), + ].freeze, + 'obj_free' => [ + # This group traces the invocation of `rb_gc_obj_free` which finalizes the objects when they are swept. + tp('gc__obj_free', 'ruby', 'gc_obj_free', 'i', args: {obj: :to_i, flags: RubyFlags}), + ].freeze, + 'xmalloc' => [ + # This group traces the invocation of `xmalloc` and `xcalloc`. + tp('gc__xmalloc', 'ruby', 'gc_xmalloc', 'i', args: {n: :to_i, size: :to_i}), + tp('gc__xcalloc', 'ruby', 'gc_xcalloc', 'i', args: {n: :to_i, size: :to_i}), + ].freeze, + 'xfree' => [ + # This group traces the invocation of `xfree`. + tp('gc__xfree', 'ruby', 'gc_xfree', 'i', args: {obj: :to_i, size: :to_i}), + ].freeze, + 'gvl' => [ + # This group visualizes the durations in which a thread holds the global VM lock. + tp('gvl__acquire', 'ruby', 'GVL', 'B'), + tp('gvl__release', 'ruby', 'GVL', 'E'), + ].freeze, + 'rts' => [ + # This group visualizes the event where a thread is scheduled on or off a Ractor. + # "RTS" stands for `ractor.thread.sched`. + tp('rts__set_running', 'ruby', 'rts_set_running', 'i', args: {sched: :to_i, old_thread: :to_i, new_thread: :to_i}), + ].freeze + }.freeze + + # The default groups are enabled by default + DEFAULT_GROUPS = ['default'].freeze +end diff --git a/tool/timeline/visualize.rb b/tool/timeline/visualize.rb new file mode 100755 index 00000000000000..dae41ca8a3812b --- /dev/null +++ b/tool/timeline/visualize.rb @@ -0,0 +1,246 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'json' +require 'optparse' +require 'pathname' +require 'zlib' + +require_relative 'lib/tracepoint_defs' +require_relative 'lib/converter_defs' +require_relative 'lib/converter' + +PROBE_NAME_TO_USDT = RubyTimelineTool::USDT_DEFS.values.flatten.to_h { |t| [t.probe_name, t] } + +module FetchStore + refine Hash do + def fetch_store(key) + if include?(key) + self[key] + else + self[key] = yield + end + end + end +end + +using FetchStore + +class LogProcessor + def initialize(verbose: false) + @verbose = verbose + @type_id_name = {} + @start_time = nil + @results = [] + # @current maps [pid, tid, block_name] to block. + @current = {} + @started = false + end + + attr_accessor :started + + def process_line(line) + if !@started + if line == '====RUBY_TRACING_LOG_START====' + @started = true + end + return + end + + if line.include?(',') + process_log_line(line) + else + puts "Discarded line '#{line}'" if @verbose + end + end + + def output(outfile) + json_str = JSON.generate(@results) + Zlib::GzipWriter.open(outfile) do |gz| + gz.write(json_str) + end + end + + private + + def process_log_line(line) + parts = line.split(',') + probe_name, pid, tid, ts = parts[...4] + pid = pid.to_i + tid = tid.to_i + ts = resolve_time(ts.to_i) + raw_args = parts[4...] + + puts "probe_name: #{probe_name}, pid: #{pid}, tid: #{tid}, ts: #{ts}, raw_args: #{raw_args}" if @verbose + + if @start_time.nil? + @start_time = ts + end + + usdt_def = PROBE_NAME_TO_USDT[probe_name] + + ph = usdt_def.ph + vis_name = usdt_def.vis_name + + args = {} + usdt_def.args.each_pair.zip(raw_args) do |arg_def, arg_val| + arg_name, arg_converter = arg_def + begin + converted_val = RubyTimelineTool.convert_arg(arg_val, arg_converter) + rescue + puts "error converting argument #{arg_name}, value: [#{arg_val}]" + puts "line: #{line}" + raise + end + + args[arg_name] = converted_val + end + + result = { name: vis_name, ph:, pid:, tid:, ts:, args: } + + if ph == 'B' || ph == 'E' + # Register the current block so that other events can "enrich" it. + set_current_block(pid, tid, result) + end + + # Some results need special treatment. + case vis_name + when 'GCEnterExit' + # Register to the virtual :global TID, too. + set_current_block(pid, :global, result) + when 'gc_mark_stacked_objects' + enrich([pid, :global, 'GCEnterExit'], [pid, tid, 'gc_mark']) do |old_result| + old_result[:args].fetch_store(:gc_mark_stacked_objects) do + { popped_count: 0 } + end[:popped_count] += args[:popped_count] + end + when 'gc_sweep_page' + args[:free_slots] = args[:freed_slots] + args[:empty_slots] + enrich([pid, :global, 'GCEnterExit'], [pid, tid, 'gc_sweep']) do |old_result| + old_dict = old_result[:args].fetch_store(:gc_sweep_page) do + { swept_pages: 0, final_slots: 0, freed_slots: 0, empty_slots: 0, free_slots: 0 } + end + old_dict[:swept_pages] += 1 + old_dict[:final_slots] += args[:final_slots] + old_dict[:freed_slots] += args[:freed_slots] + old_dict[:empty_slots] += args[:empty_slots] + old_dict[:free_slots] += args[:free_slots] + end + when 'rts_set_running' + sched = args[:sched] + sched_hex = "0x#{sched.to_s(16)}" + # A virtual TID for the rb_thread_sched instance. + sched_id = "sched-#{sched_hex}" + prev = get_current(pid, sched_id, 'RTS') + if !prev.nil? + block_end = result.dup.update({ + name: 'RTS', + ph: 'E', + tid: sched_id, + args: {} + }) + @results << block_end + clear_current(pid, sched_id, 'RTS') + end + + if args[:new_thread] != 0 + block_begin = result.dup.update({ + name: 'RTS', + ph: 'B', + tid: sched_id, + args: { + sched:, + sched_hex:, + thread: args[:new_thread], + } + }) + @results << block_begin + set_current(pid, sched_id, 'RTS', block_begin) + end + end + + if result[:ph] != 'meta' + @results << result + end + end + + def resolve_time(ts) + if @start_time.nil? + @start_time = ts + end + (ts - @start_time) / 1000.0 + end + + def set_current(pid, tid, key, value) + @current[[pid, tid, key]] = value + end + + def get_current(pid, tid, key) + @current[[pid, tid, key]] + end + + def clear_current(pid, tid, key) + @current.delete([pid, tid, key]) + end + + def set_current_block(pid, tid, result) + case result[:ph] + when 'B' + set_current(pid, tid, result[:name], result) + when 'E' + clear_current(pid, tid, result[:name]) + else + raise "unexpected ph: #{result}" + end + end + + # Look up an entry in the `@current` hash so that + # you can add more arguments or make adjustments to it. + def enrich(*targets) + targets.each do |target| + case target + in [pid, tid, key] + result = get_current(pid, tid, key) + if !result.nil? + yield result + end + end + end + end +end + +def main + options = {} + + OptionParser.new do |parser| + parser.on('-v', '--verbose', TrueClass, 'Print verbose messages. For debugging.') + parser.on('-d', '--dry-run', TrueClass, 'Dry run. DO not actually write into output file.') + end.parse!(into: options) + + input = ARGV[0] + if input.nil? + raise 'Need positional argument' + end + + input_path = Pathname.new(input) + if !input_path.file? + raise "File #{input_path} does not exist" + end + + output_path = input_path.dirname / "#{input_path.basename}.json.gz" + + log_processor = LogProcessor.new(verbose: options[:verbose]) + + input_path.each_line(chomp: true) do |line| + log_processor.process_line(line) + end + + if options[:'dry-run'] + puts "Dry run. Output file: #{output_path}" + else + puts "Writing to output file: #{output_path}" + log_processor.output(output_path) + end +end + +main diff --git a/vm_sync.c b/vm_sync.c index aca83dde5a73aa..d6eb47dfe725cf 100644 --- a/vm_sync.c +++ b/vm_sync.c @@ -4,6 +4,7 @@ #include "vm_sync.h" #include "ractor_core.h" #include "vm_debug.h" +#include "probes.h" void rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr); void rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr); @@ -115,6 +116,10 @@ vm_lock_enter(rb_ractor_t *cr, rb_vm_t *vm, bool locked, bool no_barrier, unsign RUBY_DEBUG_LOG2(file, line, "rec:%u owner:%u", vm->ractor.sync.lock_rec, (unsigned int)rb_ractor_id(vm->ractor.sync.lock_owner)); + + if (RUBY_DTRACE_GVL_ACQUIRE_ENABLED()) { + RUBY_DTRACE_GVL_ACQUIRE(); + } } static void @@ -139,6 +144,10 @@ vm_lock_leave(rb_vm_t *vm, bool no_barrier, unsigned int *lev APPEND_LOCATION_AR } #endif + if (RUBY_DTRACE_GVL_RELEASE_ENABLED()) { + RUBY_DTRACE_GVL_RELEASE(); + } + vm->ractor.sync.lock_rec--; *lev = vm->ractor.sync.lock_rec; From 36ced7351e2abeb256ceb1451d975bf0e8c86976 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 7 Jul 2026 13:51:04 +0900 Subject: [PATCH 13/20] Change test_capacity_embedded for dynamic slot size If the slot size is dynamic, a string of length (max_embed_len - 1) will have exact capacity rather than rounded up to max_embed_len. --- test/-ext-/string/test_capacity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/-ext-/string/test_capacity.rb b/test/-ext-/string/test_capacity.rb index a23892142afa94..3766dd3d303218 100644 --- a/test/-ext-/string/test_capacity.rb +++ b/test/-ext-/string/test_capacity.rb @@ -8,7 +8,7 @@ class Test_StringCapacity < Test::Unit::TestCase def test_capacity_embedded assert_equal pool_slot_size(0) - embed_header_size - 1, capa('foo') assert_equal max_embed_len, capa('1' * max_embed_len) - assert_equal max_embed_len, capa('1' * (max_embed_len - 1)) + assert_include ((max_embed_len - 1)..max_embed_len), capa('1' * (max_embed_len - 1)) end def test_capacity_shared From 588d5910d56f48fea5527e1c65543b05d4ddf44b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 09:24:22 +0200 Subject: [PATCH 14/20] shape.h: rb_shape_capacity -> rb_shape_embedded_capacity To avoid confusion with the actual capacity which may be larger if the object has been extended (ROBJECT_HEAP). --- shape.c | 4 ++-- shape.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shape.c b/shape.c index bb4bb437d74e08..c1672167c2b9dd 100644 --- a/shape.c +++ b/shape.c @@ -1305,7 +1305,7 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) } } - attr_index_t shape_id_capacity = rb_shape_capacity(shape_id); + attr_index_t shape_id_capacity = rb_shape_embedded_capacity(shape_id); if (RB_TYPE_P(obj, T_OBJECT)) { RUBY_ASSERT(shape_id_capacity > 0); size_t shape_id_slot_size = shape_id_capacity * sizeof(VALUE) + sizeof(struct RBasic); @@ -1389,7 +1389,7 @@ shape_id_t_to_rb_cShape(shape_id_t shape_id) INT2NUM(shape->parent_offset), rb_shape_edge_name(shape), INT2NUM(shape->next_field_index), - INT2NUM(rb_shape_capacity(shape_id)), + INT2NUM(rb_shape_embedded_capacity(shape_id)), INT2NUM(shape->type), INT2NUM(RSHAPE_CAPACITY(shape_id))); rb_obj_freeze(obj); diff --git a/shape.h b/shape.h index 763a43fddd703d..5c297dd5d55505 100644 --- a/shape.h +++ b/shape.h @@ -278,7 +278,7 @@ rb_shape_id_with_robject_layout(shape_id_t shape_id) } static inline attr_index_t -rb_shape_capacity(shape_id_t shape_id) +rb_shape_embedded_capacity(shape_id_t shape_id) { return (attr_index_t)((shape_id & SHAPE_ID_CAPACITY_MASK) >> SHAPE_ID_CAPACITY_OFFSET); } @@ -290,7 +290,7 @@ rb_shape_id_with_capacity(size_t capacity) RUBY_ASSERT(capacity <= SHAPE_ID_CAPACITY_MAX); RUBY_ASSERT((capacity_flags & SHAPE_ID_CAPACITY_MASK) == capacity_flags); - RUBY_ASSERT(rb_shape_capacity(capacity_flags) == capacity); + RUBY_ASSERT(rb_shape_embedded_capacity(capacity_flags) == capacity); return ROOT_SHAPE_ID | capacity_flags; } @@ -330,7 +330,7 @@ RSHAPE_TYPE_P(shape_id_t shape_id, enum shape_type type) static inline attr_index_t RSHAPE_CAPACITY(shape_id_t shape_id) { - attr_index_t embedded_capacity = rb_shape_capacity(shape_id); + attr_index_t embedded_capacity = rb_shape_embedded_capacity(shape_id); if (embedded_capacity > RSHAPE(shape_id)->capacity) { return embedded_capacity; From 263f06ad49a83fd332fb917bf0aa651c3cc0a723 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 09:44:54 +0200 Subject: [PATCH 15/20] RBASIC_SET_SHAPE_ID: also strip the layout bits Like the capacity part, the layout part of an object shape almost never changes. The few exceptions are: - On allocation. - On being compacted by GC. - When RObject oberflows. As such it simplifies a lot of code if `RBASIC_SET_SHAPE_ID` strips the layout bits, as we often copy the shape from IMEMO/fields to the owner object and vice-versa. Also change `RBASIC_SET_SHAPE_ID_WITH_CAPACITY` into `RBASIC_SET_FULL_SHAPE_ID` so it can be used for assigning both capacity and layout. --- array.c | 2 +- gc.c | 10 +++++----- imemo.c | 6 +++--- shape.h | 33 +++++++++++++++++++++------------ string.c | 2 +- variable.c | 17 ++++++++--------- vm_insnhelper.c | 7 ++----- 7 files changed, 41 insertions(+), 36 deletions(-) diff --git a/array.c b/array.c index 6514015de21628..26ff60fb79738c 100644 --- a/array.c +++ b/array.c @@ -902,7 +902,7 @@ init_fake_ary_flags(void) struct RArray fake_ary = {0}; fake_ary.basic.flags = T_ARRAY; VALUE ary = (VALUE)&fake_ary; - RBASIC_SET_SHAPE_ID(ary, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER); + RBASIC_SET_FULL_SHAPE_ID(ary, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER); rb_ary_freeze(ary); return fake_ary.basic.flags; } diff --git a/gc.c b/gc.c index 8266f11e8a4a6e..ff99b5b256e578 100644 --- a/gc.c +++ b/gc.c @@ -378,7 +378,7 @@ rb_gc_obj_changed_slot_size(VALUE obj, size_t slot_size) { RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); - RBASIC_SET_SHAPE_ID_WITH_CAPACITY(obj, rb_obj_shape_transition_capacity(obj, rb_shape_capacity_for_slot_size(slot_size))); + RBASIC_SET_FULL_SHAPE_ID(obj, rb_obj_shape_transition_capacity(obj, rb_shape_capacity_for_slot_size(slot_size))); } void rb_vm_update_references(void *ptr); @@ -1046,7 +1046,7 @@ rb_newobj(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape rb_ractor_setup_belonging(obj); #endif - RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); + RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(obj, shape_id); gc_validate_pc(obj); @@ -1102,7 +1102,7 @@ rb_newobj_of(VALUE klass, VALUE flags, size_t size) static VALUE class_allocate_complex_instance(VALUE klass, uint32_t capacity) { - shape_id_t initial_shape_id = rb_shape_id_with_robject_layout(0); + shape_id_t initial_shape_id = rb_shape_transition_robject(0); VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, initial_shape_id, sizeof(struct RObject)); rb_obj_init_complex(obj, rb_st_init_numtable_with_size(capacity)); return obj; @@ -1127,7 +1127,7 @@ rb_class_allocate_instance(VALUE klass) // There might be a NEWOBJ tracepoint callback, and it may set fields. // So the shape must be passed to `NEWOBJ_OF`. - obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_id_with_robject_layout(0), size); + obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_transition_robject(0), size); #if RUBY_DEBUG VALUE *ptr = ROBJECT_FIELDS(obj); @@ -4256,7 +4256,7 @@ vm_weak_table_gen_fields_foreach(st_data_t key, st_data_t value, st_data_t data) // set the shape on it so that the GC finalizer won't try to remove // it again. A "root shape" indicates to the GC that this object // has no fields on it, hence it won't be in the gen fields table. - RBASIC_SET_SHAPE_ID((VALUE)key, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER); + RBASIC_SET_SHAPE_ID((VALUE)key, ROOT_SHAPE_ID); return ST_DELETE; case ST_REPLACE: { diff --git a/imemo.c b/imemo.c index a823b74b5b4525..88d7c332dc6da6 100644 --- a/imemo.c +++ b/imemo.c @@ -141,7 +141,7 @@ rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable) // layout in the shape describes the layout of the thing on which it is set. // Imemo fields have the same layout as robject, therefore the layout // should reflect that fact. - RBASIC_SET_SHAPE_ID(fields, rb_shape_id_with_robject_layout(shape_id)); + RBASIC_SET_FULL_SHAPE_ID(fields, rb_shape_transition_robject(shape_id)); RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_fields)); return fields; } @@ -156,7 +156,7 @@ rb_imemo_fields_new_complex(VALUE owner, shape_id_t shape_id, size_t capa, bool // layout in the shape describes the layout of the thing on which it is set. // Imemo fields have the same layout as robject, therefore the layout // should reflect that fact. - RBASIC_SET_SHAPE_ID(fields, rb_shape_id_with_robject_layout(shape_id)); + RBASIC_SET_FULL_SHAPE_ID(fields, rb_shape_transition_robject(shape_id)); return fields; } @@ -185,7 +185,7 @@ rb_imemo_fields_new_complex_tbl(VALUE owner, shape_id_t shape_id, st_table *tbl, // layout in the shape describes the layout of the thing on which it is set. // Imemo fields have the same layout as robject, therefore the layout // should reflect that fact. - RBASIC_SET_SHAPE_ID(fields, rb_shape_id_with_robject_layout(shape_id)); + RBASIC_SET_FULL_SHAPE_ID(fields, rb_shape_transition_robject(shape_id)); st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields); return fields; } diff --git a/shape.h b/shape.h index 5c297dd5d55505..ef4ff245c8788d 100644 --- a/shape.h +++ b/shape.h @@ -166,7 +166,7 @@ bool rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id); #endif static inline void -RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) +RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) { #if RBASIC_SHAPE_ID_FIELD RBASIC(obj)->shape_id = (VALUE)shape_id; @@ -183,14 +183,20 @@ rb_shape_layout(shape_id_t shape_id) return shape_id & SHAPE_ID_LAYOUT_MASK; } +// Assigns the entire shape_id. +// shape_id_t is composed of two parts: +// - The layout and capacity part, which never changes except on GC compaction. +// - All the other bits that regularly change. +// In the overwhelming majority of cases, you want to use RBASIC_SET_SHAPE_ID +// which preserves the object's layout and capacity bits. +// In rare cases you may want to set all bits. static inline void -RBASIC_SET_SHAPE_ID_WITH_CAPACITY(VALUE obj, shape_id_t shape_id) +RBASIC_SET_FULL_SHAPE_ID(VALUE obj, shape_id_t shape_id) { RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_fields)); - RUBY_ASSERT(!IMEMO_TYPE_P(obj, imemo_fields) || rb_shape_layout(shape_id) == SHAPE_ID_LAYOUT_ROBJECT); - RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); + RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(obj, shape_id); RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id)); } @@ -200,8 +206,11 @@ RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) { RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); - shape_id = (shape_id & ~SHAPE_ID_CAPACITY_MASK) | (RBASIC_SHAPE_ID(obj) & SHAPE_ID_CAPACITY_MASK); - RBASIC_SET_SHAPE_ID_WITH_CAPACITY(obj, shape_id); + shape_id = ( + (shape_id & ~(SHAPE_ID_CAPACITY_MASK|SHAPE_ID_LAYOUT_MASK)) | + (RBASIC_SHAPE_ID(obj) & (SHAPE_ID_CAPACITY_MASK|SHAPE_ID_LAYOUT_MASK)) + ); + RBASIC_SET_FULL_SHAPE_ID(obj, shape_id); } static inline shape_id_t @@ -271,12 +280,6 @@ rb_shape_canonical_p(shape_id_t shape_id) return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK); } -static inline shape_id_t -rb_shape_id_with_robject_layout(shape_id_t shape_id) -{ - return (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_ROBJECT; -} - static inline attr_index_t rb_shape_embedded_capacity(shape_id_t shape_id) { @@ -484,6 +487,12 @@ rb_obj_using_gen_fields_table_p(VALUE obj) return rb_obj_gen_fields_p(obj); } +static inline shape_id_t +rb_shape_transition_robject(shape_id_t shape_id) +{ + return (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_ROBJECT; +} + static inline shape_id_t rb_shape_transition_frozen(shape_id_t shape_id) { diff --git a/string.c b/string.c index a499c60a817a28..73e16b031d3a9b 100644 --- a/string.c +++ b/string.c @@ -630,7 +630,7 @@ static VALUE setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx) { fake_str->basic.flags = T_STRING|RSTRING_NOEMBED|STR_NOFREE|STR_FAKESTR; - RBASIC_SET_SHAPE_ID((VALUE)fake_str, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER); + RBASIC_SET_FULL_SHAPE_ID((VALUE)fake_str, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER); if (!name) { RUBY_ASSERT_ALWAYS(len == 0); diff --git a/variable.c b/variable.c index dc015b4cbce168..6aa1db771373d4 100644 --- a/variable.c +++ b/variable.c @@ -1343,7 +1343,7 @@ rb_free_generic_ivar(VALUE obj) } } } - RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | ROOT_SHAPE_ID); + RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID); } } @@ -1395,7 +1395,7 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie } } - RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | RBASIC_SHAPE_ID(fields_obj)); + RBASIC_SET_SHAPE_ID(obj, RBASIC_SHAPE_ID(fields_obj)); } void @@ -1710,7 +1710,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) size_t trailing_fields = new_fields_count - removed_index; MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields); - RUBY_ASSERT(rb_shape_layout(next_shape_id) == SHAPE_ID_LAYOUT_ROBJECT); RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id); if (FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)) { @@ -1733,7 +1732,7 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) } } - RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | next_shape_id); + RBASIC_SET_SHAPE_ID(obj, next_shape_id); if (fields_obj != original_fields_obj) { switch (type) { case T_OBJECT: @@ -1844,7 +1843,7 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f RUBY_ASSERT(field_name); st_insert(table, (st_data_t)field_name, (st_data_t)val); RB_OBJ_WRITTEN(fields_obj, Qundef, val); - RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(target_shape_id)); + RBASIC_SET_SHAPE_ID(fields_obj, target_shape_id); } else { attr_index_t index = RSHAPE_INDEX(target_shape_id); @@ -1856,7 +1855,7 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f RB_OBJ_WRITE(fields_obj, &table[index], val); if (index >= RSHAPE_LEN(current_shape_id)) { - RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(target_shape_id)); + RBASIC_SET_SHAPE_ID(fields_obj, target_shape_id); } } @@ -2305,7 +2304,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) } if (!RSHAPE_LEN(dest_shape_id)) { - RBASIC_SET_SHAPE_ID(dest, rb_shape_layout(RBASIC_SHAPE_ID(dest)) | dest_shape_id); + RBASIC_SET_SHAPE_ID(dest, dest_shape_id); return; } @@ -4688,7 +4687,7 @@ class_ivar_set(VALUE obj, ID id, VALUE val, bool *new_ivar) // TODO: What should we set as the T_CLASS shape_id? // In most case we can replicate the single `fields_obj` shape // but in namespaced case? Perhaps INVALID_SHAPE_ID? - RBASIC_SET_SHAPE_ID(obj, rb_shape_layout(RBASIC_SHAPE_ID(obj)) | RBASIC_SHAPE_ID(new_fields_obj)); + RBASIC_SET_SHAPE_ID(obj, RBASIC_SHAPE_ID(new_fields_obj)); return index; } @@ -4713,7 +4712,7 @@ rb_fields_tbl_copy(VALUE dst, VALUE src) VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(src); if (fields_obj) { RCLASS_WRITABLE_SET_FIELDS_OBJ(dst, rb_imemo_fields_clone(fields_obj)); - RBASIC_SET_SHAPE_ID(dst, rb_shape_layout(RBASIC_SHAPE_ID(dst)) | RBASIC_SHAPE_ID(src)); + RBASIC_SET_SHAPE_ID(dst, RBASIC_SHAPE_ID(src)); } } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 8abbfb15099379..57a56e6a628d43 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1412,8 +1412,7 @@ vm_setivar_class(VALUE obj, VALUE val, rb_setivar_cache cache) RB_OBJ_WRITE(fields_obj, &rb_imemo_fields_ptr(fields_obj)[cache.index], val); if (shape_id != dest_shape_id) { - // The dest_shape_id comes from the fields_obj - RBASIC_SET_SHAPE_ID(obj, SHAPE_ID_LAYOUT_RCLASS | (dest_shape_id & ~SHAPE_ID_LAYOUT_MASK)); + RBASIC_SET_SHAPE_ID(obj, dest_shape_id); RBASIC_SET_SHAPE_ID(fields_obj, dest_shape_id); } @@ -1441,9 +1440,7 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, rb_setivar_cache cache) if (shape_id != dest_shape_id) { RBASIC_SET_SHAPE_ID(obj, dest_shape_id); - // The dest_shape_id comes from the owner, but fields_obj must always - // have layout RObject, so give the fields_object the right layout. - RBASIC_SET_SHAPE_ID(fields_obj, rb_shape_id_with_robject_layout(dest_shape_id)); + RBASIC_SET_SHAPE_ID(fields_obj, dest_shape_id); } RB_DEBUG_COUNTER_INC(ivar_set_ic_hit); From c966f3abd774bcffcab045c0d75dfa505c3e78b1 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 11:44:39 +0200 Subject: [PATCH 16/20] imemo.c: refactor imemo_fields_new Lean on `rb_newobj` to get the shape embedded capacity properly set. --- imemo.c | 45 +++++++++++++++++++++++---------------------- shape.c | 3 ++- variable.c | 1 + 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/imemo.c b/imemo.c index 88d7c332dc6da6..a70ae1a5523fcd 100644 --- a/imemo.c +++ b/imemo.c @@ -130,33 +130,40 @@ rb_imemo_cdhash_new(size_t size, const struct st_hash_type *type) return (VALUE)memo; } -VALUE -rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable) +static VALUE +imemo_fields_new(VALUE owner, VALUE flags, shape_id_t shape_id, size_t size, bool is_shareable) { - size_t capa = RSHAPE(shape_id)->capacity; - size_t embedded_size = offsetof(struct rb_fields, as.embed) + capa * sizeof(VALUE); - RUBY_ASSERT(rb_gc_size_allocatable_p(embedded_size)); - VALUE fields = rb_imemo_new(imemo_fields, owner, embedded_size, shareable); + RUBY_ASSERT(rb_gc_size_allocatable_p(size)); + + flags |= T_IMEMO | (imemo_fields << FL_USHIFT) | (is_shareable ? FL_SHAREABLE : 0); // imemo fields objects should always have "RObject" layout. The // layout in the shape describes the layout of the thing on which it is set. // Imemo fields have the same layout as robject, therefore the layout // should reflect that fact. - RBASIC_SET_FULL_SHAPE_ID(fields, rb_shape_transition_robject(shape_id)); + shape_id = rb_shape_transition_robject(shape_id); + return rb_newobj(GET_EC(), owner, flags, shape_id, true, size); +} + +VALUE +rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable) +{ + size_t capa = RSHAPE(shape_id)->capacity; + size_t embedded_size = offsetof(struct rb_fields, as.embed) + capa * sizeof(VALUE); + + VALUE fields = imemo_fields_new(owner, 0, shape_id, embedded_size, shareable); + RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_fields)); + RUBY_ASSERT(rb_shape_embedded_capacity(RBASIC_SHAPE_ID(fields)) >= capa); + return fields; } VALUE rb_imemo_fields_new_complex(VALUE owner, shape_id_t shape_id, size_t capa, bool shareable) { - VALUE fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields), shareable); - IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa); - FL_SET_RAW(fields, OBJ_FIELD_HEAP); - // imemo fields objects should always have "RObject" layout. The - // layout in the shape describes the layout of the thing on which it is set. - // Imemo fields have the same layout as robject, therefore the layout - // should reflect that fact. - RBASIC_SET_FULL_SHAPE_ID(fields, rb_shape_transition_robject(shape_id)); + st_table *tbl = st_init_numtable_with_size(capa); + VALUE fields = imemo_fields_new(owner, OBJ_FIELD_HEAP, shape_id, sizeof(struct rb_fields), shareable); + IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl; return fields; } @@ -178,14 +185,8 @@ imemo_fields_complex_wb_i(st_data_t key, st_data_t value, st_data_t arg) VALUE rb_imemo_fields_new_complex_tbl(VALUE owner, shape_id_t shape_id, st_table *tbl, bool shareable) { - VALUE fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields), shareable); + VALUE fields = imemo_fields_new(owner, OBJ_FIELD_HEAP, shape_id, sizeof(struct rb_fields), shareable); IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl; - FL_SET_RAW(fields, OBJ_FIELD_HEAP); - // imemo fields objects should always have "RObject" layout. The - // layout in the shape describes the layout of the thing on which it is set. - // Imemo fields have the same layout as robject, therefore the layout - // should reflect that fact. - RBASIC_SET_FULL_SHAPE_ID(fields, rb_shape_transition_robject(shape_id)); st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields); return fields; } diff --git a/shape.c b/shape.c index c1672167c2b9dd..7e48654c7c5c96 100644 --- a/shape.c +++ b/shape.c @@ -1306,8 +1306,9 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) } attr_index_t shape_id_capacity = rb_shape_embedded_capacity(shape_id); - if (RB_TYPE_P(obj, T_OBJECT)) { + if (RB_TYPE_P(obj, T_OBJECT) || IMEMO_TYPE_P(obj, imemo_fields)) { RUBY_ASSERT(shape_id_capacity > 0); + size_t shape_id_slot_size = shape_id_capacity * sizeof(VALUE) + sizeof(struct RBasic); size_t actual_slot_size = rb_gc_obj_slot_size(obj); diff --git a/variable.c b/variable.c index 6aa1db771373d4..55e9e2f9335a9f 100644 --- a/variable.c +++ b/variable.c @@ -1847,6 +1847,7 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f } else { attr_index_t index = RSHAPE_INDEX(target_shape_id); + if (concurrent || index >= RSHAPE_CAPACITY(current_shape_id)) { return imemo_fields_copy_append(owner, original_fields_obj, current_shape_id, target_shape_id, val); } From f9d0531cc7809284be317fac4a5637391c934e6a Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 12:05:02 +0200 Subject: [PATCH 17/20] shape.h: get rid of rb_shape_id_with_capacity --- gc.c | 2 +- shape.h | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/gc.c b/gc.c index ff99b5b256e578..a4e87b99bd9557 100644 --- a/gc.c +++ b/gc.c @@ -1039,7 +1039,7 @@ rb_newobj(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size, &actual_alloc_size); GC_ASSERT(actual_alloc_size >= size); - shape_id = (shape_id & ~SHAPE_ID_CAPACITY_MASK) | rb_shape_id_with_capacity(rb_shape_capacity_for_slot_size(actual_alloc_size)); + shape_id = rb_shape_transition_capacity(shape_id, rb_shape_capacity_for_slot_size(actual_alloc_size)); #if RACTOR_CHECK_MODE void rb_ractor_setup_belonging(VALUE obj); diff --git a/shape.h b/shape.h index ef4ff245c8788d..e7cb88df36257b 100644 --- a/shape.h +++ b/shape.h @@ -286,18 +286,6 @@ rb_shape_embedded_capacity(shape_id_t shape_id) return (attr_index_t)((shape_id & SHAPE_ID_CAPACITY_MASK) >> SHAPE_ID_CAPACITY_OFFSET); } -static inline shape_id_t -rb_shape_id_with_capacity(size_t capacity) -{ - shape_id_t capacity_flags = (shape_id_t)capacity << SHAPE_ID_CAPACITY_OFFSET; - - RUBY_ASSERT(capacity <= SHAPE_ID_CAPACITY_MAX); - RUBY_ASSERT((capacity_flags & SHAPE_ID_CAPACITY_MASK) == capacity_flags); - RUBY_ASSERT(rb_shape_embedded_capacity(capacity_flags) == capacity); - - return ROOT_SHAPE_ID | capacity_flags; -} - static inline attr_index_t rb_shape_capacity_for_slot_size(size_t slot_size) { @@ -526,7 +514,10 @@ rb_shape_transition_offset(shape_id_t shape_id, shape_id_t offset) static inline shape_id_t rb_shape_transition_capacity(shape_id_t shape_id, size_t capacity) { - return (shape_id & (~SHAPE_ID_CAPACITY_MASK)) | rb_shape_id_with_capacity(capacity); + RUBY_ASSERT(capacity <= SHAPE_ID_CAPACITY_MAX); + + shape_id_t capacity_flags = (shape_id_t)capacity << SHAPE_ID_CAPACITY_OFFSET; + return (shape_id & (~SHAPE_ID_CAPACITY_MASK)) | capacity_flags; } shape_id_t rb_shape_transition_object_id(shape_id_t shape_id); From 64e9855098d53dc79e4d38c7be4d1d7f9f68518f Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 7 Jul 2026 12:10:01 +0200 Subject: [PATCH 18/20] Introduce rb_shape_transition_slot_size --- gc.c | 4 ++-- shape.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index a4e87b99bd9557..aec4aba173a9f8 100644 --- a/gc.c +++ b/gc.c @@ -378,7 +378,7 @@ rb_gc_obj_changed_slot_size(VALUE obj, size_t slot_size) { RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); - RBASIC_SET_FULL_SHAPE_ID(obj, rb_obj_shape_transition_capacity(obj, rb_shape_capacity_for_slot_size(slot_size))); + RBASIC_SET_FULL_SHAPE_ID(obj, rb_obj_shape_transition_slot_size(obj, slot_size)); } void rb_vm_update_references(void *ptr); @@ -1039,7 +1039,7 @@ rb_newobj(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size, &actual_alloc_size); GC_ASSERT(actual_alloc_size >= size); - shape_id = rb_shape_transition_capacity(shape_id, rb_shape_capacity_for_slot_size(actual_alloc_size)); + shape_id = rb_shape_transition_slot_size(shape_id, actual_alloc_size); #if RACTOR_CHECK_MODE void rb_ractor_setup_belonging(VALUE obj); diff --git a/shape.h b/shape.h index e7cb88df36257b..fb5a9616e0ca13 100644 --- a/shape.h +++ b/shape.h @@ -520,6 +520,12 @@ rb_shape_transition_capacity(shape_id_t shape_id, size_t capacity) return (shape_id & (~SHAPE_ID_CAPACITY_MASK)) | capacity_flags; } +static inline shape_id_t +rb_shape_transition_slot_size(shape_id_t shape_id, size_t slot_size) +{ + return rb_shape_transition_capacity(shape_id, rb_shape_capacity_for_slot_size(slot_size)); +} + shape_id_t rb_shape_transition_object_id(shape_id_t shape_id); static inline shape_id_t @@ -541,6 +547,12 @@ rb_obj_shape_transition_capacity(VALUE obj, size_t capacity) return rb_shape_transition_capacity(RBASIC_SHAPE_ID(obj), capacity); } +static inline shape_id_t +rb_obj_shape_transition_slot_size(VALUE obj, size_t slot_size) +{ + return rb_shape_transition_slot_size(RBASIC_SHAPE_ID(obj), slot_size); +} + static inline shape_id_t rb_obj_shape_transition_object_id(VALUE obj) { From 23f02835e817d32aa14eac256ce9e47eaa70b0ad Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 7 Jul 2026 19:49:01 +0900 Subject: [PATCH 19/20] Fix argument types Follow-up of GH-17190. --- probes.d | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/probes.d b/probes.d index abf0d50e7f1c26..cfe28838343992 100644 --- a/probes.d +++ b/probes.d @@ -240,7 +240,7 @@ provider ruby { * `popped_count` the number of objects popped from the mark stack in that invocation */ - probe gc__mark_stacked_objects(int popped_count); + probe gc__mark_stacked_objects(size_t popped_count); /* ruby:::gc-sweep_page(slot_size, final_slots, freed_slots, empty_slots); @@ -260,7 +260,7 @@ provider ruby { * `obj` the pointer to the allocated object * `flags` the initial flags of the object */ - probe gc__obj_new(void *obj, int flags); + probe gc__obj_new(void *obj, uintptr_t flags); /* ruby:::gc-obj_free(); @@ -270,7 +270,7 @@ provider ruby { * `obj` the pointer to the finalized object * `flags` the flags of the object when it is finalized */ - probe gc__obj_free(void *obj, int flags); + probe gc__obj_free(void *obj, uintptr_t flags); /* ruby:::gc-xmalloc(n, size); @@ -280,7 +280,7 @@ provider ruby { * `n` the number of elements. For `ruby_xmalloc` it is 1. * `size` the size of each element */ - probe gc__xmalloc(int n, int size); + probe gc__xmalloc(size_t n, size_t size); /* ruby:::gc-xcalloc(); @@ -290,7 +290,7 @@ provider ruby { * `n` the number of elements. For `ruby_xmalloc` it is 1. * `size` the size of each element */ - probe gc__xcalloc(int n, int size); + probe gc__xcalloc(size_t n, size_t size); /* ruby:::gc-xfree(ptr, size); @@ -300,7 +300,7 @@ provider ruby { * `ptr` the pointer to the object * `size` the size of the object. 0 if called with `xfree` */ - probe gc__xfree(void *obj, int size); + probe gc__xfree(void *obj, size_t size); /* ruby:::gvl-acquire(); From 3f7d4bc859f03f4c7ea131af910d1c4ae142586d Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 7 Jul 2026 19:36:19 +0900 Subject: [PATCH 20/20] [Bug #22183] Preserve `lpar_beg` across interpolation Fix unexpected `keyword_do_LAMBDA` inside string interpolation within default argument of lambda. --- parse.y | 5 +++++ test/ruby/test_parse.rb | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/parse.y b/parse.y index 55403f7bde96fa..b6fc74c5a0af3a 100644 --- a/parse.y +++ b/parse.y @@ -6062,6 +6062,10 @@ string_content : tSTRING_CONTENT[content] $$ = p->lex.brace_nest; p->lex.brace_nest = 0; }[brace] + { + $$ = p->lex.lpar_beg; + p->lex.lpar_beg = -1; + }[lpar] { $$ = p->heredoc_indent; p->heredoc_indent = 0; @@ -6073,6 +6077,7 @@ string_content : tSTRING_CONTENT[content] p->lex.strterm = $term; SET_LEX_STATE($state); p->lex.brace_nest = $brace; + p->lex.lpar_beg = $lpar; p->heredoc_indent = $indent; p->heredoc_line_indent = -1; if ($compstmt) nd_unset_fl_newline($compstmt); diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index f1a7b9a311a328..e9e274a32dd2ab 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -323,6 +323,12 @@ def test_do_lambda end a.call assert_equal(42, b) + + obj = BasicObject.new + assert_nothing_raised do + a = obj.instance_eval('-> a = "#{foo do end}" do end', __FILE__, __LINE__) + end + assert_raise_with_message(NoMethodError, /foo/) {a.call} end def test_block_call_colon2