Skip to content

Commit

Permalink
YJIT: Support inlining putself (#10137)
Browse files Browse the repository at this point in the history
  • Loading branch information
k0kubun committed Feb 29, 2024
1 parent 4c0f0b9 commit 5891c70
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 11 deletions.
4 changes: 4 additions & 0 deletions bootstraptest/test_yjit.rb
Expand Up @@ -4513,6 +4513,10 @@ def entry = putobject(nil)
def entry = yield
entry { true }
}
assert_equal 'sym', %q{
def entry = :sym.to_sym
entry
}

assert_normal_exit %q{
ivars = 1024.times.map { |i| "@iv_#{i} = #{i}\n" }.join
Expand Down
38 changes: 27 additions & 11 deletions yjit/src/codegen.rs
Expand Up @@ -6638,8 +6638,14 @@ fn gen_send_bmethod(
perf_call! { gen_send_iseq(jit, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, flags, argc, None) }
}

/// The kind of a value an ISEQ returns
enum IseqReturn {
Value(VALUE),
Receiver,
}

/// Return the ISEQ's return value if it consists of only putnil/putobject and leave.
fn iseq_get_return_value(iseq: IseqPtr) -> Option<VALUE> {
fn iseq_get_return_value(iseq: IseqPtr, captured_opnd: Option<Opnd>) -> Option<IseqReturn> {
// Expect only two instructions and one possible operand
let iseq_size = unsafe { get_iseq_encoded_size(iseq) };
if !(2..=3).contains(&iseq_size) {
Expand All @@ -6655,10 +6661,12 @@ fn iseq_get_return_value(iseq: IseqPtr) -> Option<VALUE> {
return None;
}
match first_insn {
YARVINSN_putnil => Some(Qnil),
YARVINSN_putobject => unsafe { Some(*rb_iseq_pc_at_idx(iseq, 1)) },
YARVINSN_putobject_INT2FIX_0_ => Some(VALUE::fixnum_from_usize(0)),
YARVINSN_putobject_INT2FIX_1_ => Some(VALUE::fixnum_from_usize(1)),
YARVINSN_putnil => Some(IseqReturn::Value(Qnil)),
YARVINSN_putobject => Some(IseqReturn::Value(unsafe { *rb_iseq_pc_at_idx(iseq, 1) })),
YARVINSN_putobject_INT2FIX_0_ => Some(IseqReturn::Value(VALUE::fixnum_from_usize(0))),
YARVINSN_putobject_INT2FIX_1_ => Some(IseqReturn::Value(VALUE::fixnum_from_usize(1))),
// We don't support invokeblock for now. Such ISEQs are likely not used by blocks anyway.
YARVINSN_putself if captured_opnd.is_none() => Some(IseqReturn::Receiver),
_ => None,
}
}
Expand Down Expand Up @@ -6934,16 +6942,24 @@ fn gen_send_iseq(
}

// Inline simple ISEQs whose return value is known at compile time
if let (Some(value), None, false) = (iseq_get_return_value(iseq), block_arg_type, opt_send_call) {
if let (Some(value), None, false) = (iseq_get_return_value(iseq, captured_opnd), block_arg_type, opt_send_call) {
asm_comment!(asm, "inlined simple ISEQ");
gen_counter_incr(asm, Counter::num_send_iseq_inline);

// Pop receiver and arguments
asm.stack_pop(argc as usize + if captured_opnd.is_some() { 0 } else { 1 });
match value {
IseqReturn::Value(value) => {
// Pop receiver and arguments
asm.stack_pop(argc as usize + if captured_opnd.is_some() { 0 } else { 1 });

// Push the return value
let stack_ret = asm.stack_push(Type::from(value));
asm.mov(stack_ret, value.into());
// Push the return value
let stack_ret = asm.stack_push(Type::from(value));
asm.mov(stack_ret, value.into());
},
IseqReturn::Receiver => {
// Just pop arguments and leave the receiver on stack
asm.stack_pop(argc as usize);
}
}

// Let guard chains share the same successor
jump_to_next_insn(jit, asm, ocb);
Expand Down

0 comments on commit 5891c70

Please sign in to comment.