899 Commits

Author SHA1 Message Date
Nobuyoshi Nakada
2d616a9bb0
Remove unused autoload table copier
Remove the copier left unused when class fields cloning replaced raw
table copying in commit 3abdd4241fd5.
2026-07-29 11:42:03 +09:00
Jean Boussier
40f105bd55 Change RStruct to match SHAPE_ID_LAYOUT_EXTENDED
THe downside is that we now always reserve one 8B reference for the
IMEMO/fields, even for structs that never have any ivars.

But it is an acceptable tradeoff given that ivars on structs aren't
rare.

Co-Authored-By: John Hawthorn <john@hawthorn.email>
2026-07-21 08:30:12 +02:00
Jean Boussier
74bd057f9c Get rid of ROBJECT_FIELDS_HASH 2026-07-15 20:52:47 +02:00
Jean Boussier
efb3a73445 rb_imemo_fields_ptr: assume embedded 2026-07-15 20:52:47 +02:00
Jean Boussier
6cd755deab variable.c: generalize use of rb_obj_fields()
Numerous codepath have the same `switch/case` with very little
differing logic compared to `rb_obj_fields`, might as well
use it everywhere and share more code.
2026-07-15 19:09:20 +02:00
Jean Boussier
86951165b8 Get rid of ROBJECT_HEAP flag
This flag is now entirely redundant with the shape_id layout bits.
2026-07-15 15:43:28 +02:00
Jean Boussier
4bd94ccbbd variable.c: only filter out object_id, not all internal ivars 2026-07-14 23:50:17 +02:00
Jean Boussier
944d936d7c variable.c: Refactor too_complex evacuation paths
Now share more code between the various cases:

  - Whether to copy all fields or just ivars
  - Whether to add or remove some capacity.
2026-07-14 23:50:17 +02:00
Jean Boussier
24337e3cbe Refactor rb_obj_copy_ivar
Share more code with `rb_copy_generic_ivar`.
2026-07-14 23:50:17 +02:00
Jean Boussier
6c23075086 rb_copy_generic_ivar: cleanup goto statement
It used to be that multiple paths would need to clear
the generic ivar reference, but it's no longer the case.
2026-07-14 23:50:17 +02:00
Jean Boussier
6f0548a0bd Simplify rb_obj_copy_ivar and rb_copy_generic_ivar
By handling complex shapes in `rb_shape_rebuild` we can merge
the two complex cases (when `src` is complex and when `dest`
becomes complex).
2026-07-14 23:50:17 +02:00
Jean Boussier
71ee81f7d9 imemo.c: embed the st_table inside rb_fields
Followup: https://github.com/ruby/ruby/pull/17631

Now that complex `RObject` always have an IMEMO/fields, we no
longer need to have an external `st_table` to replicate the
memory layout of `RObject`.
2026-07-14 23:50:17 +02:00
Jean Boussier
e0083cffa3 Refactor RObject to use T_IMEMO/fields_obj on overflow
Instead of spilling into a raw buffer, `RObject` now spills
into an `IMEMO/fields` object like other types.

From an instance variable layout standpoint, an extended `RObject`
is now identical to a `RTypedData`, as in they both store the
reference to their `IMEMO/fields` at the same offset (`VALUE * 2`).

One positive consequence of this is that the only case where a `T_OBJECT`
needs sweeping is if a finalizer was registered.

YJIT now side exit when it need to write a new ivar into a `RObject`
that is out of space. This is unlikely to cause a performance regression
as this codepath isn't supposed to happen after warmup given `max_iv_count`
is recorded on classes, so future objects should be large enough.
Hence it's best not to waste executable memory for such codepath.

ZJIT lost support for writting into extended `RObject`.
Most of the code to support it is there it's just missing an
implementation of `RBASIC_SET_SHAPE_ID`, which recently changed
to strip some bits out of the shape (see comments).
I will leave it to the ZJIT team to implement it (sorry).

There are a few future cleanups planned that I keep for followups:

  - We can now get rid of the `ROBJECT_HEAP` flag, it's redundant with
    the shape layout bits.
  - `imemo_fields` can now embed the `st_table` when complex.

Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-authored-by: Randy Stauner <randy@r4s6.net>
2026-07-14 10:44:39 +02:00
Jean Boussier
2c574ca7e2 Symbol#to_s now returns a frozen string.
[Feature #22137]

It has returned a chilled string, hence emitting deprecation
warnings for two versions.
2026-07-10 10:02:00 +02:00
Jean Boussier
c966f3abd7 imemo.c: refactor imemo_fields_new
Lean on `rb_newobj` to get the shape embedded capacity properly
set.
2026-07-07 13:12:35 +02:00
Jean Boussier
263f06ad49 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.
2026-07-07 13:12:35 +02:00
John Hawthorn
daffaaee73 Mark most TypedData types as THREAD_SAFE_FREE
Set RUBY_TYPED_THREAD_SAFE_FREE on TypedData types whose dfree function
is trivially safe, and only frees its own memory

Types not yet marked as THREAD_SAFE_FREE:
- id2ref_tbl_type: sets the process-global id2ref_tbl to NULL.
- mutex_data_type: unlinks itself from another thread's keeping_mutexes list.
- autoload_data_type: deletes nodes from a shared intrusive list.
- autoload_const_type: deletes itself from a shared list.
- rb_cont_data_type: mutates the shared fiber pool and the global first_jit_cont list.
- rb_fiber_data_type: delegates to cont_free.
- FiberPoolDataType: mutates shared fiber pool free-lists.
- ractor_data_type: reconfigures global VM event-hook flags and call caches.
- exported_object_registry: frees a global table under the VM lock.
- rb_box_data_type (Box::Entry): unlinks classext from other live class and module objects.
- box_ext_cleanup_type: dereferences another String object and calls unlink.
- monitor_data_type: uses the default free, so the flag has no effect.

Co-authored-by: Luke Gruber <luke.gruber@shopify.com>
2026-06-23 13:30:26 -07:00
Aaron Patterson
c4d09f416c
Reapply "Reserve 2 bits for expressing object layout (#17139)" (#17158)
This reverts commit ddb5055d961d970aded287cfebd07b78efee3ca7.
2026-06-01 19:30:58 +00:00
Aaron Patterson
ddb5055d96
Revert "Reserve 2 bits for expressing object layout (#17139)"
This reverts commit 63d9f090b5d9461cf0b9446e0039d9c56156b826.
2026-05-29 16:41:31 -07:00
Aaron Patterson
63d9f090b5
Reserve 2 bits for expressing object layout (#17139)
* Reserve 2 bits for expressing object layout

We would like to make instance variable reads in the JIT compiler faster
(as well as simplify the JIT implementation).  Currently, in order to
read an instance variable, we have to:

1. Test for heap object
2. Load object to a 64 bit register
3. Mask the object header
4. Bit test against the masked header
5. JNE
6. Load field

We would like to:

1. Test for heap object
2. Load object shape to a 32 bit register
3. Bit test against the shape
4. JNE
5. Load field

The way we fetch instance variables is not consistent across objects.
In order to realize our goal, we need to encode object layout inside the
shape.  If we encode object layout inside the shape, then the shape
itself will guarantee that the access pattern generated by the JIT
compiler is correct.

We should encode the following load patterns into the shape tag bits.
This way we can share shapes on transitions, but be able to
differentiate the access patterns for the JIT compiler.  In other words,
two objects can have an `@a -> @b -> @c` transition and share the same
shape, but the tag bits can differentiate the access pattern so that the
JIT compiler can be confident that the machine code is correct.

Here are the patterns:

1. Embedded/Extended T_OBJECT Instance Variables

Objects with direct references to instance variables or via malloc
buffer

2. Objects with fields_objects fields

These are Data and TypedData objects.  They have an associated axillary
imemo/fields object that stores the instance variables.  The access
pattern is `object[2] + 2`.  The fields object is the 3rd field, and the
instance variables start at +2 inside the fields object.  The fields
object itself is a Ruby object, so it contains the usual header bits +
class headers.

3. Non Boxable Classes / Modules

This is similar to Objects with fields_objects, but the fields object is
stored at a different offset.  We’re differentiating this from boxable
classes and modules because those are harder to support.

4. Other

"Other" pattern is for objects that are rare, or have
difficult-to-implement access patterns.  This includes:

* Boxable classes and modules
* Structs (for now)
* Objects that use the geniv table

Proposed shape bit layout:

```
  Current shape_id_t is 32 bits:
  31        28 27 26 25 24 23 22        19 18                         0
  +-----------+--+--+--+--+--+------------+----------------------------+
  | unused    |L1|L0|OI|FR|CX| heap index | shape tree offset          |
  +-----------+--+--+--+--+--+------------+----------------------------+
               |  |  |  |  |  |            |
               |  |  |  |  |  |            +-- bits 0-18: SHAPE_ID_OFFSET_MASK
               |  |  |  |  |  +--------------- bits 19-22: SHAPE_ID_HEAP_INDEX_MASK
               |  |  |  |  +------------------ bit 23: SHAPE_ID_FL_COMPLEX
               |  |  |  +--------------------- bit 24: SHAPE_ID_FL_FROZEN
               |  |  +------------------------ bit 25: SHAPE_ID_FL_HAS_OBJECT_ID
               +--+--------------------------- bits 26-27: SHAPE_ID_LAYOUT_MASK
```

The important part about these layout patterns is that they do not
reflect the _type_ of object, only how the object is laid out in memory.
For example, we currently treat structs as "other", but we can refactor
them to have the same layout as "Objects with fields_objects", and when
we do that they should get a different bit in the shape header.

This commit only reserves the two bits, it doesn't use them in the JIT
compiler yet.

Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-Authored-By: Max Bernstein <tekknolagi@gmail.com>

* Update gc.c

Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>

* Update shape.h

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>

* fix function name

* Update shape.c

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>

* fix function name

* Revert "Update shape.c"

This reverts commit 900711defc6c541a93f3393a350819ae88cf87f1.

* add comment

---------

Co-authored-by: John Hawthorn <john@hawthorn.email>
Co-authored-by: Max Bernstein <tekknolagi@gmail.com>
Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2026-05-29 15:09:34 -07:00
John Hawthorn
594ae9f073 Give legacy T_DATA the same layout as TypedData
This allows us to treat the two types of T_DATA the same way when
reading instance variables.

Non-TypedData T_DATA are deprecated, so making them faster isn't
particularly important, but it is helpful to avoid needing to test for
TypedData and adding another special case.
2026-05-27 16:18:43 -07:00
Peter Zhu
6bc1aa31cc [Bug #22072] Fix rb_cvar_set for multi-Ractor
rb_cvar_set breaks the RCLASS_CVC_TBL because it creates a new cvc_tbl but
forgets to copy the contents over. This causes the following script to
crash:

    r = Ractor.new {}

    module Foo
      def self.foo = @@foo
    end

    Foo.class_variable_set(:@@foo, nil)
    Foo.class_variable_set(:@@bar, nil)

    puts Foo.foo

Crashes with:

    test.rb:4: [BUG] should have cvar cache entry
    ruby 4.1.0dev (2026-05-16T21:11:21Z define-obj-has-suf.. 642cfc59a7) +PRISM [x86_64-linux]

    -- Control frame information -----------------------------------------------
    c:0003 p:0003 s:0012 e:000011 l:y b:0001 r:(nil) METHOD test.rb:4
    c:0002 p:0036 s:0008 E:001118 l:n b:---- r:(nil) EVAL   test.rb:10 [FINISH]
    c:0001 p:0000 s:0003 E:0013c0 l:y b:---- r:(nil) DUMMY  [FINISH]

    -- Ruby level backtrace information ----------------------------------------
    test.rb:10:in '<main>'
    test.rb:4:in 'foo'

    -- Threading information ---------------------------------------------------
    Total ractor count: 1
    Ruby thread count for this ractor: 1

    -- C level backtrace information -------------------------------------------
    miniruby(rb_print_backtrace+0x24) [0x5701425120eb] vm_dump.c:1108
    miniruby(rb_vm_bugreport+0x374) [0x5701425128f2] vm_dump.c:1456
    miniruby(rb_bug_without_die_internal+0xa0) [0x5701422ad332] error.c:1107
    miniruby(rb_bug+0xb5) [0x5701422ad4de] error.c:1125
    miniruby(update_classvariable_cache+0xd1) [0x5701424d159c] vm_insnhelper.c:1501
    miniruby(vm_getclassvariable+0xd1) [0x5701424d16e4] vm_insnhelper.c:1532
    miniruby(vm_exec_core+0xfa6) [0x5701424e6fa9] insns.def:243
    miniruby(rb_vm_exec+0x140) [0x570142504532] vm.c:2805
    miniruby(rb_iseq_eval_main+0x3d) [0x5701425054c8] vm.c:3101
    miniruby(rb_ec_exec_node+0x128) [0x5701422b914a] eval.c:284
    miniruby(ruby_run_node+0x8d) [0x5701422b92be] eval.c:322
    miniruby(rb_main+0x4c) [0x5701421ccd8e] main.c:42
    miniruby(main+0x4b) [0x5701421ccdef] main.c:62
2026-05-17 09:09:53 -04:00
Jean Boussier
deceb9f62e Rename rb_shape_obj_* into rb_obj_shape_*
For consistency.
2026-05-06 10:10:16 +02:00
Jean Boussier
4eeec614bb shapes: Rename TOO_COMPLEX in just COMPLEX
The `too_` prefix wasn't consistently used and just make the
thing longer for no benefit.
2026-05-06 10:10:16 +02:00
Jean Boussier
678d86f5e4 shape.c: transition to complex when max_capacity is reached
Now that we have 1024B slots, we can store up to 126 fields inline.

Objects larger than this are rare if not non-existent, hence we can
get rid of the `malloc` path for imemo/fields and simply transition
to `TOO_COMPLEX`.

This additionally allows to shrink `attr_index_t` from 16 to 8B.

Note: the ZJIT "ivar on extended" tests are renamed as "complex" because
"extended" AKA malloc allocated imemo/fields no longer exists.
They're now complex fields, AKA st tables.

rb_class_allocate_instance: start as complex when over max_fields

If `RCLASS_MAX_IV_COUNT` is over `max_fields`, allocating a large
slot to end up transitioning to `TOO_COMPLEX` is wasteful.

We might as well start as complex directly.
2026-05-06 09:02:02 +02:00
Nobuyoshi Nakada
31cdf7a9f8
Adjust style 2026-05-04 09:46:54 +09:00
Jean Boussier
c9b53473e3 Refactor shape transition functions
Expose both `rb_obj_shape_` functions that take a `VALUE`
and `rb_shape_` functions that take a `shape_id`.

Make common transition functions such as `complex` and `frozen`
inlineable.

Also get rid of RB_SET_SHAPE_ID and rb_set_boxed_class_shape_id.
2026-05-03 11:36:26 +02:00
Jean Boussier
289343cf0f Create imemo/fields with shape_id
Simplify the API by taking a shape_id upon creation.
2026-05-02 10:33:48 +02:00
John Hawthorn
f2d0ef269b Add and use rb_ivar_foreach_buffered
Previously, rb_ivar_foreach would walk up the shape tree, but yield
instance variables to the callback as it went. If the object shape was
modified during this callback, particularly with removing an instance
variable, it could result in reading free'd memory.

This commit solves this by adding a version which buffering all instance
variable names and values before calling the callback, giving a snapshot
of the object at the time rb_ivar_foreach_buffered is called.

The buffer is made with ALLOCV_N, so the performance difference should
be minimal, and I don't think this method is particuarly heavily used.

[Bug #21996]
2026-04-26 17:14:39 +09:00
John Hawthorn
96cd1bc765 Fix iv_count for too_complex generic ivar
Previously this branch forgot to assign iv_count when used with a
generic ivar which is too_complex, which could result in it incorrectly
returning 0.
2026-04-26 17:14:39 +09:00
Jean Boussier
47e061277a class.c: Make cvc_tbl a managed object
[Bug #21952]

Solves the double-free or use after-free concern with boxes.
Now entries can safely be used for copy-on-write.

Also is likely necessary to make it save to read cvar from
secondary ractors, as allowed since: ab32c0e690b805cdaaf264ad4c3421696c588204
2026-04-21 16:30:14 +09:00
John Hawthorn
ab324956d7 Avoid array resize in rb_obj_instance_variables
We know (an estimate of) the ivar count upfront (+/- hidden internal
fields), from the shape, so we should reserve the expected length
upfront.
2026-04-13 08:46:15 -07:00
Andrii Furmanets
526344b56e Fix Box regexp match vars after non-match 2026-04-12 02:47:11 +09:00
Jean Boussier
7f4db64f28 Emit already initialzed constant warnings in a single call
When decorating `Warning.warn`, it's much more convenient to get
the whole warning in a single string.
2026-04-10 15:06:19 +02:00
John Hawthorn
dd3542a0cd Fix class variable cache not invalidated by class_variable_set
The previous invalidation walked subclasses but missed cvars from
included modules, and skipped invalidation when creating a new cvar on
modules entirely. Always invalidate when a new class variable is
created, since this should be relatively rare.

We previously bumped the global state on any module inclusion, which
should be far more common than this.

[Bug #21978]
2026-04-01 19:21:18 -07:00
Jean Boussier
53099633e2 Make ruby_xfree_sized and ruby_xrealloc_sized public
[Feature #21861]
2026-04-01 08:10:07 +01:00
Takashi Kokubun
6a5a2612fd variable.c: Fix -Wmaybe-uninitialized warnings in rb_gvar_set/rb_gvar_get
Initialize `entry` and `var` to NULL. They are assigned inside
RB_VM_LOCKING() but used after it, and the compiler cannot prove the
locked block always executes.
2026-03-31 17:24:01 -07:00
Aaron Patterson
ab32c0e690
Allow reading cvars from non-main Ractors (#16308)
Today you can read instance variables from non-main Ractors, but many
Rails applications use cvars, and we cannot read them.

For example:

```ruby
class Foo
  # This is NOT allowed to be read in non-main Ractors
  @@bar = 123
  def self.bar; @@bar; end

  # This is allowed to be read in non-main Ractors
  @baz = 123
  def self.baz; @baz; end
end

  # This is OK
Ractor.new {
  p Foo.baz
}.value

  # Exception here
Ractor.new {
  p Foo.bar
}.value
```

This commit changes the semantics of cvars to be the same as instance
variables:

* It's ok to read Ractor shareable objects from the non-main Ractor
* It's NOT ok to write from the non-main Ractor

[Feature #21942]
2026-03-19 00:41:25 +00:00
John Hawthorn
a2531ba293 Simplify subclasses list, remove from Box
Currently we maintain the subclasses list for two separate purposes (we essentially have to different relationships we're putting into the same list):

1. On a T_MODULE, we track the T_ICLASSes created to include it into
   other classes. Used for method invalidation and propagating includes
   on the module that happen after it's been used
2. On a T_CLASS/T_ICLASS, we track the T_CLASS/T_ICLASS which are the
   immediate children of the class. We use this for method invalidation,
   some cvar things, and to iterate through subclasses.

Purpose 1 does not have any issues with box, the T_ICLASS always belongs
to one specific module and that's immutable. This list can be box-global
(always use the prime classext or hoist it out) and only needs to be
pruned during free. If we care about behaviour under a particular box
(ie. the propagating includes), we should look up the current box being
modified on the ICLASS itself.

Purpose 2 is more complicated. It currently tracks the immediate
children, the T_CLASS or T_ICLASS whose super points back. Because super
is per-box and is mutable (include/prepend insert ICLASSes into the
chain) we need to update the list on include/prepend, entries must be
per-box, and we can have multiple entries per-box. *I propose we
simplify this by no longer tracking the immediate subclass*, but instead
tracking the T_CLASS -> ... -> T_CLASS relationship, ie. the inverse of
rb_class_superclass. That relationship is the same across all boxes and
immutable after Class creation.

As a special case the ICLASS for refinements are also added to the
purpose 2 list (on T_CLASS). As those ICLASS do not chain to an eventual
leaf T_CLASS.

When we need to find the classes which have included a module, we can
use the module subclasses list to find the ICLASS and then use
RCLASS_INCLUDER. If we needed to iterate all T_ICLASS, we could then
walk up the CLASS_SUPER chain, but I didn't find anywhere we needed to
do that.
2026-03-17 14:42:07 -07:00
Jean Boussier
14eb083152 Speedup repeated calls to rb_gccct_clear_table
Profiling of `ruby --disable-all -e 1` shows between 5 and 10%
of the time is spent in `_platform_memset`, called from `rb_gccct_clear_table()`,
itself called from `clear_method_cache_by_id_in_class()`.

Which makes sense, during boot we define numerous methods, and after
each we have to invalidate the `gccct`, which is done by zeroing
1023 pointers, so roughtly 8kiB of memory.

By keeping track of whether the table was used since the last clear,
we can save a lot of useless memory writes.

This may also be beneficial when loading user defined code, as it's unlikely
the `gccct` would be dirtied between two `def`.

NB: profiling such a short lived process gives a lot of variance, but
with a very high sampling rate and multiple attempts, `clear_method_cache_by_id_in_class`
is always in the top 2 hotspots.
2026-03-16 20:53:49 +01:00
Jean Boussier
4e196af17c Use ruby_sized_xfree 2026-02-01 21:57:59 +01:00
Jean Boussier
050c00141c variable.c: Replace ruby_xfree by ruby_sized_xfree when applicable 2026-01-31 10:35:48 +01:00
aguspe
6e2bf5df4e [Tests] Assert Module#set_temporary_name returns self
The return value of Module#set_temporary_name was changed
to return `self`, but the existing tests
did not verify this.
2025-12-24 11:03:20 +09:00
Daisuke Aritomo
0fe111caa6 Respect encoding of ID in exception messages 2025-12-16 17:15:59 -08:00
Daisuke Aritomo
cbcbbb2fbe Let Ractor::IsolationError report correct constant path
Before this patch, Ractor::IsolationError reported an incorrect constant
path when constant was found through `rb_const_get_0()`.

In this code, Ractor::IsolationError reported illegal access against
`M::TOPLEVEL`, where it should be `Object::TOPLEVEL`.

```ruby
TOPLEVEL = [1]

module M
  def self.f
    TOPLEVEL
  end
end

Ractor.new { M.f }.value
```

This was because `rb_const_get_0()` built the "path" part referring to
the module/class passed to it in the first place. When a constant was
found through recursive search upwards, the module/class which the
constant was found should be reported.

This patch fixes this issue by modifying rb_const_search() to take a
VALUE pointer to be filled with the module/class where the constant was
found.

[Bug #21782]
2025-12-16 17:15:59 -08:00
Étienne Barrié
aab4f6287d
Add the instance variable name and the module in Ractor::IsolationError (#15563) 2025-12-16 11:47:13 -05:00
Étienne Barrié
09a29e1312
Add the class variable and the class itself in Ractor::IsolationError (#15562) 2025-12-16 11:06:33 -05:00
Nobuyoshi Nakada
7f41c3e7b1
Add rb_eval_cmd_call_kw to shortcut 2025-12-04 18:07:49 +09:00
John Hawthorn
f9cd94f17d wb-protect autoload_const 2025-12-03 10:13:36 -08:00
Jean Boussier
5770c186d1 Rename rb_obj_exivar_p -> rb_obj_gen_fields_p
The "EXIVAR" terminology has been replaced by "gen fields"
AKA "generic fields".

Exivar implies variable, but generic fields include more than
just variables, e.g. `object_id`.
2025-12-03 15:57:26 +01:00