From 4992d2c2980d150cdbfc9750f124c3a9ccc71945 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 28 Aug 2025 23:23:38 +0200 Subject: [PATCH] YJIT: rb_ivar_get_at skip ractor checks Using `assume_single_ractor_mode` we can skip all ractor safety checks if we're in single ractor mode. ``` compare-ruby: ruby 3.5.0dev (2025-08-27T14:58:58Z merge-vm-setivar-d.. 5b749d8e53) +YJIT +PRISM [arm64-darwin24] built-ruby: ruby 3.5.0dev (2025-08-28T21:23:38Z yjit-get-exivar 3cc21b76d4) +YJIT +PRISM [arm64-darwin24] | |compare-ruby|built-ruby| |:--------------------------|-----------:|---------:| |vm_ivar_get_on_obj | 975.981| 975.772| | | 1.00x| -| |vm_ivar_get_on_class | 136.214| 470.912| | | -| 3.46x| |vm_ivar_get_on_generic | 148.315| 299.122| | | -| 2.02x| ``` --- internal/variable.h | 1 + variable.c | 20 ++++++++++++++++++++ yjit/bindgen/src/main.rs | 1 + yjit/src/codegen.rs | 6 ++++-- yjit/src/cruby_bindings.inc.rs | 1 + 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/internal/variable.h b/internal/variable.h index d256330168..5e2bcceb61 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -54,6 +54,7 @@ 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); +VALUE rb_ivar_get_at_no_ractor_check(VALUE obj, attr_index_t index); RUBY_SYMBOL_EXPORT_BEGIN /* variable.c (export) */ diff --git a/variable.c b/variable.c index 0a5ec60c25..cccb09e329 100644 --- a/variable.c +++ b/variable.c @@ -1493,6 +1493,26 @@ rb_ivar_get_at(VALUE obj, attr_index_t index, ID id) } } +VALUE +rb_ivar_get_at_no_ractor_check(VALUE obj, attr_index_t index) +{ + // Used by JITs, but never for T_OBJECT. + + VALUE fields_obj; + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + UNREACHABLE_RETURN(Qundef); + case T_CLASS: + case T_MODULE: + fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj); + break; + default: + fields_obj = rb_obj_fields_no_ractor_check(obj); + break; + } + return rb_imemo_fields_ptr(fields_obj)[index]; +} + VALUE rb_attr_get(VALUE obj, ID id) { diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index c1114e8089..2993e05e2f 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -314,6 +314,7 @@ fn main() { // From yjit.c .allowlist_function("rb_object_shape_count") .allowlist_function("rb_ivar_get_at") + .allowlist_function("rb_ivar_get_at_no_ractor_check") .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 ff001b6476..2dae72280f 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -2947,11 +2947,13 @@ fn gen_get_ivar( } else { asm_comment!(asm, "call rb_ivar_get_at()"); - if !assume_single_ractor_mode(jit, asm) { + if assume_single_ractor_mode(jit, asm) { + asm.ccall(rb_ivar_get_at_no_ractor_check as *const u8, vec![recv, Opnd::UImm((ivar_index as u32).into())]) + } else { // 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)]) } - asm.ccall(rb_ivar_get_at as *const u8, vec![recv, Opnd::UImm((ivar_index as u32).into()), Opnd::UImm(ivar_name)]) }; // Push the ivar on the stack diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index f78354a611..ca459ceafb 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1116,6 +1116,7 @@ extern "C" { 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_ivar_get_at_no_ractor_check(obj: VALUE, index: attr_index_t) -> 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);