4#include "internal/gc.h"
5#include "internal/imemo.h"
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
12STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS ==
sizeof(shape_id_t) * CHAR_BIT);
14#define SHAPE_BUFFER_SIZE (1 << SHAPE_ID_OFFSET_NUM_BITS)
15#define SHAPE_ID_OFFSET_MASK (SHAPE_BUFFER_SIZE - 1)
17#define SHAPE_ID_CAPACITY_BITS 7
18#define SHAPE_ID_CAPACITY_MAX ((1U << SHAPE_ID_CAPACITY_BITS) - 1)
20#define SHAPE_ID_CAPACITY_OFFSET SHAPE_ID_OFFSET_NUM_BITS
21#define SHAPE_ID_FL_USHIFT (SHAPE_ID_OFFSET_NUM_BITS + SHAPE_ID_CAPACITY_BITS)
38STATIC_ASSERT(robject_rdata_fields_offset, offsetof(
struct RObject, as.
extended) == offsetof(
struct RTypedData, fields_obj));
40enum shape_id_fl_type {
41#define RBIMPL_SHAPE_ID_FL(n) (1<<(SHAPE_ID_FL_USHIFT+n))
43 SHAPE_ID_CAPACITY_MASK = ((1 << SHAPE_ID_CAPACITY_BITS) - 1) << SHAPE_ID_CAPACITY_OFFSET,
45 SHAPE_ID_FL_COMPLEX = RBIMPL_SHAPE_ID_FL(0),
46 SHAPE_ID_FL_FROZEN = RBIMPL_SHAPE_ID_FL(1),
47 SHAPE_ID_FL_HAS_OBJECT_ID = RBIMPL_SHAPE_ID_FL(2),
51 SHAPE_ID_LAYOUT_ROBJECT = 0,
55 SHAPE_ID_LAYOUT_RCLASS = RBIMPL_SHAPE_ID_FL(3),
59 SHAPE_ID_LAYOUT_EXTENDED = RBIMPL_SHAPE_ID_FL(4),
60 SHAPE_ID_LAYOUT_RDATA = SHAPE_ID_LAYOUT_EXTENDED,
64 SHAPE_ID_LAYOUT_OTHER = SHAPE_ID_LAYOUT_RCLASS | SHAPE_ID_LAYOUT_EXTENDED,
66 SHAPE_ID_LAYOUT_MASK = SHAPE_ID_LAYOUT_OTHER,
68 SHAPE_ID_FL_NON_CANONICAL_MASK = SHAPE_ID_FL_FROZEN | SHAPE_ID_FL_HAS_OBJECT_ID,
69 SHAPE_ID_FLAGS_MASK = SHAPE_ID_CAPACITY_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_COMPLEX | SHAPE_ID_LAYOUT_MASK,
75 SHAPE_ID_FL_PRIVATE_MASK = SHAPE_ID_LAYOUT_MASK|SHAPE_ID_CAPACITY_MASK,
76#undef RBIMPL_SHAPE_ID_FL
82 SHAPE_ID_HAS_IVAR_MASK = SHAPE_ID_FL_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1),
89#define SHAPE_ID_READ_ONLY_MASK (~(SHAPE_ID_FL_FROZEN | SHAPE_ID_CAPACITY_MASK | SHAPE_ID_FL_HAS_OBJECT_ID | SHAPE_ID_LAYOUT_MASK))
91#define SHAPE_ID_WRITE_MASK (~(SHAPE_ID_CAPACITY_MASK | SHAPE_ID_FL_HAS_OBJECT_ID | SHAPE_ID_LAYOUT_MASK))
93typedef uint32_t redblack_id_t;
95#define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * CHAR_BIT) - SHAPE_ID_NUM_BITS)
96#define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
98#define SHAPE_MAX_VARIATIONS 8
100#define INVALID_SHAPE_ID (SHAPE_BUFFER_SIZE - 1)
101#define ATTR_INDEX_NOT_SET ((attr_index_t)-1)
103#define ROOT_SHAPE_ID 0x0
104#define ROOT_SHAPE_WITH_OBJ_ID 0x1
105#define ROOT_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_COMPLEX)
106#define ROOT_COMPLEX_WITH_OBJ_ID (ROOT_SHAPE_WITH_OBJ_ID | SHAPE_ID_FL_COMPLEX | SHAPE_ID_FL_HAS_OBJECT_ID)
117 redblack_id_t ancestor_index;
118 shape_id_t parent_offset;
119 attr_index_t next_field_index;
120 attr_index_t capacity;
121 enum shape_type type : 8;
127 SHAPE_FL_FROZEN = 1 << 0,
128 SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
129 SHAPE_FL_COMPLEX = 1 << 2,
131 SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
136 attr_index_t max_capacity;
140RUBY_SYMBOL_EXPORT_BEGIN
142RUBY_SYMBOL_EXPORT_END
144size_t rb_shapes_cache_size(
void);
145size_t rb_shapes_count(
void);
147static inline attr_index_t
148rb_shape_max_capacity(
void)
150 return rb_shape_tree.max_capacity;
153static inline shape_id_t
154RBASIC_SHAPE_ID(
VALUE obj)
158#if RBASIC_SHAPE_ID_FIELD
159 return (shape_id_t)((
RBASIC(obj)->shape_id));
161 return (shape_id_t)((
RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT);
167static inline shape_id_t
168RBASIC_SHAPE_ID_FOR_READ(
VALUE obj)
170 return RBASIC_SHAPE_ID(obj) & SHAPE_ID_READ_ONLY_MASK;
174bool rb_shape_verify_consistency(
VALUE obj, shape_id_t shape_id);
178RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(
VALUE obj, shape_id_t shape_id)
180#if RBASIC_SHAPE_ID_FIELD
184 RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
185 RBASIC(obj)->flags |= ((
VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
189static inline shape_id_t
190rb_shape_layout(shape_id_t shape_id)
192 return shape_id & SHAPE_ID_LAYOUT_MASK;
196rb_shape_embedded_p(shape_id_t shape_id)
198 return rb_shape_layout(shape_id) == SHAPE_ID_LAYOUT_ROBJECT;
202rb_shape_extended_p(shape_id_t shape_id)
204 return rb_shape_layout(shape_id) == SHAPE_ID_LAYOUT_EXTENDED;
208rb_obj_shape_embedded_p(
VALUE obj)
210 return rb_shape_embedded_p(RBASIC_SHAPE_ID(obj));
214rb_obj_shape_extended_p(
VALUE obj)
216 return rb_shape_extended_p(RBASIC_SHAPE_ID(obj));
227RBASIC_SET_FULL_SHAPE_ID(
VALUE obj, shape_id_t shape_id)
232 RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(obj, shape_id);
234 RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id));
237static inline shape_id_t rb_shape_transition_layout(shape_id_t, shape_id_t);
240RBASIC_SET_SHAPE_ID_WITH_LAYOUT(
VALUE obj, shape_id_t target_shape_id, shape_id_t layout)
242 RUBY_ASSERT((layout & SHAPE_ID_LAYOUT_MASK) == layout);
243 shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
244 current_shape_id = rb_shape_transition_layout(current_shape_id, layout);
245 current_shape_id = (current_shape_id & SHAPE_ID_FL_PRIVATE_MASK) | (target_shape_id & ~SHAPE_ID_FL_PRIVATE_MASK);
246 RBASIC_SET_FULL_SHAPE_ID(obj, current_shape_id);
250RBASIC_SET_SHAPE_ID(
VALUE obj, shape_id_t shape_id)
254 RBASIC_SET_FULL_SHAPE_ID(obj, (
255 (shape_id & ~SHAPE_ID_FL_PRIVATE_MASK) |
256 (RBASIC_SHAPE_ID(obj) & SHAPE_ID_FL_PRIVATE_MASK)
260static inline shape_id_t
261RSHAPE_FLAGS(shape_id_t shape_id)
263 return shape_id & SHAPE_ID_FLAGS_MASK;
266static inline shape_id_t
267RSHAPE_OFFSET(shape_id_t shape_id)
269 return shape_id & SHAPE_ID_OFFSET_MASK;
273RSHAPE(shape_id_t shape_id)
275 shape_id_t offset = RSHAPE_OFFSET(shape_id);
277 return &rb_shape_tree.shape_list[offset];
280int32_t rb_shape_id_offset(
void);
282RUBY_FUNC_EXPORTED shape_id_t rb_obj_shape_id(
VALUE obj);
283bool rb_shape_get_iv_index(shape_id_t shape_id,
ID id, attr_index_t *value);
284bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id,
ID id, attr_index_t *value, shape_id_t *shape_id_hint);
285bool rb_shape_find_ivar(shape_id_t shape_id,
ID id, shape_id_t *ivar_shape);
287typedef int rb_shape_foreach_transition_callback(shape_id_t shape_id,
void *data);
288bool rb_shape_foreach_field(shape_id_t shape_id, rb_shape_foreach_transition_callback func,
void *data);
290shape_id_t rb_shape_transition_add_ivar_no_warnings(shape_id_t shape_id,
ID id,
VALUE klass);
292shape_id_t rb_shape_object_id(shape_id_t original_shape_id);
293shape_id_t rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id);
294void rb_shape_copy_fields(
VALUE dest,
VALUE *dest_buf, shape_id_t dest_shape_id,
VALUE *src_buf, shape_id_t src_shape_id);
297rb_shape_frozen_p(shape_id_t shape_id)
299 return shape_id & SHAPE_ID_FL_FROZEN;
303rb_shape_complex_p(shape_id_t shape_id)
305 return shape_id & SHAPE_ID_FL_COMPLEX;
309rb_obj_shape_complex_p(
VALUE obj)
315rb_shape_has_object_id(shape_id_t shape_id)
317 return shape_id & SHAPE_ID_FL_HAS_OBJECT_ID;
321rb_shape_canonical_p(shape_id_t shape_id)
323 return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK);
326static inline attr_index_t
327rb_shape_embedded_capacity(shape_id_t shape_id)
329 return (attr_index_t)((shape_id & SHAPE_ID_CAPACITY_MASK) >> SHAPE_ID_CAPACITY_OFFSET);
333rb_shape_slot_size(shape_id_t shape_id)
335 return sizeof(
struct RBasic) + (rb_shape_embedded_capacity(shape_id) *
sizeof(
VALUE));
339rb_obj_shape_slot_size(
VALUE obj)
343 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
344 size_t slot_size = rb_shape_slot_size(shape_id);
346 if (rb_shape_embedded_capacity(shape_id) == SHAPE_ID_CAPACITY_MAX) {
347 size_t gc_slot_size = rb_gc_obj_slot_size(obj);
355static inline attr_index_t
356rb_shape_capacity_for_slot_size(
size_t slot_size)
358 size_t capacity = (slot_size -
sizeof(
struct RBasic)) / sizeof(
VALUE);
359 if (capacity > SHAPE_ID_CAPACITY_MAX) {
360 capacity = SHAPE_ID_CAPACITY_MAX;
362 return (attr_index_t)capacity;
365static inline shape_id_t
366RSHAPE_PARENT_OFFSET(shape_id_t shape_id)
368 return RSHAPE(shape_id)->parent_offset;
372RSHAPE_DIRECT_CHILD_P(shape_id_t parent_offset, shape_id_t child_id)
374 return RSHAPE_PARENT_OFFSET(child_id) == RSHAPE_OFFSET(parent_offset);
377static inline enum shape_type
378RSHAPE_TYPE(shape_id_t shape_id)
380 return RSHAPE(shape_id)->type;
384RSHAPE_TYPE_P(shape_id_t shape_id,
enum shape_type
type)
386 return RSHAPE_TYPE(shape_id) ==
type;
389static inline attr_index_t
390RSHAPE_CAPACITY(shape_id_t shape_id)
392 attr_index_t embedded_capacity = rb_shape_embedded_capacity(shape_id);
394 if (embedded_capacity > RSHAPE(shape_id)->capacity) {
395 return embedded_capacity;
398 return RSHAPE(shape_id)->capacity;
402static inline attr_index_t
403RSHAPE_LEN(shape_id_t shape_id)
405 return RSHAPE(shape_id)->next_field_index;
408static inline attr_index_t
409RSHAPE_INDEX(shape_id_t shape_id)
412 return RSHAPE_LEN(shape_id) - 1;
416RSHAPE_EDGE_NAME(shape_id_t shape_id)
418 return RSHAPE(shape_id)->edge_name;
422rb_imemo_fields_ptr(
VALUE fields_obj)
429 return IMEMO_OBJ_FIELDS(fields_obj)->as.embed.fields;
432static inline uint32_t
433RBASIC_FIELDS_COUNT(
VALUE obj)
435 return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index;
439rb_obj_shape_has_id(
VALUE obj)
441 return rb_shape_has_object_id(RBASIC_SHAPE_ID(obj));
445rb_shape_has_ivars(shape_id_t shape_id)
447 return shape_id & SHAPE_ID_HAS_IVAR_MASK;
451rb_obj_shape_has_ivars(
VALUE obj)
453 return rb_shape_has_ivars(RBASIC_SHAPE_ID(obj));
457rb_shape_has_fields(shape_id_t shape_id)
459 return shape_id & (SHAPE_ID_OFFSET_MASK | SHAPE_ID_FL_COMPLEX);
463rb_obj_shape_has_fields(
VALUE obj)
465 return rb_shape_has_fields(RBASIC_SHAPE_ID(obj));
469rb_obj_gen_fields_p(
VALUE obj)
481 return rb_obj_shape_has_fields(obj);
484static inline shape_id_t
485rb_shape_transition_layout(shape_id_t shape_id, shape_id_t layout)
487 return (shape_id & (~SHAPE_ID_LAYOUT_MASK)) | layout;
490static inline shape_id_t
491rb_shape_transition_robject(shape_id_t shape_id)
493 return rb_shape_transition_layout(shape_id, SHAPE_ID_LAYOUT_ROBJECT);
496static inline shape_id_t
497rb_shape_transition_extended(shape_id_t shape_id)
499 return rb_shape_transition_layout(shape_id, SHAPE_ID_LAYOUT_EXTENDED);
502static inline shape_id_t
503rb_shape_transition_frozen(shape_id_t shape_id)
505 return shape_id | SHAPE_ID_FL_FROZEN;
508static inline shape_id_t
509rb_shape_transition_complex(shape_id_t shape_id)
511 shape_id_t next_shape_id = rb_shape_layout(shape_id) | ROOT_COMPLEX_SHAPE_ID;
513 if (rb_shape_has_object_id(shape_id)) {
514 next_shape_id = rb_shape_layout(shape_id) | ROOT_COMPLEX_WITH_OBJ_ID;
517 next_shape_id |= shape_id & SHAPE_ID_CAPACITY_MASK;
519 RUBY_ASSERT(rb_shape_has_object_id(shape_id) == rb_shape_has_object_id(next_shape_id));
521 return next_shape_id;
524static inline shape_id_t
525rb_shape_transition_offset(shape_id_t shape_id, shape_id_t offset)
527 offset = RSHAPE_OFFSET(offset);
528 RUBY_ASSERT(RSHAPE_OFFSET(shape_id) == offset || RSHAPE_DIRECT_CHILD_P(shape_id, offset));
529 return RSHAPE_FLAGS(shape_id) | offset;
532static inline shape_id_t
533rb_shape_transition_capacity(shape_id_t shape_id,
size_t capacity)
537 shape_id_t capacity_flags = (shape_id_t)capacity << SHAPE_ID_CAPACITY_OFFSET;
538 return (shape_id & (~SHAPE_ID_CAPACITY_MASK)) | capacity_flags;
541static inline shape_id_t
542rb_shape_transition_slot_size(shape_id_t shape_id,
size_t slot_size)
544 return rb_shape_transition_capacity(shape_id, rb_shape_capacity_for_slot_size(slot_size));
547shape_id_t rb_shape_transition_object_id(shape_id_t shape_id);
549static inline shape_id_t
550rb_obj_shape_transition_frozen(
VALUE obj)
553 return rb_shape_transition_frozen(RBASIC_SHAPE_ID(obj));
556static inline shape_id_t
557rb_obj_shape_transition_complex(
VALUE obj)
559 return rb_shape_transition_complex(RBASIC_SHAPE_ID(obj));
562static inline shape_id_t
563rb_obj_shape_transition_capacity(
VALUE obj,
size_t capacity)
565 return rb_shape_transition_capacity(RBASIC_SHAPE_ID(obj), capacity);
568static inline shape_id_t
569rb_obj_shape_transition_slot_size(
VALUE obj,
size_t slot_size)
571 return rb_shape_transition_slot_size(RBASIC_SHAPE_ID(obj), slot_size);
574static inline shape_id_t
575rb_obj_shape_transition_object_id(
VALUE obj)
577 return rb_shape_transition_object_id(RBASIC_SHAPE_ID(obj));
580shape_id_t rb_obj_shape_transition_remove_ivar(
VALUE obj,
ID id, shape_id_t *removed_shape_id);
581shape_id_t rb_obj_shape_transition_add_ivar(
VALUE obj,
ID id);
584RUBY_SYMBOL_EXPORT_BEGIN
585typedef void each_shape_callback(shape_id_t shape_id,
void *data);
586void rb_shape_each_shape_id(each_shape_callback callback,
void *data);
587size_t rb_shape_memsize(shape_id_t shape);
588size_t rb_shape_edges_count(shape_id_t shape_id);
589size_t rb_shape_depth(shape_id_t shape_id);
590RUBY_SYMBOL_EXPORT_END
596 shape_id_t shape_offset;
603STATIC_ASSERT(rb_getivar_cache_size,
sizeof(
union rb_getivar_cache) <=
sizeof(uint64_t));
605#define IVAR_CACHE_INIT ((uint64_t)-1)
606#define ATTR_INDEX_T_NUM_BITS (sizeof(attr_index_t) * CHAR_BIT)
609rb_getivar_cache_unpack(uint64_t packed)
617 cache.unpack.shape_offset &= SHAPE_ID_OFFSET_MASK;
621static inline uint64_t
622rb_getivar_cache_pack(shape_id_t shape_offset, attr_index_t index)
624 RUBY_ASSERT(shape_offset == RSHAPE_OFFSET(shape_offset));
629 .shape_offset = shape_offset,
638 shape_id_t source_shape_offset;
639 shape_id_t dest_shape_offset;
643rb_setivar_cache_unpack(uint64_t packed)
646 .index = (attr_index_t)packed,
647 .source_shape_offset = RSHAPE_OFFSET((shape_id_t)(packed >> ATTR_INDEX_T_NUM_BITS)),
648 .dest_shape_offset = RSHAPE_OFFSET((shape_id_t)(packed >> (ATTR_INDEX_T_NUM_BITS + SHAPE_ID_OFFSET_NUM_BITS))),
653static inline uint64_t
654rb_setivar_cache_pack(shape_id_t shape_offset, shape_id_t dest_shape_offset, attr_index_t index)
656 RUBY_ASSERT(shape_offset == RSHAPE_OFFSET(shape_offset));
657 RUBY_ASSERT(dest_shape_offset == RSHAPE_OFFSET(dest_shape_offset));
658 RUBY_ASSERT(shape_offset == dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_offset, dest_shape_offset));
660 uint64_t packed_cache = (uint64_t)dest_shape_offset << (ATTR_INDEX_T_NUM_BITS + SHAPE_ID_OFFSET_NUM_BITS);
661 packed_cache |= (uint64_t)shape_offset << ATTR_INDEX_T_NUM_BITS;
662 packed_cache |= (uint64_t)index;
666ALWAYS_INLINE(
static shape_id_t rb_setivar_cache_revalidate(shape_id_t shape_id, shape_id_t fields_shape_id,
rb_setivar_cache cache));
668rb_setivar_cache_revalidate(shape_id_t shape_id, shape_id_t fields_shape_id,
rb_setivar_cache cache)
671 RUBY_ASSERT(cache.dest_shape_offset == INVALID_SHAPE_ID || cache.dest_shape_offset == RSHAPE_OFFSET(cache.dest_shape_offset));
673 shape_id_t normalized_shape_id = shape_id & SHAPE_ID_WRITE_MASK;
674 if (UNLIKELY(normalized_shape_id != cache.source_shape_offset)) {
675 return INVALID_SHAPE_ID;
678 if (UNLIKELY(cache.index >= RSHAPE_CAPACITY(fields_shape_id))) {
681 return INVALID_SHAPE_ID;
685 RUBY_ASSERT(cache.source_shape_offset == cache.dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_id, cache.dest_shape_offset));
686 RUBY_ASSERT(cache.index < RSHAPE_CAPACITY(shape_id));
691 return rb_shape_transition_offset(shape_id, cache.dest_shape_offset);
695rb_imemo_fields_complex_tbl(
VALUE fields_obj)
701 RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields));
705 RUBY_ASSERT((
st_table *)rb_imemo_fields_ptr(fields_obj) == &IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table);
707 return &IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table;
711ROBJECT_FIELDS_OBJ(
VALUE obj)
715 return rb_obj_shape_embedded_p(obj) ? obj :
ROBJECT(obj)->as.extended;
719ROBJECT_EMBEDDED_FIELDS(
VALUE obj)
725ROBJECT_FIELDS(
VALUE obj)
729 return ROBJECT_EMBEDDED_FIELDS(ROBJECT_FIELDS_OBJ(obj));
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define RUBY_EXTERN
Declaration of externally visible global variables.
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
#define TYPE(_)
Old name of rb_type.
#define T_IMEMO
Old name of RUBY_T_IMEMO.
#define T_NONE
Old name of RUBY_T_NONE.
#define T_MODULE
Old name of RUBY_T_MODULE.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
#define T_CLASS
Old name of RUBY_T_CLASS.
VALUE type(ANYARGS)
ANYARGS-ed function type.
#define RBASIC(obj)
Convenient casting macro.
#define ROBJECT(obj)
Convenient casting macro.
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Ruby object's base components.
VALUE extended
When an object slot is too small or too complex to store instance variables inline,...
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.