summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb20
-rw-r--r--yjit.c1
-rw-r--r--yjit_codegen.c28
3 files changed, 42 insertions, 7 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 47744efb73..a52ed8027d 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -2766,3 +2766,23 @@ assert_equal 'ok', %q{
foo(s) rescue :ok
foo(s) rescue :ok
}
+
+# File.join is a cfunc accepting variable arguments as a Ruby array (argc = -2)
+assert_equal 'foo/bar', %q{
+ def foo
+ File.join("foo", "bar")
+ end
+
+ foo
+ foo
+}
+
+# File.join is a cfunc accepting variable arguments as a Ruby array (argc = -2)
+assert_equal '', %q{
+ def foo
+ File.join()
+ end
+
+ foo
+ foo
+}
diff --git a/yjit.c b/yjit.c
index e4fa84d510..39ade5f1e2 100644
--- a/yjit.c
+++ b/yjit.c
@@ -69,7 +69,6 @@ YJIT_DECLARE_COUNTERS(
send_missing_method,
send_bmethod,
send_refined_method,
- send_cfunc_ruby_array_varg,
send_cfunc_argc_mismatch,
send_cfunc_toomany_args,
send_cfunc_tracing,
diff --git a/yjit_codegen.c b/yjit_codegen.c
index c9a4404550..c8de630747 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -3244,12 +3244,6 @@ gen_send_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const
{
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
- // If the function expects a Ruby array of arguments
- if (cfunc->argc < 0 && cfunc->argc != -1) {
- GEN_COUNTER_INC(cb, send_cfunc_ruby_array_varg);
- return YJIT_CANT_COMPILE;
- }
-
// If the argument count doesn't match
if (cfunc->argc >= 0 && cfunc->argc != argc) {
GEN_COUNTER_INC(cb, send_cfunc_argc_mismatch);
@@ -3402,6 +3396,28 @@ gen_send_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const
lea(cb, C_ARG_REGS[1], ctx_stack_opnd(ctx, argc - 1));
mov(cb, C_ARG_REGS[2], ctx_stack_opnd(ctx, argc));
}
+ // Variadic method with Ruby array
+ if (cfunc->argc == -2) {
+ // Create a Ruby array from the arguments.
+ //
+ // This follows similar behaviour to vm_call_cfunc_with_frame() and
+ // call_cfunc_m2(). We use rb_ec_ary_new_from_values() instead of
+ // rb_ary_new4() since we have REG_EC available.
+ //
+ // Before getting here we will have set the new CFP in the EC, and the
+ // stack at CFP's SP will contain the values we are inserting into the
+ // Array, so they will be properly marked if we hit a GC.
+
+ // rb_ec_ary_new_from_values(rb_execution_context_t *ec, long n, const VLAUE *elts)
+ mov(cb, C_ARG_REGS[0], REG_EC);
+ mov(cb, C_ARG_REGS[1], imm_opnd(argc));
+ lea(cb, C_ARG_REGS[2], ctx_stack_opnd(ctx, argc - 1));
+ call_ptr(cb, REG0, (void *)rb_ec_ary_new_from_values);
+
+ // rb_file_s_join(VALUE recv, VALUE args)
+ mov(cb, C_ARG_REGS[0], ctx_stack_opnd(ctx, argc));
+ mov(cb, C_ARG_REGS[1], RAX);
+ }
// Pop the C function arguments from the stack (in the caller)
ctx_stack_pop(ctx, argc + 1);