diff options
author | Takashi Kokubun <takashikkbn@gmail.com> | 2022-08-04 11:47:53 -0700 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2022-08-29 08:47:06 -0700 |
commit | 4539c21367cf63e34afe4f14656779d33883647b (patch) | |
tree | d43eaf33d50de75c9111b993eff154f9d67ec7a1 | |
parent | c91a44cba49c8164982f241c00dc7c6418a4757b (diff) | |
download | ruby-4539c21367cf63e34afe4f14656779d33883647b.tar.gz |
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@gmail.com>
* Add a missing lea for build_kwargs
* Split cfunc test cases
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
-rw-r--r-- | bootstraptest/test_yjit.rb | 32 | ||||
-rw-r--r-- | 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{ 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; /* // 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( // 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( 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( 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 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 { } } -/* // 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 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( // 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( // 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( 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( jump_to_next_insn(jit, ctx, cb, ocb); return EndBlock; } + */ } } @@ -3962,57 +3963,49 @@ fn gen_send_cfunc( 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( if !kw_arg.is_null() { frame_type |= VM_FRAME_FLAG_CFRAME_KW } - mov(cb, mem_opnd(64, REG0, 8 * -1), uimm_opnd(frame_type.into())); + asm.mov(Opnd::mem(64, sp, 8 * -1), Opnd::UImm(frame_type.into())); // Allocate a new CFP (ec->cfp--) - let ec_cfp_opnd = mem_opnd(64, REG_EC, RUBY_OFFSET_EC_CFP); - sub(cb, ec_cfp_opnd, uimm_opnd(RUBY_SIZEOF_CONTROL_FRAME as u64)); + let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); + let new_cfp = asm.sub(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); + asm.store(ec_cfp_opnd, new_cfp); // Setup the new frame // *cfp = (const struct rb_control_frame_struct) { @@ -4039,22 +4033,15 @@ fn gen_send_cfunc( // }; // Can we re-use ec_cfp_opnd from above? - let ec_cfp_opnd = mem_opnd(64, REG_EC, RUBY_OFFSET_EC_CFP); - mov(cb, REG1, ec_cfp_opnd); - mov(cb, mem_opnd(64, REG1, RUBY_OFFSET_CFP_PC), imm_opnd(0)); - - mov(cb, mem_opnd(64, REG1, RUBY_OFFSET_CFP_SP), REG0); - mov(cb, mem_opnd(64, REG1, RUBY_OFFSET_CFP_ISEQ), imm_opnd(0)); - mov( - cb, - mem_opnd(64, REG1, RUBY_OFFSET_CFP_BLOCK_CODE), - imm_opnd(0), - ); - mov(cb, mem_opnd(64, REG1, RUBY_OFFSET_CFP_BP), REG0); - sub(cb, REG0, uimm_opnd(SIZEOF_VALUE as u64)); - mov(cb, mem_opnd(64, REG1, RUBY_OFFSET_CFP_EP), REG0); - mov(cb, REG0, recv); - mov(cb, mem_opnd(64, REG1, RUBY_OFFSET_CFP_SELF), REG0); + let ec_cfp_opnd = asm.load(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP)); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_PC), Opnd::Imm(0)); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SP), sp); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_ISEQ), Opnd::Imm(0)); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_BLOCK_CODE), Opnd::Imm(0)); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_BP), sp); + let ep = asm.sub(sp, Opnd::UImm(SIZEOF_VALUE as u64)); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_EP), ep); + asm.mov(Opnd::mem(64, ec_cfp_opnd, RUBY_OFFSET_CFP_SELF), recv); /* // Verify that we are calling the right function @@ -4070,71 +4057,66 @@ fn gen_send_cfunc( if !kw_arg.is_null() { // Build a hash from all kwargs passed - jit_mov_gc_ptr(jit, cb, C_ARG_REGS[0], VALUE(ci as usize)); - lea(cb, C_ARG_REGS[1], ctx.sp_opnd(0)); - call_ptr(cb, REG0, build_kwhash as *const u8); + asm.comment("build_kwhash"); + let sp = asm.lea(ctx.sp_opnd(0)); + let kwargs = asm.ccall(build_kwhash as *const u8, vec![Opnd::UImm(ci as u64), sp]); // Replace the stack location at the start of kwargs with the new hash let stack_opnd = ctx.stack_opnd(argc - passed_argc); - mov(cb, stack_opnd, RAX); + asm.mov(stack_opnd, kwargs); } - // Copy SP into RAX because REG_SP will get overwritten - lea(cb, RAX, ctx.sp_opnd(0)); + // Copy SP because REG_SP will get overwritten + let sp = asm.lea(ctx.sp_opnd(0)); // Pop the C function arguments from the stack (in the caller) ctx.stack_pop((argc + 1).try_into().unwrap()); // Write interpreter SP into CFP. // Needed in case the callee yields to the block. - gen_save_sp(cb, ctx); + gen_save_sp(jit, asm, ctx); // Non-variadic method - if cfunc_argc >= 0 { + let args = if cfunc_argc >= 0 { // Copy the arguments from the stack to the C argument registers // self is the 0th argument and is at index argc from the stack top - for i in 0..=passed_argc as usize { - let stack_opnd = mem_opnd(64, RAX, -(argc + 1 - (i as i32)) * SIZEOF_VALUE_I32); - let c_arg_reg = C_ARG_REGS[i]; - mov(cb, c_arg_reg, stack_opnd); - } + (0..=passed_argc).map(|i| + Opnd::mem(64, sp, -(argc + 1 - (i as i32)) * SIZEOF_VALUE_I32) + ).collect() } - // Variadic method - if cfunc_argc == -1 { + else if cfunc_argc == -1 { // The method gets a pointer to the first argument // rb_f_puts(int argc, VALUE *argv, VALUE recv) - mov(cb, C_ARG_REGS[0], imm_opnd(passed_argc.into())); - lea( - cb, - C_ARG_REGS[1], - mem_opnd(64, RAX, -(argc) * SIZEOF_VALUE_I32), - ); - mov( - cb, - C_ARG_REGS[2], - mem_opnd(64, RAX, -(argc + 1) * SIZEOF_VALUE_I32), - ); + vec![ + Opnd::Imm(passed_argc.into()), + asm.lea(Opnd::mem(64, sp, -(argc) * SIZEOF_VALUE_I32)), + Opnd::mem(64, sp, -(argc + 1) * SIZEOF_VALUE_I32), + ] } + else { + panic!("unexpected cfunc_args: {}", cfunc_argc) + }; // Call the C function // VALUE ret = (cfunc->func)(recv, argv[0], argv[1]); // cfunc comes from compile-time cme->def, which we assume to be stable. // Invalidation logic is in yjit_method_lookup_change() - add_comment(cb, "call C function"); - call_ptr(cb, REG0, unsafe { get_mct_func(cfunc) }); + asm.comment("call C function"); + let ret = asm.ccall(unsafe { get_mct_func(cfunc) }, args); // Record code position for TracePoint patching. See full_cfunc_return(). - record_global_inval_patch(cb, CodegenGlobals::get_outline_full_cfunc_return_pos()); + record_global_inval_patch(asm, CodegenGlobals::get_outline_full_cfunc_return_pos()); // Push the return value on the Ruby stack let stack_ret = ctx.stack_push(Type::Unknown); - mov(cb, stack_ret, RAX); + asm.mov(stack_ret, ret); // Pop the stack frame (ec->cfp++) // Can we reuse ec_cfp_opnd from above? - let ec_cfp_opnd = mem_opnd(64, REG_EC, RUBY_OFFSET_EC_CFP); - add(cb, ec_cfp_opnd, uimm_opnd(RUBY_SIZEOF_CONTROL_FRAME as u64)); + let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP); + let new_cfp = asm.add(ec_cfp_opnd, Opnd::UImm(RUBY_SIZEOF_CONTROL_FRAME as u64)); + asm.store(ec_cfp_opnd, new_cfp); // cfunc calls may corrupt types ctx.clear_local_types(); @@ -4144,10 +4126,11 @@ fn gen_send_cfunc( // Jump (fall through) to the call continuation block // We do this to end the current block after the call - jump_to_next_insn(jit, ctx, cb, ocb); + jump_to_next_insn(jit, ctx, asm, ocb); EndBlock } +/* fn gen_return_branch( cb: &mut CodeBlock, target0: CodePtr, @@ -4852,12 +4835,10 @@ fn gen_send_general( // Points to the receiver operand on the stack let recv = ctx.stack_opnd(argc); let recv_opnd = StackOpnd(argc.try_into().unwrap()); - // TODO: Resurrect this once jit_guard_known_klass is implemented for getivar - /* jit_guard_known_klass( jit, ctx, - cb, + asm, ocb, comptime_recv_klass, recv, @@ -4865,7 +4846,7 @@ fn gen_send_general( comptime_recv, SEND_MAX_DEPTH, side_exit, - ); */ + ); // Do method lookup let mut cme = unsafe { rb_callable_method_entry(comptime_recv_klass, mid) }; @@ -4911,7 +4892,6 @@ fn gen_send_general( return CantCompile; // return gen_send_iseq(jit, ctx, cb, ocb, ci, cme, block, argc); } VM_METHOD_TYPE_CFUNC => { - return CantCompile; /* return gen_send_cfunc( jit, ctx, @@ -4922,7 +4902,7 @@ fn gen_send_general( block, argc, &comptime_recv_klass, - ); */ + ); } VM_METHOD_TYPE_IVAR => { if argc != 0 { |