summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/vm_ivar_get.yml67
-rw-r--r--internal/variable.h1
-rw-r--r--shape.h1
-rw-r--r--variable.c30
-rw-r--r--yjit/bindgen/src/main.rs1
-rw-r--r--yjit/src/codegen.rs48
-rw-r--r--yjit/src/cruby_bindings.inc.rs1
7 files changed, 124 insertions, 25 deletions
diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml
index 9174af6965..1e0dad665f 100644
--- a/benchmark/vm_ivar_get.yml
+++ b/benchmark/vm_ivar_get.yml
@@ -1,17 +1,75 @@
prelude: |
class Example
def initialize
+ @levar = 1
@v0 = 1
@v1 = 2
@v3 = 3
+ end
+
+ def get_value_loop
+ sum = 0
+
+ i = 0
+ while i < 100_000
+ # 10 times to de-emphasize loop overhead
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ i += 1
+ end
+
+ return sum
+ end
+
+ @levar = 1
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
+
+ def self.get_value_loop
+ sum = 0
+
+ i = 0
+ while i < 100_000
+ # 10 times to de-emphasize loop overhead
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ i += 1
+ end
+
+ return sum
+ end
+ end
+
+ class GenExample < Time
+ def initialize
@levar = 1
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
end
def get_value_loop
sum = 0
i = 0
- while i < 1000000
+ while i < 100_000
# 10 times to de-emphasize loop overhead
sum += @levar
sum += @levar
@@ -31,7 +89,12 @@ prelude: |
end
obj = Example.new
+ gen = GenExample.new
benchmark:
- vm_ivar_get: |
+ vm_ivar_get_on_obj: |
obj.get_value_loop
+ vm_ivar_get_on_class: |
+ Example.get_value_loop
+ vm_ivar_get_on_generic: |
+ gen.get_value_loop
loop_count: 100
diff --git a/internal/variable.h b/internal/variable.h
index 2cb50f66ae..d256330168 100644
--- a/internal/variable.h
+++ b/internal/variable.h
@@ -53,6 +53,7 @@ VALUE rb_obj_field_get(VALUE obj, shape_id_t target_shape_id);
void rb_ivar_set_internal(VALUE obj, ID id, VALUE val);
attr_index_t rb_ivar_set_index(VALUE obj, ID id, VALUE val);
attr_index_t rb_obj_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE val);
+VALUE rb_ivar_get_at(VALUE obj, attr_index_t index, ID id);
RUBY_SYMBOL_EXPORT_BEGIN
/* variable.c (export) */
diff --git a/shape.h b/shape.h
index 1f444c545f..fdc2b3ddd6 100644
--- a/shape.h
+++ b/shape.h
@@ -24,6 +24,7 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_
// index in rb_shape_tree.shape_list. Allow to access `rb_shape_t *`.
// 19-21 SHAPE_ID_HEAP_INDEX_MASK
// index in rb_shape_tree.capacities. Allow to access slot size.
+// Always 0 except for T_OBJECT.
// 22 SHAPE_ID_FL_FROZEN
// Whether the object is frozen or not.
// 23 SHAPE_ID_FL_HAS_OBJECT_ID
diff --git a/variable.c b/variable.c
index f80f1a56a6..0a5ec60c25 100644
--- a/variable.c
+++ b/variable.c
@@ -1464,6 +1464,36 @@ rb_ivar_get(VALUE obj, ID id)
}
VALUE
+rb_ivar_get_at(VALUE obj, attr_index_t index, ID id)
+{
+ RUBY_ASSERT(rb_is_instance_id(id));
+ // Used by JITs, but never for T_OBJECT.
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ UNREACHABLE_RETURN(Qundef);
+ case T_CLASS:
+ case T_MODULE:
+ {
+ VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
+ VALUE val = rb_imemo_fields_ptr(fields_obj)[index];
+
+ if (UNLIKELY(!rb_ractor_main_p()) && !rb_ractor_shareable_p(val)) {
+ rb_raise(rb_eRactorIsolationError,
+ "can not get unshareable values from instance variables of classes/modules from non-main Ractors");
+ }
+
+ return val;
+ }
+ default:
+ {
+ VALUE fields_obj = rb_obj_fields(obj, id);
+ return rb_imemo_fields_ptr(fields_obj)[index];
+ }
+ }
+}
+
+VALUE
rb_attr_get(VALUE obj, ID id)
{
return rb_ivar_lookup(obj, id, Qnil);
diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs
index dd0cb6dbf5..c1114e8089 100644
--- a/yjit/bindgen/src/main.rs
+++ b/yjit/bindgen/src/main.rs
@@ -313,6 +313,7 @@ fn main() {
// From yjit.c
.allowlist_function("rb_object_shape_count")
+ .allowlist_function("rb_ivar_get_at")
.allowlist_function("rb_iseq_(get|set)_yjit_payload")
.allowlist_function("rb_iseq_pc_at_idx")
.allowlist_function("rb_iseq_opcode_at_pc")
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 10aeac2d85..f27d09d7d4 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -2864,7 +2864,7 @@ fn gen_get_ivar(
// NOTE: This assumes T_OBJECT can't ever have the same shape_id as any other type.
// too-complex shapes can't use index access, so we use rb_ivar_get for them too.
- if !receiver_t_object || comptime_receiver.shape_too_complex() || megamorphic {
+ if !comptime_receiver.heap_object_p() || comptime_receiver.shape_too_complex() || megamorphic {
// General case. Call rb_ivar_get().
// VALUE rb_ivar_get(VALUE obj, ID id)
asm_comment!(asm, "call rb_ivar_get()");
@@ -2900,9 +2900,6 @@ fn gen_get_ivar(
// Guard heap object (recv_opnd must be used before stack_pop)
guard_object_is_heap(asm, recv, recv_opnd, Counter::getivar_not_heap);
- // Compile time self is embedded and the ivar index lands within the object
- let embed_test_result = comptime_receiver.embedded_p();
-
let expected_shape = unsafe { rb_obj_shape_id(comptime_receiver) };
let shape_id_offset = unsafe { rb_shape_id_offset() };
let shape_opnd = Opnd::mem(SHAPE_ID_NUM_BITS as u8, recv, shape_id_offset);
@@ -2931,28 +2928,33 @@ fn gen_get_ivar(
asm.mov(out_opnd, Qnil.into());
}
Some(ivar_index) => {
- if embed_test_result {
- // See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h
-
- // Load the variable
- let offs = ROBJECT_OFFSET_AS_ARY as i32 + (ivar_index * SIZEOF_VALUE) as i32;
- let ivar_opnd = Opnd::mem(64, recv, offs);
-
- // Push the ivar on the stack
- let out_opnd = asm.stack_push(Type::Unknown);
- asm.mov(out_opnd, ivar_opnd);
+ let ivar_opnd = if receiver_t_object {
+ if comptime_receiver.embedded_p() {
+ // See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h
+
+ // Load the variable
+ let offs = ROBJECT_OFFSET_AS_ARY as i32 + (ivar_index * SIZEOF_VALUE) as i32;
+ Opnd::mem(64, recv, offs)
+ } else {
+ // Compile time value is *not* embedded.
+
+ // Get a pointer to the extended table
+ let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_FIELDS as i32));
+
+ // Read the ivar from the extended table
+ Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32)
+ }
} else {
- // Compile time value is *not* embedded.
+ asm_comment!(asm, "call rb_ivar_get_at()");
- // Get a pointer to the extended table
- let tbl_opnd = asm.load(Opnd::mem(64, recv, ROBJECT_OFFSET_AS_HEAP_FIELDS as i32));
-
- // Read the ivar from the extended table
- let ivar_opnd = Opnd::mem(64, tbl_opnd, (SIZEOF_VALUE * ivar_index) as i32);
+ // The function could raise RactorIsolationError.
+ jit_prepare_non_leaf_call(jit, asm);
+ asm.ccall(rb_ivar_get_at as *const u8, vec![recv, Opnd::UImm((ivar_index as u32).into()), Opnd::UImm(ivar_name)])
+ };
- let out_opnd = asm.stack_push(Type::Unknown);
- asm.mov(out_opnd, ivar_opnd);
- }
+ // Push the ivar on the stack
+ let out_opnd = asm.stack_push(Type::Unknown);
+ asm.mov(out_opnd, ivar_opnd);
}
}
diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs
index 4cae138c95..f78354a611 100644
--- a/yjit/src/cruby_bindings.inc.rs
+++ b/yjit/src/cruby_bindings.inc.rs
@@ -1115,6 +1115,7 @@ extern "C" {
pub fn rb_obj_shape_id(obj: VALUE) -> shape_id_t;
pub fn rb_shape_get_iv_index(shape_id: shape_id_t, id: ID, value: *mut attr_index_t) -> bool;
pub fn rb_shape_transition_add_ivar_no_warnings(obj: VALUE, id: ID) -> shape_id_t;
+ pub fn rb_ivar_get_at(obj: VALUE, index: attr_index_t, id: ID) -> VALUE;
pub fn rb_gvar_get(arg1: ID) -> VALUE;
pub fn rb_gvar_set(arg1: ID, arg2: VALUE) -> VALUE;
pub fn rb_ensure_iv_list_size(obj: VALUE, current_len: u32, newsize: u32);