| Age | Commit message (Collapse) | Author |
|
This reverts commit 228d13f6ed914d1e7f6bd2416e3f5be8283be865.
This commit makes default.c and mmtk.c depend on shape.h, which prevents
them from building independently.
|
|
|
|
Attempt to fix the following SEGV:
```
ruby(gc_mark) ../src/gc/default/default.c:4429
ruby(gc_mark_children+0x45) [0x560b380bf8b5] ../src/gc/default/default.c:4625
ruby(gc_mark_stacked_objects) ../src/gc/default/default.c:4647
ruby(gc_mark_stacked_objects_all) ../src/gc/default/default.c:4685
ruby(gc_marks_rest) ../src/gc/default/default.c:5707
ruby(gc_marks+0x4e7) [0x560b380c41c1] ../src/gc/default/default.c:5821
ruby(gc_start) ../src/gc/default/default.c:6502
ruby(heap_prepare+0xa4) [0x560b380c4efc] ../src/gc/default/default.c:2074
ruby(heap_next_free_page) ../src/gc/default/default.c:2289
ruby(newobj_cache_miss) ../src/gc/default/default.c:2396
ruby(RB_SPECIAL_CONST_P+0x0) [0x560b380c5df4] ../src/gc/default/default.c:2420
ruby(RB_BUILTIN_TYPE) ../src/include/ruby/internal/value_type.h:184
ruby(newobj_init) ../src/gc/default/default.c:2136
ruby(rb_gc_impl_new_obj) ../src/gc/default/default.c:2500
ruby(newobj_of) ../src/gc.c:996
ruby(rb_imemo_new+0x37) [0x560b380d8bed] ../src/imemo.c:46
ruby(imemo_fields_new) ../src/imemo.c:105
ruby(rb_imemo_fields_new) ../src/imemo.c:120
```
I have no reproduction, but my understanding based on the backtrace
and error is that GC is triggered inside `newobj_init` causing the
new object to be marked while in a incomplete state.
I believe the fix is to pass the `shape_id` down to `newobj_init`
so it can be set before the GC has a chance to trigger.
|
|
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`.
|
|
[Bug #21710]
- struct.c: `struct_alloc`
It is possible for a `NEWOBJ` tracepoint call back to write fields
into a newly allocated object before `struct_alloc` had the time
to set the `RSTRUCT_GEN_FIELDS` flags and such.
Hence we can't blindly initialize the `fields_obj` reference to `0`
we first need to check no fields were added yet.
- object.c: `rb_class_allocate_instance`
Similarly, if a `NEWOBJ` tracepoint tries to set fields on the object,
the `shape_id` must already be set, as it's required on T_OBJECT to
know where to write fields.
`NEWOBJ_OF` had to be refactored to accept a `shape_id`.
|
|
We know the argument is not a class, module or special const, so we can
skip these checks.
|
|
|
|
|
|
The type name bindgen picks for anonymous enums creates desync issues on
the bindgen CI checks.
|
|
While accessing the ivars of other types is too complicated to
realistically generate the ASM for it, we can at least provide
the ivar index as to not have to lookup the shape tree every
time.
```
compare-ruby: ruby 3.5.0dev (2025-08-27T14:58:58Z merge-vm-setivar-d.. 5b749d8e53) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-08-28T17:58:32Z yjit-get-exivar efaa8c9b09) +YJIT +PRISM [arm64-darwin24]
| |compare-ruby|built-ruby|
|:--------------------------|-----------:|---------:|
|vm_ivar_get_on_obj | 930.458| 936.865|
| | -| 1.01x|
|vm_ivar_get_on_class | 134.471| 431.622|
| | -| 3.21x|
|vm_ivar_get_on_generic | 146.679| 284.408|
| | -| 1.94x|
```
Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
|
|
The embed layout is way more common than the heap one,
especially since WVA.
I think it makes for more readable code to inverse the
flag.
|
|
|
|
|
|
Now that the shape_id has been unified across all types
this helper function doesn't do much over `RBASIC_SET_SHAPE_ID`.
It still check if the write is needed, but it doesn't seem useful
in places where it's used.
|
|
Its usage was removed in 306d50811dd060d876d1eb364a0d5e6106f5e4f1.
|
|
`RSHAPE_PARENT` is error prone because it returns a raw untagged
shape_id.
To check if a shape is a direct parent of another, tags should be
discarded. So providing a comparison function is better than exposing
untagged ids.
|
|
* YJIT: Side-exit on String#dup when it's not leaf
* Use an enum instead of a macro for bindgen
|
|
When sharing between threads we need both atomic reads and writes. We
probably didn't need to use this in some cases (where we weren't running
in multi-ractor mode) but I think it's best to be consistent.
|
|
|
|
These two functions are very similar, they can share most of their
logic.
|
|
|
|
The `FL_FREEZE` flag is redundant with `SHAPE_ID_FL_FROZEN`, so
ideally it should be eliminated in favor of the later.
Doing so would eliminate the risk of desync between the two, but
also solve the problem of the frozen status being global in namespace
context (See Bug #21330).
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13626
|
|
We still keep setting `FL_EXIVAR` so that `rb_shape_verify_consistency`
can detect discrepancies.
Notes:
Merged: https://github.com/ruby/ruby/pull/13612
|
|
The FL_EXIVAR is a bit redundant with the shape_id.
Now that the `shape_id` is embedded in all objects on all archs,
we can cheaply check if an object has any fields with a simple
bitmask.
Notes:
Merged: https://github.com/ruby/ruby/pull/13612
|
|
This allow checking if an object has ivars with just a shape_id
mask.
Notes:
Merged: https://github.com/ruby/ruby/pull/13606
|
|
id_frozen and id_t_object are no longer used.
id_object_id no longer need to be exposed.
Notes:
Merged: https://github.com/ruby/ruby/pull/13605
|
|
There is no point allocating it during init, it adds
a useless indirection.
Notes:
Merged: https://github.com/ruby/ruby/pull/13596
|
|
It's a useless indirection.
Notes:
Merged: https://github.com/ruby/ruby/pull/13596
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13596
|
|
Since the shape_tree_ptr is `extern` it should be possible to
fully inline `RSHAPE`.
Notes:
Merged: https://github.com/ruby/ruby/pull/13596
|
|
This behave almost exactly as a T_OBJECT, the layout is entirely
compatible.
This aims to solve two problems.
First, it solves the problem of namspaced classes having
a single `shape_id`. Now each namespaced classext
has an object that can hold the namespace specific
shape.
Second, it open the door to later make class instance variable
writes atomics, hence be able to read class variables
without locking the VM.
In the future, in multi-ractor mode, we can do the write
on a copy of the `fields_obj` and then atomically swap it.
Considerations:
- Right now the `RClass` shape_id is always synchronized,
but with namespace we should likely mark classes that have
multiple namespace with a specific shape flag.
Notes:
Merged: https://github.com/ruby/ruby/pull/13411
|
|
The type isn't opaque because Ruby isn't often compiled with LTO,
so for optimization purpose it's better to allow as much inlining
as possible.
However ideally only `shape.c` and `shape.h` should deal with
the actual struct, and everything else should just deal with opaque
`shape_id_t`.
Notes:
Merged: https://github.com/ruby/ruby/pull/13586
|
|
YJIT x86 backend would crahs if the shape_id top bit was set.
This should have been fixed now.
Notes:
Merged: https://github.com/ruby/ruby/pull/13585
|
|
Now that there no longer multiple shape roots, all we need to do
when moving an object from one slot to the other is to update the
`heap_index` part of the shape_id.
Since this never need to create a shape transition, it will always
work and never result in a complex shape.
Notes:
Merged: https://github.com/ruby/ruby/pull/13556
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13556
|
|
Now that we have the `heap_index` in shape flags we no longer
need `T_OBJECT` shapes.
Notes:
Merged: https://github.com/ruby/ruby/pull/13556
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13556
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13556
|
|
This is preparation to getting rid of `T_OBJECT` transitions.
By first only replicating the information it's easier to ensure
consistency.
Notes:
Merged: https://github.com/ruby/ruby/pull/13556
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13524
|
|
Now all flags are only in the `shape_id_t`, and can all be checked
without needing to dereference a pointer.
Notes:
Merged: https://github.com/ruby/ruby/pull/13515
|
|
Instead it's now a `shape_id` flag.
This allows to check if an object is complex without having
to chase the `rb_shape_t` pointer.
Notes:
Merged: https://github.com/ruby/ruby/pull/13511
|
|
Freezing an object changes its `shape_id` This is necessary
so that `setivar` routines can use the `shape_id` as a cache key
and save on checking the frozen status every time.
However for `getivar` routines, this causes needless cache misses.
By clearing that bit we increase hit rate in codepaths that see
both frozen and mutable objects.
Notes:
Merged: https://github.com/ruby/ruby/pull/13289
|
|
Instead `shape_id_t` higher bits contain flags, and the first one
tells whether the shape is frozen.
This has multiple benefits:
- Can check if a shape is frozen with a single bit check instead of
dereferencing a pointer.
- Guarantees it is always possible to transition to frozen.
- This allow reclaiming `FL_FREEZE` (not done yet).
The downside is you have to be careful to preserve these flags
when transitioning.
Notes:
Merged: https://github.com/ruby/ruby/pull/13289
|
|
Followup: https://github.com/ruby/ruby/pull/13341 / [Feature #21353]
Even thought `shape_id_t` has been make 32bits, we were still limited
to use only the lower 16 bits because they had to fit alongside `attr_index_t`
inside a `uintptr_t` in inline caches.
By enlarging inline caches we can unlock the full 32bits on all
platforms, allowing to use these extra bits for tagging.
Notes:
Merged: https://github.com/ruby/ruby/pull/13500
|
|
Whenever we run into an inline cache miss when we try to set
an ivar, we may need to take the global lock, just to be able to
lookup inside `shape->edges`.
To solve that, when we're in multi-ractor mode, we can treat
the `shape->edges` as immutable. When we need to add a new
edge, we first copy the table, and then replace it with
CAS.
This increases memory allocations, however we expect that
creating new transitions becomes increasingly rare over time.
```ruby
class A
def initialize(bool)
@a = 1
if bool
@b = 2
else
@c = 3
end
end
def test
@d = 4
end
end
def bench(iterations)
i = iterations
while i > 0
A.new(true).test
A.new(false).test
i -= 1
end
end
if ARGV.first == "ractor"
ractors = 8.times.map do
Ractor.new do
bench(20_000_000 / 8)
end
end
ractors.each(&:take)
else
bench(20_000_000)
end
```
The above benchmark takes 27 seconds in Ractor mode on Ruby 3.4,
and only 1.7s with this branch.
Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
Notes:
Merged: https://github.com/ruby/ruby/pull/13441
|
|
Ensure the same helpers are used for packing and unpacking.
Notes:
Merged: https://github.com/ruby/ruby/pull/13455
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13450
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13450
|