ruby-changes:73247
From: Takashi <ko1@a...>
Date: Tue, 30 Aug 2022 01:04:59 +0900 (JST)
Subject: [ruby-changes:73247] 4539c21367 (master): Port gen_send_cfunc to the new backend (https://github.com/Shopify/ruby/pull/357)
https://git.ruby-lang.org/ruby.git/commit/?id=4539c21367 From 4539c21367cf63e34afe4f14656779d33883647b Mon Sep 17 00:00:00 2001 From: Takashi Kokubun <takashikkbn@g...> Date: Thu, 4 Aug 2022 11:47:53 -0700 Subject: Port gen_send_cfunc to the new backend (https://github.com/Shopify/ruby/pull/357) * Port gen_send_cfunc to the new backend * Remove an obsoleted test * Add more cfunc tests * Use csel_e instead and more into() Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...> * Add a missing lea for build_kwargs * Split cfunc test cases Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...> --- bootstraptest/test_yjit.rb | 32 +++++++++ yjit/src/codegen.rs | 162 ++++++++++++++++++++------------------------- 2 files changed, 103 insertions(+), 91 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 2409306106..16eda7fa84 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -3111,3 +3111,35 @@ assert_equal '9001', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L3111 end foo() } + +# opt_send_without_block (VM_METHOD_TYPE_CFUNC) +assert_equal 'nil', %q{ + def foo + nil.inspect # argc: 0 + end + foo +} +assert_equal '4', %q{ + def foo + 2.pow(2) # argc: 1 + end + foo +} +assert_equal 'aba', %q{ + def foo + "abc".tr("c", "a") # argc: 2 + end + foo +} +assert_equal 'true', %q{ + def foo + respond_to?(:inspect) # argc: -1 + end + foo +} +assert_equal '["a", "b"]', %q{ + def foo + "a\nb".lines(chomp: true) # kwargs + end + foo +} diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f08b073e34..c89cfc366f 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1881,10 +1881,10 @@ pub const GET_IVAR_MAX_DEPTH: i32 = 10; https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1881 /* // hashes and arrays pub const OPT_AREF_MAX_CHAIN_DEPTH: i32 = 2; +*/ // up to 5 different classes pub const SEND_MAX_DEPTH: i32 = 5; -*/ // Codegen for setting an instance variable. // Preconditions: @@ -2221,7 +2221,7 @@ fn gen_checktype( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L2221 // Only three types are emitted by compile.c at the moment if let RUBY_T_STRING | RUBY_T_ARRAY | RUBY_T_HASH = type_val { let val_type = ctx.get_opnd_type(StackOpnd(0)); - let val = ctx.stack_pop(1); + let val = asm.load(ctx.stack_pop(1)); // Check if we know from type information match val_type.known_value_type() { @@ -2253,7 +2253,7 @@ fn gen_checktype( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L2253 Opnd::mem(64, val, RUBY_OFFSET_RBASIC_FLAGS), Opnd::UImm(RUBY_T_MASK.into())); asm.cmp(object_type, Opnd::UImm(type_val.into())); - let ret_opnd = asm.csel_e(Opnd::UImm(Qfalse.into()), Opnd::UImm(Qtrue.into())); + let ret_opnd = asm.csel_e(Qtrue.into(), Qfalse.into()); asm.write_label(ret); let stack_ret = ctx.stack_push(Type::UnknownImm); @@ -3851,6 +3851,7 @@ fn jit_thread_s_current( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3851 mov(cb, stack_ret, REG0); true } +*/ // Check if we know how to codegen for a particular cfunc method fn lookup_cfunc_codegen(def: *const rb_method_definition_t) -> Option<MethodGenFn> { @@ -3858,7 +3859,6 @@ fn lookup_cfunc_codegen(def: *const rb_method_definition_t) -> Option<MethodGenF https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3859 CodegenGlobals::look_up_codegen_method(method_serial) } -*/ // Is anyone listening for :c_call and :c_return event currently? fn c_method_tracing_currently_enabled(jit: &JITState) -> bool { @@ -3868,7 +3868,6 @@ fn c_method_tracing_currently_enabled(jit: &JITState) -> bool { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3868 } } -/* // Similar to args_kw_argv_to_hash. It is called at runtime from within the // generated assembly to build a Ruby hash of the passed keyword arguments. The // keys are the Symbol objects associated with the keywords and the values are @@ -3889,7 +3888,7 @@ unsafe extern "C" fn build_kwhash(ci: *const rb_callinfo, sp: *const VALUE) -> V https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3888 fn gen_send_cfunc( jit: &mut JITState, ctx: &mut Context, - cb: &mut CodeBlock, + asm: &mut Assembler, ocb: &mut OutlinedCb, ci: *const rb_callinfo, cme: *const rb_callable_method_entry_t, @@ -3902,7 +3901,7 @@ fn gen_send_cfunc( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3901 // If the function expects a Ruby array of arguments if cfunc_argc < 0 && cfunc_argc != -1 { - gen_counter_incr!(cb, send_cfunc_ruby_array_varg); + gen_counter_incr!(asm, send_cfunc_ruby_array_varg); return CantCompile; } @@ -3923,19 +3922,19 @@ fn gen_send_cfunc( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3922 // If the argument count doesn't match if cfunc_argc >= 0 && cfunc_argc != passed_argc { - gen_counter_incr!(cb, send_cfunc_argc_mismatch); + gen_counter_incr!(asm, send_cfunc_argc_mismatch); return CantCompile; } // Don't JIT functions that need C stack arguments for now - if cfunc_argc >= 0 && passed_argc + 1 > (C_ARG_REGS.len() as i32) { - gen_counter_incr!(cb, send_cfunc_toomany_args); + if cfunc_argc >= 0 && passed_argc + 1 > (C_ARG_OPNDS.len() as i32) { + gen_counter_incr!(asm, send_cfunc_toomany_args); return CantCompile; } if c_method_tracing_currently_enabled(jit) { // Don't JIT if tracing c_call or c_return - gen_counter_incr!(cb, send_cfunc_tracing); + gen_counter_incr!(asm, send_cfunc_tracing); return CantCompile; } @@ -3943,6 +3942,7 @@ fn gen_send_cfunc( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3942 if kw_arg.is_null() { let codegen_p = lookup_cfunc_codegen(unsafe { (*cme).def }); if let Some(known_cfunc_codegen) = codegen_p { + return CantCompile; /* let start_pos = cb.get_write_ptr().raw_ptr() as usize; if known_cfunc_codegen(jit, ctx, cb, ocb, ci, cme, block, argc, recv_known_klass) { let written_bytes = cb.get_write_ptr().raw_ptr() as usize - start_pos; @@ -3955,6 +3955,7 @@ fn gen_send_cfunc( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3955 jump_to_next_insn(jit, ctx, cb, ocb); return EndBlock; } + */ } } @@ -3962,57 +3963,49 @@ fn gen_send_cfunc( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L3963 let side_exit = get_side_exit(jit, ocb, ctx); // Check for interrupts - gen_check_ints(cb, side_exit); + gen_check_ints(asm, side_exit); // Stack overflow check // #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin) // REG_CFP <= REG_SP + 4 * SIZEOF_VALUE + sizeof(rb_control_frame_t) - add_comment(cb, "stack overflow check"); - lea( - cb, - REG0, - ctx.sp_opnd((SIZEOF_VALUE * 4 + 2 * RUBY_SIZEOF_CONTROL_FRAME) as isize), - ); - cmp(cb, REG_CFP, REG0); - jle_ptr(cb, counted_exit!(ocb, side_exit, send_se_cf_overflow)); + asm.comment("stack overflow check"); + let stack_limit = asm.lea(ctx.sp_opnd((SIZEOF_VALUE * 4 + 2 * RUBY_SIZEOF_CONTROL_FRAME) as isize)); + asm.cmp(CFP, stack_limit); + asm.jbe(counted_exit!(ocb, side_exit, send_se_cf_overflow).into()); // Points to the receiver operand on the stack let recv = ctx.stack_opnd(argc); // Store incremented PC into current control frame in case callee raises. - jit_save_pc(jit, cb, REG0); + jit_save_pc(jit, asm); if let Some(block_iseq) = block { // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block(). // VM_CFP_TO_CAPTURED_BLOCK does &cfp->self, rb_captured_block->code.iseq aliases // with cfp->block_code. - jit_mov_gc_ptr(jit, cb, REG0, VALUE(block_iseq as usize)); - let block_code_opnd = mem_opnd(64, REG_CFP, RUBY_OFFSET_CFP_BLOCK_CODE); - mov(cb, block_code_opnd, REG0); + asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), Opnd::UImm(block_iseq as u64)); } // Increment the stack pointer by 3 (in the callee) // sp += 3 - lea(cb, REG0, ctx.sp_opnd((SIZEOF_VALUE as isize) * 3)); + let sp = asm.lea(ctx.sp_opnd((SIZEOF_VALUE as isize) * 3)); // Write method entry at sp[-3] // sp[-3] = me; // Put compile time cme into REG1. It's assumed to be valid because we are notified when // any cme we depend on become outdated. See yjit_method_lookup_change(). - jit_mov_gc_ptr(jit, cb, REG1, VALUE(cme as usize)); - mov(cb, mem_opnd(64, REG0, 8 * -3), REG1); + asm.mov(Opnd::mem(64, sp, 8 * -3), Opnd::UImm(cme as u64)); // Write block handler at sp[-2] // sp[-2] = block_handler; if let Some(_block_iseq) = block { // reg1 = VM_BH_FROM_ISEQ_BLOCK(VM_CFP_TO_CAPTURED_BLOCK(reg_cfp)); - let cfp_self = mem_opnd(64, REG_CFP, RUBY_OFFSET_CFP_SELF); - lea(cb, REG1, cfp_self); - or(cb, REG1, imm_opnd(1)); - mov(cb, mem_opnd(64, REG0, 8 * -2), REG1); + let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); + let block_handler = asm.or(cfp_self, Opnd::Imm(1)); + asm.mov(Opnd::mem(64, sp, 8 * -2), block_handler); } else { - let dst_opnd = mem_opnd(64, REG0, 8 * -2); - mov(cb, dst_opnd, uimm_opnd(VM_BLOCK_HANDLER_NONE.into())); + let dst_opnd = Opnd::mem(64, sp, 8 * -2); + asm.mov(dst_opnd, Opnd::UImm(VM_BLOCK_HANDLER_NONE.into())); } // Write env flags at sp[-1] @@ -4021,11 +4014,12 @@ fn gen_send_cfunc( https://github.com/ruby/r (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/