Skip to content

Commit f51ab43

Browse files
committed
ZJIT: Spill stack from direct send with args in callee order
1 parent dcfbbdc commit f51ab43

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

test/ruby/test_zjit.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,27 @@ def f(a)
383383
}, call_threshold: 2
384384
end
385385

386+
def test_kwargs_with_exit_and_local_invalidation
387+
assert_compiles ':ok', %q{
388+
def a(b:, c:)
389+
if c == :b
390+
return -> {}
391+
end
392+
Class # invalidate locals
393+
394+
raise "c is :b!" if c == :b
395+
end
396+
397+
def test
398+
# note opposite order of kwargs
399+
a(c: :c, b: :b)
400+
end
401+
402+
4.times { test }
403+
:ok
404+
}, call_threshold: 2
405+
end
406+
386407
def test_setlocal_on_eval
387408
assert_compiles '1', %q{
388409
@b = binding

zjit/src/codegen.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ fn gen_send_without_block_direct(
13461346
gen_save_sp(asm, state.stack().len() - args.len() - 1); // -1 for receiver
13471347

13481348
gen_spill_locals(jit, asm, state);
1349-
gen_spill_stack(jit, asm, state);
1349+
gen_spill_stack_for_send(jit, asm, state, &args);
13501350

13511351
let (frame_type, specval) = if VM_METHOD_TYPE_BMETHOD == unsafe { get_cme_def_type(cme) } {
13521352
// Extract EP from the Proc instance
@@ -2211,6 +2211,24 @@ fn gen_spill_stack(jit: &JITState, asm: &mut Assembler, state: &FrameState) {
22112211
}
22122212
}
22132213

2214+
/// Spill stack for a direct send, with args in callee order.
2215+
/// This is needed because reorder_keyword_arguments may reorder the args in the HIR
2216+
/// but the FrameState's stack remains in caller order.
2217+
fn gen_spill_stack_for_send(jit: &JITState, asm: &mut Assembler, state: &FrameState, args: &[Opnd]) {
2218+
gen_incr_counter(asm, Counter::vm_write_stack_count);
2219+
let stack: Vec<_> = state.stack().collect();
2220+
let recv_idx = stack.len() - args.len() - 1;
2221+
asm_comment!(asm, "spill stack up to and including receiver");
2222+
for idx in 0..=recv_idx {
2223+
asm.mov(Opnd::mem(64, SP, idx as i32 * SIZEOF_VALUE_I32), jit.get_opnd(*stack[idx]));
2224+
}
2225+
asm_comment!(asm, "spill args in callee order");
2226+
for (i, arg) in args.iter().enumerate() {
2227+
let offset = (recv_idx + 1 + i) as i32 * SIZEOF_VALUE_I32;
2228+
asm.mov(Opnd::mem(64, SP, offset), *arg);
2229+
}
2230+
}
2231+
22142232
/// Prepare for calling a C function that may call an arbitrary method.
22152233
/// Use gen_prepare_leaf_call_with_gc() if the method is leaf but allocates objects.
22162234
fn gen_prepare_non_leaf_call(jit: &JITState, asm: &mut Assembler, state: &FrameState) {

0 commit comments

Comments
 (0)