mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
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 <snip> 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]
This commit is contained in:
parent
5e5cec1b86
commit
41149a96ef
2 changed files with 28 additions and 13 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue