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)
39STATIC_ASSERT(robject_rdata_fields_offset, offsetof(
struct RObject, as.
extended) == offsetof(
struct RTypedData, fields_obj));
41enum shape_id_fl_type {
42#define RBIMPL_SHAPE_ID_FL(n) (1<<(SHAPE_ID_FL_USHIFT+n))
44 SHAPE_ID_CAPACITY_MASK = ((1 << SHAPE_ID_CAPACITY_BITS) - 1) << SHAPE_ID_CAPACITY_OFFSET,
46 SHAPE_ID_FL_COMPLEX = RBIMPL_SHAPE_ID_FL(0),
47 SHAPE_ID_FL_FROZEN = RBIMPL_SHAPE_ID_FL(1),
48 SHAPE_ID_FL_HAS_OBJECT_ID = RBIMPL_SHAPE_ID_FL(2),
52 SHAPE_ID_LAYOUT_ROBJECT = 0,
56 SHAPE_ID_LAYOUT_RCLASS = RBIMPL_SHAPE_ID_FL(3),
60 SHAPE_ID_LAYOUT_EXTENDED = RBIMPL_SHAPE_ID_FL(4),
61 SHAPE_ID_LAYOUT_RDATA = SHAPE_ID_LAYOUT_EXTENDED,
65 SHAPE_ID_LAYOUT_OTHER = SHAPE_ID_LAYOUT_RCLASS | SHAPE_ID_LAYOUT_EXTENDED,
67 SHAPE_ID_LAYOUT_MASK = SHAPE_ID_LAYOUT_OTHER,
69 SHAPE_ID_FL_NON_CANONICAL_MASK = SHAPE_ID_FL_FROZEN | SHAPE_ID_FL_HAS_OBJECT_ID,
70 SHAPE_ID_FLAGS_MASK = SHAPE_ID_CAPACITY_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_COMPLEX | SHAPE_ID_LAYOUT_MASK,
76 SHAPE_ID_FL_PRIVATE_MASK = SHAPE_ID_LAYOUT_MASK|SHAPE_ID_CAPACITY_MASK,
77#undef RBIMPL_SHAPE_ID_FL
83 SHAPE_ID_HAS_IVAR_MASK = SHAPE_ID_FL_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1),
90#define SHAPE_ID_READ_ONLY_MASK (~(SHAPE_ID_FL_FROZEN | SHAPE_ID_CAPACITY_MASK | SHAPE_ID_FL_HAS_OBJECT_ID | SHAPE_ID_LAYOUT_MASK))
92#define SHAPE_ID_WRITE_MASK (~(SHAPE_ID_CAPACITY_MASK | SHAPE_ID_FL_HAS_OBJECT_ID | SHAPE_ID_LAYOUT_MASK))
94typedef uint32_t redblack_id_t;
96#define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * CHAR_BIT) - SHAPE_ID_NUM_BITS)
97#define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
99#define SHAPE_MAX_VARIATIONS 8
101#define INVALID_SHAPE_ID (SHAPE_BUFFER_SIZE - 1)
102#define ATTR_INDEX_NOT_SET ((attr_index_t)-1)
104#define ROOT_SHAPE_ID 0x0
105#define ROOT_SHAPE_WITH_OBJ_ID 0x1
106#define ROOT_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_COMPLEX)
107#define ROOT_COMPLEX_WITH_OBJ_ID (ROOT_SHAPE_WITH_OBJ_ID | SHAPE_ID_FL_COMPLEX | SHAPE_ID_FL_HAS_OBJECT_ID)
118 redblack_id_t ancestor_index;
119 shape_id_t parent_offset;
120 attr_index_t next_field_index;
121 attr_index_t capacity;
122 enum shape_type type : 8;
128 SHAPE_FL_FROZEN = 1 << 0,
129 SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
130 SHAPE_FL_COMPLEX = 1 << 2,
132 SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
137 attr_index_t max_capacity;
141RUBY_SYMBOL_EXPORT_BEGIN
143RUBY_SYMBOL_EXPORT_END
145size_t rb_shapes_cache_size(
void);
146size_t rb_shapes_count(
void);
148static inline attr_index_t
149rb_shape_max_capacity(
void)
151 return rb_shape_tree.max_capacity;
154static inline shape_id_t
155RBASIC_SHAPE_ID(
VALUE obj)
159#if RBASIC_SHAPE_ID_FIELD
160 return (shape_id_t)((
RBASIC(obj)->shape_id));
162 return (shape_id_t)((
RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT);
168static inline shape_id_t
169RBASIC_SHAPE_ID_FOR_READ(
VALUE obj)
171 return RBASIC_SHAPE_ID(obj) & SHAPE_ID_READ_ONLY_MASK;
175bool rb_shape_verify_consistency(
VALUE obj, shape_id_t shape_id);
179RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(
VALUE obj, shape_id_t shape_id)
181#if RBASIC_SHAPE_ID_FIELD
185 RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
186 RBASIC(obj)->flags |= ((
VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
190static inline shape_id_t
191rb_shape_layout(shape_id_t shape_id)
193 return shape_id & SHAPE_ID_LAYOUT_MASK;
197rb_shape_embedded_p(shape_id_t shape_id)
199 return rb_shape_layout(shape_id) == SHAPE_ID_LAYOUT_ROBJECT;
203rb_shape_extended_p(shape_id_t shape_id)
205 return rb_shape_layout(shape_id) == SHAPE_ID_LAYOUT_EXTENDED;
209rb_obj_shape_embedded_p(
VALUE obj)
211 return rb_shape_embedded_p(RBASIC_SHAPE_ID(obj));
215rb_obj_shape_extended_p(
VALUE obj)
217 return rb_shape_extended_p(RBASIC_SHAPE_ID(obj));
228RBASIC_SET_FULL_SHAPE_ID(
VALUE obj, shape_id_t shape_id)
233 RBASIC_SET_FULL_SHAPE_ID_NO_CHECKS(obj, shape_id);
235 RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id));
238static inline shape_id_t rb_shape_transition_layout(shape_id_t, shape_id_t);
241RBASIC_SET_SHAPE_ID_WITH_LAYOUT(
VALUE obj, shape_id_t target_shape_id, shape_id_t layout)
243 RUBY_ASSERT((layout & SHAPE_ID_LAYOUT_MASK) == layout);
244 shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj);
245 current_shape_id = rb_shape_transition_layout(current_shape_id, layout);
246 current_shape_id = (current_shape_id & SHAPE_ID_FL_PRIVATE_MASK) | (target_shape_id & ~SHAPE_ID_FL_PRIVATE_MASK);
247 RBASIC_SET_FULL_SHAPE_ID(obj, current_shape_id);
251RBASIC_SET_SHAPE_ID(
VALUE obj, shape_id_t shape_id)
255 RBASIC_SET_FULL_SHAPE_ID(obj, (
256 (shape_id & ~SHAPE_ID_FL_PRIVATE_MASK) |
257 (RBASIC_SHAPE_ID(obj) & SHAPE_ID_FL_PRIVATE_MASK)
261static inline shape_id_t
262RSHAPE_FLAGS(shape_id_t shape_id)
264 return shape_id & SHAPE_ID_FLAGS_MASK;
267static inline shape_id_t
268RSHAPE_OFFSET(shape_id_t shape_id)
270 return shape_id & SHAPE_ID_OFFSET_MASK;
274RSHAPE(shape_id_t shape_id)
276 shape_id_t offset = RSHAPE_OFFSET(shape_id);
278 return &rb_shape_tree.shape_list[offset];
281int32_t rb_shape_id_offset(
void);
283RUBY_FUNC_EXPORTED shape_id_t rb_obj_shape_id(
VALUE obj);
284bool rb_shape_get_iv_index(shape_id_t shape_id,
ID id, attr_index_t *value);
285bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id,
ID id, attr_index_t *value, shape_id_t *shape_id_hint);
286bool rb_shape_find_ivar(shape_id_t shape_id,
ID id, shape_id_t *ivar_shape);
288typedef int rb_shape_foreach_transition_callback(shape_id_t shape_id,
void *data);
289bool rb_shape_foreach_field(shape_id_t shape_id, rb_shape_foreach_transition_callback func,
void *data);
291shape_id_t rb_shape_transition_add_ivar_no_warnings(shape_id_t shape_id,
ID id,
VALUE klass);
293shape_id_t rb_shape_object_id(shape_id_t original_shape_id);
294shape_id_t rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id);
295void rb_shape_copy_fields(
VALUE dest,
VALUE *dest_buf, shape_id_t dest_shape_id,
VALUE *src_buf, shape_id_t src_shape_id);
298rb_shape_frozen_p(shape_id_t shape_id)
300 return shape_id & SHAPE_ID_FL_FROZEN;
304rb_shape_complex_p(shape_id_t shape_id)
306 return shape_id & SHAPE_ID_FL_COMPLEX;
310rb_obj_shape_complex_p(
VALUE obj)
316rb_shape_has_object_id(shape_id_t shape_id)
318 return shape_id & SHAPE_ID_FL_HAS_OBJECT_ID;
322rb_shape_canonical_p(shape_id_t shape_id)
324 return !(shape_id & SHAPE_ID_FL_NON_CANONICAL_MASK);
327static inline attr_index_t
328rb_shape_embedded_capacity(shape_id_t shape_id)
330 return (attr_index_t)((shape_id & SHAPE_ID_CAPACITY_MASK) >> SHAPE_ID_CAPACITY_OFFSET);
334rb_shape_slot_size(shape_id_t shape_id)
336 return sizeof(
struct RBasic) + (rb_shape_embedded_capacity(shape_id) *
sizeof(
VALUE));
340rb_obj_shape_slot_size(
VALUE obj)
344 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
345 size_t slot_size = rb_shape_slot_size(shape_id);
347 if (rb_shape_embedded_capacity(shape_id) == SHAPE_ID_CAPACITY_MAX) {
348 size_t gc_slot_size = rb_gc_obj_slot_size(obj);
356static inline attr_index_t
357rb_shape_capacity_for_slot_size(
size_t slot_size)
359 size_t capacity = (slot_size -
sizeof(
struct RBasic)) / sizeof(
VALUE);
360 if (capacity > SHAPE_ID_CAPACITY_MAX) {
361 capacity = SHAPE_ID_CAPACITY_MAX;
363 return (attr_index_t)capacity;
366static inline shape_id_t
367RSHAPE_PARENT_OFFSET(shape_id_t shape_id)
369 return RSHAPE(shape_id)->parent_offset;
373RSHAPE_DIRECT_CHILD_P(shape_id_t parent_offset, shape_id_t child_id)
375 return RSHAPE_PARENT_OFFSET(child_id) == RSHAPE_OFFSET(parent_offset);
378static inline enum shape_type
379RSHAPE_TYPE(shape_id_t shape_id)
381 return RSHAPE(shape_id)->type;
385RSHAPE_TYPE_P(shape_id_t shape_id,
enum shape_type
type)
387 return RSHAPE_TYPE(shape_id) ==
type;
390static inline attr_index_t
391RSHAPE_CAPACITY(shape_id_t shape_id)
393 attr_index_t embedded_capacity = rb_shape_embedded_capacity(shape_id);
395 if (embedded_capacity > RSHAPE(shape_id)->capacity) {
396 return embedded_capacity;
399 return RSHAPE(shape_id)->capacity;
403static inline attr_index_t
404RSHAPE_LEN(shape_id_t shape_id)
406 return RSHAPE(shape_id)->next_field_index;
409static inline attr_index_t
410RSHAPE_INDEX(shape_id_t shape_id)
413 return RSHAPE_LEN(shape_id) - 1;
417RSHAPE_EDGE_NAME(shape_id_t shape_id)
419 return RSHAPE(shape_id)->edge_name;
423rb_imemo_fields_ptr(
VALUE fields_obj)
430 return IMEMO_OBJ_FIELDS(fields_obj)->as.embed.fields;
433static inline uint32_t
434RBASIC_FIELDS_COUNT(
VALUE obj)
436 return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index;
440rb_obj_shape_has_id(
VALUE obj)
442 return rb_shape_has_object_id(RBASIC_SHAPE_ID(obj));
446rb_shape_has_ivars(shape_id_t shape_id)
448 return shape_id & SHAPE_ID_HAS_IVAR_MASK;
452rb_obj_shape_has_ivars(
VALUE obj)
454 return rb_shape_has_ivars(RBASIC_SHAPE_ID(obj));
458rb_shape_has_fields(shape_id_t shape_id)
460 return shape_id & (SHAPE_ID_OFFSET_MASK | SHAPE_ID_FL_COMPLEX);
464rb_obj_shape_has_fields(
VALUE obj)
466 return rb_shape_has_fields(RBASIC_SHAPE_ID(obj));
470rb_obj_gen_fields_p(
VALUE obj)
482 return rb_obj_shape_has_fields(obj);
485static inline shape_id_t
486rb_shape_transition_layout(shape_id_t shape_id, shape_id_t layout)
488 return (shape_id & (~SHAPE_ID_LAYOUT_MASK)) | layout;
491static inline shape_id_t
492rb_shape_transition_robject(shape_id_t shape_id)
494 return rb_shape_transition_layout(shape_id, SHAPE_ID_LAYOUT_ROBJECT);
497static inline shape_id_t
498rb_shape_transition_extended(shape_id_t shape_id)
500 return rb_shape_transition_layout(shape_id, SHAPE_ID_LAYOUT_EXTENDED);
503static inline shape_id_t
504rb_shape_transition_frozen(shape_id_t shape_id)
506 return shape_id | SHAPE_ID_FL_FROZEN;
509static inline shape_id_t
510rb_shape_transition_complex(shape_id_t shape_id)
512 shape_id_t next_shape_id = rb_shape_layout(shape_id) | ROOT_COMPLEX_SHAPE_ID;
514 if (rb_shape_has_object_id(shape_id)) {
515 next_shape_id = rb_shape_layout(shape_id) | ROOT_COMPLEX_WITH_OBJ_ID;
518 next_shape_id |= shape_id & SHAPE_ID_CAPACITY_MASK;
520 RUBY_ASSERT(rb_shape_has_object_id(shape_id) == rb_shape_has_object_id(next_shape_id));
522 return next_shape_id;
525static inline shape_id_t
526rb_shape_transition_offset(shape_id_t shape_id, shape_id_t offset)
528 offset = RSHAPE_OFFSET(offset);
529 RUBY_ASSERT(RSHAPE_OFFSET(shape_id) == offset || RSHAPE_DIRECT_CHILD_P(shape_id, offset));
530 return RSHAPE_FLAGS(shape_id) | offset;
533static inline shape_id_t
534rb_shape_transition_capacity(shape_id_t shape_id,
size_t capacity)
538 shape_id_t capacity_flags = (shape_id_t)capacity << SHAPE_ID_CAPACITY_OFFSET;
539 return (shape_id & (~SHAPE_ID_CAPACITY_MASK)) | capacity_flags;
542static inline shape_id_t
543rb_shape_transition_slot_size(shape_id_t shape_id,
size_t slot_size)
545 return rb_shape_transition_capacity(shape_id, rb_shape_capacity_for_slot_size(slot_size));
548shape_id_t rb_shape_transition_object_id(shape_id_t shape_id);
550static inline shape_id_t
551rb_obj_shape_transition_frozen(
VALUE obj)
554 return rb_shape_transition_frozen(RBASIC_SHAPE_ID(obj));
557static inline shape_id_t
558rb_obj_shape_transition_complex(
VALUE obj)
560 return rb_shape_transition_complex(RBASIC_SHAPE_ID(obj));
563static inline shape_id_t
564rb_obj_shape_transition_capacity(
VALUE obj,
size_t capacity)
566 return rb_shape_transition_capacity(RBASIC_SHAPE_ID(obj), capacity);
569static inline shape_id_t
570rb_obj_shape_transition_slot_size(
VALUE obj,
size_t slot_size)
572 return rb_shape_transition_slot_size(RBASIC_SHAPE_ID(obj), slot_size);
575static inline shape_id_t
576rb_obj_shape_transition_object_id(
VALUE obj)
578 return rb_shape_transition_object_id(RBASIC_SHAPE_ID(obj));
581shape_id_t rb_obj_shape_transition_remove_ivar(
VALUE obj,
ID id, shape_id_t *removed_shape_id);
582shape_id_t rb_obj_shape_transition_add_ivar(
VALUE obj,
ID id);
585RUBY_SYMBOL_EXPORT_BEGIN
586typedef void each_shape_callback(shape_id_t shape_id,
void *data);
587void rb_shape_each_shape_id(each_shape_callback callback,
void *data);
588size_t rb_shape_memsize(shape_id_t shape);
589size_t rb_shape_edges_count(shape_id_t shape_id);
590size_t rb_shape_depth(shape_id_t shape_id);
591RUBY_SYMBOL_EXPORT_END
597 shape_id_t shape_offset;
604STATIC_ASSERT(rb_getivar_cache_size,
sizeof(
union rb_getivar_cache) <=
sizeof(uint64_t));
606#define IVAR_CACHE_INIT ((uint64_t)-1)
607#define ATTR_INDEX_T_NUM_BITS (sizeof(attr_index_t) * CHAR_BIT)
610rb_getivar_cache_unpack(uint64_t packed)
618 cache.unpack.shape_offset &= SHAPE_ID_OFFSET_MASK;
622static inline uint64_t
623rb_getivar_cache_pack(shape_id_t shape_offset, attr_index_t index)
625 RUBY_ASSERT(shape_offset == RSHAPE_OFFSET(shape_offset));
630 .shape_offset = shape_offset,
639 shape_id_t source_shape_offset;
640 shape_id_t dest_shape_offset;
644rb_setivar_cache_unpack(uint64_t packed)
647 .index = (attr_index_t)packed,
648 .source_shape_offset = RSHAPE_OFFSET((shape_id_t)(packed >> ATTR_INDEX_T_NUM_BITS)),
649 .dest_shape_offset = RSHAPE_OFFSET((shape_id_t)(packed >> (ATTR_INDEX_T_NUM_BITS + SHAPE_ID_OFFSET_NUM_BITS))),
654static inline uint64_t
655rb_setivar_cache_pack(shape_id_t shape_offset, shape_id_t dest_shape_offset, attr_index_t index)
657 RUBY_ASSERT(shape_offset == RSHAPE_OFFSET(shape_offset));
658 RUBY_ASSERT(dest_shape_offset == RSHAPE_OFFSET(dest_shape_offset));
659 RUBY_ASSERT(shape_offset == dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_offset, dest_shape_offset));
661 uint64_t packed_cache = (uint64_t)dest_shape_offset << (ATTR_INDEX_T_NUM_BITS + SHAPE_ID_OFFSET_NUM_BITS);
662 packed_cache |= (uint64_t)shape_offset << ATTR_INDEX_T_NUM_BITS;
663 packed_cache |= (uint64_t)index;
667ALWAYS_INLINE(
static shape_id_t rb_setivar_cache_revalidate(shape_id_t shape_id, shape_id_t fields_shape_id,
rb_setivar_cache cache));
669rb_setivar_cache_revalidate(shape_id_t shape_id, shape_id_t fields_shape_id,
rb_setivar_cache cache)
672 RUBY_ASSERT(cache.dest_shape_offset == INVALID_SHAPE_ID || cache.dest_shape_offset == RSHAPE_OFFSET(cache.dest_shape_offset));
674 shape_id_t normalized_shape_id = shape_id & SHAPE_ID_WRITE_MASK;
675 if (UNLIKELY(normalized_shape_id != cache.source_shape_offset)) {
676 return INVALID_SHAPE_ID;
679 if (UNLIKELY(cache.index >= RSHAPE_CAPACITY(fields_shape_id))) {
682 return INVALID_SHAPE_ID;
686 RUBY_ASSERT(cache.source_shape_offset == cache.dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_id, cache.dest_shape_offset));
687 RUBY_ASSERT(cache.index < RSHAPE_CAPACITY(shape_id));
692 return rb_shape_transition_offset(shape_id, cache.dest_shape_offset);
696rb_imemo_fields_complex_tbl(
VALUE fields_obj)
702 RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields));
706 RUBY_ASSERT((
st_table *)rb_imemo_fields_ptr(fields_obj) == &IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table);
708 return &IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table;
712ROBJECT_FIELDS_OBJ(
VALUE obj)
716 return rb_obj_shape_embedded_p(obj) ? obj :
ROBJECT(obj)->as.extended;
720ROBJECT_EMBEDDED_FIELDS(
VALUE obj)
726ROBJECT_FIELDS(
VALUE obj)
730 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.