summaryrefslogtreecommitdiff
path: root/internal/imemo.h
diff options
context:
space:
mode:
Diffstat (limited to 'internal/imemo.h')
-rw-r--r--internal/imemo.h336
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 */