From cb8a040b7906c09d9d3ac3d3fe853f633005024f Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Wed, 1 Mar 2023 16:33:16 -0500 Subject: YJIT: Properly deal with cfunc splat when no args needed (#7413) Related to: https://github.com/ruby/ruby/pull/7377 Previously it was believed that there was a problem with a combination of cfuncs + splat + send, but it turns out the same issue happened without send. For example `Integer.sqrt(1, *[])`. The issue was happened not because of send, but because of setting the wrong argc when we don't need to splat any args. --- bootstraptest/test_yjit.rb | 10 ++++++++++ yjit/src/codegen.rs | 28 +++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 68b738477b..568cc583b1 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -3584,3 +3584,13 @@ assert_equal 'true', %q{ assert_equal 'true', %q{ 1.send(:==, 1, *[]) } + +# Test send with splat to a cfunc +assert_equal '2', %q{ + def foo + Integer.sqrt(4, *[]) + end + # call twice to deal with constant exiting + foo + foo +} diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index d659e87374..9d4f51f8df 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4864,23 +4864,29 @@ fn gen_send_cfunc( // push_splat_args does stack manipulation so we can no longer side exit if flags & VM_CALL_ARGS_SPLAT != 0 { - if flags & VM_CALL_OPT_SEND != 0 { - // FIXME: This combination is buggy. - // For example `1.send(:==, 1, *[])` fails to adjust the stack properly - gen_counter_incr!(asm, send_cfunc_splat_send); - return CantCompile; - } let required_args : u32 = (cfunc_argc as u32).saturating_sub(argc as u32 - 1); // + 1 because we pass self if required_args + 1 >= C_ARG_OPNDS.len() as u32 { gen_counter_incr!(asm, send_cfunc_toomany_args); return CantCompile; } - // We are going to assume that the splat fills - // all the remaining arguments. In the generated code - // we test if this is true and if not side exit. - argc = required_args as i32; - passed_argc = argc; + + if required_args == 0 { + // The splat is not going to supply us any arguments + // so we set the argc to the number of arguments + // the cfunc expects. + // We still need to do all the splat logic because someone + // could pass in a non-zero sized array and we need to + // exit to error. + argc = cfunc_argc; + passed_argc = argc; + } else { + // We are going to assume that the splat fills + // all the remaining arguments. In the generated code + // we test if this is true and if not side exit. + argc = required_args as i32; + passed_argc = argc; + } push_splat_args(required_args, ctx, asm, ocb, side_exit) } -- cgit v1.2.1