Skip to content

Commit 89517a7

Browse files
committed
ZJIT: Remove duplicated frame switching logic
Extract frame switching logic into helper functions that explain the meaning of each step in the frame switching process.
1 parent 6f27783 commit 89517a7

1 file changed

Lines changed: 47 additions & 48 deletions

File tree

‎zjit/src/codegen.rs‎

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,31 +1092,60 @@ fn gen_ccall_with_frame(
10921092
forwarded_argc: None, // cfunc doesn't support forwarded arguments
10931093
});
10941094

1095-
asm_comment!(asm, "switch to new SP register");
10961095
let sp_offset = (caller_stack_size + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE;
1097-
let new_sp = asm.add(SP, sp_offset.into());
1098-
asm.mov(SP, new_sp);
1099-
1100-
asm_comment!(asm, "switch to new CFP");
1101-
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1102-
asm.mov(CFP, new_cfp);
1103-
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
1096+
gen_enter_cfunc_frame(asm, sp_offset);
11041097

11051098
let mut cfunc_args = vec![recv];
11061099
cfunc_args.extend(args);
11071100
asm.count_call_to_with(|| qualified_method_name(unsafe { (*cme).owner }, name));
11081101
let result = asm.ccall(cfunc, cfunc_args);
11091102

1103+
gen_pop_cfunc_frame(asm, sp_offset);
1104+
1105+
result
1106+
}
1107+
1108+
/// Switch registers to a direct ISEQ callee, but leave ec->cfp on the caller.
1109+
/// gen_entry_point() publishes the callee only after its JITFrame and jit_return are valid.
1110+
fn gen_enter_iseq_frame(asm: &mut Assembler, sp_offset: usize) {
1111+
gen_switch_to_callee(asm, sp_offset);
1112+
}
1113+
1114+
/// Enter a C frame whose SP, block_code, and C-frame sentinel are already initialized.
1115+
fn gen_enter_cfunc_frame(asm: &mut Assembler, sp_offset: usize) {
1116+
gen_switch_to_callee(asm, sp_offset);
1117+
gen_set_ec_cfp(asm);
1118+
}
1119+
1120+
/// Restore the caller's control frame after a C call.
1121+
fn gen_pop_cfunc_frame(asm: &mut Assembler, sp_offset: usize) {
11101122
asm_comment!(asm, "pop C frame");
11111123
let new_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
11121124
asm.mov(CFP, new_cfp);
1125+
gen_set_ec_cfp(asm);
1126+
gen_restore_sp(asm, sp_offset);
1127+
}
1128+
1129+
/// Set `ec->cfp` to the current CFP.
1130+
fn gen_set_ec_cfp(asm: &mut Assembler) {
11131131
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
1132+
}
11141133

1134+
/// Restore the caller's SP after a call that switched to a callee frame.
1135+
fn gen_restore_sp(asm: &mut Assembler, sp_offset: usize) {
11151136
asm_comment!(asm, "restore SP register for the caller");
11161137
let new_sp = asm.sub(SP, sp_offset.into());
11171138
asm.mov(SP, new_sp);
1139+
}
11181140

1119-
result
1141+
fn gen_switch_to_callee(asm: &mut Assembler, sp_offset: usize) {
1142+
asm_comment!(asm, "switch to new SP register");
1143+
let new_sp = asm.add(SP, sp_offset.into());
1144+
asm.mov(SP, new_sp);
1145+
1146+
asm_comment!(asm, "switch to new CFP");
1147+
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1148+
asm.mov(CFP, new_cfp);
11201149
}
11211150

11221151
/// Lowering for [`Insn::CCall`]. This is a low-level raw call that doesn't know
@@ -1183,28 +1212,14 @@ fn gen_ccall_variadic(
11831212
forwarded_argc: None, // cfunc doesn't support forwarded arguments
11841213
});
11851214

1186-
asm_comment!(asm, "switch to new SP register");
11871215
let sp_offset = (caller_stack_size + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE;
1188-
let new_sp = asm.add(SP, sp_offset.into());
1189-
asm.mov(SP, new_sp);
1190-
1191-
asm_comment!(asm, "switch to new CFP");
1192-
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1193-
asm.mov(CFP, new_cfp);
1194-
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
1216+
gen_enter_cfunc_frame(asm, sp_offset);
11951217

11961218
let argv_ptr = gen_push_opnds(jit, asm, &args);
11971219
asm.count_call_to_with(|| qualified_method_name(unsafe { (*cme).owner }, name));
11981220
let result = asm.ccall(cfunc, vec![args.len().into(), argv_ptr, recv]);
11991221

1200-
asm_comment!(asm, "pop C frame");
1201-
let new_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1202-
asm.mov(CFP, new_cfp);
1203-
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
1204-
1205-
asm_comment!(asm, "restore SP register for the caller");
1206-
let new_sp = asm.sub(SP, sp_offset.into());
1207-
asm.mov(SP, new_sp);
1222+
gen_pop_cfunc_frame(asm, sp_offset);
12081223

12091224
result
12101225
}
@@ -1744,7 +1759,7 @@ fn gen_push_inline_frame(
17441759
asm_comment!(asm, "switch to inlined callee CFP");
17451760
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
17461761
asm.mov(CFP, new_cfp);
1747-
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP as i32), CFP);
1762+
gen_set_ec_cfp(asm);
17481763
}
17491764

17501765
/// Pop the interpreter frame for an inlined callee, restoring the caller's SP and CFP.
@@ -1762,7 +1777,7 @@ fn gen_pop_inline_frame(
17621777

17631778
asm_comment!(asm, "restore caller CFP after inline");
17641779
asm.add_into(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1765-
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP as i32), CFP);
1780+
gen_set_ec_cfp(asm);
17661781
}
17671782

17681783
/// Compile a direct call to an ISEQ method.
@@ -1869,14 +1884,8 @@ fn gen_send_iseq_direct(
18691884
// the callee will spill the callinfo passed as part of `c_args` into the `...` local.
18701885
}
18711886

1872-
asm_comment!(asm, "switch to new SP register");
18731887
let sp_offset = (state.stack().len() + local_size - args.len() + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE;
1874-
let new_sp = asm.add(SP, sp_offset.into());
1875-
asm.mov(SP, new_sp);
1876-
1877-
asm_comment!(asm, "switch to new CFP");
1878-
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1879-
asm.mov(CFP, new_cfp); // will be published at `ec->cfp` after callee's entrypoint
1888+
gen_enter_iseq_frame(asm, sp_offset);
18801889

18811890
let params = unsafe { iseq.params() };
18821891

@@ -1925,9 +1934,7 @@ fn gen_send_iseq_direct(
19251934
// Restore the C stack pointer on exit
19261935
asm.je(jit, ZJITState::get_exit_trampoline().into());
19271936

1928-
asm_comment!(asm, "restore SP register for the caller");
1929-
let new_sp = asm.sub(SP, sp_offset.into());
1930-
asm.mov(SP, new_sp);
1937+
gen_restore_sp(asm, sp_offset);
19311938

19321939
ret
19331940
}
@@ -2067,15 +2074,9 @@ fn gen_invoke_block_iseq_direct(
20672074
forwarded_argc: None, // `...` is not allowed in block arguments
20682075
});
20692076

2070-
asm_comment!(asm, "switch to new SP register");
20712077
let sp_offset = (stack_size + local_size + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE;
2072-
let new_sp = asm.add(SP, sp_offset.into());
2073-
asm.mov(SP, new_sp);
2074-
2075-
asm_comment!(asm, "switch to new CFP");
2076-
let new_cfp = asm.sub(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
2077-
asm.mov(CFP, new_cfp);
2078-
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
2078+
gen_switch_to_callee(asm, sp_offset);
2079+
gen_set_ec_cfp(asm);
20792080

20802081
// JIT-to-JIT convention: self as c_args[0], then positional args. The block is
20812082
// gated to simple + lead-only + exact arity, so there are no optionals/kw/block.
@@ -2093,9 +2094,7 @@ fn gen_invoke_block_iseq_direct(
20932094
asm.cmp(ret, Qundef.into());
20942095
asm.je(jit, ZJITState::get_exit_trampoline().into());
20952096

2096-
asm_comment!(asm, "restore SP register for the caller");
2097-
let new_sp = asm.sub(SP, sp_offset.into());
2098-
asm.mov(SP, new_sp);
2097+
gen_restore_sp(asm, sp_offset);
20992098

21002099
ret
21012100
}

0 commit comments

Comments
 (0)