YJIT: Optimize attr_writer (#9986)

* YJIT: Optimize attr_writer

* Comment about StackOpnd vs SelfOpnd
This commit is contained in:
Takashi Kokubun 2024-02-22 10:33:42 -08:00 committed by GitHub
parent e65315a725
commit a16fefcff5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 79 additions and 67 deletions

View File

@ -2413,49 +2413,6 @@ pub const CASE_WHEN_MAX_DEPTH: u8 = 20;
pub const MAX_SPLAT_LENGTH: i32 = 127;
// Codegen for setting an instance variable.
// Preconditions:
// - receiver is in REG0
// - receiver has the same class as CLASS_OF(comptime_receiver)
// - no stack push or pops to ctx since the entry to the codegen of the instruction being compiled
fn gen_set_ivar(
jit: &mut JITState,
asm: &mut Assembler,
ivar_name: ID,
flags: u32,
argc: i32,
) -> Option<CodegenStatus> {
// This is a .send call and we need to adjust the stack
if flags & VM_CALL_OPT_SEND != 0 {
handle_opt_send_shift_stack(asm, argc);
}
// Save the PC and SP because the callee may allocate or raise FrozenError
// Note that this modifies REG_SP, which is why we do it first
jit_prepare_non_leaf_call(jit, asm);
// Get the operands from the stack
let val_opnd = asm.stack_opnd(0);
let recv_opnd = asm.stack_opnd(1);
// Call rb_vm_set_ivar_id with the receiver, the ivar name, and the value
let val = asm.ccall(
rb_vm_set_ivar_id as *const u8,
vec![
recv_opnd,
Opnd::UImm(ivar_name),
val_opnd,
],
);
asm.stack_pop(2); // Keep them on stack during ccall for GC
let out_opnd = asm.stack_push(Type::Unknown);
asm.mov(out_opnd, val);
Some(KeepCompiling)
}
// Codegen for getting an instance variable.
// Preconditions:
// - receiver has the same class as CLASS_OF(comptime_receiver)
@ -2678,7 +2635,32 @@ fn gen_setinstancevariable(
}
let ivar_name = jit.get_arg(0).as_u64();
let ic = jit.get_arg(1).as_ptr();
let comptime_receiver = jit.peek_at_self();
gen_set_ivar(
jit,
asm,
ocb,
comptime_receiver,
ivar_name,
SelfOpnd,
Some(ic),
)
}
/// Set an instance variable on setinstancevariable or attr_writer.
/// It switches the behavior based on what recv_opnd is given.
/// * SelfOpnd: setinstancevariable, which doesn't push a result onto the stack.
/// * StackOpnd: attr_writer, which pushes a result onto the stack.
fn gen_set_ivar(
jit: &mut JITState,
asm: &mut Assembler,
ocb: &mut OutlinedCb,
comptime_receiver: VALUE,
ivar_name: ID,
recv_opnd: YARVOpnd,
ic: Option<*const iseq_inline_iv_cache_entry>,
) -> Option<CodegenStatus> {
let comptime_val_klass = comptime_receiver.class_of();
// If the comptime receiver is frozen, writing an IV will raise an exception
@ -2759,10 +2741,6 @@ fn gen_setinstancevariable(
// then just write out the IV write as a function call.
// too-complex shapes can't use index access, so we use rb_ivar_get for them too.
if !receiver_t_object || uses_custom_allocator || shape_too_complex || new_shape_too_complex || megamorphic {
asm_comment!(asm, "call rb_vm_setinstancevariable()");
let ic = jit.get_arg(1).as_u64(); // type IVC
// The function could raise FrozenError.
// Note that this modifies REG_SP, which is why we do it first
jit_prepare_non_leaf_call(jit, asm);
@ -2770,22 +2748,37 @@ fn gen_setinstancevariable(
// Get the operands from the stack
let val_opnd = asm.stack_opnd(0);
// Call rb_vm_setinstancevariable(iseq, obj, id, val, ic);
asm.ccall(
rb_vm_setinstancevariable as *const u8,
vec![
Opnd::const_ptr(jit.iseq as *const u8),
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF),
ivar_name.into(),
val_opnd,
Opnd::const_ptr(ic as *const u8),
]
);
if let StackOpnd(index) = recv_opnd { // attr_writer
let recv = asm.stack_opnd(index as i32);
asm_comment!(asm, "call rb_vm_set_ivar_id()");
asm.ccall(
rb_vm_set_ivar_id as *const u8,
vec![
recv,
Opnd::UImm(ivar_name),
val_opnd,
],
);
} else { // setinstancevariable
asm_comment!(asm, "call rb_vm_setinstancevariable()");
asm.ccall(
rb_vm_setinstancevariable as *const u8,
vec![
Opnd::const_ptr(jit.iseq as *const u8),
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF),
ivar_name.into(),
val_opnd,
Opnd::const_ptr(ic.unwrap() as *const u8),
],
);
}
} else {
// Get the receiver
let mut recv = asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
let recv_opnd = SelfOpnd;
let mut recv = asm.load(if let StackOpnd(index) = recv_opnd {
asm.stack_opnd(index as i32)
} else {
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
});
// Upgrade type
guard_object_is_heap(asm, recv, recv_opnd, Counter::setivar_not_heap);
@ -2829,7 +2822,11 @@ fn gen_setinstancevariable(
);
// Load the receiver again after the function call
recv = asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF))
recv = asm.load(if let StackOpnd(index) = recv_opnd {
asm.stack_opnd(index as i32)
} else {
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
});
}
write_val = asm.stack_opnd(0);
@ -2880,7 +2877,16 @@ fn gen_setinstancevariable(
asm.write_label(skip_wb);
}
}
asm.stack_pop(1); // Keep it on stack during ccall for GC
let write_val = asm.stack_pop(1); // Keep write_val on stack during ccall for GC
// If it's attr_writer, i.e. recv_opnd is StackOpnd, we need to pop
// the receiver and push the written value onto the stack.
if let StackOpnd(_) = recv_opnd {
asm.stack_pop(1); // Pop receiver
let out_opnd = asm.stack_push(Type::Unknown); // Push a return value
asm.mov(out_opnd, write_val);
}
Some(KeepCompiling)
}
@ -8046,9 +8052,9 @@ fn gen_send_general(
) };
}
VM_METHOD_TYPE_IVAR => {
// This is a .send call not supported right now for getters
// This is a .send call not supported right now for attr_reader
if flags & VM_CALL_OPT_SEND != 0 {
gen_counter_incr(asm, Counter::send_send_getter);
gen_counter_incr(asm, Counter::send_send_attr_reader);
return None;
}
@ -8114,6 +8120,11 @@ fn gen_send_general(
);
}
VM_METHOD_TYPE_ATTRSET => {
// This is a .send call not supported right now for attr_writer
if flags & VM_CALL_OPT_SEND != 0 {
gen_counter_incr(asm, Counter::send_send_attr_writer);
return None;
}
if flags & VM_CALL_ARGS_SPLAT != 0 {
gen_counter_incr(asm, Counter::send_args_splat_attrset);
return None;
@ -8134,7 +8145,7 @@ fn gen_send_general(
return None;
} else {
let ivar_name = unsafe { get_cme_def_body_attr_id(cme) };
return gen_set_ivar(jit, asm, ivar_name, flags, argc);
return gen_set_ivar(jit, asm, ocb, comptime_recv, ivar_name, StackOpnd(1), None);
}
}
// Block method, e.g. define_method(:foo) { :my_block }

View File

@ -393,7 +393,8 @@ make_counters! {
send_send_null_mid,
send_send_null_cme,
send_send_nested,
send_send_getter,
send_send_attr_reader,
send_send_attr_writer,
send_iseq_has_rest_and_captured,
send_iseq_has_kwrest_and_captured,
send_iseq_has_rest_and_kw_supplied,