diff options
author | Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> | 2020-10-19 16:31:15 -0400 |
---|---|---|
committer | Alan Wu <XrXr@users.noreply.github.com> | 2021-10-20 18:19:25 -0400 |
commit | a88d6207dd87b53f54debb1a3cf044da60b82fb3 (patch) | |
tree | 16fee1350e70b338c7003b1b52e7b8468d0bdd25 | |
parent | 770f3929b31da37c06e015b292c3b7ec3dfc969c (diff) |
Updated C function call sketch
-rw-r--r-- | ujit_compile.c | 193 |
1 files changed, 105 insertions, 88 deletions
diff --git a/ujit_compile.c b/ujit_compile.c index 1539658e2d..666963b11e 100644 --- a/ujit_compile.c +++ b/ujit_compile.c @@ -484,7 +484,11 @@ gen_opt_minus(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx) bool gen_opt_send_without_block(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx) { - // Definitions relevant to the call cache are in vm_callinfo.h + // Relevant definitions: + // vm_call_cfunc_with_frame : vm_insnhelper.c + // rb_callcache : vm_callinfo.h + // invoker, cfunc logic : method.h, vm_method.c + // rb_callable_method_entry_t: method.h struct rb_call_data * cd = (struct rb_call_data *)ctx_get_arg(ctx, 0); int32_t argc = (int32_t)vm_ci_argc(cd->ci); @@ -514,22 +518,25 @@ gen_opt_send_without_block(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx) return false; } + const rb_callable_method_entry_t *me = vm_cc_cme(cd->cc); + // Don't JIT if this is not a C call - if (vm_cc_cme(cd->cc)->def->type != VM_METHOD_TYPE_CFUNC) + if (me->def->type != VM_METHOD_TYPE_CFUNC) { return false; } - //printf("call to C function \"%s\", argc: %lu\n", rb_id2name(mid), argc); + const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(me->def, body.cfunc); - // Things we need to do at run-time: - // - Check that the cached klass matches - // - Check that method entry is not invalidated - // - ??? - // - Create a new frame - // - copy function arguments? - // - Call the C function - // - Remove the frame + // Don't jit if the argument count doesn't match + if (cfunc->argc < 0 || cfunc->argc != argc) + { + return false; + } + + //printf("call to C function \"%s\", argc: %lu\n", rb_id2name(mid), argc); + //print_str(cb, rb_id2name(mid)); + //print_ptr(cb, RCX); // Create a size-exit to fall back to the interpreter uint8_t* side_exit = ujit_side_exit(ocb, ctx, ctx->pc); @@ -538,11 +545,10 @@ gen_opt_send_without_block(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx) x86opnd_t recv = ctx_stack_opnd(ctx, argc); mov(cb, RCX, recv); - //print_str(cb, rb_id2name(mid)); - //print_ptr(cb, RCX); - - // IDEA: may be able to eliminate this in some cases if we know the previous instruction? + // IDEA: we may be able to eliminate this in some cases if we know the previous instruction? // TODO: guard_is_object() helper function? + // + // Check that the receiver is a heap object test(cb, RCX, imm_opnd(RUBY_IMMEDIATE_MASK)); jnz_ptr(cb, side_exit); cmp(cb, RCX, imm_opnd(Qfalse)); @@ -553,137 +559,148 @@ gen_opt_send_without_block(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx) // Pointer to the klass field of the receiver &(recv->klass) x86opnd_t klass_opnd = mem_opnd(64, RCX, offsetof(struct RBasic, klass)); - // IDEA: Aaron suggested we could possibly treat a changed - // class pointer as a cache miss + // FIXME: currently assuming that cc->klass doesn't change + // Ideally we would like the GC to update the klass pointer + // // Check if we have a cache hit mov(cb, RAX, const_ptr_opnd((void*)cd->cc->klass)); cmp(cb, RAX, klass_opnd); jne_ptr(cb, side_exit); - //print_str(cb, "cache klass hit"); - + // NOTE: there *has to be* a way to optimize the entry invalidated check + // Could we have Ruby invalidate the JIT code instead of invalidating CME? + // + // Check that the method entry is not invalidated + // cd->cc->cme->flags + // #define METHOD_ENTRY_INVALIDATED(me) ((me)->flags & IMEMO_FL_USER5) + mov(cb, RAX, const_ptr_opnd(cd)); + x86opnd_t ptr_to_cc = member_opnd(RAX, struct rb_call_data, cc); + mov(cb, RAX, ptr_to_cc); + x86opnd_t ptr_to_cme_ = mem_opnd(64, RAX, offsetof(struct rb_callcache, cme_)); + mov(cb, RAX, ptr_to_cme_); + x86opnd_t flags_opnd = mem_opnd(64, RAX, offsetof(rb_callable_method_entry_t, flags)); + test(cb, flags_opnd, imm_opnd(IMEMO_FL_USER5)); + jnz_ptr(cb, side_exit); + // NOTE: stack frame setup may not be needed for some C functions + // TODO: do we need this check? + //vm_check_frame(type, specval, cref_or_me, iseq); + // FIXME: stack overflow check + //vm_check_canary(ec, sp); + // TODO: under construction, stop here for now + jmp_ptr(cb, side_exit); + return true; + // FIXME: for now, we hardcode ec + // TODO: hardcode EC + // Allocate a new CFP + //ec->cfp--; + // Increment the stack pointer by 3 + //sp += 3 + // Write method entry at sp[-2] + //sp[-2] = me; + // Write block handller at sp[-1] + //VALUE recv = calling->recv; + //VALUE block_handler = calling->block_handler; + //sp[-1] = block_handler; - jmp_ptr(cb, side_exit); - return true; + // Write env flags at sp[0] + uint64_t frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; + //sp[0] = frame_type; - /* - // Create a size-exit to fall back to the interpreter - uint8_t* side_exit = ujit_side_exit(ocb, ctx, ctx->pc); - struct rb_calling_info *calling = (struct rb_calling_info*)malloc(sizeof(struct rb_calling_info)); - calling->block_handler = VM_BLOCK_HANDLER_NONE; - calling->kw_splat = 0; - calling->argc = argc; - mov(cb, RAX, const_ptr_opnd(cd)); - x86opnd_t ptr_to_cc = member_opnd(RAX, struct rb_call_data, cc); - mov(cb, RAX, ptr_to_cc); + // Setup the new frame + /* + *cfp = (const struct rb_control_frame_struct) { + .pc = 0, + .sp = sp, + .iseq = 0, + .self = recv, + .ep = sp - 1, + .block_code = 0, + .__bp__ = sp, + }; + ec->cfp = cfp; + */ - x86opnd_t ptr_to_klass = mem_opnd(64, RAX, offsetof(struct rb_callcache, klass)); - x86opnd_t ptr_to_cme_ = mem_opnd(64, RAX, offsetof(struct rb_callcache, cme_)); - x86opnd_t ptr_to_call_ = mem_opnd(64, RAX, offsetof(struct rb_callcache, call_)); - mov(cb, R9, ptr_to_klass); - mov(cb, RCX, ptr_to_cme_); - // Points to the receiver operand on the stack - x86opnd_t recv = ctx_stack_opnd(ctx, argc); - mov(cb, RDX, recv); - //print_int(cb, recv); - // Store calling->recv - mov(cb, R8, const_ptr_opnd(calling)); - x86opnd_t recv_opnd = mem_opnd(64, R8, offsetof(struct rb_calling_info, recv)); - mov(cb, recv_opnd, RDX); + // NOTE: we need a helper to pop multiple values here + // Pop the C function arguments from the stack (in the caller) + //reg_cfp->sp -= orig_argc + 1; - // Pointer to the klass field of the receiver - x86opnd_t klass_opnd = mem_opnd(64, RDX, offsetof(struct RBasic, klass)); - // IDEA: Aaron suggested we could possibly treat a changed - // class pointer as a cache miss - // Check if we have a cache hit - cmp(cb, R9, klass_opnd); - jne_ptr(cb, side_exit); - //print_int(cb, klass_opnd); - print_str(cb, "cache klass hit"); - //#define METHOD_ENTRY_INVALIDATED(me) ((me)->flags & IMEMO_FL_USER5) - x86opnd_t flags_opnd = mem_opnd(64, RCX, offsetof( rb_callable_method_entry_t, flags)); - test(cb, flags_opnd, imm_opnd(IMEMO_FL_USER5)); - jnz_ptr(cb, side_exit); + // Save the MicroJIT registers push(cb, RDI); push(cb, RSI); - x86opnd_t ptr_to_pc = mem_opnd(64, RDI, offsetof(rb_control_frame_t, pc)); - mov(cb, ptr_to_pc, const_ptr_opnd(ctx->pc + insn_len(BIN(opt_send_without_block)))); - // Write the adjusted SP back into the CFP - if (ctx->stack_diff != 0) - { - x86opnd_t stack_pointer = ctx_sp_opnd(ctx, 1); - lea(cb, RSI, stack_pointer); - mov(cb, mem_opnd(64, RDI, 8), RSI); - } - // val = vm_cc_call(cc)(ec, GET_CFP(), &calling, cd); - mov(cb, RSI, RDI); - mov(cb, RDI, const_ptr_opnd(rb_current_execution_context())); - mov(cb, RDX, R8); - print_int(cb, RDX); - mov(cb, RCX, const_ptr_opnd(cd)); - call(cb, ptr_to_call_); + // Call the function + //VALUE ret = (cfunc->func)(recv, argv[0], argv[1]); + + + + // Restore MicroJIT registers pop(cb, RSI); pop(cb, RDI); - size_t continue_in_jit = cb_new_label(cb, "continue_in_jit"); - cmp(cb, RAX, imm_opnd(Qundef)); - jne(cb, continue_in_jit); - //print_str(cb, "method entry not invalidated!!!1"); - mov(cb, RDI, const_ptr_opnd(rb_current_execution_context())); - mov(cb, RDI, mem_opnd(64, RDI, offsetof(rb_execution_context_t, cfp))); - // Read the PC from the CFP - mov(cb, RAX, mem_opnd(64, RDI, 0)); - // Write the post call bytes - for (size_t i = 0; i < sizeof(ujit_post_call_bytes); ++i) - cb_write_byte(cb, ujit_post_call_bytes[i]); + // TODO: later + // RUBY_VM_CHECK_INTS(ec); + + // Pop the stack frame + //ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + - cb_write_label(cb, continue_in_jit); - cb_link_labels(cb); + + + + + + + + + + + + + + + jmp_ptr(cb, side_exit); return true; - */ } - void rb_ujit_compile_iseq(const rb_iseq_t *iseq) { |