summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb12
-rw-r--r--test/ruby/test_yjit.rb13
-rw-r--r--yjit_codegen.c36
3 files changed, 60 insertions, 1 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 8cb4432b6a..6d507e17d7 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1210,6 +1210,18 @@ assert_equal 'foo123', %q{
make_str("foo", 123)
}
+# test invokebuiltin as used in struct assignment
+assert_equal '123', %q{
+ def foo(obj)
+ obj.foo = 123
+ end
+
+ struct = Struct.new(:foo)
+ obj = struct.new
+ foo(obj)
+ foo(obj)
+}
+
# test invokebuiltin_delegate as used inside Dir.open
assert_equal '.', %q{
def foo(path)
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb
index 4672e83c9c..ed83bd681a 100644
--- a/test/ruby/test_yjit.rb
+++ b/test/ruby/test_yjit.rb
@@ -310,6 +310,19 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
+ def test_invokebuiltin
+ assert_compiles(<<~RUBY)
+ def foo(obj)
+ obj.foo = 123
+ obj.bar = 123
+ end
+
+ Foo = Struct.new(:foo, :bar)
+ foo(Foo.new(123))
+ foo(Foo.new(123))
+ RUBY
+ end
+
def test_super_iseq
assert_compiles(<<~'RUBY', insns: %i[invokesuper opt_plus opt_mult], result: 15)
class A
diff --git a/yjit_codegen.c b/yjit_codegen.c
index 9f0029712d..c056b1c216 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -4136,6 +4136,39 @@ gen_getblockparamproxy(jitstate_t *jit, ctx_t *ctx)
return YJIT_KEEP_COMPILING;
}
+static codegen_status_t
+gen_invokebuiltin(jitstate_t *jit, ctx_t *ctx)
+{
+ const struct rb_builtin_function *bf = (struct rb_builtin_function *)jit_get_arg(jit, 0);
+
+ if (bf->argc + 2 > NUM_C_ARG_REGS) {
+ return YJIT_CANT_COMPILE;
+ }
+
+ // If the calls don't allocate, do they need up to date PC, SP?
+ jit_prepare_routine_call(jit, ctx, REG0);
+
+ // Call the builtin func (ec, recv, arg1, arg2, ...)
+ mov(cb, C_ARG_REGS[0], REG_EC);
+ mov(cb, C_ARG_REGS[1], member_opnd(REG_CFP, rb_control_frame_t, self));
+
+ // Copy arguments from locals
+ for (int32_t i = 0; i < bf->argc; i++) {
+ x86opnd_t stack_opnd = ctx_stack_opnd(ctx, bf->argc - i - 1);
+ x86opnd_t c_arg_reg = C_ARG_REGS[2 + i];
+ mov(cb, c_arg_reg, stack_opnd);
+ }
+
+ call_ptr(cb, REG0, (void *)bf->func_ptr);
+
+ // Push the return value
+ ctx_stack_pop(ctx, bf->argc);
+ x86opnd_t stack_ret = ctx_stack_push(ctx, TYPE_UNKNOWN);
+ mov(cb, stack_ret, RAX);
+
+ return YJIT_KEEP_COMPILING;
+}
+
// opt_invokebuiltin_delegate calls a builtin function, like
// invokebuiltin does, but instead of taking arguments from the top of the
// stack uses the argument locals (and self) from the current method.
@@ -4145,7 +4178,7 @@ gen_opt_invokebuiltin_delegate(jitstate_t *jit, ctx_t *ctx)
const struct rb_builtin_function *bf = (struct rb_builtin_function *)jit_get_arg(jit, 0);
int32_t start_index = (int32_t)jit_get_arg(jit, 1);
- if (bf->argc + 2 >= NUM_C_ARG_REGS) {
+ if (bf->argc + 2 > NUM_C_ARG_REGS) {
return YJIT_CANT_COMPILE;
}
@@ -4387,6 +4420,7 @@ yjit_init_codegen(void)
yjit_reg_op(BIN(opt_length), gen_opt_length);
yjit_reg_op(BIN(opt_regexpmatch2), gen_opt_regexpmatch2);
yjit_reg_op(BIN(opt_getinlinecache), gen_opt_getinlinecache);
+ yjit_reg_op(BIN(invokebuiltin), gen_invokebuiltin);
yjit_reg_op(BIN(opt_invokebuiltin_delegate), gen_opt_invokebuiltin_delegate);
yjit_reg_op(BIN(opt_invokebuiltin_delegate_leave), gen_opt_invokebuiltin_delegate);
yjit_reg_op(BIN(opt_case_dispatch), gen_opt_case_dispatch);