mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Extract yjit_force_iv_index and make it work when object is frozen
In an effort to simplify the logic YJIT generates for accessing instance variable, YJIT ensures that a given name-to-index mapping exists at compile time. In the case that the mapping doesn't exist, it was created by using rb_ivar_set() with Qundef on the sample object we see at compile time. This hack isn't fine if the sample object happens to be frozen, in which case YJIT would raise a FrozenError unexpectedly. To deal with this, make a new function that only reserves the mapping but doesn't touch the object. This is rb_obj_ensure_iv_index_mapping(). This new function superceeds the functionality of rb_iv_index_tbl_lookup() so it was removed. Reported by and includes a test case from John Hawthorn <john@hawthorn.email> Fixes: GH-282
This commit is contained in:
parent
2e2430564a
commit
b74d6563a6
5 changed files with 51 additions and 32 deletions
|
@ -2160,3 +2160,18 @@ assert_equal '[2]', %q{
|
||||||
|
|
||||||
5.times.map { default_expression(value: 2) }.uniq
|
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?
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ void rb_gvar_ractor_local(const char *name);
|
||||||
static inline bool ROBJ_TRANSIENT_P(VALUE obj);
|
static inline bool ROBJ_TRANSIENT_P(VALUE obj);
|
||||||
static inline void ROBJ_TRANSIENT_SET(VALUE obj);
|
static inline void ROBJ_TRANSIENT_SET(VALUE obj);
|
||||||
static inline void ROBJ_TRANSIENT_UNSET(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
|
RUBY_SYMBOL_EXPORT_BEGIN
|
||||||
/* variable.c (export) */
|
/* variable.c (export) */
|
||||||
|
|
33
variable.c
33
variable.c
|
@ -1446,12 +1446,13 @@ rb_init_iv_list(VALUE obj)
|
||||||
init_iv_list(obj, len, newsize, index_tbl);
|
init_iv_list(obj, len, newsize, index_tbl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
// Retreive or create the id-to-index mapping for a given object and an
|
||||||
obj_ivar_set(VALUE obj, ID id, VALUE val)
|
// instance variable name.
|
||||||
|
static struct ivar_update
|
||||||
|
obj_ensure_iv_index_mapping(VALUE obj, ID id)
|
||||||
{
|
{
|
||||||
VALUE klass = rb_obj_class(obj);
|
VALUE klass = rb_obj_class(obj);
|
||||||
struct ivar_update ivup;
|
struct ivar_update ivup;
|
||||||
uint32_t len;
|
|
||||||
ivup.iv_extended = 0;
|
ivup.iv_extended = 0;
|
||||||
ivup.u.iv_index_tbl = iv_index_tbl_make(obj, klass);
|
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();
|
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);
|
len = ROBJECT_NUMIV(obj);
|
||||||
if (len <= ivup.index) {
|
if (len <= ivup.index) {
|
||||||
uint32_t newsize = iv_index_tbl_newsize(&ivup);
|
uint32_t newsize = iv_index_tbl_newsize(&ivup);
|
||||||
|
|
|
@ -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;
|
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));
|
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
|
static inline void
|
||||||
|
|
|
@ -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 {
|
enum {
|
||||||
GETIVAR_MAX_DEPTH = 10, // up to 5 different classes, and embedded or not for each
|
GETIVAR_MAX_DEPTH = 10, // up to 5 different classes, and embedded or not for each
|
||||||
OPT_AREF_MAX_CHAIN_DEPTH = 2, // hashes and arrays
|
OPT_AREF_MAX_CHAIN_DEPTH = 2, // hashes and arrays
|
||||||
SEND_MAX_DEPTH = 5, // up to 5 different classes
|
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);
|
VALUE rb_vm_set_ivar_idx(VALUE obj, uint32_t idx, VALUE val);
|
||||||
|
|
||||||
// Codegen for setting an instance variable.
|
// 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 val_opnd = ctx_stack_pop(ctx, 1);
|
||||||
x86opnd_t recv_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
|
// 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);
|
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);
|
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
|
// Pop receiver if it's on the temp stack
|
||||||
if (!reg0_opnd.is_self) {
|
if (!reg0_opnd.is_self) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue