mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Introduce yjit_guard_known_klass()
This commit is contained in:
parent
a8f7eb2f35
commit
fbb514f67e
3 changed files with 66 additions and 56 deletions
114
yjit_codegen.c
114
yjit_codegen.c
|
@ -1329,6 +1329,41 @@ gen_jump(jitstate_t* jit, ctx_t* ctx)
|
||||||
return YJIT_END_BLOCK;
|
return YJIT_END_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guard that recv_opnd has the same class as known_klass. Recompile as contingency if possible, or take side exit a last resort.
|
||||||
|
static bool
|
||||||
|
jit_guard_known_klass(jitstate_t *jit, const ctx_t *recompile_context, VALUE known_klass, x86opnd_t recv_opnd, const int max_chain_depth, uint8_t *side_exit)
|
||||||
|
{
|
||||||
|
// Can't guard for for these classes because some of they are sometimes immediate (special const).
|
||||||
|
// Can remove this by adding appropriate dynamic checks.
|
||||||
|
if (known_klass == rb_cInteger ||
|
||||||
|
known_klass == rb_cSymbol ||
|
||||||
|
known_klass == rb_cFloat ||
|
||||||
|
known_klass == rb_cNilClass ||
|
||||||
|
known_klass == rb_cTrueClass ||
|
||||||
|
known_klass == rb_cFalseClass) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the receiver is a heap object
|
||||||
|
{
|
||||||
|
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, known_klass);
|
||||||
|
cmp(cb, klass_opnd, REG1);
|
||||||
|
jit_chain_guard(JCC_JNE, jit, recompile_context, max_chain_depth, side_exit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
jit_protected_guard(jitstate_t *jit, codeblock_t *cb, const rb_callable_method_entry_t *cme, uint8_t *side_exit)
|
jit_protected_guard(jitstate_t *jit, codeblock_t *cb, const rb_callable_method_entry_t *cme, uint8_t *side_exit)
|
||||||
{
|
{
|
||||||
|
@ -1345,8 +1380,8 @@ jit_protected_guard(jitstate_t *jit, codeblock_t *cb, const rb_callable_method_e
|
||||||
jz_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_protected_check_failed));
|
jz_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_protected_check_failed));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static codegen_status_t
|
||||||
gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_callable_method_entry_t *cme, int32_t argc)
|
gen_oswb_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const rb_callable_method_entry_t *cme, int32_t argc)
|
||||||
{
|
{
|
||||||
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
|
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
|
||||||
|
|
||||||
|
@ -1368,6 +1403,15 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c
|
||||||
return YJIT_CANT_COMPILE;
|
return YJIT_CANT_COMPILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callee method ID
|
||||||
|
//ID mid = vm_ci_mid(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);
|
||||||
|
|
||||||
// Create a size-exit to fall back to the interpreter
|
// Create a size-exit to fall back to the interpreter
|
||||||
uint8_t *side_exit = yjit_side_exit(jit, ctx);
|
uint8_t *side_exit = yjit_side_exit(jit, ctx);
|
||||||
|
|
||||||
|
@ -1452,7 +1496,7 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c
|
||||||
|
|
||||||
// Call check_cfunc_dispatch
|
// Call check_cfunc_dispatch
|
||||||
mov(cb, RDI, recv);
|
mov(cb, RDI, recv);
|
||||||
jit_mov_gc_ptr(jit, cb, RSI, (VALUE)cd);
|
jit_mov_gc_ptr(jit, cb, RSI, (VALUE)ci);
|
||||||
mov(cb, RDX, const_ptr_opnd((void *)cfunc->func));
|
mov(cb, RDX, const_ptr_opnd((void *)cfunc->func));
|
||||||
jit_mov_gc_ptr(jit, cb, RCX, (VALUE)cme);
|
jit_mov_gc_ptr(jit, cb, RCX, (VALUE)cme);
|
||||||
call_ptr(cb, REG0, (void *)&check_cfunc_dispatch);
|
call_ptr(cb, REG0, (void *)&check_cfunc_dispatch);
|
||||||
|
@ -1552,7 +1596,7 @@ gen_return_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t s
|
||||||
}
|
}
|
||||||
|
|
||||||
static codegen_status_t
|
static codegen_status_t
|
||||||
gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_callable_method_entry_t *cme, int32_t argc)
|
gen_oswb_iseq(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const rb_callable_method_entry_t *cme, int32_t argc)
|
||||||
{
|
{
|
||||||
const rb_iseq_t *iseq = def_iseq_ptr(cme->def);
|
const rb_iseq_t *iseq = def_iseq_ptr(cme->def);
|
||||||
const VALUE* start_pc = iseq->body->iseq_encoded;
|
const VALUE* start_pc = iseq->body->iseq_encoded;
|
||||||
|
@ -1571,7 +1615,7 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca
|
||||||
return YJIT_CANT_COMPILE;
|
return YJIT_CANT_COMPILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm_ci_flag(cd->ci) & VM_CALL_TAILCALL) {
|
if (vm_ci_flag(ci) & VM_CALL_TAILCALL) {
|
||||||
// We can't handle tailcalls
|
// We can't handle tailcalls
|
||||||
GEN_COUNTER_INC(cb, oswb_iseq_tailcall);
|
GEN_COUNTER_INC(cb, oswb_iseq_tailcall);
|
||||||
return YJIT_CANT_COMPILE;
|
return YJIT_CANT_COMPILE;
|
||||||
|
@ -1677,7 +1721,7 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca
|
||||||
);
|
);
|
||||||
|
|
||||||
//print_str(cb, "calling Ruby func:");
|
//print_str(cb, "calling Ruby func:");
|
||||||
//print_str(cb, rb_id2name(vm_ci_mid(cd->ci)));
|
//print_str(cb, rb_id2name(vm_ci_mid(ci)));
|
||||||
|
|
||||||
// Load the updated SP
|
// Load the updated SP
|
||||||
mov(cb, REG_SP, member_opnd(REG_CFP, rb_control_frame_t, sp));
|
mov(cb, REG_SP, member_opnd(REG_CFP, rb_control_frame_t, sp));
|
||||||
|
@ -1704,8 +1748,8 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
|
||||||
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
|
const struct rb_callinfo *ci = cd->ci; // info about the call site
|
||||||
|
|
||||||
int32_t argc = (int32_t)vm_ci_argc(cd->ci);
|
int32_t argc = (int32_t)vm_ci_argc(ci);
|
||||||
ID mid = vm_ci_mid(cd->ci);
|
ID mid = vm_ci_mid(ci);
|
||||||
|
|
||||||
// Don't JIT calls with keyword splat
|
// Don't JIT calls with keyword splat
|
||||||
if (vm_ci_flag(ci) & VM_CALL_KW_SPLAT) {
|
if (vm_ci_flag(ci) & VM_CALL_KW_SPLAT) {
|
||||||
|
@ -1726,13 +1770,15 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE comptime_recv = jit_peek_at_stack(jit, ctx, argc);
|
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);
|
VALUE comptime_recv_klass = CLASS_OF(comptime_recv);
|
||||||
|
|
||||||
// Can't guard for for these classes because some of they are sometimes
|
// Guard that the receiver has the same class as the one from compile time
|
||||||
// immediate (special const). Can remove this once jit_guard_known_class is able to hanlde them.
|
uint8_t *side_exit = yjit_side_exit(jit, ctx);
|
||||||
if (comptime_recv_klass == rb_cInteger || comptime_recv_klass == rb_cSymbol || comptime_recv_klass == rb_cFloat) {
|
|
||||||
|
// Points to the receiver operand on the stack
|
||||||
|
x86opnd_t recv = ctx_stack_opnd(ctx, argc);
|
||||||
|
mov(cb, REG0, recv);
|
||||||
|
if (!jit_guard_known_klass(jit, ctx, comptime_recv_klass, REG0, OSWB_MAX_DEPTH, side_exit)) {
|
||||||
return YJIT_CANT_COMPILE;
|
return YJIT_CANT_COMPILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1743,51 +1789,15 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
|
||||||
return YJIT_CANT_COMPILE;
|
return YJIT_CANT_COMPILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register block for invalidation
|
||||||
RUBY_ASSERT(cme->called_id == mid);
|
RUBY_ASSERT(cme->called_id == mid);
|
||||||
assume_method_lookup_stable(comptime_recv_klass, cme, jit->block);
|
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) {
|
switch (cme->def->type) {
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
return gen_oswb_iseq(jit, ctx, cd, cme, argc);
|
return gen_oswb_iseq(jit, ctx, ci, cme, argc);
|
||||||
case VM_METHOD_TYPE_CFUNC:
|
case VM_METHOD_TYPE_CFUNC:
|
||||||
return gen_oswb_cfunc(jit, ctx, cd, cme, argc);
|
return gen_oswb_cfunc(jit, ctx, ci, cme, argc);
|
||||||
case VM_METHOD_TYPE_ATTRSET:
|
case VM_METHOD_TYPE_ATTRSET:
|
||||||
GEN_COUNTER_INC(cb, oswb_ivar_set_method);
|
GEN_COUNTER_INC(cb, oswb_ivar_set_method);
|
||||||
return YJIT_CANT_COMPILE;
|
return YJIT_CANT_COMPILE;
|
||||||
|
|
|
@ -103,14 +103,14 @@ opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc)
|
||||||
|
|
||||||
// Verify that calling with cd on receiver goes to callee
|
// Verify that calling with cd on receiver goes to callee
|
||||||
void
|
void
|
||||||
check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme)
|
check_cfunc_dispatch(VALUE receiver, struct rb_callinfo *ci, void *callee, rb_callable_method_entry_t *compile_time_cme)
|
||||||
{
|
{
|
||||||
if (METHOD_ENTRY_INVALIDATED(compile_time_cme)) {
|
if (METHOD_ENTRY_INVALIDATED(compile_time_cme)) {
|
||||||
rb_bug("yjit: output code uses invalidated cme %p", (void *)compile_time_cme);
|
rb_bug("yjit: output code uses invalidated cme %p", (void *)compile_time_cme);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool callee_correct = false;
|
bool callee_correct = false;
|
||||||
const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(receiver), vm_ci_mid(cd->ci));
|
const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(receiver), vm_ci_mid(ci));
|
||||||
if (cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
if (cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
||||||
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
|
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
|
||||||
if ((void *)cfunc->func == callee) {
|
if ((void *)cfunc->func == callee) {
|
||||||
|
@ -118,7 +118,7 @@ check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!callee_correct) {
|
if (!callee_correct) {
|
||||||
rb_bug("yjit: output code calls wrong method cd->cc->klass: %p", (void *)cd->cc->klass);
|
rb_bug("yjit: output code calls wrong method");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ VALUE *iseq_pc_at_idx(const rb_iseq_t *iseq, uint32_t insn_idx);
|
||||||
void map_addr2insn(void *code_ptr, int insn);
|
void map_addr2insn(void *code_ptr, int insn);
|
||||||
int opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc);
|
int opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc);
|
||||||
|
|
||||||
void check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme);
|
void check_cfunc_dispatch(VALUE receiver, struct rb_callinfo *ci, void *callee, rb_callable_method_entry_t *compile_time_cme);
|
||||||
bool cfunc_needs_frame(const rb_method_cfunc_t *cfunc);
|
bool cfunc_needs_frame(const rb_method_cfunc_t *cfunc);
|
||||||
|
|
||||||
RBIMPL_ATTR_NODISCARD() bool assume_bop_not_redefined(block_t *block, int redefined_flag, enum ruby_basic_operators bop);
|
RBIMPL_ATTR_NODISCARD() bool assume_bop_not_redefined(block_t *block, int redefined_flag, enum ruby_basic_operators bop);
|
||||||
|
|
Loading…
Add table
Reference in a new issue