diff options
Diffstat (limited to 'internal/imemo.h')
| -rw-r--r-- | internal/imemo.h | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/internal/imemo.h b/internal/imemo.h new file mode 100644 index 0000000000..e8a5f0fc8e --- /dev/null +++ b/internal/imemo.h @@ -0,0 +1,336 @@ +#ifndef INTERNAL_IMEMO_H /*-*-C-*-vi:se ft=c:*/ +#define INTERNAL_IMEMO_H +/** + * @author Ruby developers <ruby-core@ruby-lang.org> + * @copyright This file is a part of the programming language Ruby. + * Permission is hereby granted, to either redistribute and/or + * modify this file, provided that the conditions mentioned in the + * file COPYING are met. Consult the file for details. + * @brief IMEMO: Internal memo object. + */ +#include "ruby/internal/config.h" +#include <stddef.h> /* for size_t */ +#include "id_table.h" +#include "internal/array.h" /* for rb_ary_hidden_new_fill */ +#include "ruby/internal/stdbool.h" /* for bool */ +#include "ruby/ruby.h" /* for rb_block_call_func_t */ + +#define IMEMO_MASK 0x0f + +/* FL_USER0 to FL_USER3 is for type */ +#define IMEMO_FL_USHIFT (FL_USHIFT + 4) +#define IMEMO_FL_USER0 FL_USER4 +#define IMEMO_FL_USER1 FL_USER5 +#define IMEMO_FL_USER2 FL_USER6 +#define IMEMO_FL_USER3 FL_USER7 +#define IMEMO_FL_USER4 FL_USER8 +#define IMEMO_FL_USER5 FL_USER9 +#define IMEMO_FL_USER6 FL_USER10 + +enum imemo_type { + imemo_env = 0, + imemo_cref = 1, /*!< class reference */ + imemo_svar = 2, /*!< special variable */ + imemo_throw_data = 3, + imemo_ifunc = 4, /*!< iterator function */ + imemo_memo = 5, + imemo_ment = 6, + imemo_iseq = 7, + imemo_tmpbuf = 8, + imemo_cvar_entry = 9, + imemo_callinfo = 10, + imemo_callcache = 11, + imemo_constcache = 12, + imemo_fields = 13, + imemo_subclasses = 14, + imemo_cdhash = 15, +}; + +/* CREF (Class REFerence) is defined in method.h */ + +/*! SVAR (Special VARiable) */ +struct vm_svar { + VALUE flags; + const VALUE cref_or_me; /*!< class reference or rb_method_entry_t */ + const VALUE lastline; + const VALUE backref; + const VALUE others; +}; + +/*! THROW_DATA */ +struct vm_throw_data { + VALUE flags; + const VALUE throw_obj; + const struct rb_control_frame_struct *catch_frame; + int throw_state; +}; + +#define THROW_DATA_CONSUMED IMEMO_FL_USER0 + +/* IFUNC (Internal FUNCtion) */ + +struct vm_ifunc_argc { +#if SIZEOF_INT * 2 > SIZEOF_VALUE + signed int min: (SIZEOF_VALUE * CHAR_BIT) / 2; + signed int max: (SIZEOF_VALUE * CHAR_BIT) / 2; +#else + int min, max; +#endif +}; + +/*! IFUNC (Internal FUNCtion) + * + * Bookkeeping for converting a C function and some closed-over data into a + * block passable to methods. Like Ruby Proc, but not directly accessible at + * Ruby level since this is an imemo. See rb_block_call() and friends. + */ +struct vm_ifunc { + VALUE flags; + VALUE *svar_lep; + rb_block_call_func_t func; + const void *data; + struct vm_ifunc_argc argc; +}; +#define IFUNC_YIELD_OPTIMIZABLE IMEMO_FL_USER0 + +struct rb_imemo_tmpbuf_struct { + VALUE flags; + VALUE *ptr; /* malloc'ed buffer */ + size_t size; /* buffer size in bytes */ +}; + +struct rb_imemo_cdhash { + VALUE flags; + st_table tbl; +}; + +/* Set on imemo_memo when u3 holds a VALUE that GC must mark. + * When unset, u3 is a non-VALUE (cnt/state). */ +#define MEMO_U3_IS_VALUE IMEMO_FL_USER0 + +/*! MEMO + * + * @see imemo_type + * */ +struct MEMO { + VALUE flags; + const VALUE v1; + const VALUE v2; + union { + long cnt; + long state; + const VALUE value; + } u3; +}; + +#define IMEMO_NEW(T, type, v0) ((T *)rb_imemo_new((type), (v0), sizeof(T), false)) +#define SHAREABLE_IMEMO_NEW(T, type, v0) ((T *)rb_imemo_new((type), (v0), sizeof(T), true)) + +/* ment is in method.h */ + +#define THROW_DATA_P(err) imemo_throw_data_p((VALUE)err) +#define MEMO_CAST(m) ((struct MEMO *)(m)) +#define MEMO_FOR(type, value) ((type *)RARRAY_PTR(value)) +#define NEW_MEMO_FOR(type, value) \ + ((value) = rb_ary_hidden_new_fill(type_roomof(type, VALUE)), MEMO_FOR(type, value)) +#define NEW_PARTIAL_MEMO_FOR(type, value, member) \ + ((value) = rb_ary_hidden_new_fill(type_roomof(type, VALUE)), \ + rb_ary_set_len((value), offsetof(type, member) / sizeof(VALUE)), \ + MEMO_FOR(type, value)) + +#ifndef RUBY_RUBYPARSER_H +typedef struct rb_imemo_tmpbuf_struct rb_imemo_tmpbuf_t; +#endif +VALUE rb_imemo_new(enum imemo_type type, VALUE v0, size_t size, bool is_shareable); +VALUE rb_imemo_tmpbuf_new(void); +struct MEMO *rb_imemo_memo_new(VALUE a, VALUE b, long c); +struct MEMO *rb_imemo_memo_new_value(VALUE a, VALUE b, VALUE c); +struct vm_ifunc *rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int max_argc); +static inline enum imemo_type imemo_type(VALUE imemo); +static inline int imemo_type_p(VALUE imemo, enum imemo_type imemo_type); +static inline bool imemo_throw_data_p(VALUE imemo); +static inline struct vm_ifunc *rb_vm_ifunc_proc_new(rb_block_call_func_t func, const void *data); +static inline void *RB_IMEMO_TMPBUF_PTR(VALUE v); +static inline void MEMO_V1_SET(struct MEMO *m, VALUE v); +static inline void MEMO_V2_SET(struct MEMO *m, VALUE v); + +size_t rb_imemo_memsize(VALUE obj); +void rb_imemo_mark_and_move(VALUE obj, bool reference_updating); +void rb_imemo_free(VALUE obj); + +RUBY_SYMBOL_EXPORT_BEGIN +const char *rb_imemo_name(enum imemo_type type); +RUBY_SYMBOL_EXPORT_END + +static inline enum imemo_type +imemo_type(VALUE imemo) +{ + return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK; +} + +static inline int +imemo_type_p(VALUE imemo, enum imemo_type imemo_type) +{ + if (LIKELY(!RB_SPECIAL_CONST_P(imemo))) { + /* fixed at compile time if imemo_type is given. */ + const VALUE mask = (IMEMO_MASK << FL_USHIFT) | RUBY_T_MASK; + const VALUE expected_type = (imemo_type << FL_USHIFT) | T_IMEMO; + /* fixed at runtime. */ + return expected_type == (RBASIC(imemo)->flags & mask); + } + else { + return 0; + } +} + +#define IMEMO_TYPE_P(v, t) imemo_type_p((VALUE)(v), t) + +static inline bool +imemo_throw_data_p(VALUE imemo) +{ + return RB_TYPE_P(imemo, T_IMEMO); +} + +static inline struct vm_ifunc * +rb_vm_ifunc_proc_new(rb_block_call_func_t func, const void *data) +{ + return rb_vm_ifunc_new(func, data, 0, UNLIMITED_ARGUMENTS); +} + +static inline void * +RB_IMEMO_TMPBUF_PTR(VALUE v) +{ + const struct rb_imemo_tmpbuf_struct *p = (const void *)v; + return p->ptr; +} + +static inline VALUE +rb_imemo_tmpbuf_new_from_an_RString(VALUE str) +{ + VALUE imemo; + size_t len; + + StringValue(str); + len = RSTRING_LEN(str); + rb_alloc_tmp_buffer(&imemo, len); + memcpy(RB_IMEMO_TMPBUF_PTR(imemo), RSTRING_PTR(str), len); + return imemo; +} + +static inline void +MEMO_V1_SET(struct MEMO *m, VALUE v) +{ + RB_OBJ_WRITE(m, &m->v1, v); +} + +static inline void +MEMO_V2_SET(struct MEMO *m, VALUE v) +{ + RB_OBJ_WRITE(m, &m->v2, v); +} + +VALUE rb_imemo_cdhash_new(size_t size, const struct st_hash_type *type); + +static inline st_table * +rb_imemo_cdhash_tbl(VALUE obj) +{ + RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_cdhash)); + return &((struct rb_imemo_cdhash *)obj)->tbl; +} + +struct rb_fields { + struct RBasic basic; + union { + struct { + VALUE fields[1]; + } embed; + struct { + VALUE *ptr; + } external; + struct { + // Note: the st_table could be embedded, but complex T_CLASS should be rare to + // non-existent, so not really worth the trouble. + st_table *table; + } complex; + } as; +}; + +// IMEMO/fields and T_OBJECT have exactly the same layout. +// This is useful for JIT and common codepaths. +#define OBJ_FIELD_HEAP ROBJECT_HEAP +STATIC_ASSERT(imemo_fields_flags, OBJ_FIELD_HEAP == IMEMO_FL_USER0); +STATIC_ASSERT(imemo_fields_embed_offset, offsetof(struct RObject, as.ary) == offsetof(struct rb_fields, as.embed.fields)); +STATIC_ASSERT(imemo_fields_external_offset, offsetof(struct RObject, as.heap.fields) == offsetof(struct rb_fields, as.external.ptr)); +STATIC_ASSERT(imemo_fields_complex_offset, offsetof(struct RObject, as.heap.fields) == offsetof(struct rb_fields, as.complex.table)); + +#define IMEMO_OBJ_FIELDS(fields) ((struct rb_fields *)fields) + +#define IMEMO_SUBCLASSES_HEAP IMEMO_FL_USER0 + +struct rb_subclasses { + VALUE flags; + uint32_t count; + uint32_t capacity; + union { + VALUE *external; + VALUE embed[1]; + } as; +}; + +static inline VALUE * +rb_imemo_subclasses_entries(VALUE v) +{ + struct rb_subclasses *s = (struct rb_subclasses *)v; + return FL_TEST_RAW(v, IMEMO_SUBCLASSES_HEAP) ? s->as.external : s->as.embed; +} + +VALUE rb_imemo_fields_new(VALUE owner, /* shape_id_t */ uint32_t shape_id, bool shareable); +VALUE rb_imemo_subclasses_new(uint32_t capacity); +VALUE rb_imemo_fields_new_complex(VALUE owner, /* shape_id_t */ uint32_t shape_id, size_t capa, bool shareable); +VALUE rb_imemo_fields_new_complex_tbl(VALUE owner, /* shape_id_t */ uint32_t shape_id, st_table *tbl, bool shareable); +VALUE rb_imemo_fields_clone(VALUE fields_obj); +void rb_imemo_fields_clear(VALUE fields_obj); + +static inline VALUE +rb_imemo_fields_owner(VALUE fields_obj) +{ + RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields)); + + return CLASS_OF(fields_obj); +} + +static inline VALUE * +rb_imemo_fields_ptr(VALUE fields_obj) +{ + if (!fields_obj) { + return NULL; + } + + RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields) || RB_TYPE_P(fields_obj, T_OBJECT)); + + if (UNLIKELY(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP))) { + return IMEMO_OBJ_FIELDS(fields_obj)->as.external.ptr; + } + else { + return IMEMO_OBJ_FIELDS(fields_obj)->as.embed.fields; + } +} + +static inline st_table * +rb_imemo_fields_complex_tbl(VALUE fields_obj) +{ + if (!fields_obj) { + return NULL; + } + + RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields) || RB_TYPE_P(fields_obj, T_OBJECT)); + RUBY_ASSERT(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)); + + // Some codepaths unconditionally access the fields_ptr, and assume it can be used as st_table if the + // shape is complex. + RUBY_ASSERT((st_table *)rb_imemo_fields_ptr(fields_obj) == IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table); + + return IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table; +} + +#endif /* INTERNAL_IMEMO_H */ |
