YJIT: Optimize attr_writer (#9986)
* YJIT: Optimize attr_writer * Comment about StackOpnd vs SelfOpnd
This commit is contained in:
parent
e65315a725
commit
a16fefcff5
@ -2413,49 +2413,6 @@ pub const CASE_WHEN_MAX_DEPTH: u8 = 20;
|
|||||||
|
|
||||||
pub const MAX_SPLAT_LENGTH: i32 = 127;
|
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.
|
// Codegen for getting an instance variable.
|
||||||
// Preconditions:
|
// Preconditions:
|
||||||
// - receiver has the same class as CLASS_OF(comptime_receiver)
|
// - 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 ivar_name = jit.get_arg(0).as_u64();
|
||||||
|
let ic = jit.get_arg(1).as_ptr();
|
||||||
let comptime_receiver = jit.peek_at_self();
|
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();
|
let comptime_val_klass = comptime_receiver.class_of();
|
||||||
|
|
||||||
// If the comptime receiver is frozen, writing an IV will raise an exception
|
// 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.
|
// 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.
|
// 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 {
|
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.
|
// The function could raise FrozenError.
|
||||||
// Note that this modifies REG_SP, which is why we do it first
|
// Note that this modifies REG_SP, which is why we do it first
|
||||||
jit_prepare_non_leaf_call(jit, asm);
|
jit_prepare_non_leaf_call(jit, asm);
|
||||||
@ -2770,7 +2748,19 @@ fn gen_setinstancevariable(
|
|||||||
// Get the operands from the stack
|
// Get the operands from the stack
|
||||||
let val_opnd = asm.stack_opnd(0);
|
let val_opnd = asm.stack_opnd(0);
|
||||||
|
|
||||||
// Call rb_vm_setinstancevariable(iseq, obj, id, val, ic);
|
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(
|
asm.ccall(
|
||||||
rb_vm_setinstancevariable as *const u8,
|
rb_vm_setinstancevariable as *const u8,
|
||||||
vec![
|
vec![
|
||||||
@ -2778,14 +2768,17 @@ fn gen_setinstancevariable(
|
|||||||
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF),
|
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF),
|
||||||
ivar_name.into(),
|
ivar_name.into(),
|
||||||
val_opnd,
|
val_opnd,
|
||||||
Opnd::const_ptr(ic as *const u8),
|
Opnd::const_ptr(ic.unwrap() as *const u8),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Get the receiver
|
// Get the receiver
|
||||||
let mut recv = asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
|
let mut recv = asm.load(if let StackOpnd(index) = recv_opnd {
|
||||||
|
asm.stack_opnd(index as i32)
|
||||||
let recv_opnd = SelfOpnd;
|
} else {
|
||||||
|
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
|
||||||
|
});
|
||||||
|
|
||||||
// Upgrade type
|
// Upgrade type
|
||||||
guard_object_is_heap(asm, recv, recv_opnd, Counter::setivar_not_heap);
|
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
|
// 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);
|
write_val = asm.stack_opnd(0);
|
||||||
@ -2880,7 +2877,16 @@ fn gen_setinstancevariable(
|
|||||||
asm.write_label(skip_wb);
|
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)
|
Some(KeepCompiling)
|
||||||
}
|
}
|
||||||
@ -8046,9 +8052,9 @@ fn gen_send_general(
|
|||||||
) };
|
) };
|
||||||
}
|
}
|
||||||
VM_METHOD_TYPE_IVAR => {
|
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 {
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8114,6 +8120,11 @@ fn gen_send_general(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
VM_METHOD_TYPE_ATTRSET => {
|
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 {
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
gen_counter_incr(asm, Counter::send_args_splat_attrset);
|
gen_counter_incr(asm, Counter::send_args_splat_attrset);
|
||||||
return None;
|
return None;
|
||||||
@ -8134,7 +8145,7 @@ fn gen_send_general(
|
|||||||
return None;
|
return None;
|
||||||
} else {
|
} else {
|
||||||
let ivar_name = unsafe { get_cme_def_body_attr_id(cme) };
|
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 }
|
// Block method, e.g. define_method(:foo) { :my_block }
|
||||||
|
@ -393,7 +393,8 @@ make_counters! {
|
|||||||
send_send_null_mid,
|
send_send_null_mid,
|
||||||
send_send_null_cme,
|
send_send_null_cme,
|
||||||
send_send_nested,
|
send_send_nested,
|
||||||
send_send_getter,
|
send_send_attr_reader,
|
||||||
|
send_send_attr_writer,
|
||||||
send_iseq_has_rest_and_captured,
|
send_iseq_has_rest_and_captured,
|
||||||
send_iseq_has_kwrest_and_captured,
|
send_iseq_has_kwrest_and_captured,
|
||||||
send_iseq_has_rest_and_kw_supplied,
|
send_iseq_has_rest_and_kw_supplied,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user