Skip to content

Commit 707a94d

Browse files
committed
Add SHAPE_ID_HAS_IVAR_MASK for quick ivar check
This allow checking if an object has ivars with just a shape_id mask.
1 parent ffa99e4 commit 707a94d

6 files changed

Lines changed: 51 additions & 16 deletions

File tree

shape.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,23 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
12341234
}
12351235
}
12361236

1237+
// Make sure SHAPE_ID_HAS_IVAR_MASK is valid.
1238+
if (rb_shape_too_complex_p(shape_id)) {
1239+
RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK);
1240+
}
1241+
else {
1242+
attr_index_t ivar_count = RSHAPE_LEN(shape_id);
1243+
if (has_object_id) {
1244+
ivar_count--;
1245+
}
1246+
if (ivar_count) {
1247+
RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK);
1248+
}
1249+
else {
1250+
RUBY_ASSERT(!(shape_id & SHAPE_ID_HAS_IVAR_MASK));
1251+
}
1252+
}
1253+
12371254
uint8_t flags_heap_index = rb_shape_heap_index(shape_id);
12381255
if (RB_TYPE_P(obj, T_OBJECT)) {
12391256
size_t shape_id_slot_size = rb_shape_tree.capacities[flags_heap_index - 1] * sizeof(VALUE) + sizeof(struct RBasic);
@@ -1524,13 +1541,15 @@ Init_default_shapes(void)
15241541
root->type = SHAPE_ROOT;
15251542
rb_shape_tree.root_shape = root;
15261543
RUBY_ASSERT(raw_shape_id(rb_shape_tree.root_shape) == ROOT_SHAPE_ID);
1544+
RUBY_ASSERT(!(raw_shape_id(rb_shape_tree.root_shape) & SHAPE_ID_HAS_IVAR_MASK));
15271545

15281546
bool dontcare;
15291547
rb_shape_t *root_with_obj_id = get_next_shape_internal(root, id_object_id, SHAPE_OBJ_ID, &dontcare, true);
15301548
RUBY_ASSERT(raw_shape_id(root_with_obj_id) == ROOT_SHAPE_WITH_OBJ_ID);
15311549
RUBY_ASSERT(root_with_obj_id->type == SHAPE_OBJ_ID);
15321550
RUBY_ASSERT(root_with_obj_id->edge_name == id_object_id);
15331551
RUBY_ASSERT(root_with_obj_id->next_field_index == 1);
1552+
RUBY_ASSERT(!(raw_shape_id(root_with_obj_id) & SHAPE_ID_HAS_IVAR_MASK));
15341553
}
15351554

15361555
void

shape.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ typedef uint32_t shape_id_t;
1010

1111
STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_BIT);
1212

13+
enum shape_flags {
14+
SHAPE_FL_FROZEN = 1 << 0,
15+
SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
16+
SHAPE_FL_TOO_COMPLEX = 1 << 2,
17+
18+
SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
19+
};
20+
1321
#define SHAPE_BUFFER_SIZE (1 << SHAPE_ID_OFFSET_NUM_BITS)
1422
#define SHAPE_ID_OFFSET_MASK (SHAPE_BUFFER_SIZE - 1)
1523
#define SHAPE_ID_FLAGS_MASK (shape_id_t)(((1 << (SHAPE_ID_NUM_BITS - SHAPE_ID_OFFSET_NUM_BITS)) - 1) << SHAPE_ID_OFFSET_NUM_BITS)
@@ -23,6 +31,11 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_
2331
#define SHAPE_ID_HEAP_INDEX_MAX ((1 << SHAPE_ID_HEAP_INDEX_BITS) - 1)
2432
#define SHAPE_ID_HEAP_INDEX_MASK (SHAPE_ID_HEAP_INDEX_MAX << SHAPE_ID_HEAP_INDEX_OFFSET)
2533

34+
// This masks allows to check if a shape_id contains any ivar.
35+
// It rely on ROOT_SHAPE_WITH_OBJ_ID==1.
36+
#define SHAPE_ID_HAS_IVAR_MASK 0x27fffe
37+
STATIC_ASSERT(shape_id_has_ivar_mask, SHAPE_ID_HAS_IVAR_MASK == (SHAPE_ID_FL_TOO_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1)));
38+
2639
// The interpreter doesn't care about frozen status or slot size when reading ivars.
2740
// So we normalize shape_id by clearing these bits to improve cache hits.
2841
// JITs however might care about it.
@@ -72,14 +85,6 @@ enum shape_type {
7285
SHAPE_OBJ_ID,
7386
};
7487

75-
enum shape_flags {
76-
SHAPE_FL_FROZEN = 1 << 0,
77-
SHAPE_FL_HAS_OBJECT_ID = 1 << 1,
78-
SHAPE_FL_TOO_COMPLEX = 1 << 2,
79-
80-
SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID,
81-
};
82-
8388
typedef struct {
8489
/* object shapes */
8590
rb_shape_t *shape_list;
@@ -327,6 +332,18 @@ rb_shape_obj_has_id(VALUE obj)
327332
return rb_shape_has_object_id(RBASIC_SHAPE_ID(obj));
328333
}
329334

335+
static inline bool
336+
rb_shape_has_ivars(shape_id_t shape_id)
337+
{
338+
return shape_id & SHAPE_ID_HAS_IVAR_MASK;
339+
}
340+
341+
static inline bool
342+
rb_shape_obj_has_ivars(VALUE obj)
343+
{
344+
return rb_shape_has_ivars(RBASIC_SHAPE_ID(obj));
345+
}
346+
330347
// For ext/objspace
331348
RUBY_SYMBOL_EXPORT_BEGIN
332349
typedef void each_shape_callback(shape_id_t shape_id, void *data);

string.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,7 @@ fstring_hash(VALUE str)
388388
static inline bool
389389
BARE_STRING_P(VALUE str)
390390
{
391-
if (RBASIC_CLASS(str) != rb_cString) return false;
392-
393-
if (FL_TEST_RAW(str, FL_EXIVAR)) {
394-
return rb_ivar_count(str) == 0;
395-
}
396-
return true;
391+
return RBASIC_CLASS(str) == rb_cString && !rb_shape_obj_has_ivars(str);
397392
}
398393

399394
static inline st_index_t

yjit/bindgen/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ fn main() {
103103
.allowlist_function("rb_yjit_shape_capacity")
104104
.allowlist_function("rb_yjit_shape_index")
105105
.allowlist_var("SHAPE_ID_NUM_BITS")
106+
.allowlist_var("SHAPE_ID_HAS_IVAR_MASK")
106107

107108
// From ruby/internal/intern/object.h
108109
.allowlist_function("rb_obj_is_kind_of")

yjit/src/codegen.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6278,8 +6278,10 @@ fn jit_rb_str_dup(
62786278
// Check !FL_ANY_RAW(str, FL_EXIVAR), which is part of BARE_STRING_P.
62796279
let recv_opnd = asm.stack_pop(1);
62806280
let recv_opnd = asm.load(recv_opnd);
6281-
let flags_opnd = Opnd::mem(64, recv_opnd, RUBY_OFFSET_RBASIC_FLAGS);
6282-
asm.test(flags_opnd, Opnd::Imm(RUBY_FL_EXIVAR as i64));
6281+
6282+
let shape_id_offset = unsafe { rb_shape_id_offset() };
6283+
let shape_opnd = Opnd::mem(SHAPE_ID_NUM_BITS as u8, recv_opnd, shape_id_offset);
6284+
asm.test(shape_opnd, Opnd::Imm(SHAPE_ID_HAS_IVAR_MASK as i64));
62836285
asm.jnz(Target::side_exit(Counter::send_str_dup_exivar));
62846286

62856287
// Call rb_str_dup

yjit/src/cruby_bindings.inc.rs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)