summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb15
-rw-r--r--internal/variable.h1
-rw-r--r--variable.c33
-rw-r--r--vm_insnhelper.c6
-rw-r--r--yjit_codegen.c28
5 files changed, 51 insertions, 32 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index a190d8ffb1..765580a030 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -2160,3 +2160,18 @@ assert_equal '[2]', %q{
5.times.map { default_expression(value: 2) }.uniq
}
+
+# attr_reader on frozen object
+assert_equal 'false', %q{
+ class Foo
+ attr_reader :exception
+
+ def failed?
+ !exception.nil?
+ end
+ end
+
+ foo = Foo.new.freeze
+ foo.failed?
+ foo.failed?
+}
diff --git a/internal/variable.h b/internal/variable.h
index c527bdbd1f..4b67bef907 100644
--- a/internal/variable.h
+++ b/internal/variable.h
@@ -35,6 +35,7 @@ void rb_gvar_ractor_local(const char *name);
static inline bool ROBJ_TRANSIENT_P(VALUE obj);
static inline void ROBJ_TRANSIENT_SET(VALUE obj);
static inline void ROBJ_TRANSIENT_UNSET(VALUE obj);
+uint32_t rb_obj_ensure_iv_index_mapping(VALUE obj, ID id);
RUBY_SYMBOL_EXPORT_BEGIN
/* variable.c (export) */
diff --git a/variable.c b/variable.c
index 5712ccf0ba..6cfc3f68f2 100644
--- a/variable.c
+++ b/variable.c
@@ -1446,12 +1446,13 @@ rb_init_iv_list(VALUE obj)
init_iv_list(obj, len, newsize, index_tbl);
}
-static VALUE
-obj_ivar_set(VALUE obj, ID id, VALUE val)
+// Retreive or create the id-to-index mapping for a given object and an
+// instance variable name.
+static struct ivar_update
+obj_ensure_iv_index_mapping(VALUE obj, ID id)
{
VALUE klass = rb_obj_class(obj);
struct ivar_update ivup;
- uint32_t len;
ivup.iv_extended = 0;
ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass);
@@ -1461,6 +1462,32 @@ obj_ivar_set(VALUE obj, ID id, VALUE val)
}
RB_VM_LOCK_LEAVE();
+ return ivup;
+}
+
+// Return the instance variable index for a given name and T_OBJECT object. The
+// mapping between name and index lives on `rb_obj_class(obj)` and is created
+// if not already present.
+//
+// @note May raise when there are too many instance variables.
+// @note YJIT uses this function at compile time to simplify the work needed to
+// access the variable at runtime.
+uint32_t
+rb_obj_ensure_iv_index_mapping(VALUE obj, ID id)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT));
+ // This uint32_t cast shouldn't lose information as it's checked in
+ // iv_index_tbl_extend(). The index is stored as an uint32_t in
+ // struct rb_iv_index_tbl_entry.
+ return (uint32_t)obj_ensure_iv_index_mapping(obj, id).index;
+}
+
+static VALUE
+obj_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ uint32_t len;
+ struct ivar_update ivup = obj_ensure_iv_index_mapping(obj, id);
+
len = ROBJECT_NUMIV(obj);
if (len <= ivup.index) {
uint32_t newsize = iv_index_tbl_newsize(&ivup);
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 3b99f7c350..5d83d3dfd5 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1105,12 +1105,6 @@ iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_index_tbl
return found ? true : false;
}
-bool
-rb_iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_index_tbl_entry **ent)
-{
- return iv_index_tbl_lookup(iv_index_tbl, id, ent);
-}
-
ALWAYS_INLINE(static void fill_ivar_cache(const rb_iseq_t *iseq, IVC ic, const struct rb_callcache *cc, int is_attr, struct rb_iv_index_tbl_entry *ent));
static inline void
diff --git a/yjit_codegen.c b/yjit_codegen.c
index bca5f203ec..136d7d4dea 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -1478,33 +1478,12 @@ jit_chain_guard(enum jcc_kinds jcc, jitstate_t *jit, const ctx_t *ctx, uint8_t d
}
}
-bool rb_iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_index_tbl_entry **ent); // vm_insnhelper.c
-
enum {
GETIVAR_MAX_DEPTH = 10, // up to 5 different classes, and embedded or not for each
OPT_AREF_MAX_CHAIN_DEPTH = 2, // hashes and arrays
SEND_MAX_DEPTH = 5, // up to 5 different classes
};
-static uint32_t
-yjit_force_iv_index(VALUE comptime_receiver, VALUE klass, ID name)
-{
- ID id = name;
- struct rb_iv_index_tbl_entry *ent;
- struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(comptime_receiver);
-
- // Make sure there is a mapping for this ivar in the index table
- if (!iv_index_tbl || !rb_iv_index_tbl_lookup(iv_index_tbl, id, &ent)) {
- rb_ivar_set(comptime_receiver, id, Qundef);
- iv_index_tbl = ROBJECT_IV_INDEX_TBL(comptime_receiver);
- RUBY_ASSERT(iv_index_tbl);
- // Redo the lookup
- RUBY_ASSERT_ALWAYS(rb_iv_index_tbl_lookup(iv_index_tbl, id, &ent));
- }
-
- return ent->index;
-}
-
VALUE rb_vm_set_ivar_idx(VALUE obj, uint32_t idx, VALUE val);
// Codegen for setting an instance variable.
@@ -1523,7 +1502,7 @@ gen_set_ivar(jitstate_t *jit, ctx_t *ctx, VALUE recv, VALUE klass, ID ivar_name)
x86opnd_t val_opnd = ctx_stack_pop(ctx, 1);
x86opnd_t recv_opnd = ctx_stack_pop(ctx, 1);
- uint32_t ivar_index = yjit_force_iv_index(recv, klass, ivar_name);
+ uint32_t ivar_index = rb_obj_ensure_iv_index_mapping(recv, ivar_name);
// Call rb_vm_set_ivar_idx with the receiver, the index of the ivar, and the value
mov(cb, C_ARG_REGS[0], recv_opnd);
@@ -1596,7 +1575,10 @@ gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE compt
jit_chain_guard(JCC_JNE, jit, &starting_context, max_chain_depth, side_exit);
*/
- uint32_t ivar_index = yjit_force_iv_index(comptime_receiver, CLASS_OF(comptime_receiver), ivar_name);
+ // FIXME: Mapping the index could fail when there is too many ivar names. If we're
+ // compiling for a branch stub that can cause the exception to be thrown from the
+ // wrong PC.
+ uint32_t ivar_index = rb_obj_ensure_iv_index_mapping(comptime_receiver, ivar_name);
// Pop receiver if it's on the temp stack
if (!reg0_opnd.is_self) {