ruby-changes:74323
From: Takashi <ko1@a...>
Date: Thu, 3 Nov 2022 06:20:47 +0900 (JST)
Subject: [ruby-changes:74323] 81e84e0a4d (master): YJIT: Support invokeblock (#6640)
https://git.ruby-lang.org/ruby.git/commit/?id=81e84e0a4d From 81e84e0a4d348309d5d38311d283d049ffeeb7a2 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun <takashikkbn@g...> Date: Wed, 2 Nov 2022 09:30:48 -0700 Subject: YJIT: Support invokeblock (#6640) * YJIT: Support invokeblock * Update yjit/src/backend/arm64/mod.rs * Update yjit/src/codegen.rs Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...> --- yjit.c | 14 +++- yjit.rb | 1 + yjit/bindgen/src/main.rs | 5 +- yjit/src/backend/arm64/mod.rs | 8 +-- yjit/src/backend/x86_64/mod.rs | 1 + yjit/src/codegen.rs | 150 +++++++++++++++++++++++++++++++++++------ yjit/src/core.rs | 10 +++ yjit/src/cruby.rs | 20 +++++- yjit/src/cruby_bindings.inc.rs | 11 ++- yjit/src/stats.rs | 8 +++ 10 files changed, 198 insertions(+), 30 deletions(-) diff --git a/yjit.c b/yjit.c index 1694e1edd7..b943277d61 100644 --- a/yjit.c +++ b/yjit.c @@ -626,6 +626,12 @@ rb_get_iseq_body_stack_max(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/yjit.c#L626 return iseq->body->stack_max; } +bool +rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_lead; +} + bool rb_get_iseq_flags_has_opt(const rb_iseq_t *iseq) { @@ -669,7 +675,13 @@ rb_get_iseq_flags_has_block(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/yjit.c#L675 } bool -rb_get_iseq_flags_has_accepts_no_kwarg(const rb_iseq_t *iseq) +rb_get_iseq_flags_ambiguous_param0(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.ambiguous_param0; +} + +bool +rb_get_iseq_flags_accepts_no_kwarg(const rb_iseq_t *iseq) { return iseq->body->param.flags.accepts_no_kwarg; } diff --git a/yjit.rb b/yjit.rb index ac49a30e90..bb344948eb 100644 --- a/yjit.rb +++ b/yjit.rb @@ -187,6 +187,7 @@ module RubyVM::YJIT https://github.com/ruby/ruby/blob/trunk/yjit.rb#L187 $stderr.puts("***YJIT: Printing YJIT statistics on exit***") print_counters(stats, prefix: 'send_', prompt: 'method call exit reasons: ') + print_counters(stats, prefix: 'invokeblock_', prompt: 'invokeblock exit reasons: ') print_counters(stats, prefix: 'invokesuper_', prompt: 'invokesuper exit reasons: ') print_counters(stats, prefix: 'leave_', prompt: 'leave exit reasons: ') print_counters(stats, prefix: 'gbpp_', prompt: 'getblockparamproxy exit reasons: ') diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 21aaec84cb..167ab2a74f 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -250,6 +250,7 @@ fn main() { https://github.com/ruby/ruby/blob/trunk/yjit/bindgen/src/main.rs#L250 .blocklist_type("rb_control_frame_struct") .opaque_type("rb_control_frame_struct") .allowlist_function("rb_vm_bh_to_procval") + .allowlist_function("rb_vm_ep_local_ep") .allowlist_type("vm_special_object_type") .allowlist_var("VM_ENV_DATA_INDEX_SPECVAL") .allowlist_var("VM_ENV_DATA_INDEX_FLAGS") @@ -353,13 +354,15 @@ fn main() { https://github.com/ruby/ruby/blob/trunk/yjit/bindgen/src/main.rs#L354 .allowlist_function("rb_get_iseq_body_parent_iseq") .allowlist_function("rb_get_iseq_body_iseq_encoded") .allowlist_function("rb_get_iseq_body_stack_max") + .allowlist_function("rb_get_iseq_flags_has_lead") .allowlist_function("rb_get_iseq_flags_has_opt") .allowlist_function("rb_get_iseq_flags_has_kw") .allowlist_function("rb_get_iseq_flags_has_rest") .allowlist_function("rb_get_iseq_flags_has_post") .allowlist_function("rb_get_iseq_flags_has_kwrest") .allowlist_function("rb_get_iseq_flags_has_block") - .allowlist_function("rb_get_iseq_flags_has_accepts_no_kwarg") + .allowlist_function("rb_get_iseq_flags_ambiguous_param0") + .allowlist_function("rb_get_iseq_flags_accepts_no_kwarg") .allowlist_function("rb_get_iseq_flags_ruby2_keywords") .allowlist_function("rb_get_iseq_body_local_table_size") .allowlist_function("rb_get_iseq_body_param_keyword") diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 0c784c0bea..ce1dd2e43c 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -165,8 +165,8 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L165 Opnd::Reg(_) | Opnd::InsnOut { .. } => opnd, Opnd::Mem(_) => split_load_operand(asm, opnd), Opnd::Imm(imm) => { - if imm <= 0 { - asm.load(opnd) + if imm == 0 { + Opnd::Reg(XZR_REG) } else if (dest_num_bits == 64 && BitmaskImmediate::try_from(imm as u64).is_ok()) || (dest_num_bits == 32 && @@ -1352,8 +1352,8 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L1352 asm.test(Opnd::Reg(X0_REG), Opnd::Imm(-7)); asm.compile_with_num_regs(&mut cb, 1); - // Assert that a load and a test instruction were written. - assert_eq!(8, cb.get_write_pos()); + // Assert that a test instruction is written. + assert_eq!(4, cb.get_write_pos()); } #[test] diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index ac5ac0fff4..dc5f21221d 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -93,6 +93,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L93 vec![ RAX_REG, RCX_REG, + RDX_REG, ] } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index de0a2dec0f..9ec6c26f89 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4011,6 +4011,7 @@ enum SpecVal { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L4011 BlockISeq(IseqPtr), BlockParamProxy, PrevEP(*const VALUE), + PrevEPOpnd(Opnd), } struct ControlFrame { @@ -4050,17 +4051,6 @@ fn gen_push_frame( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L4051 let sp = frame.sp; - let num_locals = frame.local_size; - if num_locals > 0 { - asm.comment("initialize locals"); - - // Initialize local variables to Qnil - for i in 0..num_locals { - let offs = (SIZEOF_VALUE as i32) * (i - num_locals - 3); - asm.store(Opnd::mem(64, sp, offs), Qnil.into()); - } - } - asm.comment("push cme, specval, frame type"); // Write method entry at sp[-3] @@ -4099,9 +4089,25 @@ fn gen_push_frame( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L4089 let tagged_prev_ep = (prev_ep as usize) | 1; VALUE(tagged_prev_ep).into() } + SpecVal::PrevEPOpnd(ep_opnd) => { + asm.or(ep_opnd, 1.into()) + }, }; asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -2), specval); + // Arm requires another register to load the immediate value of Qnil before storing it. + // So doing this after releasing the register for specval to avoid register spill. + let num_locals = frame.local_size; + if num_locals > 0 { + asm.comment("initialize locals"); + + // Initialize local variables to Qnil + for i in 0..num_locals { + let offs = (SIZEOF_VALUE as i32) * (i - num_locals - 3); + asm.store(Opnd::mem(64, sp, offs), Qnil.into()); + } + } + // Write env flags at sp[-1] // sp[-1] = frame_type; asm.store(Opnd::mem(64, sp, SIZEOF_VALUE_I32 * -1), frame.frame_type.into()); @@ -4522,7 +4528,7 @@ fn gen_send_bmethod( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L4528 } let frame_type = VM_FRAME_MAGIC_BLOCK | VM_FRAME_FLAG_BMETHOD | VM_FRAME_FLAG_LAMBDA; - gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, flags, argc) + gen_send_iseq(jit, ctx, asm, ocb, iseq, ci, frame_type, Some(capture.ep), cme, block, flags, argc, None) } fn gen_send_iseq( @@ -4538,10 +4544,10 @@ fn gen_send_iseq( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L4544 block: Option<IseqPtr>, flags: u32, argc: i32, + captured_opnd: Option<Opnd>, ) -> CodegenStatus { let mut argc = argc; - // Create a side-exit to fall back to the interpreter let side_exit = get_side_exit(jit, ocb, ctx); @@ -4592,7 +4598,7 @@ fn gen_send_iseq( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L4598 // If we have a method accepting no kwargs (**nil), exit if we have passed // it any kwargs. - if supplying_kws && unsafe { get_iseq_flags_has_accepts_no_kwarg(iseq) } { + if supplying_kws && unsafe { get_iseq_flags_accepts_no_kwarg(iseq) } { gen_counter_incr!(asm, send_iseq_complex_callee); return CantCompile; } @@ -4997,12 +5003,17 @@ fn gen_send_iseq( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L5003 asm.mov(ctx.stack_opnd(-1), unspec_opnd.into()); } - // Points to the receiver operand on the stack - let recv = ctx.stack_opnd(argc); + // Points to the receiver operand on the stack unless a captured environment is used + let recv = match captured_opnd { + Some(captured_opnd) => asm.load(Opnd::mem(64, captured_opnd, 0)), // captured->self + _ => ctx.stack_opnd(argc), + }; + let captured_self = captured_opnd.is_some(); + let sp_offset = (argc as isize) + if captured_self { 0 } else { 1 }; // Store the updated SP on the current frame (pop arguments and receiver) asm.comment("store caller sp"); - let caller_sp = asm.lea(ctx.sp_opnd((SIZEOF_VALUE as isize) * -((argc as isize) + 1))); + let caller_sp = asm.lea(ctx.sp_opnd((SIZEO (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/