summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJimmy Miller <jimmy.miller@shopify.com>2023-03-01 16:33:16 -0500
committerGitHub <noreply@github.com>2023-03-01 16:33:16 -0500
commitcb8a040b7906c09d9d3ac3d3fe853f633005024f (patch)
treefee2b55370711f58478082960a5d7793413c8380
parenta80906a7147c6e67e5f6c15c2e414126b019b8d6 (diff)
downloadruby-cb8a040b7906c09d9d3ac3d3fe853f633005024f.tar.gz
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.
-rw-r--r--bootstraptest/test_yjit.rb10
-rw-r--r--yjit/src/codegen.rs28
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)
}