aboutsummaryrefslogtreecommitdiffstats
path: root/yjit_codegen.c
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2021-03-22 20:12:34 -0400
committerAlan Wu <XrXr@users.noreply.github.com>2021-10-20 18:19:32 -0400
commita8f7eb2f3566194ead1d5aa541cec32b968855cf (patch)
tree0b2a69920faf2c9242ed8e8c219983caa3edbd4a /yjit_codegen.c
parentec1cbbb07d00828e6265074ca4977a8dae6b8b29 (diff)
downloadruby-a8f7eb2f3566194ead1d5aa541cec32b968855cf.tar.gz
Polymorphic opt_send_without_block
Diffstat (limited to 'yjit_codegen.c')
-rw-r--r--yjit_codegen.c159
1 files changed, 80 insertions, 79 deletions
diff --git a/yjit_codegen.c b/yjit_codegen.c
index bd3ab5543c..b9d84a7570 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -96,7 +96,11 @@ jit_peek_at_stack(jitstate_t* jit, ctx_t* ctx, int n)
{
RUBY_ASSERT(jit_at_current_insn(jit));
- VALUE *sp = jit->ec->cfp->sp + ctx->sp_offset;
+ // Note: this does not account for ctx->sp_offset because
+ // this is only available when hitting a stub, and while
+ // hitting a stub, cfp->sp needs to be up to date in case
+ // codegen functions trigger GC. See :stub-sp-flush:.
+ VALUE *sp = jit->ec->cfp->sp;
return *(sp - 1 - n);
}
@@ -664,6 +668,7 @@ bool rb_iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_i
enum {
GETIVAR_MAX_DEPTH = 10, // up to 5 different classes, and embedded or not for each
OPT_AREF_MAX_CHAIN_DEPTH = 2, // hashes and arrays
+ OSWB_MAX_DEPTH = 5, // up to 5 different classes
};
static codegen_status_t
@@ -1346,15 +1351,13 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c
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)
- {
+ if (cfunc->argc < 0 && cfunc->argc != -1) {
GEN_COUNTER_INC(cb, oswb_cfunc_ruby_array_varg);
return YJIT_CANT_COMPILE;
}
// If the argument count doesn't match
- if (cfunc->argc >= 0 && cfunc->argc != argc)
- {
+ if (cfunc->argc >= 0 && cfunc->argc != argc) {
GEN_COUNTER_INC(cb, oswb_cfunc_argc_mismatch);
return YJIT_CANT_COMPILE;
}
@@ -1373,38 +1376,6 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c
// Points to the receiver operand on the stack
x86opnd_t recv = ctx_stack_opnd(ctx, argc);
- mov(cb, REG0, recv);
-
- // Callee method ID
- //ID mid = vm_ci_mid(cd->ci);
- //printf("JITting call to C function \"%s\", argc: %lu\n", rb_id2name(mid), argc);
- //print_str(cb, "");
- //print_str(cb, "calling CFUNC:");
- //print_str(cb, rb_id2name(mid));
- //print_str(cb, "recv");
- //print_ptr(cb, recv);
-
- // Check that the receiver is a heap object
- {
- uint8_t *receiver_not_heap = COUNTED_EXIT(side_exit, oswb_se_receiver_not_heap);
- test(cb, REG0, imm_opnd(RUBY_IMMEDIATE_MASK));
- jnz_ptr(cb, receiver_not_heap);
- cmp(cb, REG0, imm_opnd(Qfalse));
- je_ptr(cb, receiver_not_heap);
- cmp(cb, REG0, imm_opnd(Qnil));
- je_ptr(cb, receiver_not_heap);
- }
-
- // Pointer to the klass field of the receiver &(recv->klass)
- x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
-
- // FIXME: This leaks when st_insert raises NoMemoryError
- assume_method_lookup_stable(cd->cc->klass, cme, jit->block);
-
- // Bail if receiver class is different from compile-time call cache class
- jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass);
- cmp(cb, klass_opnd, REG1);
- jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
// Store incremented PC into current control frame in case callee raises.
mov(cb, REG0, const_ptr_opnd(jit->pc + insn_len(BIN(opt_send_without_block))));
@@ -1545,9 +1516,14 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c
);
}
+ // TODO: gen_oswb_iseq() jumps to the next instruction with ctx->sp_offset == 0
+ // after the call, while this does not. This difference prevents
+ // the two call types from sharing the same successor.
+
// Jump (fall through) to the call continuation block
// We do this to end the current block after the call
- blockid_t cont_block = { jit->iseq, jit_next_idx(jit) };
+ blockid_t cont_block = { jit->iseq, jit_next_insn_idx(jit) };
+ ctx->chain_depth = 0;
gen_direct_jump(
ctx,
cont_block
@@ -1609,35 +1585,6 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca
// Points to the receiver operand on the stack
x86opnd_t recv = ctx_stack_opnd(ctx, argc);
- mov(cb, REG0, recv);
-
- // Callee method ID
- //ID mid = vm_ci_mid(cd->ci);
- //printf("JITting call to Ruby function \"%s\", argc: %d\n", rb_id2name(mid), argc);
- //print_str(cb, "");
- //print_str(cb, "recv");
- //print_ptr(cb, recv);
-
- // Check that the receiver is a heap object
- {
- uint8_t *receiver_not_heap = COUNTED_EXIT(side_exit, oswb_se_receiver_not_heap);
- test(cb, REG0, imm_opnd(RUBY_IMMEDIATE_MASK));
- jnz_ptr(cb, receiver_not_heap);
- cmp(cb, REG0, imm_opnd(Qfalse));
- je_ptr(cb, receiver_not_heap);
- cmp(cb, REG0, imm_opnd(Qnil));
- je_ptr(cb, receiver_not_heap);
- }
-
- // Pointer to the klass field of the receiver &(recv->klass)
- x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
-
- assume_method_lookup_stable(cd->cc->klass, cme, jit->block);
-
- // Bail if receiver class is different from compile-time call cache class
- jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass);
- cmp(cb, klass_opnd, REG1);
- jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
if (METHOD_ENTRY_VISI(cme) == METHOD_VISI_PROTECTED) {
// Generate ancestry guard for protected callee.
@@ -1717,6 +1664,7 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca
ctx_stack_pop(&return_ctx, argc + 1);
ctx_stack_push(&return_ctx, T_NONE);
return_ctx.sp_offset = 0;
+ return_ctx.chain_depth = 0;
// Write the JIT return address on the callee frame
gen_branch(
@@ -1749,39 +1697,92 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
// Relevant definitions:
// rb_execution_context_t : vm_core.h
// invoker, cfunc logic : method.h, vm_method.c
+ // rb_callinfo : vm_callinfo.h
// rb_callable_method_entry_t : method.h
// vm_call_cfunc_with_frame : vm_insnhelper.c
- // rb_callcache : vm_callinfo.h
- struct rb_call_data * cd = (struct rb_call_data *)jit_get_arg(jit, 0);
+ struct rb_call_data *cd = (struct rb_call_data *)jit_get_arg(jit, 0);
+ const struct rb_callinfo *ci = cd->ci; // info about the call site
+
int32_t argc = (int32_t)vm_ci_argc(cd->ci);
+ ID mid = vm_ci_mid(cd->ci);
// Don't JIT calls with keyword splat
- if (vm_ci_flag(cd->ci) & VM_CALL_KW_SPLAT) {
+ if (vm_ci_flag(ci) & VM_CALL_KW_SPLAT) {
GEN_COUNTER_INC(cb, oswb_kw_splat);
return YJIT_CANT_COMPILE;
}
// Don't JIT calls that aren't simple
- if (!(vm_ci_flag(cd->ci) & VM_CALL_ARGS_SIMPLE)) {
+ if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)) {
GEN_COUNTER_INC(cb, oswb_callsite_not_simple);
return YJIT_CANT_COMPILE;
}
- // Don't JIT if the inline cache is not set
- if (!cd->cc || !cd->cc->klass) {
- GEN_COUNTER_INC(cb, oswb_ic_empty);
- return YJIT_CANT_COMPILE;
+ // Defer compilation so we can specialize on class of receiver
+ if (!jit_at_current_insn(jit)) {
+ defer_compilation(jit->block, jit->insn_idx, ctx);
+ return YJIT_END_BLOCK;
}
- const rb_callable_method_entry_t *cme = vm_cc_cme(cd->cc);
+ VALUE comptime_recv = jit_peek_at_stack(jit, ctx, argc);
+ //rp(comptime_recv);
+ //fprintf(stderr, "offset=%d\n", (int)ctx->sp_offset);
+ VALUE comptime_recv_klass = CLASS_OF(comptime_recv);
+
+ // Can't guard for for these classes because some of they are sometimes
+ // immediate (special const). Can remove this once jit_guard_known_class is able to hanlde them.
+ if (comptime_recv_klass == rb_cInteger || comptime_recv_klass == rb_cSymbol || comptime_recv_klass == rb_cFloat) {
+ return YJIT_CANT_COMPILE;
+ }
- // Don't JIT if the method entry is out of date
- if (METHOD_ENTRY_INVALIDATED(cme)) {
- GEN_COUNTER_INC(cb, oswb_invalid_cme);
+ // Do method lookup
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(comptime_recv_klass, mid);
+ if (!cme) {
+ // TODO: counter
return YJIT_CANT_COMPILE;
}
+ RUBY_ASSERT(cme->called_id == mid);
+ assume_method_lookup_stable(comptime_recv_klass, cme, jit->block);
+
+ // Known class guard. jit_konwn_klass
+ {
+ uint8_t *side_exit = yjit_side_exit(jit, ctx);
+
+ // Points to the receiver operand on the stack
+ x86opnd_t recv = ctx_stack_opnd(ctx, argc);
+ mov(cb, REG0, recv);
+
+ // Callee method ID
+ //ID mid = vm_ci_mid(cd->ci);
+ //printf("JITting call to C function \"%s\", argc: %lu\n", rb_id2name(mid), argc);
+ //print_str(cb, "");
+ //print_str(cb, "calling CFUNC:");
+ //print_str(cb, rb_id2name(mid));
+ //print_str(cb, "recv");
+ //print_ptr(cb, recv);
+
+ // Check that the receiver is a heap object
+ {
+ // uint8_t *receiver_not_heap = COUNTED_EXIT(side_exit, oswb_se_receiver_not_heap);
+ test(cb, REG0, imm_opnd(RUBY_IMMEDIATE_MASK));
+ jnz_ptr(cb, side_exit);
+ cmp(cb, REG0, imm_opnd(Qfalse));
+ je_ptr(cb, side_exit);
+ cmp(cb, REG0, imm_opnd(Qnil));
+ je_ptr(cb, side_exit);
+ }
+
+ // Pointer to the klass field of the receiver &(recv->klass)
+ x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
+
+ // Bail if receiver class is different from compile-time call cache class
+ jit_mov_gc_ptr(jit, cb, REG1, comptime_recv_klass);
+ cmp(cb, klass_opnd, REG1);
+ jit_chain_guard(JCC_JNE, jit, ctx, OSWB_MAX_DEPTH, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
+ }
+
switch (cme->def->type) {
case VM_METHOD_TYPE_ISEQ:
return gen_oswb_iseq(jit, ctx, cd, cme, argc);