Beispiel #1
0
/* 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;
}
Beispiel #2
0
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");
    }
}
Beispiel #3
0
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);
    });
Beispiel #4
0
/* 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);
}
Beispiel #5
0
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");
    }
}
Beispiel #6
0
/* 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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
/* 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;
}
Beispiel #9
0
 * 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
Beispiel #10
0
/* 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;
        }
    }
}
Beispiel #11
0
/* 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");
            }
        }
    }
}