From 33a849e385b98326f43b394c456d2bf2981fdf24 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 22 Jul 2025 11:43:16 -0400 Subject: [PATCH 1/4] Remove global symbol lock for rb_gc_free_dsymbol --- symbol.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/symbol.c b/symbol.c index c66e7f067d0a21..d621717e7ba236 100644 --- a/symbol.c +++ b/symbol.c @@ -957,9 +957,7 @@ rb_gc_free_dsymbol(VALUE sym) VALUE str = RSYMBOL(sym)->fstr; if (str) { - GLOBAL_SYMBOLS_LOCKING(symbols) { - rb_concurrent_set_delete_by_identity(symbols->sym_set, sym); - } + rb_concurrent_set_delete_by_identity(ruby_global_symbols.sym_set, sym); RSYMBOL(sym)->fstr = 0; } From 93be578691b294255d7a074cbb4c59ac8094e24a Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 22 Jul 2025 11:43:44 -0400 Subject: [PATCH 2/4] Remove global symbol locks for rb_intern --- symbol.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/symbol.c b/symbol.c index d621717e7ba236..9c5f9a11718d47 100644 --- a/symbol.c +++ b/symbol.c @@ -881,10 +881,7 @@ rb_intern3(const char *name, long len, rb_encoding *enc) VALUE str = rb_setup_fake_str(&fake_str, name, len, enc); OBJ_FREEZE(str); - VALUE sym; - GLOBAL_SYMBOLS_LOCKING(symbols) { - sym = sym_find_or_insert_static_symbol(symbols, str); - } + VALUE sym = sym_find_or_insert_static_symbol(&ruby_global_symbols, str); return rb_sym2id(sym); } @@ -904,10 +901,7 @@ rb_intern(const char *name) ID rb_intern_str(VALUE str) { - VALUE sym; - GLOBAL_SYMBOLS_LOCKING(symbols) { - sym = sym_find_or_insert_static_symbol(symbols, str); - } + VALUE sym = sym_find_or_insert_static_symbol(&ruby_global_symbols, str); return SYM2ID(sym); } From 5e5cec1b86837653b2106af377561045f4bbecef Mon Sep 17 00:00:00 2001 From: tomoya ishida Date: Thu, 24 Jul 2025 02:12:12 +0900 Subject: [PATCH 3/4] Fix bigand_int edgecase returning false (#13987) --- bignum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bignum.c b/bignum.c index fb4714307e6375..ac86951372f4d4 100644 --- a/bignum.c +++ b/bignum.c @@ -6346,7 +6346,7 @@ bigand_int(VALUE x, long xn, BDIGIT hibitsx, long y) BDIGIT hibitsy; if (y == 0) return INT2FIX(0); - if (xn == 0) return hibitsx ? LONG2NUM(y) : 0; + if (xn == 0) return hibitsx ? LONG2NUM(y) : INT2FIX(0); hibitsy = 0 <= y ? 0 : BDIGMAX; xds = BDIGITS(x); #if SIZEOF_BDIGIT >= SIZEOF_LONG From 41149a96efa27ad297ea71119d56d7861066b5ee Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 22 Jul 2025 22:54:54 -0400 Subject: [PATCH 4/4] ZJIT: Fix clobbering register for `self` in gen_entry_params() Previously, for 8+ params we wound up clobbering the self param when putting the last param in memory in the JIT entry point: # ZJIT entry point: a@../test.rb:5 ldur x0, [x19, #0x18] # set method params: 8 ldur x1, [x21, #-0x58] ldur x2, [x21, #-0x50] ldur x3, [x21, #-0x48] ldur x4, [x21, #-0x40] ldur x5, [x21, #-0x38] ldur x11, [x21, #-0x30] ldur x12, [x21, #-0x28] ldur x0, [x21, #-0x20] stur x0, [sp, #-0x20] bl #0x11e17018c Doing the memcpys for parameters in memory first avoids this clobbering. # set method params: 8 ldur x0, [x21, #-0x20] stur x0, [sp, #-0x20] ldur x12, [x21, #-0x28] ldur x11, [x21, #-0x30] ldur x5, [x21, #-0x38] ldur x4, [x21, #-0x40] ldur x3, [x21, #-0x48] ldur x2, [x21, #-0x50] ldur x1, [x21, #-0x58] ldur x0, [x19, #0x18] --- test/ruby/test_zjit.rb | 6 ++++++ zjit/src/codegen.rs | 35 ++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 69c440f5f08fba..0dcdb8e4cb8275 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -811,6 +811,12 @@ def a(n1,n2,n3,n4,n5,n6,n7,n8,n9) = n1+n9 def a(n1,n2,n3,n4,n5,n6,n7,n8) = n8 a(1,1,1,1,1,1,1,0) } + + # self param with spilled param + assert_compiles '"main"', %q{ + def a(n1,n2,n3,n4,n5,n6,n7,n8) = self + a(1,0,0,0,0,0,0,0).to_s + } end def test_opt_aref_with diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index fee6537c375d49..c460dddfb82ce1 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -187,6 +187,10 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt asm.frame_teardown(); asm.cret(C_RET_OPND); + if get_option!(dump_lir) { + println!("LIR:\nJIT entry for {}:\n{:?}", iseq_name(iseq), asm); + } + let result = asm.compile(cb).map(|(start_ptr, _)| start_ptr); if let Some(start_addr) = result { if get_option!(perf) { @@ -584,16 +588,16 @@ fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) { /// Assign method arguments to basic block arguments at JIT entry fn gen_entry_params(asm: &mut Assembler, iseq: IseqPtr, entry_block: &Block, c_stack_bytes: usize) { - let self_param = gen_param(asm, SELF_PARAM_IDX); - asm.mov(self_param, Opnd::mem(VALUE_BITS, CFP, RUBY_OFFSET_CFP_SELF)); - let num_params = entry_block.params().len() - 1; // -1 to exclude self if num_params > 0 { asm_comment!(asm, "set method params: {num_params}"); - // Allocate registers for basic block arguments - for idx in 0..num_params { - let param = gen_param(asm, idx + 1); // +1 for self + // Fill basic block parameters. + // Doing it in reverse is load-bearing. High index params have memory slots that might + // require using a register to fill. Filling them first avoids clobbering. + for idx in (0..num_params).rev() { + let param = param_opnd(idx + 1); // +1 for self + let local = gen_entry_param(asm, iseq, idx); // Funky offset adjustment to write into the native stack frame of the // HIR function we'll be calling into. This only makes sense in context @@ -611,17 +615,22 @@ fn gen_entry_params(asm: &mut Assembler, iseq: IseqPtr, entry_block: &Block, c_s // would be while │ │ // the HIR function ────────────► └────────────┘ // is running - let param = if let Opnd::Mem(lir::Mem { base, disp, num_bits }) = param { - Opnd::Mem(lir::Mem { num_bits, base, disp: disp - c_stack_bytes as i32 - Assembler::frame_size() }) - } else { - param - }; + match param { + Opnd::Mem(lir::Mem { base, disp, num_bits }) => { + let param_slot = Opnd::Mem(lir::Mem { num_bits, base, disp: disp - c_stack_bytes as i32 - Assembler::frame_size() }); + asm.mov(param_slot, local); + } + // Prepare for parallel move for locals in registers + reg @ Opnd::Reg(_) => { + asm.load_into(reg, local); + } + _ => unreachable!("on entry, params are either in memory or in reg. Got {param:?}") + } // Assign local variables to the basic block arguments - let local = gen_entry_param(asm, iseq, idx); - asm.mov(param, local); } } + asm.load_into(param_opnd(SELF_PARAM_IDX), Opnd::mem(VALUE_BITS, CFP, RUBY_OFFSET_CFP_SELF)); } /// Set branch params to basic block arguments