summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2025-06-13 11:23:32 +0200
committerJean Boussier <[email protected]>2025-06-13 19:46:29 +0200
commita99d941cacbb9d5d277400abf76f5648f91009ea (patch)
tree062e514cbaf393b275e5c111c7ace438bfed08be
parentfb0dbbc0e660d0c77ebba292578945aca8baafac (diff)
Add SHAPE_ID_HAS_IVAR_MASK for quick ivar check
This allow checking if an object has ivars with just a shape_id mask.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13606
-rw-r--r--internal/string.h1
-rw-r--r--shape.c20
-rw-r--r--shape.h16
-rw-r--r--string.c9
-rw-r--r--yjit/bindgen/src/main.rs1
-rw-r--r--yjit/src/codegen.rs6
-rw-r--r--yjit/src/cruby_bindings.inc.rs1
7 files changed, 41 insertions, 13 deletions
diff --git a/internal/string.h b/internal/string.h
index 50561924f2..d6fea62061 100644
--- a/internal/string.h
+++ b/internal/string.h
@@ -30,6 +30,7 @@ enum ruby_rstring_private_flags {
#endif
/* string.c */
+VALUE rb_str_dup_m(VALUE str);
VALUE rb_fstring(VALUE);
VALUE rb_fstring_cstr(const char *str);
VALUE rb_fstring_enc_new(const char *ptr, long len, rb_encoding *enc);
diff --git a/shape.c b/shape.c
index b2f2851c2c..c50147124a 100644
--- a/shape.c
+++ b/shape.c
@@ -1234,6 +1234,23 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
}
}
+ // Make sure SHAPE_ID_HAS_IVAR_MASK is valid.
+ if (rb_shape_too_complex_p(shape_id)) {
+ RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK);
+ }
+ else {
+ attr_index_t ivar_count = RSHAPE_LEN(shape_id);
+ if (has_object_id) {
+ ivar_count--;
+ }
+ if (ivar_count) {
+ RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK);
+ }
+ else {
+ RUBY_ASSERT(!(shape_id & SHAPE_ID_HAS_IVAR_MASK));
+ }
+ }
+
uint8_t flags_heap_index = rb_shape_heap_index(shape_id);
if (RB_TYPE_P(obj, T_OBJECT)) {
size_t shape_id_slot_size = rb_shape_tree.capacities[flags_heap_index - 1] * sizeof(VALUE) + sizeof(struct RBasic);
@@ -1524,6 +1541,7 @@ Init_default_shapes(void)
root->type = SHAPE_ROOT;
rb_shape_tree.root_shape = root;
RUBY_ASSERT(raw_shape_id(rb_shape_tree.root_shape) == ROOT_SHAPE_ID);
+ RUBY_ASSERT(!(raw_shape_id(rb_shape_tree.root_shape) & SHAPE_ID_HAS_IVAR_MASK));
bool dontcare;
rb_shape_t *root_with_obj_id = get_next_shape_internal(root, id_object_id, SHAPE_OBJ_ID, &dontcare, true);
@@ -1531,7 +1549,7 @@ Init_default_shapes(void)
RUBY_ASSERT(root_with_obj_id->type == SHAPE_OBJ_ID);
RUBY_ASSERT(root_with_obj_id->edge_name == id_object_id);
RUBY_ASSERT(root_with_obj_id->next_field_index == 1);
- (void)root_with_obj_id;
+ RUBY_ASSERT(!(raw_shape_id(root_with_obj_id) & SHAPE_ID_HAS_IVAR_MASK));
}
void
diff --git a/shape.h b/shape.h
index a28f5b9780..35d28b41ed 100644
--- a/shape.h
+++ b/shape.h
@@ -23,6 +23,10 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_
#define SHAPE_ID_HEAP_INDEX_MAX ((1 << SHAPE_ID_HEAP_INDEX_BITS) - 1)
#define SHAPE_ID_HEAP_INDEX_MASK (SHAPE_ID_HEAP_INDEX_MAX << SHAPE_ID_HEAP_INDEX_OFFSET)
+// This masks allows to check if a shape_id contains any ivar.
+// It rely on ROOT_SHAPE_WITH_OBJ_ID==1.
+#define SHAPE_ID_HAS_IVAR_MASK (SHAPE_ID_FL_TOO_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1))
+
// The interpreter doesn't care about frozen status or slot size when reading ivars.
// So we normalize shape_id by clearing these bits to improve cache hits.
// JITs however might care about it.
@@ -327,6 +331,18 @@ rb_shape_obj_has_id(VALUE obj)
return rb_shape_has_object_id(RBASIC_SHAPE_ID(obj));
}
+static inline bool
+rb_shape_has_ivars(shape_id_t shape_id)
+{
+ return shape_id & SHAPE_ID_HAS_IVAR_MASK;
+}
+
+static inline bool
+rb_shape_obj_has_ivars(VALUE obj)
+{
+ return rb_shape_has_ivars(RBASIC_SHAPE_ID(obj));
+}
+
// For ext/objspace
RUBY_SYMBOL_EXPORT_BEGIN
typedef void each_shape_callback(shape_id_t shape_id, void *data);
diff --git a/string.c b/string.c
index 3ddd64ef25..6ab768c244 100644
--- a/string.c
+++ b/string.c
@@ -388,12 +388,7 @@ fstring_hash(VALUE str)
static inline bool
BARE_STRING_P(VALUE str)
{
- if (RBASIC_CLASS(str) != rb_cString) return false;
-
- if (FL_TEST_RAW(str, FL_EXIVAR)) {
- return rb_ivar_count(str) == 0;
- }
- return true;
+ return RBASIC_CLASS(str) == rb_cString && !rb_shape_obj_has_ivars(str);
}
static inline st_index_t
@@ -2316,7 +2311,7 @@ VALUE
rb_str_dup_m(VALUE str)
{
if (LIKELY(BARE_STRING_P(str))) {
- return str_duplicate(rb_obj_class(str), str);
+ return str_duplicate(rb_cString, str);
}
else {
return rb_obj_dup(str);
diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs
index e65f001145..41d383f8bd 100644
--- a/yjit/bindgen/src/main.rs
+++ b/yjit/bindgen/src/main.rs
@@ -228,6 +228,7 @@ fn main() {
.allowlist_function("rb_obj_as_string_result")
.allowlist_function("rb_str_byte_substr")
.allowlist_function("rb_str_substr_two_fixnums")
+ .allowlist_function("rb_str_dup_m")
// From include/ruby/internal/intern/parse.h
.allowlist_function("rb_backref_get")
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 2e2ca51b17..3e08857295 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -6275,16 +6275,12 @@ fn jit_rb_str_dup(
jit_prepare_call_with_gc(jit, asm);
- // Check !FL_ANY_RAW(str, FL_EXIVAR), which is part of BARE_STRING_P.
let recv_opnd = asm.stack_pop(1);
let recv_opnd = asm.load(recv_opnd);
- let flags_opnd = Opnd::mem(64, recv_opnd, RUBY_OFFSET_RBASIC_FLAGS);
- asm.test(flags_opnd, Opnd::Imm(RUBY_FL_EXIVAR as i64));
- asm.jnz(Target::side_exit(Counter::send_str_dup_exivar));
// Call rb_str_dup
let stack_ret = asm.stack_push(Type::CString);
- let ret_opnd = asm.ccall(rb_str_dup as *const u8, vec![recv_opnd]);
+ let ret_opnd = asm.ccall(rb_str_dup_m as *const u8, vec![recv_opnd]);
asm.mov(stack_ret, ret_opnd);
true
diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs
index d42df7b267..a1c7464805 100644
--- a/yjit/src/cruby_bindings.inc.rs
+++ b/yjit/src/cruby_bindings.inc.rs
@@ -1119,6 +1119,7 @@ extern "C" {
pub fn rb_gvar_set(arg1: ID, arg2: VALUE) -> VALUE;
pub fn rb_ensure_iv_list_size(obj: VALUE, current_len: u32, newsize: u32);
pub fn rb_vm_barrier();
+ pub fn rb_str_dup_m(str_: VALUE) -> VALUE;
pub fn rb_str_byte_substr(str_: VALUE, beg: VALUE, len: VALUE) -> VALUE;
pub fn rb_str_substr_two_fixnums(
str_: VALUE,