[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / shape.c
blob0f9e3d54fa7ec7e297a52232e499257ddb4d4607
1 #include "vm_core.h"
2 #include "vm_sync.h"
3 #include "shape.h"
4 #include "symbol.h"
5 #include "id_table.h"
6 #include "internal/class.h"
7 #include "internal/error.h"
8 #include "internal/gc.h"
9 #include "internal/object.h"
10 #include "internal/symbol.h"
11 #include "internal/variable.h"
12 #include "variable.h"
13 #include <stdbool.h>
15 #ifndef _WIN32
16 #include <sys/mman.h>
17 #endif
19 #ifndef SHAPE_DEBUG
20 #define SHAPE_DEBUG (VM_CHECK_MODE > 0)
21 #endif
23 #if SIZEOF_SHAPE_T == 4
24 #if RUBY_DEBUG
25 #define SHAPE_BUFFER_SIZE 0x8000
26 #else
27 #define SHAPE_BUFFER_SIZE 0x80000
28 #endif
29 #else
30 #define SHAPE_BUFFER_SIZE 0x8000
31 #endif
33 #define REDBLACK_CACHE_SIZE (SHAPE_BUFFER_SIZE * 32)
35 #define SINGLE_CHILD_TAG 0x1
36 #define TAG_SINGLE_CHILD(x) (struct rb_id_table *)((uintptr_t)x | SINGLE_CHILD_TAG)
37 #define SINGLE_CHILD_MASK (~((uintptr_t)SINGLE_CHILD_TAG))
38 #define SINGLE_CHILD_P(x) (((uintptr_t)x) & SINGLE_CHILD_TAG)
39 #define SINGLE_CHILD(x) (rb_shape_t *)((uintptr_t)x & SINGLE_CHILD_MASK)
40 #define ANCESTOR_CACHE_THRESHOLD 10
41 #define MAX_SHAPE_ID (SHAPE_BUFFER_SIZE - 1)
42 #define ANCESTOR_SEARCH_MAX_DEPTH 2
44 static ID id_frozen;
45 static ID id_t_object;
47 #define LEAF 0
48 #define BLACK 0x0
49 #define RED 0x1
51 static redblack_node_t *
52 redblack_left(redblack_node_t * node)
54 if (node->l == LEAF) {
55 return LEAF;
57 else {
58 RUBY_ASSERT(node->l < GET_SHAPE_TREE()->cache_size);
59 redblack_node_t * left = &GET_SHAPE_TREE()->shape_cache[node->l - 1];
60 return left;
64 static redblack_node_t *
65 redblack_right(redblack_node_t * node)
67 if (node->r == LEAF) {
68 return LEAF;
70 else {
71 RUBY_ASSERT(node->r < GET_SHAPE_TREE()->cache_size);
72 redblack_node_t * right = &GET_SHAPE_TREE()->shape_cache[node->r - 1];
73 return right;
77 static redblack_node_t *
78 redblack_find(redblack_node_t * tree, ID key)
80 if (tree == LEAF) {
81 return LEAF;
83 else {
84 RUBY_ASSERT(redblack_left(tree) == LEAF || redblack_left(tree)->key < tree->key);
85 RUBY_ASSERT(redblack_right(tree) == LEAF || redblack_right(tree)->key > tree->key);
87 if (tree->key == key) {
88 return tree;
90 else {
91 if (key < tree->key) {
92 return redblack_find(redblack_left(tree), key);
94 else {
95 return redblack_find(redblack_right(tree), key);
101 static inline char
102 redblack_color(redblack_node_t * node)
104 return node && ((uintptr_t)node->value & RED);
107 static inline bool
108 redblack_red_p(redblack_node_t * node)
110 return redblack_color(node) == RED;
113 static inline rb_shape_t *
114 redblack_value(redblack_node_t * node)
116 // Color is stored in the bottom bit of the shape pointer
117 // Mask away the bit so we get the actual pointer back
118 return (rb_shape_t *)((uintptr_t)node->value & (((uintptr_t)-1) - 1));
121 #ifdef HAVE_MMAP
122 static redblack_id_t
123 redblack_id_for(redblack_node_t * node)
125 RUBY_ASSERT(node || node == LEAF);
126 if (node == LEAF) {
127 return 0;
129 else {
130 redblack_node_t * redblack_nodes = GET_SHAPE_TREE()->shape_cache;
131 redblack_id_t id = (redblack_id_t)(node - redblack_nodes);
132 return id + 1;
136 static redblack_node_t *
137 redblack_new(char color, ID key, rb_shape_t * value, redblack_node_t * left, redblack_node_t * right)
139 if (GET_SHAPE_TREE()->cache_size + 1 >= REDBLACK_CACHE_SIZE) {
140 // We're out of cache, just quit
141 return LEAF;
144 RUBY_ASSERT(left == LEAF || left->key < key);
145 RUBY_ASSERT(right == LEAF || right->key > key);
147 redblack_node_t * redblack_nodes = GET_SHAPE_TREE()->shape_cache;
148 redblack_node_t * node = &redblack_nodes[(GET_SHAPE_TREE()->cache_size)++];
149 node->key = key;
150 node->value = (rb_shape_t *)((uintptr_t)value | color);
151 node->l = redblack_id_for(left);
152 node->r = redblack_id_for(right);
153 return node;
156 static redblack_node_t *
157 redblack_balance(char color, ID key, rb_shape_t * value, redblack_node_t * left, redblack_node_t * right)
159 if (color == BLACK) {
160 ID new_key, new_left_key, new_right_key;
161 rb_shape_t *new_value, *new_left_value, *new_right_value;
162 redblack_node_t *new_left_left, *new_left_right, *new_right_left, *new_right_right;
164 if (redblack_red_p(left) && redblack_red_p(redblack_left(left))) {
165 new_right_key = key;
166 new_right_value = value;
167 new_right_right = right;
169 new_key = left->key;
170 new_value = redblack_value(left);
171 new_right_left = redblack_right(left);
173 new_left_key = redblack_left(left)->key;
174 new_left_value = redblack_value(redblack_left(left));
176 new_left_left = redblack_left(redblack_left(left));
177 new_left_right = redblack_right(redblack_left(left));
179 else if (redblack_red_p(left) && redblack_red_p(redblack_right(left))) {
180 new_right_key = key;
181 new_right_value = value;
182 new_right_right = right;
184 new_left_key = left->key;
185 new_left_value = redblack_value(left);
186 new_left_left = redblack_left(left);
188 new_key = redblack_right(left)->key;
189 new_value = redblack_value(redblack_right(left));
190 new_left_right = redblack_left(redblack_right(left));
191 new_right_left = redblack_right(redblack_right(left));
193 else if (redblack_red_p(right) && redblack_red_p(redblack_left(right))) {
194 new_left_key = key;
195 new_left_value = value;
196 new_left_left = left;
198 new_right_key = right->key;
199 new_right_value = redblack_value(right);
200 new_right_right = redblack_right(right);
202 new_key = redblack_left(right)->key;
203 new_value = redblack_value(redblack_left(right));
204 new_left_right = redblack_left(redblack_left(right));
205 new_right_left = redblack_right(redblack_left(right));
207 else if (redblack_red_p(right) && redblack_red_p(redblack_right(right))) {
208 new_left_key = key;
209 new_left_value = value;
210 new_left_left = left;
212 new_key = right->key;
213 new_value = redblack_value(right);
214 new_left_right = redblack_left(right);
216 new_right_key = redblack_right(right)->key;
217 new_right_value = redblack_value(redblack_right(right));
218 new_right_left = redblack_left(redblack_right(right));
219 new_right_right = redblack_right(redblack_right(right));
221 else {
222 return redblack_new(color, key, value, left, right);
225 RUBY_ASSERT(new_left_key < new_key);
226 RUBY_ASSERT(new_right_key > new_key);
227 RUBY_ASSERT(new_left_left == LEAF || new_left_left->key < new_left_key);
228 RUBY_ASSERT(new_left_right == LEAF || new_left_right->key > new_left_key);
229 RUBY_ASSERT(new_left_right == LEAF || new_left_right->key < new_key);
230 RUBY_ASSERT(new_right_left == LEAF || new_right_left->key < new_right_key);
231 RUBY_ASSERT(new_right_left == LEAF || new_right_left->key > new_key);
232 RUBY_ASSERT(new_right_right == LEAF || new_right_right->key > new_right_key);
234 return redblack_new(
235 RED, new_key, new_value,
236 redblack_new(BLACK, new_left_key, new_left_value, new_left_left, new_left_right),
237 redblack_new(BLACK, new_right_key, new_right_value, new_right_left, new_right_right));
240 return redblack_new(color, key, value, left, right);
243 static redblack_node_t *
244 redblack_insert_aux(redblack_node_t * tree, ID key, rb_shape_t * value)
246 if (tree == LEAF) {
247 return redblack_new(RED, key, value, LEAF, LEAF);
249 else {
250 redblack_node_t *left, *right;
251 if (key < tree->key) {
252 left = redblack_insert_aux(redblack_left(tree), key, value);
253 RUBY_ASSERT(left != LEAF);
254 right = redblack_right(tree);
255 RUBY_ASSERT(right == LEAF || right->key > tree->key);
257 else if (key > tree->key) {
258 left = redblack_left(tree);
259 RUBY_ASSERT(left == LEAF || left->key < tree->key);
260 right = redblack_insert_aux(redblack_right(tree), key, value);
261 RUBY_ASSERT(right != LEAF);
263 else {
264 return tree;
267 return redblack_balance(
268 redblack_color(tree),
269 tree->key,
270 redblack_value(tree),
271 left,
272 right
277 static redblack_node_t *
278 redblack_force_black(redblack_node_t * node)
280 node->value = redblack_value(node);
281 return node;
284 static redblack_node_t *
285 redblack_insert(redblack_node_t * tree, ID key, rb_shape_t * value)
287 redblack_node_t * root = redblack_insert_aux(tree, key, value);
289 if (redblack_red_p(root)) {
290 return redblack_force_black(root);
292 else {
293 return root;
296 #endif
298 rb_shape_tree_t *rb_shape_tree_ptr = NULL;
301 * Shape getters
303 rb_shape_t *
304 rb_shape_get_root_shape(void)
306 return GET_SHAPE_TREE()->root_shape;
309 shape_id_t
310 rb_shape_id(rb_shape_t * shape)
312 return (shape_id_t)(shape - GET_SHAPE_TREE()->shape_list);
315 void
316 rb_shape_each_shape(each_shape_callback callback, void *data)
318 rb_shape_t *cursor = rb_shape_get_root_shape();
319 rb_shape_t *end = rb_shape_get_shape_by_id(GET_SHAPE_TREE()->next_shape_id);
320 while (cursor < end) {
321 callback(cursor, data);
322 cursor += 1;
326 RUBY_FUNC_EXPORTED rb_shape_t *
327 rb_shape_get_shape_by_id(shape_id_t shape_id)
329 RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
331 rb_shape_t *shape = &GET_SHAPE_TREE()->shape_list[shape_id];
332 return shape;
335 rb_shape_t *
336 rb_shape_get_parent(rb_shape_t * shape)
338 return rb_shape_get_shape_by_id(shape->parent_id);
341 #if !SHAPE_IN_BASIC_FLAGS
342 shape_id_t rb_generic_shape_id(VALUE obj);
343 #endif
345 RUBY_FUNC_EXPORTED shape_id_t
346 rb_shape_get_shape_id(VALUE obj)
348 if (RB_SPECIAL_CONST_P(obj)) {
349 return SPECIAL_CONST_SHAPE_ID;
352 #if SHAPE_IN_BASIC_FLAGS
353 return RBASIC_SHAPE_ID(obj);
354 #else
355 switch (BUILTIN_TYPE(obj)) {
356 case T_OBJECT:
357 return ROBJECT_SHAPE_ID(obj);
358 break;
359 case T_CLASS:
360 case T_MODULE:
361 return RCLASS_SHAPE_ID(obj);
362 default:
363 return rb_generic_shape_id(obj);
365 #endif
368 size_t
369 rb_shape_depth(rb_shape_t * shape)
371 size_t depth = 1;
373 while (shape->parent_id != INVALID_SHAPE_ID) {
374 depth++;
375 shape = rb_shape_get_parent(shape);
378 return depth;
381 rb_shape_t*
382 rb_shape_get_shape(VALUE obj)
384 return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj));
387 static rb_shape_t *
388 shape_alloc(void)
390 shape_id_t shape_id = GET_SHAPE_TREE()->next_shape_id;
391 GET_SHAPE_TREE()->next_shape_id++;
393 if (shape_id == (MAX_SHAPE_ID + 1)) {
394 // TODO: Make an OutOfShapesError ??
395 rb_bug("Out of shapes");
398 return &GET_SHAPE_TREE()->shape_list[shape_id];
401 static rb_shape_t *
402 rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id)
404 rb_shape_t * shape = shape_alloc();
406 shape->edge_name = edge_name;
407 shape->next_iv_index = 0;
408 shape->parent_id = parent_id;
409 shape->edges = NULL;
411 return shape;
414 static rb_shape_t *
415 rb_shape_alloc(ID edge_name, rb_shape_t * parent, enum shape_type type)
417 rb_shape_t * shape = rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent));
418 shape->type = (uint8_t)type;
419 shape->size_pool_index = parent->size_pool_index;
420 shape->capacity = parent->capacity;
421 shape->edges = 0;
422 return shape;
425 #ifdef HAVE_MMAP
426 static redblack_node_t *
427 redblack_cache_ancestors(rb_shape_t * shape)
429 if (!(shape->ancestor_index || shape->parent_id == INVALID_SHAPE_ID)) {
430 redblack_node_t * parent_index;
432 parent_index = redblack_cache_ancestors(rb_shape_get_parent(shape));
434 if (shape->type == SHAPE_IVAR) {
435 shape->ancestor_index = redblack_insert(parent_index, shape->edge_name, shape);
437 #if RUBY_DEBUG
438 if (shape->ancestor_index) {
439 redblack_node_t *inserted_node = redblack_find(shape->ancestor_index, shape->edge_name);
440 RUBY_ASSERT(inserted_node);
441 RUBY_ASSERT(redblack_value(inserted_node) == shape);
443 #endif
445 else {
446 shape->ancestor_index = parent_index;
450 return shape->ancestor_index;
452 #else
453 static redblack_node_t *
454 redblack_cache_ancestors(rb_shape_t * shape)
456 return LEAF;
458 #endif
460 static rb_shape_t *
461 rb_shape_alloc_new_child(ID id, rb_shape_t * shape, enum shape_type shape_type)
463 rb_shape_t * new_shape = rb_shape_alloc(id, shape, shape_type);
465 switch (shape_type) {
466 case SHAPE_IVAR:
467 if (UNLIKELY(shape->next_iv_index >= shape->capacity)) {
468 RUBY_ASSERT(shape->next_iv_index == shape->capacity);
469 new_shape->capacity = (uint32_t)rb_malloc_grow_capa(shape->capacity, sizeof(VALUE));
471 RUBY_ASSERT(new_shape->capacity > shape->next_iv_index);
472 new_shape->next_iv_index = shape->next_iv_index + 1;
473 if (new_shape->next_iv_index > ANCESTOR_CACHE_THRESHOLD) {
474 redblack_cache_ancestors(new_shape);
476 break;
477 case SHAPE_FROZEN:
478 new_shape->next_iv_index = shape->next_iv_index;
479 break;
480 case SHAPE_OBJ_TOO_COMPLEX:
481 case SHAPE_ROOT:
482 case SHAPE_T_OBJECT:
483 rb_bug("Unreachable");
484 break;
487 return new_shape;
490 static rb_shape_t*
491 get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created, bool new_variations_allowed)
493 rb_shape_t *res = NULL;
495 // There should never be outgoing edges from "too complex"
496 RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
498 *variation_created = false;
500 RB_VM_LOCK_ENTER();
502 // If the current shape has children
503 if (shape->edges) {
504 // Check if it only has one child
505 if (SINGLE_CHILD_P(shape->edges)) {
506 rb_shape_t * child = SINGLE_CHILD(shape->edges);
507 // If the one child has a matching edge name, then great,
508 // we found what we want.
509 if (child->edge_name == id) {
510 res = child;
513 else {
514 // If it has more than one child, do a hash lookup to find it.
515 VALUE lookup_result;
516 if (rb_id_table_lookup(shape->edges, id, &lookup_result)) {
517 res = (rb_shape_t *)lookup_result;
522 // If we didn't find the shape we're looking for we create it.
523 if (!res) {
524 // If we're not allowed to create a new variation, of if we're out of shapes
525 // we return TOO_COMPLEX_SHAPE.
526 if (!new_variations_allowed || GET_SHAPE_TREE()->next_shape_id > MAX_SHAPE_ID) {
527 res = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
529 else {
530 rb_shape_t * new_shape = rb_shape_alloc_new_child(id, shape, shape_type);
532 if (!shape->edges) {
533 // If the shape had no edge yet, we can directly set the new child
534 shape->edges = TAG_SINGLE_CHILD(new_shape);
536 else {
537 // If the edge was single child we need to allocate a table.
538 if (SINGLE_CHILD_P(shape->edges)) {
539 rb_shape_t * old_child = SINGLE_CHILD(shape->edges);
540 shape->edges = rb_id_table_create(2);
541 rb_id_table_insert(shape->edges, old_child->edge_name, (VALUE)old_child);
544 rb_id_table_insert(shape->edges, new_shape->edge_name, (VALUE)new_shape);
545 *variation_created = true;
548 res = new_shape;
552 RB_VM_LOCK_LEAVE();
554 return res;
558 rb_shape_frozen_shape_p(rb_shape_t* shape)
560 return SHAPE_FROZEN == (enum shape_type)shape->type;
563 static rb_shape_t *
564 remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
566 if (shape->parent_id == INVALID_SHAPE_ID) {
567 // We've hit the top of the shape tree and couldn't find the
568 // IV we wanted to remove, so return NULL
569 return NULL;
571 else {
572 if (shape->type == SHAPE_IVAR && shape->edge_name == id) {
573 *removed_shape = shape;
575 return rb_shape_get_parent(shape);
577 else {
578 // This isn't the IV we want to remove, keep walking up.
579 rb_shape_t *new_parent = remove_shape_recursive(rb_shape_get_parent(shape), id, removed_shape);
581 // We found a new parent. Create a child of the new parent that
582 // has the same attributes as this shape.
583 if (new_parent) {
584 if (UNLIKELY(new_parent->type == SHAPE_OBJ_TOO_COMPLEX)) {
585 return new_parent;
588 bool dont_care;
589 rb_shape_t *new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
590 if (UNLIKELY(new_child->type == SHAPE_OBJ_TOO_COMPLEX)) {
591 return new_child;
594 RUBY_ASSERT(new_child->capacity <= shape->capacity);
596 return new_child;
598 else {
599 // We went all the way to the top of the shape tree and couldn't
600 // find an IV to remove, so return NULL
601 return NULL;
607 bool
608 rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE *removed)
610 if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
611 return false;
614 rb_shape_t *removed_shape = NULL;
615 rb_shape_t *new_shape = remove_shape_recursive(shape, id, &removed_shape);
616 if (new_shape) {
617 RUBY_ASSERT(removed_shape != NULL);
619 if (UNLIKELY(new_shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
620 return false;
623 RUBY_ASSERT(new_shape->next_iv_index == shape->next_iv_index - 1);
625 VALUE *ivptr;
626 switch(BUILTIN_TYPE(obj)) {
627 case T_CLASS:
628 case T_MODULE:
629 ivptr = RCLASS_IVPTR(obj);
630 break;
631 case T_OBJECT:
632 ivptr = ROBJECT_IVPTR(obj);
633 break;
634 default: {
635 struct gen_ivtbl *ivtbl;
636 rb_gen_ivtbl_get(obj, id, &ivtbl);
637 ivptr = ivtbl->as.shape.ivptr;
638 break;
642 *removed = ivptr[removed_shape->next_iv_index - 1];
644 memmove(&ivptr[removed_shape->next_iv_index - 1], &ivptr[removed_shape->next_iv_index],
645 ((new_shape->next_iv_index + 1) - removed_shape->next_iv_index) * sizeof(VALUE));
647 // Re-embed objects when instances become small enough
648 // This is necessary because YJIT assumes that objects with the same shape
649 // have the same embeddedness for efficiency (avoid extra checks)
650 if (BUILTIN_TYPE(obj) == T_OBJECT &&
651 !RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
652 rb_obj_embedded_size(new_shape->next_iv_index) <= rb_gc_obj_slot_size(obj)) {
653 RB_FL_SET_RAW(obj, ROBJECT_EMBED);
654 memcpy(ROBJECT_IVPTR(obj), ivptr, new_shape->next_iv_index * sizeof(VALUE));
655 xfree(ivptr);
658 rb_shape_set_shape(obj, new_shape);
660 return true;
663 rb_shape_t *
664 rb_shape_transition_shape_frozen(VALUE obj)
666 rb_shape_t* shape = rb_shape_get_shape(obj);
667 RUBY_ASSERT(shape);
668 RUBY_ASSERT(RB_OBJ_FROZEN(obj));
670 if (rb_shape_frozen_shape_p(shape) || rb_shape_obj_too_complex(obj)) {
671 return shape;
674 rb_shape_t* next_shape;
676 if (shape == rb_shape_get_root_shape()) {
677 return rb_shape_get_shape_by_id(SPECIAL_CONST_SHAPE_ID);
680 bool dont_care;
681 next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
683 RUBY_ASSERT(next_shape);
684 return next_shape;
688 * This function is used for assertions where we don't want to increment
689 * max_iv_count
691 rb_shape_t *
692 rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
694 RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
695 bool dont_care;
696 return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care, true);
699 rb_shape_t *
700 rb_shape_get_next(rb_shape_t *shape, VALUE obj, ID id)
702 RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
703 if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
704 return shape;
707 #if RUBY_DEBUG
708 attr_index_t index;
709 if (rb_shape_get_iv_index(shape, id, &index)) {
710 rb_bug("rb_shape_get_next: trying to create ivar that already exists at index %u", index);
712 #endif
714 bool allow_new_shape = true;
716 if (BUILTIN_TYPE(obj) == T_OBJECT) {
717 VALUE klass = rb_obj_class(obj);
718 allow_new_shape = RCLASS_EXT(klass)->variation_count < SHAPE_MAX_VARIATIONS;
721 bool variation_created = false;
722 rb_shape_t *new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
724 // Check if we should update max_iv_count on the object's class
725 if (BUILTIN_TYPE(obj) == T_OBJECT) {
726 VALUE klass = rb_obj_class(obj);
727 if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) {
728 RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index;
731 if (variation_created) {
732 RCLASS_EXT(klass)->variation_count++;
733 if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_PERFORMANCE)) {
734 if (RCLASS_EXT(klass)->variation_count >= SHAPE_MAX_VARIATIONS) {
735 rb_category_warn(
736 RB_WARN_CATEGORY_PERFORMANCE,
737 "The class %"PRIsVALUE" reached %d shape variations, instance variables accesses will be slower and memory usage increased.\n"
738 "It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method.",
739 rb_class_path(klass),
740 SHAPE_MAX_VARIATIONS
747 return new_shape;
750 // Same as rb_shape_get_iv_index, but uses a provided valid shape id and index
751 // to return a result faster if branches of the shape tree are closely related.
752 bool
753 rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t *value, shape_id_t *shape_id_hint)
755 attr_index_t index_hint = *value;
756 rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
757 rb_shape_t *initial_shape = shape;
759 if (*shape_id_hint == INVALID_SHAPE_ID) {
760 *shape_id_hint = shape_id;
761 return rb_shape_get_iv_index(shape, id, value);
764 rb_shape_t * shape_hint = rb_shape_get_shape_by_id(*shape_id_hint);
766 // We assume it's likely shape_id_hint and shape_id have a close common
767 // ancestor, so we check up to ANCESTOR_SEARCH_MAX_DEPTH ancestors before
768 // eventually using the index, as in case of a match it will be faster.
769 // However if the shape doesn't have an index, we walk the entire tree.
770 int depth = INT_MAX;
771 if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) {
772 depth = ANCESTOR_SEARCH_MAX_DEPTH;
775 while (depth > 0 && shape->next_iv_index > index_hint) {
776 while (shape_hint->next_iv_index > shape->next_iv_index) {
777 shape_hint = rb_shape_get_parent(shape_hint);
780 if (shape_hint == shape) {
781 // We've found a common ancestor so use the index hint
782 *value = index_hint;
783 *shape_id_hint = rb_shape_id(shape);
784 return true;
786 if (shape->edge_name == id) {
787 // We found the matching id before a common ancestor
788 *value = shape->next_iv_index - 1;
789 *shape_id_hint = rb_shape_id(shape);
790 return true;
793 shape = rb_shape_get_parent(shape);
794 depth--;
797 // If the original shape had an index but its ancestor doesn't
798 // we switch back to the original one as it will be faster.
799 if (!shape->ancestor_index && initial_shape->ancestor_index) {
800 shape = initial_shape;
802 *shape_id_hint = shape_id;
803 return rb_shape_get_iv_index(shape, id, value);
806 static bool
807 shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
809 while (shape->parent_id != INVALID_SHAPE_ID) {
810 if (shape->edge_name == id) {
811 enum shape_type shape_type;
812 shape_type = (enum shape_type)shape->type;
814 switch (shape_type) {
815 case SHAPE_IVAR:
816 RUBY_ASSERT(shape->next_iv_index > 0);
817 *value = shape->next_iv_index - 1;
818 return true;
819 case SHAPE_ROOT:
820 case SHAPE_T_OBJECT:
821 return false;
822 case SHAPE_OBJ_TOO_COMPLEX:
823 case SHAPE_FROZEN:
824 rb_bug("Ivar should not exist on transition");
828 shape = rb_shape_get_parent(shape);
831 return false;
834 static bool
835 shape_cache_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
837 if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) {
838 redblack_node_t *node = redblack_find(shape->ancestor_index, id);
839 if (node) {
840 rb_shape_t *shape = redblack_value(node);
841 *value = shape->next_iv_index - 1;
843 #if RUBY_DEBUG
844 attr_index_t shape_tree_index;
845 RUBY_ASSERT(shape_get_iv_index(shape, id, &shape_tree_index));
846 RUBY_ASSERT(shape_tree_index == *value);
847 #endif
849 return true;
852 /* Verify the cache is correct by checking that this instance variable
853 * does not exist in the shape tree either. */
854 RUBY_ASSERT(!shape_get_iv_index(shape, id, value));
857 return false;
860 bool
861 rb_shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value)
863 // It doesn't make sense to ask for the index of an IV that's stored
864 // on an object that is "too complex" as it uses a hash for storing IVs
865 RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
867 if (!shape_cache_get_iv_index(shape, id, value)) {
868 // If it wasn't in the ancestor cache, then don't do a linear search
869 if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) {
870 return false;
872 else {
873 return shape_get_iv_index(shape, id, value);
877 return true;
880 void
881 rb_shape_set_shape(VALUE obj, rb_shape_t* shape)
883 rb_shape_set_shape_id(obj, rb_shape_id(shape));
886 int32_t
887 rb_shape_id_offset(void)
889 return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS / sizeof(uintptr_t);
892 rb_shape_t *
893 rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
895 RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
896 rb_shape_t *next_shape = initial_shape;
898 if (dest_shape->type != initial_shape->type) {
899 next_shape = rb_shape_traverse_from_new_root(initial_shape, rb_shape_get_parent(dest_shape));
900 if (!next_shape) {
901 return NULL;
905 switch ((enum shape_type)dest_shape->type) {
906 case SHAPE_IVAR:
907 case SHAPE_FROZEN:
908 if (!next_shape->edges) {
909 return NULL;
912 VALUE lookup_result;
913 if (SINGLE_CHILD_P(next_shape->edges)) {
914 rb_shape_t * child = SINGLE_CHILD(next_shape->edges);
915 if (child->edge_name == dest_shape->edge_name) {
916 return child;
918 else {
919 return NULL;
922 else {
923 if (rb_id_table_lookup(next_shape->edges, dest_shape->edge_name, &lookup_result)) {
924 next_shape = (rb_shape_t *)lookup_result;
926 else {
927 return NULL;
930 break;
931 case SHAPE_ROOT:
932 case SHAPE_T_OBJECT:
933 break;
934 case SHAPE_OBJ_TOO_COMPLEX:
935 rb_bug("Unreachable");
936 break;
939 return next_shape;
942 rb_shape_t *
943 rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
945 RUBY_ASSERT(rb_shape_id(initial_shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
946 RUBY_ASSERT(rb_shape_id(dest_shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
948 rb_shape_t * midway_shape;
950 RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
952 if (dest_shape->type != initial_shape->type) {
953 midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape));
954 if (UNLIKELY(rb_shape_id(midway_shape) == OBJ_TOO_COMPLEX_SHAPE_ID)) {
955 return midway_shape;
958 else {
959 midway_shape = initial_shape;
962 switch ((enum shape_type)dest_shape->type) {
963 case SHAPE_IVAR:
964 midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
965 break;
966 case SHAPE_ROOT:
967 case SHAPE_FROZEN:
968 case SHAPE_T_OBJECT:
969 break;
970 case SHAPE_OBJ_TOO_COMPLEX:
971 rb_bug("Unreachable");
972 break;
975 return midway_shape;
978 RUBY_FUNC_EXPORTED bool
979 rb_shape_obj_too_complex(VALUE obj)
981 return rb_shape_get_shape_id(obj) == OBJ_TOO_COMPLEX_SHAPE_ID;
984 size_t
985 rb_shape_edges_count(rb_shape_t *shape)
987 if (shape->edges) {
988 if (SINGLE_CHILD_P(shape->edges)) {
989 return 1;
991 else {
992 return rb_id_table_size(shape->edges);
995 return 0;
998 size_t
999 rb_shape_memsize(rb_shape_t *shape)
1001 size_t memsize = sizeof(rb_shape_t);
1002 if (shape->edges && !SINGLE_CHILD_P(shape->edges)) {
1003 memsize += rb_id_table_memsize(shape->edges);
1005 return memsize;
1008 #if SHAPE_DEBUG
1010 * Exposing Shape to Ruby via RubyVM.debug_shape
1013 /* :nodoc: */
1014 static VALUE
1015 rb_shape_too_complex(VALUE self)
1017 rb_shape_t * shape;
1018 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
1019 if (rb_shape_id(shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
1020 return Qtrue;
1022 else {
1023 return Qfalse;
1027 static VALUE
1028 parse_key(ID key)
1030 if (is_instance_id(key)) {
1031 return ID2SYM(key);
1033 return LONG2NUM(key);
1036 static VALUE rb_shape_edge_name(rb_shape_t * shape);
1038 static VALUE
1039 rb_shape_t_to_rb_cShape(rb_shape_t *shape)
1041 VALUE rb_cShape = rb_const_get(rb_cRubyVM, rb_intern("Shape"));
1043 VALUE obj = rb_struct_new(rb_cShape,
1044 INT2NUM(rb_shape_id(shape)),
1045 INT2NUM(shape->parent_id),
1046 rb_shape_edge_name(shape),
1047 INT2NUM(shape->next_iv_index),
1048 INT2NUM(shape->size_pool_index),
1049 INT2NUM(shape->type),
1050 INT2NUM(shape->capacity));
1051 rb_obj_freeze(obj);
1052 return obj;
1055 static enum rb_id_table_iterator_result
1056 rb_edges_to_hash(ID key, VALUE value, void *ref)
1058 rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value));
1059 return ID_TABLE_CONTINUE;
1062 /* :nodoc: */
1063 static VALUE
1064 rb_shape_edges(VALUE self)
1066 rb_shape_t* shape;
1068 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
1070 VALUE hash = rb_hash_new();
1072 if (shape->edges) {
1073 if (SINGLE_CHILD_P(shape->edges)) {
1074 rb_shape_t * child = SINGLE_CHILD(shape->edges);
1075 rb_edges_to_hash(child->edge_name, (VALUE)child, &hash);
1077 else {
1078 rb_id_table_foreach(shape->edges, rb_edges_to_hash, &hash);
1082 return hash;
1085 static VALUE
1086 rb_shape_edge_name(rb_shape_t * shape)
1088 if (shape->edge_name) {
1089 if (is_instance_id(shape->edge_name)) {
1090 return ID2SYM(shape->edge_name);
1092 return INT2NUM(shape->capacity);
1094 return Qnil;
1097 /* :nodoc: */
1098 static VALUE
1099 rb_shape_export_depth(VALUE self)
1101 rb_shape_t* shape;
1102 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
1103 return SIZET2NUM(rb_shape_depth(shape));
1106 /* :nodoc: */
1107 static VALUE
1108 rb_shape_parent(VALUE self)
1110 rb_shape_t * shape;
1111 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
1112 if (shape->parent_id != INVALID_SHAPE_ID) {
1113 return rb_shape_t_to_rb_cShape(rb_shape_get_parent(shape));
1115 else {
1116 return Qnil;
1120 /* :nodoc: */
1121 static VALUE
1122 rb_shape_debug_shape(VALUE self, VALUE obj)
1124 return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj));
1127 /* :nodoc: */
1128 static VALUE
1129 rb_shape_root_shape(VALUE self)
1131 return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape());
1134 /* :nodoc: */
1135 static VALUE
1136 rb_shape_shapes_available(VALUE self)
1138 return INT2NUM(MAX_SHAPE_ID - (GET_SHAPE_TREE()->next_shape_id - 1));
1141 /* :nodoc: */
1142 static VALUE
1143 rb_shape_exhaust(int argc, VALUE *argv, VALUE self)
1145 rb_check_arity(argc, 0, 1);
1146 int offset = argc == 1 ? NUM2INT(argv[0]) : 0;
1147 GET_SHAPE_TREE()->next_shape_id = MAX_SHAPE_ID - offset + 1;
1148 return Qnil;
1151 VALUE rb_obj_shape(rb_shape_t* shape);
1153 static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref)
1155 rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_obj_shape((rb_shape_t*)value));
1156 return ID_TABLE_CONTINUE;
1159 static VALUE edges(struct rb_id_table* edges)
1161 VALUE hash = rb_hash_new();
1162 if (SINGLE_CHILD_P(edges)) {
1163 rb_shape_t * child = SINGLE_CHILD(edges);
1164 collect_keys_and_values(child->edge_name, (VALUE)child, &hash);
1166 else {
1167 rb_id_table_foreach(edges, collect_keys_and_values, &hash);
1169 return hash;
1172 /* :nodoc: */
1173 VALUE
1174 rb_obj_shape(rb_shape_t* shape)
1176 VALUE rb_shape = rb_hash_new();
1178 rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape)));
1179 rb_hash_aset(rb_shape, ID2SYM(rb_intern("edges")), edges(shape->edges));
1181 if (shape == rb_shape_get_root_shape()) {
1182 rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID));
1184 else {
1185 rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(shape->parent_id));
1188 rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name));
1189 return rb_shape;
1192 /* :nodoc: */
1193 static VALUE
1194 shape_transition_tree(VALUE self)
1196 return rb_obj_shape(rb_shape_get_root_shape());
1199 /* :nodoc: */
1200 static VALUE
1201 rb_shape_find_by_id(VALUE mod, VALUE id)
1203 shape_id_t shape_id = NUM2UINT(id);
1204 if (shape_id >= GET_SHAPE_TREE()->next_shape_id) {
1205 rb_raise(rb_eArgError, "Shape ID %d is out of bounds\n", shape_id);
1207 return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id));
1209 #endif
1211 #ifdef HAVE_MMAP
1212 #include <sys/mman.h>
1213 #endif
1215 void
1216 Init_default_shapes(void)
1218 rb_shape_tree_ptr = xcalloc(1, sizeof(rb_shape_tree_t));
1220 #ifdef HAVE_MMAP
1221 rb_shape_tree_ptr->shape_list = (rb_shape_t *)mmap(NULL, rb_size_mul_or_raise(SHAPE_BUFFER_SIZE, sizeof(rb_shape_t), rb_eRuntimeError),
1222 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1223 if (GET_SHAPE_TREE()->shape_list == MAP_FAILED) {
1224 GET_SHAPE_TREE()->shape_list = 0;
1226 #else
1227 GET_SHAPE_TREE()->shape_list = xcalloc(SHAPE_BUFFER_SIZE, sizeof(rb_shape_t));
1228 #endif
1230 if (!GET_SHAPE_TREE()->shape_list) {
1231 rb_memerror();
1234 id_frozen = rb_make_internal_id();
1235 id_t_object = rb_make_internal_id();
1237 #ifdef HAVE_MMAP
1238 rb_shape_tree_ptr->shape_cache = (redblack_node_t *)mmap(NULL, rb_size_mul_or_raise(REDBLACK_CACHE_SIZE, sizeof(redblack_node_t), rb_eRuntimeError),
1239 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1240 rb_shape_tree_ptr->cache_size = 0;
1242 // If mmap fails, then give up on the redblack tree cache.
1243 // We set the cache size such that the redblack node allocators think
1244 // the cache is full.
1245 if (GET_SHAPE_TREE()->shape_cache == MAP_FAILED) {
1246 GET_SHAPE_TREE()->shape_cache = 0;
1247 GET_SHAPE_TREE()->cache_size = REDBLACK_CACHE_SIZE;
1249 #endif
1251 // Root shape
1252 rb_shape_t *root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
1253 root->capacity = 0;
1254 root->type = SHAPE_ROOT;
1255 root->size_pool_index = 0;
1256 GET_SHAPE_TREE()->root_shape = root;
1257 RUBY_ASSERT(rb_shape_id(GET_SHAPE_TREE()->root_shape) == ROOT_SHAPE_ID);
1259 bool dont_care;
1260 // Special const shape
1261 #if RUBY_DEBUG
1262 rb_shape_t *special_const_shape =
1263 #endif
1264 get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
1265 RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
1266 RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_SHAPE_TREE()->next_shape_id - 1));
1267 RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
1269 rb_shape_t *too_complex_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
1270 too_complex_shape->type = SHAPE_OBJ_TOO_COMPLEX;
1271 too_complex_shape->size_pool_index = 0;
1272 RUBY_ASSERT(OBJ_TOO_COMPLEX_SHAPE_ID == (GET_SHAPE_TREE()->next_shape_id - 1));
1273 RUBY_ASSERT(rb_shape_id(too_complex_shape) == OBJ_TOO_COMPLEX_SHAPE_ID);
1275 // Make shapes for T_OBJECT
1276 size_t *sizes = rb_gc_size_pool_sizes();
1277 for (int i = 0; sizes[i] > 0; i++) {
1278 rb_shape_t *t_object_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
1279 t_object_shape->type = SHAPE_T_OBJECT;
1280 t_object_shape->size_pool_index = i;
1281 t_object_shape->capacity = (uint32_t)((sizes[i] - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
1282 t_object_shape->edges = rb_id_table_create(0);
1283 t_object_shape->ancestor_index = LEAF;
1284 RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + FIRST_T_OBJECT_SHAPE_ID));
1288 void
1289 Init_shape(void)
1291 #if SHAPE_DEBUG
1292 VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
1293 "id",
1294 "parent_id",
1295 "edge_name",
1296 "next_iv_index",
1297 "size_pool_index",
1298 "type",
1299 "capacity",
1300 NULL);
1302 rb_define_method(rb_cShape, "parent", rb_shape_parent, 0);
1303 rb_define_method(rb_cShape, "edges", rb_shape_edges, 0);
1304 rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0);
1305 rb_define_method(rb_cShape, "too_complex?", rb_shape_too_complex, 0);
1306 rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
1307 rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
1308 rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT));
1309 rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN));
1310 rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS));
1311 rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT));
1312 rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID));
1313 rb_define_const(rb_cShape, "OBJ_TOO_COMPLEX_SHAPE_ID", INT2NUM(OBJ_TOO_COMPLEX_SHAPE_ID));
1314 rb_define_const(rb_cShape, "FIRST_T_OBJECT_SHAPE_ID", INT2NUM(FIRST_T_OBJECT_SHAPE_ID));
1315 rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS));
1316 rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t)));
1317 rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t)));
1318 rb_define_const(rb_cShape, "SHAPE_BUFFER_SIZE", INT2NUM(sizeof(rb_shape_t) * SHAPE_BUFFER_SIZE));
1319 rb_define_const(rb_cShape, "REDBLACK_CACHE_SIZE", INT2NUM(sizeof(redblack_node_t) * REDBLACK_CACHE_SIZE));
1321 rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0);
1322 rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1);
1323 rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1);
1324 rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0);
1325 rb_define_singleton_method(rb_cShape, "shapes_available", rb_shape_shapes_available, 0);
1326 rb_define_singleton_method(rb_cShape, "exhaust_shapes", rb_shape_exhaust, -1);
1327 #endif