Commit graph

203 commits

Author SHA1 Message Date
Jean Boussier
fb68721f63 Rename imemo_class_fields -> imemo_fields 2025-06-17 15:28:05 +02:00
ydah
2d96400c26 Fix typo in error message for shape_id verification 2025-06-15 22:35:50 +09:00
Jean Boussier
15084fbc3c Get rid of FL_EXIVAR
Now that the shape_id gives us all the same information, it's no
longer needed.
2025-06-13 23:50:30 +02:00
Jean Boussier
6dbe24fe56 Use the shape_id rather than FL_EXIVAR
We still keep setting `FL_EXIVAR` so that `rb_shape_verify_consistency`
can detect discrepancies.
2025-06-13 23:50:30 +02:00
Jean Boussier
b51078f82e Enforce consistency between shape_id and FL_EXIVAR
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.
2025-06-13 23:50:30 +02:00
Nobuyoshi Nakada
f2d7c6afee Suppress unused-variable warning 2025-06-13 22:24:09 +02:00
Jean Boussier
a99d941cac Add SHAPE_ID_HAS_IVAR_MASK for quick ivar check
This allow checking if an object has ivars with just a shape_id
mask.
2025-06-13 19:46:29 +02:00
Nobuyoshi Nakada
1d11e1be13
Suppress unused-variable warning 2025-06-13 21:09:20 +09:00
Jean Boussier
071aa02a4a shape.c: cleanup unused IDs
id_frozen and id_t_object are no longer used.
id_object_id no longer need to be exposed.
2025-06-13 12:03:22 +02:00
Jean Boussier
7c22330cd2 Allocate rb_shape_tree statically
There is no point allocating it during init, it adds
a useless indirection.
2025-06-12 17:08:22 +02:00
Jean Boussier
de4b910381 Get rid of GET_SHAPE_TREE()
It's a useless indirection.
2025-06-12 17:08:22 +02:00
Jean Boussier
e070d93573 Get rid of rb_shape_lookup 2025-06-12 17:08:22 +02:00
Jean Boussier
0292b702c4 shape.h: make RSHAPE static inline
Since the shape_tree_ptr is `extern` it should be possible to
fully inline `RSHAPE`.
2025-06-12 17:08:22 +02:00
Jean Boussier
8b5ac5abf2 Fix class instance variable inside namespaces
Now that classes fields are delegated to an object with its own
shape_id, we no longer need to mark all classes as TOO_COMPLEX.
2025-06-12 13:43:29 +02:00
Jean Boussier
3abdd4241f Turn rb_classext_t.fields into a T_IMEMO/class_fields
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.
2025-06-12 07:58:16 +02:00
Jean Boussier
95201299fd Refactor the last references to rb_shape_t
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`.
2025-06-11 16:38:38 +02:00
Jean Boussier
c2f2ac7db3 shape.c: Fix rb_bug call to use correct format for size_t 2025-06-11 10:10:04 +02:00
Étienne Barrié
c54e96d651 Fix RubyVM::Shape.transition_tree 2025-06-10 19:37:03 +02:00
Jean Boussier
7d8695e02f Stop pinning shape edges
Now that `rb_shape_traverse_from_new_root` has been eliminated there's
no longer any reason to pin these objects, because we no longer
need to traverse shapes downward during compaction.
2025-06-07 18:30:44 +02:00
Jean Boussier
a640723d31 Simplify rb_gc_rebuild_shape
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.
2025-06-07 18:30:44 +02:00
Jean Boussier
191f6e3b87 Get rid of rb_shape_t.heap_id 2025-06-07 18:30:44 +02:00
Jean Boussier
6eb0cd8df7 Get rid of SHAPE_T_OBJECT
Now that we have the `heap_index` in shape flags we no longer
need `T_OBJECT` shapes.
2025-06-07 18:30:44 +02:00
Jean Boussier
2de67d424f shape.c: assert we're not returning INVALID_SHAPE_ID. 2025-06-07 18:30:44 +02:00
Jean Boussier
8c4e368dcf shape.c: ensure heap_index is consistent for complex shapes 2025-06-07 18:30:44 +02:00
Jean Boussier
689ec51146 Replicate heap_index in shape_id flags.
This is preparation to getting rid of `T_OBJECT` transitions.
By first only replicating the information it's easier to ensure
consistency.
2025-06-07 18:30:44 +02:00
Jean Boussier
90ba2f4e1c Add missing lock around redblack_cache_ancestors
This used to be protected because all shape code was
under a lock, but now that the shape tree is lock-free
we still need to lock around the red-black cache.

Co-Authored-By: Luke Gruber <luke.gruber@shopify.com>
2025-06-06 23:07:22 +02:00
Jean Boussier
2b810ac595 shape.c: match capacity growth with T_OBJECT embedded sizes
This helps with getting with of `SHAPE_T_OBJECT`, by ensuring
that transitions will have capacities that match the next embed size.
2025-06-06 13:37:03 +02:00
Jean Boussier
3883c38979 shape.c: Fix improperly named routine
Meant to be `transition_complex` not `transition_frozen`.
2025-06-06 11:43:51 +02:00
Jean Boussier
4e39580992 Refactor raw accesses to rb_shape_t.capacity 2025-06-05 22:06:15 +02:00
Étienne Barrié
2f80117ce4 Fix comment about debugging shapes
This method was moved to RubyVM::Shape in 913979bede.
2025-06-05 18:02:45 +02:00
Jean Boussier
772fc1f187 Get rid of rb_shape_t.flags
Now all flags are only in the `shape_id_t`, and can all be checked
without needing to dereference a pointer.
2025-06-05 07:44:44 +02:00
Jean Boussier
675f33508c Get rid of TOO_COMPLEX shape type
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.
2025-06-04 13:13:50 +02:00
Jean Boussier
6b8dcb7c8f shape.c: fix off by one error in shape_tree_mark 2025-06-04 07:59:20 +02:00
Jean Boussier
625d6a9cbb Get rid of frozen shapes.
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.
2025-06-04 07:59:20 +02:00
Jean Boussier
db2cfebff1 Pin shape->edges 2025-06-02 17:49:53 +02:00
Jean Boussier
e9fd44dd72 shape.c: Implement a lock-free version of get_next_shape_internal
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>
2025-06-02 17:49:53 +02:00
Jean Boussier
6c4ae85211 Rename rb_shape_frozen_shape_p -> shape_frozen_p 2025-05-27 15:34:02 +02:00
Jean Boussier
ccf2b7c5b8 Refactor rb_shape_too_complex_p to take a shape_id_t. 2025-05-27 15:34:02 +02:00
Jean Boussier
a1f72d23a9 Refactor rb_shape_has_object_id
Now takes a `shape_id_t` and the version that takes a `rb_shape_t *`
is private.
2025-05-27 15:34:02 +02:00
Jean Boussier
a80a5000ab Refactor rb_obj_shape out.
It still exists but only in `shape.c`.
2025-05-27 15:34:02 +02:00
Jean Boussier
97f44ac54e Get rid of rb_shape_set_shape 2025-05-27 15:34:02 +02:00
Jean Boussier
a59835e1d5 Refactor rb_shape_get_iv_index to take a shape_id_t
Further reduce exposure of `rb_shape_t`.
2025-05-27 15:34:02 +02:00
Jean Boussier
e535f8248b Get rid of rb_shape_id(rb_shape_t *)
We should avoid conversions from `rb_shape_t *` into `shape_id_t`
outside of `shape.c` as the short term goal is to have `shape_id_t`
contain tags.
2025-05-27 12:45:24 +02:00
Jean Boussier
8b0868cbb1 Refactor rb_shape_rebuild_shape to stop exposing rb_shape_t 2025-05-27 12:45:24 +02:00
John Hawthorn
f483befd90 Add shape_id to RBasic under 32 bit
This makes `RBobject` `4B` larger on 32 bit systems
but simplifies the implementation a lot.

[Feature #21353]

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2025-05-26 10:31:54 +02:00
Nobuyoshi Nakada
aad9fa2853
Use RB_VM_LOCKING 2025-05-25 15:22:43 +09:00
Jean Boussier
52da5f8bbc Refactor rb_shape_transition_remove_ivar
Move the fields management logic in `rb_ivar_delete`, and keep
shape managment logic in `rb_shape_transition_remove_ivar`.
2025-05-23 17:33:17 +02:00
Jean Boussier
a6435befa7 variable.c: Refactor rb_obj_field_* to take shape_id_t 2025-05-13 10:35:34 +02:00
Jean Boussier
f2e5f6dbb6 Allow T_CLASS and generic types to be too_complex
The intial complex shape implementation never allowed objects
other than T_OBJECT to become too complex, unless we run out of
shapes.

I don't see any reason to prevent that.

Ref: https://github.com/ruby/ruby/pull/6931
2025-05-11 19:35:58 +02:00
Satoshi Tagomori
382645d440 namespace on read 2025-05-11 23:32:50 +09:00