diff --git a/internal/range.h b/internal/range.h index 8daba0ecab..2394937bf8 100644 --- a/internal/range.h +++ b/internal/range.h @@ -24,13 +24,13 @@ RANGE_BEG(VALUE r) static inline VALUE RANGE_END(VALUE r) { - return RSTRUCT(r)->as.ary[1]; + return RSTRUCT_GET(r, 1); } static inline VALUE RANGE_EXCL(VALUE r) { - return RSTRUCT(r)->as.ary[2]; + return RSTRUCT_GET(r, 2); } VALUE diff --git a/internal/struct.h b/internal/struct.h index 5b00e52262..9b56254541 100644 --- a/internal/struct.h +++ b/internal/struct.h @@ -12,10 +12,10 @@ #include "ruby/ruby.h" /* for struct RBasic */ enum { - RSTRUCT_EMBED_LEN_MAX = RVALUE_EMBED_LEN_MAX, - RSTRUCT_EMBED_LEN_MASK = (RUBY_FL_USER2|RUBY_FL_USER1), + RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 | + RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1, RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1), - RSTRUCT_TRANSIENT_FLAG = FL_USER3, + RSTRUCT_TRANSIENT_FLAG = RUBY_FL_USER8, }; struct RStruct { @@ -25,7 +25,12 @@ struct RStruct { long len; const VALUE *ptr; } heap; - const VALUE ary[RSTRUCT_EMBED_LEN_MAX]; + /* This is a length 1 array because: + * 1. GCC has a bug that does not optimize C flexible array members + * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452) + * 2. Zero length arrays are not supported by all compilers + */ + const VALUE ary[1]; } as; }; @@ -145,7 +150,7 @@ RSTRUCT_GET(VALUE st, long k) static inline const VALUE * rb_struct_const_heap_ptr(VALUE st) { - /* TODO: check embed on debug mode */ + assert(!FL_TEST_RAW(st, RSTRUCT_EMBED_LEN_MASK)); return RSTRUCT(st)->as.heap.ptr; } diff --git a/struct.c b/struct.c index 7a8a642021..3ee4408393 100644 --- a/struct.c +++ b/struct.c @@ -835,23 +835,28 @@ rb_struct_transient_heap_evacuate(VALUE obj, int promote) static VALUE struct_alloc(VALUE klass) { - long n; - NEWOBJ_OF(st, struct RStruct, klass, T_STRUCT | (RGENGC_WB_PROTECTED_STRUCT ? FL_WB_PROTECTED : 0), sizeof(struct RStruct), 0); + long n = num_members(klass); + size_t embedded_size = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * n); + VALUE flags = T_STRUCT | (RGENGC_WB_PROTECTED_STRUCT ? FL_WB_PROTECTED : 0); - n = num_members(klass); + if (n > 0 && rb_gc_size_allocatable_p(embedded_size)) { + flags |= n << RSTRUCT_EMBED_LEN_SHIFT; + + NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0); - if (0 < n && n <= RSTRUCT_EMBED_LEN_MAX) { - RBASIC(st)->flags &= ~RSTRUCT_EMBED_LEN_MASK; - RBASIC(st)->flags |= n << RSTRUCT_EMBED_LEN_SHIFT; rb_mem_clear((VALUE *)st->as.ary, n); + + return (VALUE)st; } else { + NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0); + st->as.heap.ptr = struct_heap_alloc((VALUE)st, n); rb_mem_clear((VALUE *)st->as.heap.ptr, n); st->as.heap.len = n; - } - return (VALUE)st; + return (VALUE)st; + } } VALUE diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 49afbae4c8..265748cd6d 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -695,7 +695,7 @@ mod manual_defs { pub const VM_CALL_OPT_SEND : u32 = 1 << VM_CALL_OPT_SEND_bit; // From internal/struct.h - in anonymous enum, so we can't easily import it - pub const RSTRUCT_EMBED_LEN_MASK: usize = (RUBY_FL_USER2 | RUBY_FL_USER1) as usize; + pub const RSTRUCT_EMBED_LEN_MASK: usize = (RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3 |RUBY_FL_USER2 | RUBY_FL_USER1) as usize; // From iseq.h - via a different constant, which seems to confuse bindgen pub const ISEQ_TRANSLATED: usize = RUBY_FL_USER7 as usize;