Ruby 4.1.0dev (2026-05-19 revision 3ef48ef9c8fb3c19d64b4bcf41ed10edb0bfeecc)
shape.h (3ef48ef9c8fb3c19d64b4bcf41ed10edb0bfeecc)
1#ifndef RUBY_SHAPE_H
2#define RUBY_SHAPE_H
3
4#include "internal/gc.h"
5#include "internal/struct.h"
6
7typedef uint8_t attr_index_t;
8typedef uint32_t shape_id_t;
9#define SHAPE_ID_NUM_BITS 32
10#define SHAPE_ID_OFFSET_NUM_BITS 19
11
12STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_BIT);
13
14#define SHAPE_BUFFER_SIZE (1 << SHAPE_ID_OFFSET_NUM_BITS)
15#define SHAPE_ID_OFFSET_MASK (SHAPE_BUFFER_SIZE - 1)
16
17#define SHAPE_ID_HEAP_INDEX_BITS 4
18#define SHAPE_ID_HEAP_INDEX_MAX (((attr_index_t)1 << SHAPE_ID_HEAP_INDEX_BITS) - 1)
19
20#define SHAPE_ID_HEAP_INDEX_OFFSET SHAPE_ID_OFFSET_NUM_BITS
21#define SHAPE_ID_FL_USHIFT (SHAPE_ID_OFFSET_NUM_BITS + SHAPE_ID_HEAP_INDEX_BITS)
22
23// shape_id_t bits:
24// 0-18 SHAPE_ID_OFFSET_MASK
25// index in rb_shape_tree.shape_list. Allow to access `rb_shape_t *`.
26// This is the part that describe how fields are laid out in memory.
27// 19-22 SHAPE_ID_HEAP_INDEX_MASK
28// index in rb_shape_tree.capacities. Allow to access slot size.
29// Currently always 0 except for T_OBJECT.
30// 23 SHAPE_ID_FL_FROZEN
31// Whether the object is frozen or not.
32// 24 SHAPE_ID_FL_HAS_OBJECT_ID
33// Whether the object has an `SHAPE_OBJ_ID` transition.
34// 25 SHAPE_ID_FL_COMPLEX
35// The object is backed by a `st_table`.
36
37enum shape_id_fl_type {
38#define RBIMPL_SHAPE_ID_FL(n) (1<<(SHAPE_ID_FL_USHIFT+n))
39
40 SHAPE_ID_HEAP_INDEX_MASK = ((1 << SHAPE_ID_HEAP_INDEX_BITS) - 1) << SHAPE_ID_HEAP_INDEX_OFFSET,
41
42 SHAPE_ID_FL_COMPLEX = RBIMPL_SHAPE_ID_FL(0),
43 SHAPE_ID_FL_FROZEN = RBIMPL_SHAPE_ID_FL(1),
44 SHAPE_ID_FL_HAS_OBJECT_ID = RBIMPL_SHAPE_ID_FL(2),
45
46 SHAPE_ID_FL_NON_CANONICAL_MASK = SHAPE_ID_FL_FROZEN | SHAPE_ID_FL_HAS_OBJECT_ID,
47 SHAPE_ID_FLAGS_MASK = SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_COMPLEX,
48
49#undef RBIMPL_SHAPE_ID_FL
50};
51
52// This mask allows to check if a shape_id contains any ivar.
53// It relies on ROOT_SHAPE_WITH_OBJ_ID==1.
54enum shape_id_mask {
55 SHAPE_ID_HAS_IVAR_MASK = SHAPE_ID_FL_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1),
56};
57
58// The interpreter doesn't care about frozen status, slot size or object id when reading ivars.
59// So we normalize shape_id by clearing these bits to improve cache hits.
60// JITs however might care about some of it.
61#define SHAPE_ID_READ_ONLY_MASK (~(SHAPE_ID_FL_FROZEN | SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_HAS_OBJECT_ID))
62// For write it's the same idea, but here we do care about frozen status.
63#define SHAPE_ID_WRITE_MASK (~(SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_HAS_OBJECT_ID))
64
65typedef uint32_t redblack_id_t;
66
67#define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * CHAR_BIT) - SHAPE_ID_NUM_BITS)
68#define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
69
70#define SHAPE_MAX_VARIATIONS 8
71
72#define INVALID_SHAPE_ID (SHAPE_BUFFER_SIZE - 1)
73#define ATTR_INDEX_NOT_SET ((attr_index_t)-1)
74
75#define ROOT_SHAPE_ID 0x0
76#define ROOT_SHAPE_WITH_OBJ_ID 0x1
77#define ROOT_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_COMPLEX)
78#define ROOT_COMPLEX_WITH_OBJ_ID (ROOT_SHAPE_WITH_OBJ_ID | SHAPE_ID_FL_COMPLEX | SHAPE_ID_FL_HAS_OBJECT_ID)
79
80enum shape_type {
81 SHAPE_ROOT,
82 SHAPE_IVAR,
83 SHAPE_OBJ_ID,
84};
85
86struct rb_shape {
87 VALUE edges; // id_table from ID (ivar) to next shape
88 ID edge_name; // ID (ivar) for transition from parent to rb_shape
89 redblack_id_t ancestor_index;
90 shape_id_t parent_offset;
91 attr_index_t next_field_index; // Fields are either ivars or internal properties like `object_id`
92 attr_index_t capacity; // Total capacity of the object with this shape
93 enum shape_type type : 8;
94};
95
96typedef struct rb_shape rb_shape_t;
97
98enum shape_flags {
99 SHAPE_FL_FROZEN = 1 << 0,
100 SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
101 SHAPE_FL_COMPLEX = 1 << 2,
102
103 SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
104};
105
106typedef struct {
107 rb_shape_t *shape_list;
108 attr_index_t max_capacity;
109 attr_index_t heaps_count;
110 attr_index_t capacities[SHAPE_ID_HEAP_INDEX_MAX];
112
113RUBY_SYMBOL_EXPORT_BEGIN
114RUBY_EXTERN rb_shape_tree_t rb_shape_tree;
115RUBY_SYMBOL_EXPORT_END
116
117size_t rb_shapes_cache_size(void);
118size_t rb_shapes_count(void);
119
120static inline shape_id_t
121RBASIC_SHAPE_ID(VALUE obj)
122{
124 RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_fields));
125#if RBASIC_SHAPE_ID_FIELD
126 return (shape_id_t)((RBASIC(obj)->shape_id));
127#else
128 return (shape_id_t)((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT);
129#endif
130}
131
132// Same as RBASIC_SHAPE_ID but with flags that have no impact
133// on reads removed. e.g. Remove FL_FROZEN.
134static inline shape_id_t
135RBASIC_SHAPE_ID_FOR_READ(VALUE obj)
136{
137 return RBASIC_SHAPE_ID(obj) & SHAPE_ID_READ_ONLY_MASK;
138}
139
140#if RUBY_DEBUG
141bool rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id);
142#endif
143
144static inline void
145RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id)
146{
147#if RBASIC_SHAPE_ID_FIELD
148 RBASIC(obj)->shape_id = (VALUE)shape_id;
149#else
150 // Object shapes are occupying top bits
151 RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
152 RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
153#endif
154}
155
156static inline void
157RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
158{
160 RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_fields));
161
162 RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id);
163
164 RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id));
165}
166
167static inline shape_id_t
168RSHAPE_FLAGS(shape_id_t shape_id)
169{
170 return shape_id & SHAPE_ID_FLAGS_MASK;
171}
172
173static inline shape_id_t
174RSHAPE_OFFSET(shape_id_t shape_id)
175{
176 return shape_id & SHAPE_ID_OFFSET_MASK;
177}
178
179static inline rb_shape_t *
180RSHAPE(shape_id_t shape_id)
181{
182 shape_id_t offset = RSHAPE_OFFSET(shape_id);
183 RUBY_ASSERT(offset != INVALID_SHAPE_ID);
184 return &rb_shape_tree.shape_list[offset];
185}
186
187int32_t rb_shape_id_offset(void);
188
189RUBY_FUNC_EXPORTED shape_id_t rb_obj_shape_id(VALUE obj);
190shape_id_t rb_shape_get_next_iv_shape(shape_id_t shape_id, ID id);
191bool rb_shape_get_iv_index(shape_id_t shape_id, ID id, attr_index_t *value);
192bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t *value, shape_id_t *shape_id_hint);
193bool rb_shape_find_ivar(shape_id_t shape_id, ID id, shape_id_t *ivar_shape);
194
195typedef int rb_shape_foreach_transition_callback(shape_id_t shape_id, void *data);
196bool rb_shape_foreach_field(shape_id_t shape_id, rb_shape_foreach_transition_callback func, void *data);
197
198shape_id_t rb_shape_transition_add_ivar_no_warnings(shape_id_t shape_id, ID id, VALUE klass);
199
200shape_id_t rb_shape_object_id(shape_id_t original_shape_id);
201shape_id_t rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id);
202void rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALUE *src_buf, shape_id_t src_shape_id);
203void rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table);
204
205static inline bool
206rb_shape_frozen_p(shape_id_t shape_id)
207{
208 return shape_id & SHAPE_ID_FL_FROZEN;
209}
210
211static inline bool
212rb_shape_complex_p(shape_id_t shape_id)
213{
214 return shape_id & SHAPE_ID_FL_COMPLEX;
215}
216
217static inline bool
218rb_obj_shape_complex_p(VALUE obj)
219{
220 return !RB_SPECIAL_CONST_P(obj) && rb_shape_complex_p(RBASIC_SHAPE_ID(obj));
221}
222
223static inline bool
224rb_shape_has_object_id(shape_id_t shape_id)
225{
226 return shape_id & SHAPE_ID_FL_HAS_OBJECT_ID;
227}
228
229static inline bool
230rb_shape_canonical_p(shape_id_t shape_id)
231{
232 return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK);
233}
234
235static inline uint8_t
236rb_shape_heap_index(shape_id_t shape_id)
237{
238 return (uint8_t)((shape_id & SHAPE_ID_HEAP_INDEX_MASK) >> SHAPE_ID_HEAP_INDEX_OFFSET);
239}
240
241static inline shape_id_t
242rb_shape_root(size_t heap_id)
243{
244 shape_id_t heap_index = (shape_id_t)(heap_id + 1);
245 shape_id_t heap_flags = heap_index << SHAPE_ID_HEAP_INDEX_OFFSET;
246
247 RUBY_ASSERT((heap_flags & SHAPE_ID_HEAP_INDEX_MASK) == heap_flags);
248 RUBY_ASSERT(rb_shape_heap_index(heap_flags) == heap_index);
249
250 return ROOT_SHAPE_ID | heap_flags;
251}
252
253static inline shape_id_t
254RSHAPE_PARENT_OFFSET(shape_id_t shape_id)
255{
256 return RSHAPE(shape_id)->parent_offset;
257}
258
259static inline bool
260RSHAPE_DIRECT_CHILD_P(shape_id_t parent_offset, shape_id_t child_id)
261{
262 return RSHAPE_PARENT_OFFSET(child_id) == RSHAPE_OFFSET(parent_offset);
263}
264
265static inline enum shape_type
266RSHAPE_TYPE(shape_id_t shape_id)
267{
268 return RSHAPE(shape_id)->type;
269}
270
271static inline bool
272RSHAPE_TYPE_P(shape_id_t shape_id, enum shape_type type)
273{
274 return RSHAPE_TYPE(shape_id) == type;
275}
276
277static inline attr_index_t
278RSHAPE_EMBEDDED_CAPACITY(shape_id_t shape_id)
279{
280 uint8_t heap_index = rb_shape_heap_index(shape_id);
281 if (heap_index) {
282 return rb_shape_tree.capacities[heap_index - 1];
283 }
284 return 0;
285}
286
287static inline attr_index_t
288RSHAPE_CAPACITY(shape_id_t shape_id)
289{
290 attr_index_t embedded_capacity = RSHAPE_EMBEDDED_CAPACITY(shape_id);
291
292 if (embedded_capacity > RSHAPE(shape_id)->capacity) {
293 return embedded_capacity;
294 }
295 else {
296 return RSHAPE(shape_id)->capacity;
297 }
298}
299
300static inline attr_index_t
301RSHAPE_LEN(shape_id_t shape_id)
302{
303 return RSHAPE(shape_id)->next_field_index;
304}
305
306static inline attr_index_t
307RSHAPE_INDEX(shape_id_t shape_id)
308{
309 RUBY_ASSERT(RSHAPE_LEN(shape_id) > 0);
310 return RSHAPE_LEN(shape_id) - 1;
311}
312
313static inline ID
314RSHAPE_EDGE_NAME(shape_id_t shape_id)
315{
316 return RSHAPE(shape_id)->edge_name;
317}
318
319static inline uint32_t
320ROBJECT_FIELDS_CAPACITY(VALUE obj)
321{
322 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
323 // Asking for capacity doesn't make sense when the object is using
324 // a hash table for storing instance variables
325 RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
326 return RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj));
327}
328
329static inline st_table *
330ROBJECT_FIELDS_HASH(VALUE obj)
331{
332 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
333 RUBY_ASSERT(rb_obj_shape_complex_p(obj));
334 RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP));
335
336 return ROBJECT(obj)->as.hash;
337}
338
339static inline void
340ROBJECT_SET_FIELDS_HASH(VALUE obj, st_table *tbl)
341{
342 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
343 RUBY_ASSERT(rb_obj_shape_complex_p(obj));
344 RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP));
345
346 ROBJECT(obj)->as.hash = tbl;
347}
348
349static inline uint32_t
350ROBJECT_FIELDS_COUNT_COMPLEX(VALUE obj)
351{
352 return (uint32_t)rb_st_table_size(ROBJECT_FIELDS_HASH(obj));
353}
354
355static inline uint32_t
356ROBJECT_FIELDS_COUNT_NOT_COMPLEX(VALUE obj)
357{
358 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
359 RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
360 return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index;
361}
362
363static inline uint32_t
364ROBJECT_FIELDS_COUNT(VALUE obj)
365{
366 if (rb_obj_shape_complex_p(obj)) {
367 return ROBJECT_FIELDS_COUNT_COMPLEX(obj);
368 }
369 else {
370 return ROBJECT_FIELDS_COUNT_NOT_COMPLEX(obj);
371 }
372}
373
374static inline uint32_t
375RBASIC_FIELDS_COUNT(VALUE obj)
376{
377 return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index;
378}
379
380static inline bool
381rb_obj_shape_has_id(VALUE obj)
382{
383 return rb_shape_has_object_id(RBASIC_SHAPE_ID(obj));
384}
385
386static inline bool
387rb_shape_has_ivars(shape_id_t shape_id)
388{
389 return shape_id & SHAPE_ID_HAS_IVAR_MASK;
390}
391
392static inline bool
393rb_obj_shape_has_ivars(VALUE obj)
394{
395 return rb_shape_has_ivars(RBASIC_SHAPE_ID(obj));
396}
397
398static inline bool
399rb_shape_has_fields(shape_id_t shape_id)
400{
401 return shape_id & (SHAPE_ID_OFFSET_MASK | SHAPE_ID_FL_COMPLEX);
402}
403
404static inline bool
405rb_obj_shape_has_fields(VALUE obj)
406{
407 return rb_shape_has_fields(RBASIC_SHAPE_ID(obj));
408}
409
410static inline bool
411rb_obj_gen_fields_p(VALUE obj)
412{
413 switch (TYPE(obj)) {
414 case T_NONE:
415 case T_OBJECT:
416 case T_CLASS:
417 case T_MODULE:
418 case T_IMEMO:
419 return false;
420 default:
421 break;
422 }
423 return rb_obj_shape_has_fields(obj);
424}
425
426static inline bool
427rb_obj_using_gen_fields_table_p(VALUE obj)
428{
429 switch (BUILTIN_TYPE(obj)) {
430 case T_DATA:
431 if (RTYPEDDATA_P(obj)) return false;
432 break;
433
434 case T_STRUCT:
435 if (!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) return false;
436 break;
437
438 default:
439 break;
440 }
441
442 return rb_obj_gen_fields_p(obj);
443}
444
445static inline shape_id_t
446rb_shape_transition_frozen(shape_id_t shape_id)
447{
448 return shape_id | SHAPE_ID_FL_FROZEN;
449}
450
451static inline shape_id_t
452rb_shape_transition_complex(shape_id_t shape_id)
453{
454 shape_id_t next_shape_id = ROOT_COMPLEX_SHAPE_ID;
455
456 if (rb_shape_has_object_id(shape_id)) {
457 next_shape_id = ROOT_COMPLEX_WITH_OBJ_ID;
458 }
459
460 uint8_t heap_index = rb_shape_heap_index(shape_id);
461 if (heap_index) {
462 next_shape_id |= rb_shape_root(heap_index - 1);
463 }
464
465 RUBY_ASSERT(rb_shape_has_object_id(shape_id) == rb_shape_has_object_id(next_shape_id));
466
467 return next_shape_id;
468}
469
470static inline shape_id_t
471rb_shape_transition_offset(shape_id_t shape_id, shape_id_t offset)
472{
473 offset = RSHAPE_OFFSET(offset);
474 RUBY_ASSERT(RSHAPE_OFFSET(shape_id) == offset || RSHAPE_DIRECT_CHILD_P(shape_id, offset));
475 return RSHAPE_FLAGS(shape_id) | offset;
476}
477
478static inline shape_id_t
479rb_shape_transition_heap(shape_id_t shape_id, size_t heap_index)
480{
481 return (shape_id & (~SHAPE_ID_HEAP_INDEX_MASK)) | rb_shape_root(heap_index);
482}
483
484shape_id_t rb_shape_transition_object_id(shape_id_t shape_id);
485
486static inline shape_id_t
487rb_obj_shape_transition_frozen(VALUE obj)
488{
490 return rb_shape_transition_frozen(RBASIC_SHAPE_ID(obj));
491}
492
493static inline shape_id_t
494rb_obj_shape_transition_complex(VALUE obj)
495{
496 return rb_shape_transition_complex(RBASIC_SHAPE_ID(obj));
497}
498
499static inline shape_id_t
500rb_obj_shape_transition_heap(VALUE obj, size_t heap_index)
501{
502 return rb_shape_transition_heap(RBASIC_SHAPE_ID(obj), heap_index);
503}
504
505static inline shape_id_t
506rb_obj_shape_transition_object_id(VALUE obj)
507{
508 return rb_shape_transition_object_id(RBASIC_SHAPE_ID(obj));
509}
510
511shape_id_t rb_obj_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id);
512shape_id_t rb_obj_shape_transition_add_ivar(VALUE obj, ID id);
513
514// For ext/objspace
515RUBY_SYMBOL_EXPORT_BEGIN
516typedef void each_shape_callback(shape_id_t shape_id, void *data);
517void rb_shape_each_shape_id(each_shape_callback callback, void *data);
518size_t rb_shape_memsize(shape_id_t shape);
519size_t rb_shape_edges_count(shape_id_t shape_id);
520size_t rb_shape_depth(shape_id_t shape_id);
521RUBY_SYMBOL_EXPORT_END
522
523// Inline cache helpers
524
525typedef struct {
526 attr_index_t index;
527 shape_id_t shape_offset;
529
530union rb_getivar_cache {
531 uint64_t pack;
532 rb_getivar_cache unpack;
533};
534STATIC_ASSERT(rb_getivar_cache_size, sizeof(union rb_getivar_cache) <= sizeof(uint64_t));
535
536#define IVAR_CACHE_INIT ((uint64_t)-1)
537#define ATTR_INDEX_T_NUM_BITS (sizeof(attr_index_t) * CHAR_BIT)
538
539static inline rb_getivar_cache
540rb_getivar_cache_unpack(uint64_t packed)
541{
542 union rb_getivar_cache cache = {
543 .pack = packed,
544 };
545
546 // Because caches may initialized with all bits set (IVAR_CACHE_INIT), and `shape_offset` if 32bits,
547 // we need to remove any potential extra bits set in the "padding".
548 cache.unpack.shape_offset &= SHAPE_ID_OFFSET_MASK;
549 return cache.unpack;
550}
551
552static inline uint64_t
553rb_getivar_cache_pack(shape_id_t shape_offset, attr_index_t index)
554{
555 RUBY_ASSERT(shape_offset == RSHAPE_OFFSET(shape_offset));
556 RUBY_ASSERT(shape_offset != INVALID_SHAPE_ID);
557
558 union rb_getivar_cache cache = {
559 .unpack = {
560 .shape_offset = shape_offset,
561 .index = index,
562 },
563 };
564 return cache.pack;
565}
566
567typedef struct {
568 attr_index_t index;
569 shape_id_t source_shape_offset;
570 shape_id_t dest_shape_offset;
572
573static inline rb_setivar_cache
574rb_setivar_cache_unpack(uint64_t packed)
575{
576 rb_setivar_cache cache = {
577 .index = (attr_index_t)packed,
578 .source_shape_offset = RSHAPE_OFFSET((shape_id_t)(packed >> ATTR_INDEX_T_NUM_BITS)),
579 .dest_shape_offset = RSHAPE_OFFSET((shape_id_t)(packed >> (ATTR_INDEX_T_NUM_BITS + SHAPE_ID_OFFSET_NUM_BITS))),
580 };
581 return cache;
582}
583
584static inline uint64_t
585rb_setivar_cache_pack(shape_id_t shape_offset, shape_id_t dest_shape_offset, attr_index_t index)
586{
587 RUBY_ASSERT(shape_offset == RSHAPE_OFFSET(shape_offset));
588 RUBY_ASSERT(dest_shape_offset == RSHAPE_OFFSET(dest_shape_offset));
589 RUBY_ASSERT(shape_offset == dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_offset, dest_shape_offset));
590
591 uint64_t packed_cache = (uint64_t)dest_shape_offset << (ATTR_INDEX_T_NUM_BITS + SHAPE_ID_OFFSET_NUM_BITS);
592 packed_cache |= (uint64_t)shape_offset << ATTR_INDEX_T_NUM_BITS;
593 packed_cache |= (uint64_t)index;
594 return packed_cache;
595}
596
597
598ALWAYS_INLINE(static shape_id_t rb_setivar_cache_revalidate(shape_id_t shape_id, rb_setivar_cache cache));
599static shape_id_t
600rb_setivar_cache_revalidate(shape_id_t shape_id, rb_setivar_cache cache)
601{
602 RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
603 RUBY_ASSERT(cache.dest_shape_offset == INVALID_SHAPE_ID || cache.dest_shape_offset == RSHAPE_OFFSET(cache.dest_shape_offset));
604
605 shape_id_t normalized_shape_id = shape_id & SHAPE_ID_WRITE_MASK;
606 if (UNLIKELY(normalized_shape_id != cache.source_shape_offset)) {
607 return INVALID_SHAPE_ID;
608 }
609
610 if (UNLIKELY(cache.index >= RSHAPE_CAPACITY(shape_id))) {
611 // That's still a hit in term of layout, but the object will need to be resized,
612 // so unfortunately we'll have to go through the slow path regardless...
613 return INVALID_SHAPE_ID;
614 }
615
616 // Cache hit case
617 RUBY_ASSERT(cache.source_shape_offset == cache.dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_id, cache.dest_shape_offset));
618 RUBY_ASSERT(cache.index < RSHAPE_CAPACITY(shape_id));
619 RUBY_ASSERT(!rb_shape_frozen_p(shape_id));
620 RUBY_ASSERT(!rb_shape_complex_p(shape_id));
621
622 // We use the cached offset, but combined with the current shape flags.
623 return rb_shape_transition_offset(shape_id, cache.dest_shape_offset);
624}
625
626#endif
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define RUBY_EXTERN
Declaration of externally visible global variables.
Definition dllexport.h:45
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition fl_type.h:711
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:108
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
#define T_DATA
Old name of RUBY_T_DATA.
Definition value_type.h:60
#define T_NONE
Old name of RUBY_T_NONE.
Definition value_type.h:74
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition fl_type.h:128
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
VALUE type(ANYARGS)
ANYARGS-ed function type.
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define ROBJECT(obj)
Convenient casting macro.
Definition robject.h:43
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
Definition rtypeddata.h:669
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376
@ RUBY_T_OBJECT
Definition value_type.h:116