Skip to content

Commit ecf7095

Browse files
authored
Merge pull request ruby#85 from jhawthorn/invokebuiltin_delegate
Add opt_invokebuiltin_delegate (and opt_invokebuiltin_delegate_leave)
2 parents f0b7c44 + 4afa81d commit ecf7095

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

bootstraptest/test_yjit.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,36 @@ def make_str(foo, bar)
10401040
make_str("foo", 123)
10411041
}
10421042

1043+
# test invokebuiltin_delegate as used inside Dir.open
1044+
assert_equal '.', %q{
1045+
def foo(path)
1046+
Dir.open(path).path
1047+
end
1048+
1049+
foo(".")
1050+
foo(".")
1051+
}
1052+
1053+
# test invokebuiltin_delegate_leave in method called from jit
1054+
assert_normal_exit %q{
1055+
def foo(obj)
1056+
obj.clone
1057+
end
1058+
1059+
foo(Object.new)
1060+
foo(Object.new)
1061+
}
1062+
1063+
# test invokebuiltin_delegate_leave in method called from cfunc
1064+
assert_normal_exit %q{
1065+
def foo(obj)
1066+
[obj].map(&:clone)
1067+
end
1068+
1069+
foo(Object.new)
1070+
foo(Object.new)
1071+
}
1072+
10431073
# getlocal with 2 levels
10441074
assert_equal '7', %q{
10451075
def foo(foo, bar)

yjit_codegen.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,6 +3166,56 @@ gen_getblockparamproxy(jitstate_t *jit, ctx_t *ctx)
31663166
return YJIT_KEEP_COMPILING;
31673167
}
31683168

3169+
// opt_invokebuiltin_delegate calls a builtin function, like
3170+
// invokebuiltin does, but instead of taking arguments from the top of the
3171+
// stack uses the argument locals (and self) from the current method.
3172+
static codegen_status_t
3173+
gen_opt_invokebuiltin_delegate(jitstate_t *jit, ctx_t *ctx)
3174+
{
3175+
const struct rb_builtin_function *bf = (struct rb_builtin_function *)jit_get_arg(jit, 0);
3176+
int32_t start_index = (int32_t)jit_get_arg(jit, 1);
3177+
3178+
if (bf->argc + 2 >= NUM_C_ARG_REGS) {
3179+
return YJIT_CANT_COMPILE;
3180+
}
3181+
3182+
// If the calls don't allocate, do they need up to date PC, SP?
3183+
jit_save_pc(jit, REG0);
3184+
jit_save_sp(jit, ctx);
3185+
3186+
// Save YJIT registers
3187+
yjit_save_regs(cb);
3188+
3189+
if (bf->argc > 0) {
3190+
// Load environment pointer EP from CFP
3191+
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, ep));
3192+
}
3193+
3194+
// Save self from CFP
3195+
mov(cb, REG1, member_opnd(REG_CFP, rb_control_frame_t, self));
3196+
3197+
// Call the builtin func (ec, recv, arg1, arg2, ...)
3198+
mov(cb, C_ARG_REGS[0], REG_EC); // clobbers REG_CFP
3199+
mov(cb, C_ARG_REGS[1], REG1); // self, clobbers REG_EC
3200+
3201+
// Copy arguments from locals
3202+
for (int32_t i = 0; i < bf->argc; i++) {
3203+
const int32_t offs = -jit->iseq->body->local_table_size - VM_ENV_DATA_SIZE + 1 + start_index + i;
3204+
x86opnd_t local_opnd = mem_opnd(64, REG0, offs * SIZEOF_VALUE);
3205+
x86opnd_t c_arg_reg = C_ARG_REGS[i + 2];
3206+
mov(cb, c_arg_reg, local_opnd);
3207+
}
3208+
call_ptr(cb, REG0, (void *)bf->func_ptr);
3209+
3210+
// Load YJIT registers
3211+
yjit_load_regs(cb);
3212+
3213+
// Push the return value
3214+
x86opnd_t stack_ret = ctx_stack_push(ctx, TYPE_UNKNOWN);
3215+
mov(cb, stack_ret, RAX);
3216+
3217+
return YJIT_KEEP_COMPILING;
3218+
}
31693219

31703220
static void
31713221
yjit_reg_op(int opcode, codegen_fn gen_fn)
@@ -3237,6 +3287,8 @@ yjit_init_codegen(void)
32373287
yjit_reg_op(BIN(opt_str_uminus), gen_opt_str_uminus);
32383288
yjit_reg_op(BIN(opt_not), gen_opt_not);
32393289
yjit_reg_op(BIN(opt_getinlinecache), gen_opt_getinlinecache);
3290+
yjit_reg_op(BIN(opt_invokebuiltin_delegate), gen_opt_invokebuiltin_delegate);
3291+
yjit_reg_op(BIN(opt_invokebuiltin_delegate_leave), gen_opt_invokebuiltin_delegate);
32403292
yjit_reg_op(BIN(branchif), gen_branchif);
32413293
yjit_reg_op(BIN(branchunless), gen_branchunless);
32423294
yjit_reg_op(BIN(branchnil), gen_branchnil);

0 commit comments

Comments
 (0)