From aae2e0d45617b8dad66a7b3f49fdd01272caf1bc Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 9 Oct 2025 21:56:47 +0900 Subject: [PATCH 01/18] [DOC] Update bundled gems list at faf86fa14bd350c77717f7d0fc64dc --- NEWS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index f41809097d5b4f..6a4bf2831564a3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -161,7 +161,7 @@ The following bundled gems are promoted from default gems. * pstore 0.2.0 * benchmark 0.4.1 * logger 1.7.0 -* rdoc 6.14.2 +* rdoc 6.15.0 * win32ole 1.9.2 * irb 1.15.2 * reline 0.6.2 @@ -205,15 +205,15 @@ The following bundled gems are added. The following bundled gems are updated. -* minitest 5.25.5 +* minitest 5.26.0 * rake 13.3.0 * test-unit 3.7.0 -* rexml 3.4.2 -* net-imap 0.5.10 +* rexml 3.4.4 +* net-imap 0.5.12 * net-smtp 0.5.1 * matrix 0.4.3 * prime 0.1.4 -* rbs 3.9.4 +* rbs 3.9.5 * debug 1.11.0 * base64 0.3.0 * bigdecimal 3.2.2 From 0b0947f84bf9f9a5e80be6b5189e3bd649f90dc7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 9 Oct 2025 18:01:42 +0900 Subject: [PATCH 02/18] missing-baseruby.bat: Accept CRuby only `RubyVM::InstructionSequence` is necessary to generate rbinc files. --- tool/missing-baseruby.bat | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tool/missing-baseruby.bat b/tool/missing-baseruby.bat index 9274482a4c9ca6..45cee8c8788ed0 100755 --- a/tool/missing-baseruby.bat +++ b/tool/missing-baseruby.bat @@ -20,4 +20,6 @@ call :warn "executable host ruby is required. use --with-baseruby option." call :warn "Note that BASERUBY must be Ruby 3.1.0 or later." call :abort -: || (:^; abort if RUBY_VERSION < s[%r"warn .*Ruby ([\d.]+)(?:\.0)?",1]) +(goto :eof ^;) +abort unless defined?(RubyVM::InstructionSequence) +abort if RUBY_VERSION < s[%r"warn .*Ruby ([\d.]+)(?:\.0)?",1] From abad1f423ba4ee2f8b5a8fc50b8d803087a0e172 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 9 Oct 2025 22:20:16 +0900 Subject: [PATCH 03/18] [DOC] Update required baseruby version --- doc/contributing/building_ruby.md | 2 +- doc/windows.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md index eac83fc00ef978..4c69515684afbc 100644 --- a/doc/contributing/building_ruby.md +++ b/doc/contributing/building_ruby.md @@ -17,7 +17,7 @@ * [autoconf] - 2.67 or later * [gperf] - 3.1 or later * Usually unneeded; only if you edit some source files using gperf - * ruby - 3.0 or later + * ruby - 3.1 or later * We can upgrade this version to system ruby version of the latest Ubuntu LTS. * git - 2.32 or later diff --git a/doc/windows.md b/doc/windows.md index dd2ed273ae1f30..26a727d7adb26a 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -122,7 +122,7 @@ sh ../../ruby/configure -C --disable-install-doc --with-opt-dir=C:\Users\usernam 4. If you want to build from GIT source, following commands are required. * `git` - * `ruby` 3.0 or later + * `ruby` 3.1 or later You can use [scoop](https://scoop.sh/) to install them like: From f486b3905f27aefa6063b8f9da0464b08d354c79 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 9 Oct 2025 09:18:12 -0400 Subject: [PATCH 04/18] [ruby/prism] Bump to v https://github.com/ruby/prism/commit/7574837b7b --- lib/prism/prism.gemspec | 2 +- prism/extension.h | 2 +- prism/templates/lib/prism/serialize.rb.erb | 2 +- prism/version.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec index 65305d0ec1a244..8f6075ad9643d4 100644 --- a/lib/prism/prism.gemspec +++ b/lib/prism/prism.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "prism" - spec.version = "1.5.1" + spec.version = "1.5.2" spec.authors = ["Shopify"] spec.email = ["ruby@shopify.com"] diff --git a/prism/extension.h b/prism/extension.h index b18e770d9213f7..1f15b775ff0266 100644 --- a/prism/extension.h +++ b/prism/extension.h @@ -1,7 +1,7 @@ #ifndef PRISM_EXT_NODE_H #define PRISM_EXT_NODE_H -#define EXPECTED_PRISM_VERSION "1.5.1" +#define EXPECTED_PRISM_VERSION "1.5.2" #include #include diff --git a/prism/templates/lib/prism/serialize.rb.erb b/prism/templates/lib/prism/serialize.rb.erb index 366878f709250d..271631b5ace817 100644 --- a/prism/templates/lib/prism/serialize.rb.erb +++ b/prism/templates/lib/prism/serialize.rb.erb @@ -14,7 +14,7 @@ module Prism # The patch version of prism that we are expecting to find in the serialized # strings. - PATCH_VERSION = 1 + PATCH_VERSION = 2 # Deserialize the dumped output from a request to parse or parse_file. # diff --git a/prism/version.h b/prism/version.h index 697ba0647df1ec..c0d82dc8e2b2f4 100644 --- a/prism/version.h +++ b/prism/version.h @@ -19,11 +19,11 @@ /** * The patch version of the Prism library as an int. */ -#define PRISM_VERSION_PATCH 1 +#define PRISM_VERSION_PATCH 2 /** * The version of the Prism library as a constant string. */ -#define PRISM_VERSION "1.5.1" +#define PRISM_VERSION "1.5.2" #endif From fa409d5f3af507a1e4f31642924dd694f37b1c33 Mon Sep 17 00:00:00 2001 From: git Date: Thu, 9 Oct 2025 13:33:33 +0000 Subject: [PATCH 05/18] Update default gems list at f486b3905f27aefa6063b8f9da0464 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6a4bf2831564a3..117aae5736bcb0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -192,7 +192,7 @@ The following default gems are updated. * openssl 4.0.0.pre * optparse 0.7.0.dev.2 * pp 0.6.3 -* prism 1.5.1 +* prism 1.5.2 * psych 5.2.6 * resolv 0.6.2 * stringio 3.1.8.dev From d7f2a1ec9a1ae569bf79f06ad44e0f76292c9b06 Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Thu, 9 Oct 2025 12:42:09 -0400 Subject: [PATCH 06/18] ZJIT: Profile opt_aref (#14778) * ZJIT: Profile opt_aref * ZJIT: Add test for opt_aref * ZJIT: Move test and add hash opt test * ZJIT: Update zjit bindgen * ZJIT: Add inspect calls to opt_aref tests --- insns.def | 1 + zjit/src/cruby_bindings.inc.rs | 7 ++-- zjit/src/hir.rs | 58 ++++++++++++++++++++++++++++++++++ zjit/src/profile.rs | 1 + 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/insns.def b/insns.def index 239fe85aa51439..eef0d3f5dc1124 100644 --- a/insns.def +++ b/insns.def @@ -1519,6 +1519,7 @@ opt_aref * default_proc. This is a method call. So opt_aref is * (surprisingly) not leaf. */ // attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */ +// attr bool zjit_profile = true; { val = vm_opt_aref(recv, obj); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 2d8a8eb11e7036..8eac87c965dbea 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -696,9 +696,10 @@ pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 231; pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 232; pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 233; pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 236; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 237; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 238; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 26145f46226fbb..248c2af7abd5c8 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -9069,6 +9069,64 @@ mod opt_tests { "); } + #[test] + fn test_opt_aref_array() { + eval(" + arr = [1,2,3] + def test(arr) = arr[0] + test(arr) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v26:ArrayExact = GuardType v9, ArrayExact + v27:BasicObject = CCallVariadic []@0x1038, v26, v13 + CheckInterrupts + Return v27 + "); + assert_snapshot!(inspect("test [1,2,3]"), @"1"); + } + + #[test] + fn test_opt_aref_hash() { + eval(" + arr = {0 => 4} + def test(arr) = arr[0] + test(arr) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:Fixnum[0] = Const Value(0) + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Hash@0x1000) + v26:HashExact = GuardType v9, HashExact + v27:BasicObject = CallCFunc []@0x1038, v26, v13 + CheckInterrupts + Return v27 + "); + assert_snapshot!(inspect("test({0 => 4})"), @"4"); + } + #[test] fn test_eliminate_new_range() { eval(" diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index 9588a541827f35..67f2fdc7403822 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -73,6 +73,7 @@ fn profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) { YARVINSN_opt_and => profile_operands(profiler, profile, 2), YARVINSN_opt_or => profile_operands(profiler, profile, 2), YARVINSN_opt_empty_p => profile_operands(profiler, profile, 1), + YARVINSN_opt_aref => profile_operands(profiler, profile, 2), YARVINSN_opt_not => profile_operands(profiler, profile, 1), YARVINSN_getinstancevariable => profile_self(profiler, profile), YARVINSN_objtostring => profile_operands(profiler, profile, 1), From 09e5c5eed1ee9a58ffa37ecdda999a6ffaea6eb4 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 19:06:49 +0200 Subject: [PATCH 07/18] ZJIT: Name enum for bindgen (#14802) Relying on having the same compiler version and behavior across platforms is brittle, as Kokubun points out. Instead, name the enum so we don't have to rely on gensym stability. Fix https://github.com/Shopify/ruby/issues/787 --- zjit.c | 2 +- zjit/bindgen/src/main.rs | 2 +- zjit/src/cruby_bindings.inc.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zjit.c b/zjit.c index d877c0bacbd507..e17abc1b37ff29 100644 --- a/zjit.c +++ b/zjit.c @@ -235,7 +235,7 @@ rb_zjit_print_exception(void) rb_warn("Ruby error: %"PRIsVALUE"", rb_funcall(exception, rb_intern("full_message"), 0)); } -enum { +enum zjit_exported_constants { RB_INVALID_SHAPE_ID = INVALID_SHAPE_ID, }; diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index e1d19f9442c62b..64b235b838baff 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -290,7 +290,7 @@ fn main() { .allowlist_function("rb_zjit_insn_leaf") .allowlist_type("robject_offsets") .allowlist_type("rstring_offsets") - .allowlist_var("RB_INVALID_SHAPE_ID") + .allowlist_type("zjit_exported_constants") .allowlist_function("rb_assert_holding_vm_lock") .allowlist_function("rb_jit_shape_too_complex_p") .allowlist_function("rb_jit_multi_ractor_p") diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 8eac87c965dbea..ab442841ff267a 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -723,8 +723,8 @@ pub const DEFINED_REF: defined_type = 15; pub const DEFINED_FUNC: defined_type = 16; pub const DEFINED_CONST_FROM: defined_type = 17; pub type defined_type = u32; -pub const RB_INVALID_SHAPE_ID: _bindgen_ty_38 = 4294967295; -pub type _bindgen_ty_38 = u32; +pub const RB_INVALID_SHAPE_ID: zjit_exported_constants = 4294967295; +pub type zjit_exported_constants = u32; pub const ROBJECT_OFFSET_AS_HEAP_FIELDS: robject_offsets = 16; pub const ROBJECT_OFFSET_AS_ARY: robject_offsets = 16; pub type robject_offsets = u32; From 3c16f321cb217e53e3e4aa9205525c4775f47a44 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 09:55:12 +0200 Subject: [PATCH 08/18] ZJIT: Add default FnProperties for unknown functions --- zjit/src/hir.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 248c2af7abd5c8..d132e6662dd733 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2342,7 +2342,14 @@ impl Function { use crate::cruby_methods::FnProperties; // Filter for simple call sites (i.e. no splats etc.) // Commit to the replacement. Put PatchPoint. - if let Some(FnProperties { leaf: true, no_gc: true, return_type, elidable }) = ZJITState::get_method_annotations().get_cfunc_properties(method) { + let props = ZJITState::get_method_annotations().get_cfunc_properties(method) + .unwrap_or(FnProperties { leaf: false, + no_gc: false, + return_type: types::BasicObject, + elidable: false }); + if props.leaf && props.no_gc { + let return_type = props.return_type; + let elidable = props.elidable; let ccall = fun.push_insn(block, Insn::CCall { cfunc, args: cfunc_args, name: method_id, return_type, elidable }); fun.make_equal_to(send_insn_id, ccall); } else { From a47048d5cfed9d51115ee91d0b30c5bbd4569abf Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 09:58:23 +0200 Subject: [PATCH 09/18] ZJIT: Add return_type to CCallWithFrame --- zjit/src/hir.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index d132e6662dd733..e247b85bed0d38 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -656,7 +656,8 @@ pub enum Insn { args: Vec, cme: *const rb_callable_method_entry_t, name: ID, - state: InsnId + state: InsnId, + return_type: Type, }, /// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv) @@ -1564,7 +1565,7 @@ impl Function { &ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state }, &ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) }, &CCall { cfunc, ref args, name, return_type, elidable } => CCall { cfunc, args: find_vec!(args), name, return_type, elidable }, - &CCallWithFrame { cd, cfunc, ref args, cme, name, state } => CCallWithFrame { cd, cfunc, args: find_vec!(args), cme, name, state: find!(state) }, + &CCallWithFrame { cd, cfunc, ref args, cme, name, state, return_type } => CCallWithFrame { cd, cfunc, args: find_vec!(args), cme, name, state: find!(state), return_type }, &CCallVariadic { cfunc, recv, ref args, cme, name, state } => CCallVariadic { cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state }, @@ -1665,7 +1666,7 @@ impl Function { Insn::NewRangeFixnum { .. } => types::RangeExact, Insn::ObjectAlloc { .. } => types::HeapObject, Insn::ObjectAllocClass { class, .. } => Type::from_class(*class), - Insn::CCallWithFrame { .. } => types::BasicObject, + &Insn::CCallWithFrame { return_type, .. } => return_type, Insn::CCall { return_type, .. } => *return_type, Insn::CCallVariadic { .. } => types::BasicObject, Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type), @@ -2347,8 +2348,8 @@ impl Function { no_gc: false, return_type: types::BasicObject, elidable: false }); + let return_type = props.return_type; if props.leaf && props.no_gc { - let return_type = props.return_type; let elidable = props.elidable; let ccall = fun.push_insn(block, Insn::CCall { cfunc, args: cfunc_args, name: method_id, return_type, elidable }); fun.make_equal_to(send_insn_id, ccall); @@ -2356,7 +2357,7 @@ impl Function { if get_option!(stats) { count_not_inlined_cfunc(fun, block, method); } - let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, cfunc, args: cfunc_args, cme: method, name: method_id, state }); + let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, cfunc, args: cfunc_args, cme: method, name: method_id, state, return_type }); fun.make_equal_to(send_insn_id, ccall); } From 5c986c7da275df0de3107fbea28e8f1ee592444b Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:07:13 +0200 Subject: [PATCH 10/18] ZJIT: Allow no properties to annotate! macro --- zjit/src/cruby_methods.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 41cff3403bcdfa..ecfaadd8e344bc 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -140,11 +140,12 @@ pub fn init() -> Annotations { let builtin_funcs = &mut HashMap::new(); macro_rules! annotate { - ($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => { + ($module:ident, $method_name:literal, $return_type:expr $(, $properties:ident)*) => { + #[allow(unused_mut)] let mut props = FnProperties { no_gc: false, leaf: false, elidable: false, return_type: $return_type }; $( props.$properties = true; - )+ + )* annotate_c_method(cfuncs, unsafe { $module }, $method_name, props); } } From e1c998ab917ba3a319ca0fd32f0857f1dbace906 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:07:27 +0200 Subject: [PATCH 11/18] ZJIT: Annotate Array#reverse as returning ArrayExact --- zjit/src/cruby_methods.rs | 1 + zjit/src/hir.rs | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index ecfaadd8e344bc..1df348bc9eef82 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -173,6 +173,7 @@ pub fn init() -> Annotations { annotate!(rb_cArray, "length", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cArray, "size", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cArray, "empty?", types::BoolExact, no_gc, leaf, elidable); + annotate!(rb_cArray, "reverse", types::ArrayExact); annotate!(rb_cHash, "empty?", types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable); annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index e247b85bed0d38..32024102cf218b 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -12437,4 +12437,28 @@ mod opt_tests { Return v14 "); } + + #[test] + fn test_array_reverse_returns_array() { + eval(r#" + def test = [].reverse + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v22:ArrayExact = CallCFunc reverse@0x1038, v11 + CheckInterrupts + Return v22 + "); + } } From fc735e257d65ba09ebc62dd698fda35bf4f0a585 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:12:46 +0200 Subject: [PATCH 12/18] ZJIT: Allow marking CCallWithFrame elidable Also mark Array#reverse as elidable. --- zjit/src/cruby_methods.rs | 2 +- zjit/src/hir.rs | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 1df348bc9eef82..334d4e2337e60b 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -173,7 +173,7 @@ pub fn init() -> Annotations { annotate!(rb_cArray, "length", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cArray, "size", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cArray, "empty?", types::BoolExact, no_gc, leaf, elidable); - annotate!(rb_cArray, "reverse", types::ArrayExact); + annotate!(rb_cArray, "reverse", types::ArrayExact, leaf, elidable); annotate!(rb_cHash, "empty?", types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable); annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 32024102cf218b..ec6746ec398c74 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -658,6 +658,7 @@ pub enum Insn { name: ID, state: InsnId, return_type: Type, + elidable: bool, }, /// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv) @@ -846,6 +847,7 @@ impl Insn { Insn::LoadIvarEmbedded { .. } => false, Insn::LoadIvarExtended { .. } => false, Insn::CCall { elidable, .. } => !elidable, + Insn::CCallWithFrame { elidable, .. } => !elidable, Insn::ObjectAllocClass { .. } => false, // TODO: NewRange is effects free if we can prove the two ends to be Fixnum, // but we don't have type information here in `impl Insn`. See rb_range_new(). @@ -1565,7 +1567,7 @@ impl Function { &ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state }, &ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) }, &CCall { cfunc, ref args, name, return_type, elidable } => CCall { cfunc, args: find_vec!(args), name, return_type, elidable }, - &CCallWithFrame { cd, cfunc, ref args, cme, name, state, return_type } => CCallWithFrame { cd, cfunc, args: find_vec!(args), cme, name, state: find!(state), return_type }, + &CCallWithFrame { cd, cfunc, ref args, cme, name, state, return_type, elidable } => CCallWithFrame { cd, cfunc, args: find_vec!(args), cme, name, state: find!(state), return_type, elidable }, &CCallVariadic { cfunc, recv, ref args, cme, name, state } => CCallVariadic { cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state }, @@ -2349,15 +2351,15 @@ impl Function { return_type: types::BasicObject, elidable: false }); let return_type = props.return_type; + let elidable = props.elidable; if props.leaf && props.no_gc { - let elidable = props.elidable; let ccall = fun.push_insn(block, Insn::CCall { cfunc, args: cfunc_args, name: method_id, return_type, elidable }); fun.make_equal_to(send_insn_id, ccall); } else { if get_option!(stats) { count_not_inlined_cfunc(fun, block, method); } - let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, cfunc, args: cfunc_args, cme: method, name: method_id, state, return_type }); + let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, cfunc, args: cfunc_args, cme: method, name: method_id, state, return_type, elidable }); fun.make_equal_to(send_insn_id, ccall); } @@ -12461,4 +12463,31 @@ mod opt_tests { Return v22 "); } + + #[test] + fn test_array_reverse_is_elidable() { + eval(r#" + def test + [].reverse + 5 + end + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v16:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v16 + "); + } } From d798e3c46ffb25ec1000893d7e41ea8bc4dffed9 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:21:46 +0200 Subject: [PATCH 13/18] ZJIT: Allow annotating CCallVariadic --- zjit/src/codegen.rs | 2 +- zjit/src/cruby_methods.rs | 12 ++++++++++++ zjit/src/hir.rs | 26 ++++++++++++++------------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index b1a2cb672641ef..4b9331e05b0892 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -409,7 +409,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::CCallWithFrame { cd, state, args, .. } if args.len() > C_ARG_OPNDS.len() => gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::CCallWithFrameTooManyArgs), Insn::CCallWithFrame { cfunc, args, cme, state, .. } => gen_ccall_with_frame(jit, asm, *cfunc, opnds!(args), *cme, &function.frame_state(*state)), - Insn::CCallVariadic { cfunc, recv, args, name: _, cme, state } => { + Insn::CCallVariadic { cfunc, recv, args, name: _, cme, state, return_type: _, elidable: _ } => { gen_ccall_variadic(jit, asm, *cfunc, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state)) } Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id), diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 334d4e2337e60b..7721ca844035ad 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -31,6 +31,18 @@ pub struct FnProperties { pub elidable: bool, } +/// A safe default for un-annotated Ruby methods: we can't optimize them or their returned values. +impl Default for FnProperties { + fn default() -> Self { + Self { + no_gc: false, + leaf: false, + return_type: types::BasicObject, + elidable: false, + } + } +} + impl Annotations { /// Query about properties of a C method pub fn get_cfunc_properties(&self, method: *const rb_callable_method_entry_t) -> Option { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index ec6746ec398c74..cee546b0261307 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -670,6 +670,8 @@ pub enum Insn { cme: *const rb_callable_method_entry_t, name: ID, state: InsnId, + return_type: Type, + elidable: bool, }, /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions @@ -1568,8 +1570,8 @@ impl Function { &ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) }, &CCall { cfunc, ref args, name, return_type, elidable } => CCall { cfunc, args: find_vec!(args), name, return_type, elidable }, &CCallWithFrame { cd, cfunc, ref args, cme, name, state, return_type, elidable } => CCallWithFrame { cd, cfunc, args: find_vec!(args), cme, name, state: find!(state), return_type, elidable }, - &CCallVariadic { cfunc, recv, ref args, cme, name, state } => CCallVariadic { - cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state + &CCallVariadic { cfunc, recv, ref args, cme, name, state, return_type, elidable } => CCallVariadic { + cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state, return_type, elidable }, &Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) }, &DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state }, @@ -1670,7 +1672,7 @@ impl Function { Insn::ObjectAllocClass { class, .. } => Type::from_class(*class), &Insn::CCallWithFrame { return_type, .. } => return_type, Insn::CCall { return_type, .. } => *return_type, - Insn::CCallVariadic { .. } => types::BasicObject, + &Insn::CCallVariadic { return_type, .. } => return_type, Insn::GuardType { val, guard_type, .. } => self.type_of(*val).intersection(*guard_type), Insn::GuardTypeNot { .. } => types::BasicObject, Insn::GuardBitEquals { val, expected, .. } => self.type_of(*val).intersection(Type::from_value(*expected)), @@ -2325,10 +2327,12 @@ impl Function { let ci_flags = unsafe { vm_ci_flag(call_info) }; + // Filter for simple call sites (i.e. no splats etc.) if ci_flags & VM_CALL_ARGS_SIMPLE == 0 { return Err(()); } + // Commit to the replacement. Put PatchPoint. gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, state); if recv_class.instance_can_have_singleton_class() { fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); @@ -2341,17 +2345,10 @@ impl Function { let mut cfunc_args = vec![recv]; cfunc_args.append(&mut args); - // Filter for a leaf and GC free function - use crate::cruby_methods::FnProperties; - // Filter for simple call sites (i.e. no splats etc.) - // Commit to the replacement. Put PatchPoint. - let props = ZJITState::get_method_annotations().get_cfunc_properties(method) - .unwrap_or(FnProperties { leaf: false, - no_gc: false, - return_type: types::BasicObject, - elidable: false }); + let props = ZJITState::get_method_annotations().get_cfunc_properties(method).unwrap_or_default(); let return_type = props.return_type; let elidable = props.elidable; + // Filter for a leaf and GC free function if props.leaf && props.no_gc { let ccall = fun.push_insn(block, Insn::CCall { cfunc, args: cfunc_args, name: method_id, return_type, elidable }); fun.make_equal_to(send_insn_id, ccall); @@ -2385,6 +2382,9 @@ impl Function { } let cfunc = unsafe { get_mct_func(cfunc) }.cast(); + let props = ZJITState::get_method_annotations().get_cfunc_properties(method).unwrap_or_default(); + let return_type = props.return_type; + let elidable = props.elidable; let ccall = fun.push_insn(block, Insn::CCallVariadic { cfunc, recv, @@ -2392,6 +2392,8 @@ impl Function { cme: method, name: method_id, state, + return_type, + elidable, }); fun.make_equal_to(send_insn_id, ccall); From 9020341bb4847d21339f6f45dceb2e498efd002c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:23:15 +0200 Subject: [PATCH 14/18] ZJIT: Annotate Array#join as returning StringExact --- zjit/src/cruby_methods.rs | 1 + zjit/src/hir.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 7721ca844035ad..5cb42291b02b22 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -186,6 +186,7 @@ pub fn init() -> Annotations { annotate!(rb_cArray, "size", types::Fixnum, no_gc, leaf, elidable); annotate!(rb_cArray, "empty?", types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cArray, "reverse", types::ArrayExact, leaf, elidable); + annotate!(rb_cArray, "join", types::StringExact); annotate!(rb_cHash, "empty?", types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cNilClass, "nil?", types::TrueClass, no_gc, leaf, elidable); annotate!(rb_mKernel, "nil?", types::FalseClass, no_gc, leaf, elidable); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index cee546b0261307..5a8ebaf2f26d61 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -12492,4 +12492,30 @@ mod opt_tests { Return v16 "); } + + #[test] + fn test_array_join_returns_string() { + eval(r#" + def test = [].join "," + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:ArrayExact = NewArray + v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v14:StringExact = StringCopy v12 + PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(Array@0x1008) + v25:StringExact = CCallVariadic join@0x1040, v11, v14 + CheckInterrupts + Return v25 + "); + } } From 6a25a8b1e28cc5c1b28fc12717a0fade4da23a7c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:38:26 +0200 Subject: [PATCH 15/18] ZJIT: Get stats for which C functions are not annotated --- zjit.rb | 1 + zjit/src/hir.rs | 25 +++++++++++++++++++++++-- zjit/src/state.rs | 9 +++++++++ zjit/src/stats.rs | 7 +++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/zjit.rb b/zjit.rb index 75c57f9a35c195..87ff52f55a1c21 100644 --- a/zjit.rb +++ b/zjit.rb @@ -156,6 +156,7 @@ def stats_string # Show counters independent from exit_* or dynamic_send_* print_counters_with_prefix(prefix: 'not_inlined_cfuncs_', prompt: 'not inlined C methods', buf:, stats:, limit: 20) + print_counters_with_prefix(prefix: 'not_annotated_cfuncs_', prompt: 'not annotated C methods', buf:, stats:, limit: 20) # Show fallback counters, ordered by the typical amount of fallbacks for the prefix at the time print_counters_with_prefix(prefix: 'unspecialized_def_type_', prompt: 'not optimized method types', buf:, stats:, limit: 20) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 5a8ebaf2f26d61..23f4e828c821eb 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2345,7 +2345,11 @@ impl Function { let mut cfunc_args = vec![recv]; cfunc_args.append(&mut args); - let props = ZJITState::get_method_annotations().get_cfunc_properties(method).unwrap_or_default(); + let props = ZJITState::get_method_annotations().get_cfunc_properties(method); + if props.is_none() && get_option!(stats) { + count_not_annotated_cfunc(fun, block, method); + } + let props = props.unwrap_or_default(); let return_type = props.return_type; let elidable = props.elidable; // Filter for a leaf and GC free function @@ -2382,7 +2386,11 @@ impl Function { } let cfunc = unsafe { get_mct_func(cfunc) }.cast(); - let props = ZJITState::get_method_annotations().get_cfunc_properties(method).unwrap_or_default(); + let props = ZJITState::get_method_annotations().get_cfunc_properties(method); + if props.is_none() && get_option!(stats) { + count_not_annotated_cfunc(fun, block, method); + } + let props = props.unwrap_or_default(); let return_type = props.return_type; let elidable = props.elidable; let ccall = fun.push_insn(block, Insn::CCallVariadic { @@ -2426,6 +2434,19 @@ impl Function { fun.push_insn(block, Insn::IncrCounterPtr { counter_ptr }); } + fn count_not_annotated_cfunc(fun: &mut Function, block: BlockId, cme: *const rb_callable_method_entry_t) { + let owner = unsafe { (*cme).owner }; + let called_id = unsafe { (*cme).called_id }; + let class_name = get_class_name(owner); + let method_name = called_id.contents_lossy(); + let qualified_method_name = format!("{}#{}", class_name, method_name); + let not_annotated_cfunc_counter_pointers = ZJITState::get_not_annotated_cfunc_counter_pointers(); + let counter_ptr = not_annotated_cfunc_counter_pointers.entry(qualified_method_name.clone()).or_insert_with(|| Box::new(0)); + let counter_ptr = &mut **counter_ptr as *mut u64; + + fun.push_insn(block, Insn::IncrCounterPtr { counter_ptr }); + } + for block in self.rpo() { let old_insns = std::mem::take(&mut self.blocks[block.0].insns); assert!(self.blocks[block.0].insns.is_empty()); diff --git a/zjit/src/state.rs b/zjit/src/state.rs index 409cac7e9bb421..1b766d5bc4b5aa 100644 --- a/zjit/src/state.rs +++ b/zjit/src/state.rs @@ -54,6 +54,9 @@ pub struct ZJITState { /// Counter pointers for full frame C functions full_frame_cfunc_counter_pointers: HashMap>, + /// Counter pointers for un-annotated C functions + not_annotated_frame_cfunc_counter_pointers: HashMap>, + /// Locations of side exists within generated code exit_locations: Option, } @@ -98,6 +101,7 @@ impl ZJITState { function_stub_hit_trampoline, exit_trampoline_with_counter: exit_trampoline, full_frame_cfunc_counter_pointers: HashMap::new(), + not_annotated_frame_cfunc_counter_pointers: HashMap::new(), exit_locations, }; unsafe { ZJIT_STATE = Some(zjit_state); } @@ -167,6 +171,11 @@ impl ZJITState { &mut ZJITState::get_instance().full_frame_cfunc_counter_pointers } + /// Get a mutable reference to non-annotated cfunc counter pointers + pub fn get_not_annotated_cfunc_counter_pointers() -> &'static mut HashMap> { + &mut ZJITState::get_instance().not_annotated_frame_cfunc_counter_pointers + } + /// Was --zjit-save-compiled-iseqs specified? pub fn should_log_compiled_iseqs() -> bool { get_option!(log_compiled_iseqs).is_some() diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index a9b7270444a7a5..6898053dca789b 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -480,6 +480,13 @@ pub extern "C" fn rb_zjit_stats(_ec: EcPtr, _self: VALUE, target_key: VALUE) -> set_stat_usize!(hash, &key_string, **counter); } + // Set not annotated cfunc counters + let not_annotated_cfuncs = ZJITState::get_not_annotated_cfunc_counter_pointers(); + for (signature, counter) in not_annotated_cfuncs.iter() { + let key_string = format!("not_annotated_cfuncs_{}", signature); + set_stat_usize!(hash, &key_string, **counter); + } + hash } From d25d993aa32cb963d9d4d9d9a8220c1fc69e1e19 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:44:02 +0200 Subject: [PATCH 16/18] ZJIT: Annotate String#to_s as returning StringExact --- zjit/src/cruby_methods.rs | 1 + zjit/src/hir.rs | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 5cb42291b02b22..7467cb50c85446 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -180,6 +180,7 @@ pub fn init() -> Annotations { annotate!(rb_mKernel, "itself", types::BasicObject, no_gc, leaf, elidable); annotate!(rb_cString, "bytesize", types::Fixnum, no_gc, leaf); + annotate!(rb_cString, "to_s", types::StringExact); annotate!(rb_cModule, "name", types::StringExact.union(types::NilClass), no_gc, leaf, elidable); annotate!(rb_cModule, "===", types::BoolExact, no_gc, leaf); annotate!(rb_cArray, "length", types::Fixnum, no_gc, leaf, elidable); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 23f4e828c821eb..f8e8c7539d92e3 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -12539,4 +12539,29 @@ mod opt_tests { Return v25 "); } + + #[test] + fn test_string_to_s_returns_string() { + eval(r#" + def test = "".to_s + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v10 + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + v23:StringExact = CallCFunc to_s@0x1040, v12 + CheckInterrupts + Return v23 + "); + } } From 117e5b68c84f48c22057f19fe60ed5468c988b76 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 10:47:57 +0200 Subject: [PATCH 17/18] ZJIT: Print CCallWithFrame as CCallWithFrame, not CallCFunc --- zjit/src/hir.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index f8e8c7539d92e3..f9283cba79b6c2 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1044,7 +1044,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Ok(()) }, Insn::CCallWithFrame { cfunc, args, name, .. } => { - write!(f, "CallCFunc {}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; + write!(f, "CCallWithFrame {}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; for arg in args { write!(f, ", {arg}")?; } @@ -10985,7 +10985,7 @@ mod opt_tests { v11:HashExact = NewHash PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) - v24:BasicObject = CallCFunc dup@0x1038, v11 + v24:BasicObject = CCallWithFrame dup@0x1038, v11 v15:BasicObject = SendWithoutBlock v24, :freeze CheckInterrupts Return v15 @@ -11078,7 +11078,7 @@ mod opt_tests { v11:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) - v24:BasicObject = CallCFunc dup@0x1038, v11 + v24:BasicObject = CCallWithFrame dup@0x1038, v11 v15:BasicObject = SendWithoutBlock v24, :freeze CheckInterrupts Return v15 @@ -11172,7 +11172,7 @@ mod opt_tests { v12:StringExact = StringCopy v10 PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) - v25:BasicObject = CallCFunc dup@0x1040, v12 + v25:BasicObject = CCallWithFrame dup@0x1040, v12 v16:BasicObject = SendWithoutBlock v25, :freeze CheckInterrupts Return v16 @@ -11267,7 +11267,7 @@ mod opt_tests { v12:StringExact = StringCopy v10 PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) - v25:BasicObject = CallCFunc dup@0x1040, v12 + v25:BasicObject = CCallWithFrame dup@0x1040, v12 v16:BasicObject = SendWithoutBlock v25, :-@ CheckInterrupts Return v16 @@ -11409,7 +11409,7 @@ mod opt_tests { PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) v30:ArrayExact = GuardType v9, ArrayExact - v31:BasicObject = CallCFunc to_s@0x1040, v30 + v31:BasicObject = CCallWithFrame to_s@0x1040, v30 v17:String = AnyToString v9, str: v31 v19:StringExact = StringConcat v13, v17 CheckInterrupts @@ -12481,7 +12481,7 @@ mod opt_tests { v11:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) - v22:ArrayExact = CallCFunc reverse@0x1038, v11 + v22:ArrayExact = CCallWithFrame reverse@0x1038, v11 CheckInterrupts Return v22 "); @@ -12559,7 +12559,7 @@ mod opt_tests { v12:StringExact = StringCopy v10 PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) - v23:StringExact = CallCFunc to_s@0x1040, v12 + v23:StringExact = CCallWithFrame to_s@0x1040, v12 CheckInterrupts Return v23 "); From b999ca0fce8116e9a218731bbbc171a849e53a86 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Thu, 9 Oct 2025 19:25:08 +0200 Subject: [PATCH 18/18] ZJIT: Fix land race --- zjit/src/hir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index f9283cba79b6c2..2fe8eb79700349 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -9153,7 +9153,7 @@ mod opt_tests { PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) v26:HashExact = GuardType v9, HashExact - v27:BasicObject = CallCFunc []@0x1038, v26, v13 + v27:BasicObject = CCallWithFrame []@0x1038, v26, v13 CheckInterrupts Return v27 ");