/* Provides auto-close functionality, for the handful of cases where we have * not ever been in the outer frame of something we're invoking. In this case, * we fake up a frame based on the static lexical environment. */ MVMFrame * autoclose(MVMThreadContext *tc, MVMStaticFrame *needed) { MVMFrame *result; /* First, see if we can find one on the call stack; return it if so. */ MVMFrame *candidate = tc->cur_frame; while (candidate) { if (candidate->static_info->body.bytecode == needed->body.bytecode) return candidate; candidate = candidate->caller; } /* If not, fake up a frame See if it also needs an outer. */ result = MVM_frame_create_context_only(tc, needed, (MVMObject *)needed->body.static_code); if (needed->body.outer) { /* See if the static code object has an outer. */ MVMCode *outer_code = needed->body.outer->body.static_code; if (outer_code->body.outer && outer_code->body.outer->static_info->body.bytecode == needed->body.bytecode) { /* Yes, just take it. */ result->outer = MVM_frame_inc_ref(tc, outer_code->body.outer); } else { /* Otherwise, recursively auto-close. */ result->outer = MVM_frame_inc_ref(tc, autoclose(tc, needed->body.outer)); } } return result; }
static void run_handler(MVMThreadContext *tc, LocatedHandler lh, MVMObject *ex_obj, MVMuint32 category) { switch (lh.handler->action) { case MVM_EX_ACTION_GOTO: if (lh.jit_handler) { void **labels = lh.frame->spesh_cand->jitcode->labels; MVMuint8 *pc = lh.frame->spesh_cand->jitcode->bytecode; lh.frame->jit_entry_label = labels[lh.jit_handler->goto_label]; MVM_frame_unwind_to(tc, lh.frame, pc, 0, NULL); } else { MVM_frame_unwind_to(tc, lh.frame, NULL, lh.handler->goto_offset, NULL); } break; case MVM_EX_ACTION_INVOKE: { /* Create active handler record. */ MVMActiveHandler *ah = MVM_malloc(sizeof(MVMActiveHandler)); MVMFrame *cur_frame = tc->cur_frame; MVMObject *handler_code; /* Ensure we have an exception object. */ if (ex_obj == NULL) { ex_obj = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTException); ((MVMException *)ex_obj)->body.category = category; } /* Find frame to invoke. */ handler_code = MVM_frame_find_invokee(tc, lh.frame->work[lh.handler->block_reg].o, NULL); /* Install active handler record. */ ah->frame = MVM_frame_inc_ref(tc, lh.frame); ah->handler = lh.handler; ah->jit_handler = lh.jit_handler; ah->ex_obj = ex_obj; ah->next_handler = tc->active_handlers; tc->active_handlers = ah; /* Set up special return to unwinding after running the * handler. */ cur_frame->return_value = (MVMRegister *)&tc->last_handler_result; cur_frame->return_type = MVM_RETURN_OBJ; cur_frame->special_return = unwind_after_handler; cur_frame->special_unwind = cleanup_active_handler; cur_frame->special_return_data = ah; /* Invoke the handler frame and return to runloop. */ STABLE(handler_code)->invoke(tc, handler_code, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS), cur_frame->args); break; } default: MVM_panic(1, "Unimplemented handler action"); } }
void MVM_continuation_control(MVMThreadContext *tc, MVMint64 protect, MVMObject *tag, MVMObject *code, MVMRegister *res_reg) { MVMObject *cont; MVMCallsite *inv_arg_callsite; /* Hunt the tag on the stack; mark frames as being incorporated into a * continuation as we go to avoid a second pass. */ MVMFrame *jump_frame = tc->cur_frame; MVMFrame *root_frame = NULL; MVMContinuationTag *tag_record = NULL; while (jump_frame) { jump_frame->in_continuation = 1; tag_record = jump_frame->continuation_tags; while (tag_record) { if (MVM_is_null(tc, tag) || tag_record->tag == tag) break; tag_record = tag_record->next; } if (tag_record) break; root_frame = jump_frame; jump_frame = jump_frame->caller; } if (!tag_record) MVM_exception_throw_adhoc(tc, "No matching continuation reset found"); if (!root_frame) MVM_exception_throw_adhoc(tc, "No continuation root frame found"); /* Create continuation. */ MVMROOT(tc, code, { cont = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTContinuation); ((MVMContinuation *)cont)->body.top = MVM_frame_inc_ref(tc, tc->cur_frame); ((MVMContinuation *)cont)->body.addr = *tc->interp_cur_op; ((MVMContinuation *)cont)->body.res_reg = res_reg; ((MVMContinuation *)cont)->body.root = MVM_frame_inc_ref(tc, root_frame); if (tc->instance->profiling) ((MVMContinuation *)cont)->body.prof_cont = MVM_profile_log_continuation_control(tc, root_frame); });
/* Given the specified code object, sets its outer to the current scope. */ void MVM_frame_capturelex(MVMThreadContext *tc, MVMObject *code) { MVMCode *code_obj; if (REPR(code)->ID != MVM_REPR_ID_MVMCode) MVM_exception_throw_adhoc(tc, "Can only perform capturelex on object with representation MVMCode"); /* XXX Following is vulnerable to a race condition. */ code_obj = (MVMCode *)code; if (code_obj->body.outer) MVM_frame_dec_ref(tc, code_obj->body.outer); code_obj->body.outer = MVM_frame_inc_ref(tc, tc->cur_frame); }
static void run_handler(MVMThreadContext *tc, LocatedHandler lh, MVMObject *ex_obj) { switch (lh.handler->action) { case MVM_EX_ACTION_GOTO: MVM_frame_unwind_to(tc, lh.frame, NULL, lh.handler->goto_offset, NULL); break; case MVM_EX_ACTION_INVOKE: { /* Create active handler record. */ MVMActiveHandler *ah = malloc(sizeof(MVMActiveHandler)); /* Find frame to invoke. */ MVMObject *handler_code = MVM_frame_find_invokee(tc, lh.frame->work[lh.handler->block_reg].o, NULL); /* Ensure we have an exception object. */ /* TODO: Can make one up. */ if (ex_obj == NULL) MVM_panic(1, "Exception object creation NYI"); /* Install active handler record. */ ah->frame = MVM_frame_inc_ref(tc, lh.frame); ah->handler = lh.handler; ah->ex_obj = ex_obj; ah->next_handler = tc->active_handlers; tc->active_handlers = ah; /* Set up special return to unwinding after running the * handler. */ tc->cur_frame->return_value = (MVMRegister *)&tc->last_handler_result; tc->cur_frame->return_type = MVM_RETURN_OBJ; tc->cur_frame->special_return = unwind_after_handler; tc->cur_frame->special_unwind = cleanup_active_handler; tc->cur_frame->special_return_data = ah; /* Invoke the handler frame and return to runloop. */ STABLE(handler_code)->invoke(tc, handler_code, &no_arg_callsite, tc->cur_frame->args); break; } default: MVM_panic(1, "Unimplemented handler action"); } }
/* Creates a new lexotic. */ MVMObject * MVM_exception_newlexotic(MVMThreadContext *tc, MVMuint32 offset) { MVMLexotic *lexotic; /* Locate handler associated with the specified label. */ MVMStaticFrame *sf = tc->cur_frame->static_info; MVMFrameHandler *h = NULL; MVMuint32 i; for (i = 0; i < sf->num_handlers; i++) { if (sf->handlers[i].action == MVM_EX_ACTION_GOTO && sf->handlers[i].goto_offset == offset) { h = &sf->handlers[i]; break; } } if (h == NULL) MVM_exception_throw_adhoc(tc, "Label with no handler passed to newlexotic"); /* Allocate lexotic object and set it up. */ lexotic = (MVMLexotic *)MVM_repr_alloc_init(tc, tc->instance->Lexotic); lexotic->body.handler = h; lexotic->body.frame = MVM_frame_inc_ref(tc, tc->cur_frame); return (MVMObject *)lexotic; }
MVMObject * MVM_thread_start(MVMThreadContext *tc, MVMObject *invokee, MVMObject *result_type) { int status; ThreadStart *ts; MVMObject *child_obj; /* Create a thread object to wrap it up in. */ MVM_gc_root_temp_push(tc, (MVMCollectable **)&invokee); child_obj = REPR(result_type)->allocate(tc, STABLE(result_type)); MVM_gc_root_temp_pop(tc); if (REPR(child_obj)->ID == MVM_REPR_ID_MVMThread) { MVMThread *child = (MVMThread *)child_obj; MVMThread * volatile *threads; /* Create a new thread context and set it up. */ MVMThreadContext *child_tc = MVM_tc_create(tc->instance); child->body.tc = child_tc; MVM_ASSIGN_REF(tc, child, child->body.invokee, invokee); child_tc->thread_obj = child; child_tc->thread_id = MVM_incr(&tc->instance->next_user_thread_id); /* Create the thread. Note that we take a reference to the current frame, * since it must survive to be the dynamic scope of where the thread was * started, and there's no promises that the thread won't start before * the code creating the thread returns. The count is decremented when * the thread is done. */ ts = malloc(sizeof(ThreadStart)); ts->tc = child_tc; ts->caller = MVM_frame_inc_ref(tc, tc->cur_frame); ts->thread_obj = child_obj; /* push this to the *child* tc's temp roots. */ MVM_gc_root_temp_push(child_tc, (MVMCollectable **)&ts->thread_obj); /* Signal to the GC we have a childbirth in progress. The GC * will null it for us. */ MVM_gc_mark_thread_blocked(child_tc); MVM_ASSIGN_REF(tc, tc->thread_obj, tc->thread_obj->body.new_child, child); /* push to starting threads list */ threads = &tc->instance->threads; do { MVMThread *curr = *threads; MVM_ASSIGN_REF(tc, child, child->body.next, curr); } while (MVM_casptr(threads, child->body.next, child) != child->body.next); status = uv_thread_create(&child->body.thread, &start_thread, ts); if (status < 0) { MVM_panic(MVM_exitcode_compunit, "Could not spawn thread: errorcode %d", status); } /* need to run the GC to clear our new_child field in case we try * try to launch another thread before the GC runs and before the * thread starts. */ GC_SYNC_POINT(tc); } else { MVM_exception_throw_adhoc(tc, "Thread result type must have representation MVMThread"); } return child_obj; }
/* Takes a static frame and a thread context. Invokes the static frame. */ void MVM_frame_invoke(MVMThreadContext *tc, MVMStaticFrame *static_frame, MVMCallsite *callsite, MVMRegister *args, MVMFrame *outer, MVMObject *code_ref) { MVMFrame *frame; MVMuint32 pool_index; MVMFrame *node; int fresh = 0; MVMStaticFrameBody *static_frame_body = &static_frame->body; /* If the frame was never invoked before, need initial calculations * and verification. */ if (!static_frame_body->invoked) prepare_and_verify_static_frame(tc, static_frame); pool_index = static_frame_body->pool_index; node = tc->frame_pool_table[pool_index]; if (node == NULL) { fresh = 1; frame = malloc(sizeof(MVMFrame)); frame->params.named_used = NULL; /* Ensure special return pointer is null. */ frame->special_return = NULL; } else { tc->frame_pool_table[pool_index] = node->outer; node->outer = NULL; frame = node; } /* Copy thread context (back?) into the frame. */ frame->tc = tc; /* Set static frame. */ frame->static_info = static_frame; /* Store the code ref (NULL at the top-level). */ frame->code_ref = code_ref; /* Allocate space for lexicals and work area, copying the default lexical * environment into place. */ if (static_frame_body->env_size) { if (fresh) frame->env = malloc(static_frame_body->env_size); memcpy(frame->env, static_frame_body->static_env, static_frame_body->env_size); } else { frame->env = NULL; } if (static_frame_body->work_size) { if (fresh) frame->work = malloc(static_frame_body->work_size); memset(frame->work, 0, static_frame_body->work_size); } else { frame->work = NULL; } /* Calculate args buffer position. */ frame->args = static_frame_body->work_size ? frame->work + static_frame_body->num_locals : NULL; /* Outer. */ if (outer) { /* We were provided with an outer frame; just ensure that it is * based on the correct static frame. */ // if (outer->static_info == static_frame_body->outer) frame->outer = outer; // else // MVM_exception_throw_adhoc(tc, // "Provided outer frame %p (%s %s) does not match expected static frame type %p (%s %s)", // outer->static_info, // MVM_string_utf8_encode_C_string(tc, MVM_repr_get_by_id(tc, REPR(outer->static_info)->ID)->name), // outer->static_info->body.name ? MVM_string_utf8_encode_C_string(tc, outer->static_info->body.name) : "<anonymous static frame>", // static_frame_body->outer, // MVM_string_utf8_encode_C_string(tc, MVM_repr_get_by_id(tc, REPR(static_frame_body->outer)->ID)->name), // static_frame_body->outer->body.name ? MVM_string_utf8_encode_C_string(tc, static_frame_body->outer->body.name) : "<anonymous static frame>"); } else if (static_frame_body->outer) { /* We need an outer, but none was provided by a closure. See if * we can find an appropriate frame on the current call stack. */ MVMFrame *candidate = tc->cur_frame; frame->outer = NULL; while (candidate) { if (candidate->static_info == static_frame_body->outer) { frame->outer = candidate; break; } candidate = candidate->caller; } if (!frame->outer) { frame->outer = static_frame_body->outer->body.prior_invocation; if (!frame->outer) MVM_exception_throw_adhoc(tc, "Cannot locate an outer frame for the call"); } } else { frame->outer = NULL; } if (frame->outer) MVM_frame_inc_ref(tc, frame->outer); /* Caller is current frame in the thread context. */ if (tc->cur_frame) frame->caller = MVM_frame_inc_ref(tc, tc->cur_frame); else frame->caller = NULL; /* Initial reference count is 1 by virtue of it being the currently * executing frame. */ frame->ref_count = 1; frame->gc_seq_number = 0; /* Initialize argument processing. */ MVM_args_proc_init(tc, &frame->params, callsite, args); /* Update interpreter and thread context, so next execution will use this * frame. */ tc->cur_frame = frame; *(tc->interp_cur_op) = static_frame_body->bytecode; *(tc->interp_bytecode_start) = static_frame_body->bytecode; *(tc->interp_reg_base) = frame->work; *(tc->interp_cu) = static_frame_body->cu; }
* captures a closure over the current scope. */ MVMObject * MVM_frame_takeclosure(MVMThreadContext *tc, MVMObject *code) { MVMCode *closure; MVMStaticFrame *sf; if (REPR(code)->ID != MVM_REPR_ID_MVMCode) MVM_exception_throw_adhoc(tc, "Can only perform takeclosure on object with representation MVMCode"); sf = ((MVMCode *)code)->body.sf; MVMROOT(tc, code, { closure = (MVMCode *)REPR(code)->allocate(tc, STABLE(code)); }); closure->body.sf = sf; closure->body.outer = MVM_frame_inc_ref(tc, tc->cur_frame); MVM_ASSIGN_REF(tc, closure, closure->body.code_object, ((MVMCode *)code)->body.code_object); return (MVMObject *)closure; } /* Looks up the address of the lexical with the specified name and the * specified type. An error is thrown if it does not exist or if the * type is incorrect */ MVMRegister * MVM_frame_find_lexical_by_name(MVMThreadContext *tc, MVMString *name, MVMuint16 type) { MVMFrame *cur_frame = tc->cur_frame; MVM_string_flatten(tc, name); while (cur_frame != NULL) { MVMLexicalHashEntry *lexical_names = cur_frame->static_info->body.lexical_names; if (lexical_names) { /* Indexes were formerly stored off-by-one
/* If we have to deopt inside of a frame containing inlines, and we're in * an inlined frame at the point we hit deopt, we need to undo the inlining * by switching all levels of inlined frame out for a bunch of frames that * are running the de-optimized code. We may, of course, be in the original, * non-inline, bit of the code - in which case we've nothing to do. */ static void uninline(MVMThreadContext *tc, MVMFrame *f, MVMSpeshCandidate *cand, MVMint32 offset, MVMint32 deopt_offset, MVMFrame *callee) { MVMFrame *last_uninlined = NULL; MVMuint16 last_res_reg; MVMReturnType last_res_type; MVMuint32 last_return_deopt_idx; MVMint32 i; for (i = 0; i < cand->num_inlines; i++) { if (offset >= cand->inlines[i].start && offset < cand->inlines[i].end) { /* Create the frame. */ MVMCode *ucode = cand->inlines[i].code; MVMStaticFrame *usf = ucode->body.sf; MVMFrame *uf = MVM_frame_create_for_deopt(tc, usf, ucode); /*fprintf(stderr, "Recreated frame '%s' (cuid '%s')\n", MVM_string_utf8_encode_C_string(tc, usf->body.name), MVM_string_utf8_encode_C_string(tc, usf->body.cuuid));*/ /* Copy the locals and lexicals into place. */ memcpy(uf->work, f->work + cand->inlines[i].locals_start, usf->body.num_locals * sizeof(MVMRegister)); memcpy(uf->env, f->env + cand->inlines[i].lexicals_start, usf->body.num_lexicals * sizeof(MVMRegister)); /* Did we already uninline a frame? */ if (last_uninlined) { /* Yes; multi-level un-inline. Switch it back to deopt'd * code. */ uf->effective_bytecode = uf->static_info->body.bytecode; uf->effective_handlers = uf->static_info->body.handlers; uf->effective_spesh_slots = NULL; uf->spesh_cand = NULL; /* Set up the return location. */ uf->return_address = uf->static_info->body.bytecode + cand->deopts[2 * last_return_deopt_idx]; /* Set result type and register. */ uf->return_type = last_res_type; if (last_res_type == MVM_RETURN_VOID) uf->return_value = NULL; else uf->return_value = uf->work + last_res_reg; /* Set up last uninlined's caller to us. */ last_uninlined->caller = MVM_frame_inc_ref(tc, uf); } else { /* First uninlined frame. Are we in the middle of the call * stack (and thus in deopt_all)? */ if (callee) { /* Tweak the callee's caller to the uninlined frame, not * the frame holding the inlinings. */ MVMFrame *orig_caller = callee->caller; callee->caller = MVM_frame_inc_ref(tc, uf); MVM_frame_dec_ref(tc, orig_caller); /* Copy over the return location. */ uf->return_address = uf->effective_bytecode + deopt_offset; /* Set result type and register. */ uf->return_type = f->return_type; if (uf->return_type == MVM_RETURN_VOID) { uf->return_value = NULL; } else { MVMuint16 orig_reg = (MVMuint16)(f->return_value - f->work); MVMuint16 ret_reg = orig_reg - cand->inlines[i].locals_start; uf->return_value = uf->work + ret_reg; } } else { /* No, it's the deopt_one case, so this is where we'll point * the interpreter. */ tc->cur_frame = uf; *(tc->interp_cur_op) = uf->effective_bytecode + deopt_offset; *(tc->interp_bytecode_start) = uf->effective_bytecode; *(tc->interp_reg_base) = uf->work; *(tc->interp_cu) = usf->body.cu; } } /* Update tracking variables for last uninline. Note that we know * an inline ends with a goto, which is how we're able to find a * return address offset. */ last_uninlined = uf; last_res_reg = cand->inlines[i].res_reg; last_res_type = cand->inlines[i].res_type; last_return_deopt_idx = cand->inlines[i].return_deopt_idx; } } if (last_uninlined) { /* Set return address, which we need to resolve to the deopt'd one. */ f->return_address = f->static_info->body.bytecode + cand->deopts[2 * last_return_deopt_idx]; /* Set result type and register. */ f->return_type = last_res_type; if (last_res_type == MVM_RETURN_VOID) f->return_value = NULL; else f->return_value = f->work + last_res_reg; /* Set up inliner as the caller, given we now have a direct inline. */ last_uninlined->caller = MVM_frame_inc_ref(tc, f); } else { /* Weren't in an inline after all. What kind of deopt? */ if (callee) { /* Deopt all. Move return address. */ f->return_address = f->effective_bytecode + deopt_offset; } else { /* Deopt one. Move interpreter. */ *(tc->interp_cur_op) = f->static_info->body.bytecode + deopt_offset; *(tc->interp_bytecode_start) = f->static_info->body.bytecode; } } }
/* Takes a static frame and a thread context. Invokes the static frame. */ void MVM_frame_invoke(MVMThreadContext *tc, MVMStaticFrame *static_frame, MVMCallsite *callsite, MVMRegister *args, MVMFrame *outer, MVMObject *code_ref) { MVMFrame *frame; MVMuint32 pool_index, found_spesh; MVMFrame *node; int fresh = 0; MVMStaticFrameBody *static_frame_body = &static_frame->body; /* If the frame was never invoked before, need initial calculations * and verification. */ if (!static_frame_body->invoked) prepare_and_verify_static_frame(tc, static_frame); /* Get frame body from the re-use pool, or allocate it. */ pool_index = static_frame_body->pool_index; if (pool_index >= tc->frame_pool_table_size) grow_frame_pool(tc, pool_index); node = tc->frame_pool_table[pool_index]; if (node == NULL) { fresh = 1; frame = malloc(sizeof(MVMFrame)); frame->params.named_used = NULL; /* Ensure special return pointers and continuation tags are null. */ frame->special_return = NULL; frame->special_unwind = NULL; frame->continuation_tags = NULL; } else { tc->frame_pool_table[pool_index] = node->outer; node->outer = NULL; frame = node; } #if MVM_HLL_PROFILE_CALLS frame->profile_index = tc->profile_index; tc->profile_data[frame->profile_index].duration_nanos = MVM_platform_now(); tc->profile_data[frame->profile_index].callsite_id = 0; /* XXX get a real callsite id */ tc->profile_data[frame->profile_index].code_id = 0; /* XXX get a real code id */ /* increment the profile data index */ ++tc->profile_index; if (tc->profile_index == tc->profile_data_size) { tc->profile_data_size *= 2; tc->profile_data = realloc(tc->profile_data, tc->profile_data_size); } #endif /* Copy thread context (back?) into the frame. */ frame->tc = tc; /* Set static frame. */ frame->static_info = static_frame; /* Store the code ref (NULL at the top-level). */ frame->code_ref = code_ref; /* Allocate space for lexicals and work area, copying the default lexical * environment into place. */ if (static_frame_body->env_size) { if (fresh) frame->env = malloc(static_frame_body->env_size); memcpy(frame->env, static_frame_body->static_env, static_frame_body->env_size); } else { frame->env = NULL; } if (static_frame_body->work_size) { if (fresh || !frame->work) frame->work = malloc(static_frame_body->work_size); memset(frame->work, 0, static_frame_body->work_size); } else { frame->work = NULL; } /* Calculate args buffer position and make sure current call site starts * empty. */ frame->args = static_frame_body->work_size ? frame->work + static_frame_body->num_locals : NULL; frame->cur_args_callsite = NULL; /* Outer. */ if (outer) { /* We were provided with an outer frame; just ensure that it is * based on the correct static frame (compare on bytecode address * to come with nqp::freshcoderef). */ if (outer->static_info->body.bytecode == static_frame_body->outer->body.bytecode) frame->outer = outer; else MVM_exception_throw_adhoc(tc, "When invoking %s, Provided outer frame %p (%s %s) does not match expected static frame type %p (%s %s)", static_frame_body->name ? MVM_string_utf8_encode_C_string(tc, static_frame_body->name) : "<anonymous static frame>", outer->static_info, MVM_repr_get_by_id(tc, REPR(outer->static_info)->ID)->name, outer->static_info->body.name ? MVM_string_utf8_encode_C_string(tc, outer->static_info->body.name) : "<anonymous static frame>", static_frame_body->outer, MVM_repr_get_by_id(tc, REPR(static_frame_body->outer)->ID)->name, static_frame_body->outer->body.name ? MVM_string_utf8_encode_C_string(tc, static_frame_body->outer->body.name) : "<anonymous static frame>"); } else if (static_frame_body->static_code && static_frame_body->static_code->body.outer) { /* We're lacking an outer, but our static code object may have one. * This comes up in the case of cloned protoregexes, for example. */ frame->outer = static_frame_body->static_code->body.outer; } else if (static_frame_body->outer) { /* Auto-close, and cache it in the static frame. */ frame->outer = autoclose(tc, static_frame_body->outer); static_frame_body->static_code->body.outer = MVM_frame_inc_ref(tc, frame->outer); } else { frame->outer = NULL; } if (frame->outer) MVM_frame_inc_ref(tc, frame->outer); /* Caller is current frame in the thread context. */ if (tc->cur_frame) frame->caller = MVM_frame_inc_ref(tc, tc->cur_frame); else frame->caller = NULL; frame->keep_caller = 0; frame->in_continuation = 0; /* Initial reference count is 1 by virtue of it being the currently * executing frame. */ MVM_store(&frame->ref_count, 1); MVM_store(&frame->gc_seq_number, 0); /* Initialize argument processing. */ MVM_args_proc_init(tc, &frame->params, callsite, args); /* Make sure there's no frame context pointer and special return data * won't be marked. */ frame->context_object = NULL; frame->mark_special_return_data = NULL; /* Clear frame flags. */ frame->flags = 0; /* See if any specializations apply. */ found_spesh = 0; if (++static_frame_body->invocations >= 10 && callsite->is_interned) { /* Look for specialized bytecode. */ MVMint32 num_spesh = static_frame_body->num_spesh_candidates; MVMint32 i, j; for (i = 0; i < num_spesh; i++) { MVMSpeshCandidate *cand = &static_frame_body->spesh_candidates[i]; if (cand->cs == callsite) { MVMint32 match = 1; for (j = 0; j < cand->num_guards; j++) { MVMint32 pos = cand->guards[j].slot; MVMSTable *st = (MVMSTable *)cand->guards[j].match; MVMObject *arg = args[pos].o; if (!arg) { match = 0; break; } switch (cand->guards[j].kind) { case MVM_SPESH_GUARD_CONC: if (!IS_CONCRETE(arg) || STABLE(arg) != st) match = 0; break; case MVM_SPESH_GUARD_TYPE: if (IS_CONCRETE(arg) || STABLE(arg) != st) match = 0; break; case MVM_SPESH_GUARD_DC_CONC: { MVMRegister dc; STABLE(arg)->container_spec->fetch(tc, arg, &dc); if (!dc.o || !IS_CONCRETE(dc.o) || STABLE(dc.o) != st) match = 0; break; } case MVM_SPESH_GUARD_DC_TYPE: { MVMRegister dc; STABLE(arg)->container_spec->fetch(tc, arg, &dc); if (!dc.o || IS_CONCRETE(dc.o) || STABLE(dc.o) != st) match = 0; break; } } if (!match) break; } if (match) { frame->effective_bytecode = cand->bytecode; frame->effective_handlers = cand->handlers; frame->effective_spesh_slots = cand->spesh_slots; frame->spesh_cand = cand; found_spesh = 1; break; } } } /* If we didn't find any, and we're below the limit, can generate a * specialization. */ if (!found_spesh && num_spesh < MVM_SPESH_LIMIT && tc->instance->spesh_enabled) { MVMSpeshCandidate *cand = MVM_spesh_candidate_generate(tc, static_frame, callsite, args); if (cand) { frame->effective_bytecode = cand->bytecode; frame->effective_handlers = cand->handlers; frame->effective_spesh_slots = cand->spesh_slots; frame->spesh_cand = cand; found_spesh = 1; } } } if (!found_spesh) { frame->effective_bytecode = static_frame_body->bytecode; frame->effective_handlers = static_frame_body->handlers; frame->spesh_cand = NULL; } /* Update interpreter and thread context, so next execution will use this * frame. */ tc->cur_frame = frame; *(tc->interp_cur_op) = frame->effective_bytecode; *(tc->interp_bytecode_start) = frame->effective_bytecode; *(tc->interp_reg_base) = frame->work; *(tc->interp_cu) = static_frame_body->cu; /* If we need to do so, make clones of things in the lexical environment * that need it. Note that we do this after tc->cur_frame became the * current frame, to make sure these new objects will certainly get * marked if GC is triggered along the way. */ if (static_frame_body->static_env_flags) { /* Drag everything out of static_frame_body before we start, * as GC action may invalidate it. */ MVMuint8 *flags = static_frame_body->static_env_flags; MVMint64 numlex = static_frame_body->num_lexicals; MVMRegister *state = NULL; MVMint64 state_act = 0; /* 0 = none so far, 1 = first time, 2 = later */ MVMint64 i; for (i = 0; i < numlex; i++) { switch (flags[i]) { case 0: break; case 1: frame->env[i].o = MVM_repr_clone(tc, frame->env[i].o); break; case 2: redo_state: switch (state_act) { case 0: if (!frame->code_ref) MVM_exception_throw_adhoc(tc, "Frame must have code-ref to have state variables"); state = ((MVMCode *)frame->code_ref)->body.state_vars; if (state) { /* Already have state vars; pull them from this. */ state_act = 2; } else { /* Allocate storage for state vars. */ state = malloc(frame->static_info->body.env_size); memset(state, 0, frame->static_info->body.env_size); ((MVMCode *)frame->code_ref)->body.state_vars = state; state_act = 1; /* Note that this frame should run state init code. */ frame->flags |= MVM_FRAME_FLAG_STATE_INIT; } goto redo_state; case 1: frame->env[i].o = MVM_repr_clone(tc, frame->env[i].o); MVM_ASSIGN_REF(tc, &(frame->code_ref->header), state[i].o, frame->env[i].o); break; case 2: frame->env[i].o = state[i].o; break; } break; default: MVM_exception_throw_adhoc(tc, "Unknown lexical environment setup flag"); } } } }