コード例 #1
0
ファイル: threads.c プロジェクト: JTimothyKing/MoarVM
/* This callback handles starting execution of a thread. */
static void start_thread(void *data) {
    ThreadStart *ts = (ThreadStart *)data;
    MVMThreadContext *tc = ts->tc;

    /* Set the current frame in the thread to be the initial caller;
     * the ref count for this was incremented in the original thread. */
    tc->cur_frame = ts->caller;

    /* wait for the GC to finish if it's not finished stealing us. */
    MVM_gc_mark_thread_unblocked(tc);
    tc->thread_obj->body.stage = MVM_thread_stage_started;

    /* Enter the interpreter, to run code. */
    MVM_interp_run(tc, &thread_initial_invoke, ts);

    /* mark as exited, so the GC will know to clear our stuff. */
    tc->thread_obj->body.stage = MVM_thread_stage_exited;

    /* Now we're done, decrement the reference count of the caller. */
    MVM_frame_dec_ref(tc, ts->caller);

    /* Mark ourselves as dying, so that another thread will take care
     * of GC-ing our objects and cleaning up our thread context. */
    MVM_gc_mark_thread_blocked(tc);

    /* hopefully pop the ts->thread_obj temp */
    MVM_gc_root_temp_pop(tc);
    free(ts);

    /* Exit the thread, now it's completed. */
    MVM_platform_thread_exit(NULL);
}
コード例 #2
0
ファイル: exceptions.c プロジェクト: danaj/MoarVM
/* Unwinds after a handler. */
static void unwind_after_handler(MVMThreadContext *tc, void *sr_data) {
    MVMFrame     *frame;
    MVMException *exception;
    MVMuint32     goto_offset;

    /* Get active handler; sanity check (though it's possible other cases
     * should be supported). */
    MVMActiveHandler *ah = (MVMActiveHandler *)sr_data;
    if (tc->active_handlers != ah)
        MVM_panic(1, "Trying to unwind from wrong handler");

    /* Grab info we'll need to unwind. */
    frame       = ah->frame;
    exception   = (MVMException *)ah->ex_obj;
    goto_offset = ah->handler->goto_offset;

    /* Clean up. */
    tc->active_handlers = ah->next_handler;
    MVM_frame_dec_ref(tc, ah->frame);
    free(ah);

    /* Do the unwinding as needed. */
    if (exception && exception->body.return_after_unwind) {
        MVM_frame_unwind_to(tc, frame->caller, NULL, 0, tc->last_handler_result);
    }
    else {
        MVM_frame_unwind_to(tc, frame, NULL, goto_offset, NULL);
    }
}
コード例 #3
0
ファイル: frame.c プロジェクト: crab2313/MoarVM
/* Return/unwind do about the same thing; this factors it out. */
static MVMuint64 return_or_unwind(MVMThreadContext *tc, MVMuint8 unwind) {
    MVMFrame *returner = tc->cur_frame;
    MVMFrame *caller = returner->caller;
    MVMFrame *prior;

    /* Decrement the frame reference of the prior invocation, and then
     * set us as it. */
    do {
        prior = returner->static_info->body.prior_invocation;
    } while (!MVM_trycas(&returner->static_info->body.prior_invocation, prior, returner));
    if (prior)
        MVM_frame_dec_ref(tc, prior);

    /* Clear up argument processing leftovers, if any. */
    if (returner->work) {
        MVM_args_proc_cleanup_for_cache(tc, &returner->params);
    }

    /* signal to the GC to ignore ->work */
    returner->tc = NULL;

    /* Switch back to the caller frame if there is one; we also need to
     * decrement its reference count. */
    if (caller && returner != tc->thread_entry_frame) {
        tc->cur_frame = caller;
        *(tc->interp_cur_op) = caller->return_address;
        *(tc->interp_bytecode_start) = caller->static_info->body.bytecode;
        *(tc->interp_reg_base) = caller->work;
        *(tc->interp_cu) = caller->static_info->body.cu;
        MVM_frame_dec_ref(tc, caller);
        returner->caller = NULL;

        /* Handle any special return hooks. */
        if (caller->special_return) {
            MVMSpecialReturn sr = caller->special_return;
            caller->special_return = NULL;
            if (!unwind)
                sr(tc, caller->special_return_data);
        }

        return 1;
    }
    else {
        tc->cur_frame = NULL;
        return 0;
    }
}
コード例 #4
0
ファイル: exceptions.c プロジェクト: danaj/MoarVM
/* Cleans up an active handler record if we unwind over it. */
static void cleanup_active_handler(MVMThreadContext *tc, void *sr_data) {
    /* Get active handler; sanity check (though it's possible other cases
     * should be supported). */
    MVMActiveHandler *ah = (MVMActiveHandler *)sr_data;
    if (tc->active_handlers != ah)
        MVM_panic(1, "Trying to unwind over wrong handler");

    /* Clean up. */
    tc->active_handlers = ah->next_handler;
    MVM_frame_dec_ref(tc, ah->frame);
    free(ah);
}
コード例 #5
0
ファイル: frame.c プロジェクト: mj41/MoarVM
/* 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);
}
コード例 #6
0
ファイル: exceptions.c プロジェクト: dagurval/MoarVM
/* Unwinds after a handler. */
static void unwind_after_handler(MVMThreadContext *tc, void *sr_data) {
    /* Get active handler; sanity check (though it's possible other cases
     * should be supported). */
    MVMActiveHandler *ah = (MVMActiveHandler *)sr_data;
    if (tc->active_handlers != ah)
        MVM_panic(1, "Trying to unwind from wrong handler");
    tc->active_handlers = ah->next_handler;

    /* Do the unwinding as needed. */
    unwind_to_frame(tc, ah->frame);
    *tc->interp_cur_op = *tc->interp_bytecode_start + ah->handler->goto_offset;

    /* Clean up. */
    MVM_frame_dec_ref(tc, ah->frame);
    free(ah);
}
コード例 #7
0
ファイル: frame.c プロジェクト: mj41/MoarVM
/* Decreases the reference count of a frame. If it hits zero, then we can
 * free it. Returns null for convenience. */
MVMFrame * MVM_frame_dec_ref(MVMThreadContext *tc, MVMFrame *frame) {
    /* MVM_dec returns what the count was before it decremented it
     * to zero, so we look for 1 here. */
    while (MVM_decr(&frame->ref_count) == 1) {
        MVMuint32 pool_index = frame->static_info->body.pool_index;
        MVMFrame *node = tc->frame_pool_table[pool_index];
        MVMFrame *outer_to_decr = frame->outer;

        /* If there's a caller pointer, decrement that. */
        if (frame->caller)
            frame->caller = MVM_frame_dec_ref(tc, frame->caller);

        if (node && MVM_load(&node->ref_count) >= MVMFramePoolLengthLimit) {
            /* There's no room on the free list, so destruction.*/
            if (frame->env) {
                free(frame->env);
                frame->env = NULL;
            }
            if (frame->work) {
                MVM_args_proc_cleanup(tc, &frame->params);
                free(frame->work);
                frame->work = NULL;
            }
            free(frame);
        }
        else { /* Unshift it to the free list */
            MVM_store(&frame->ref_count, (frame->outer = node) ? MVM_load(&node->ref_count) + 1 : 1);
            tc->frame_pool_table[pool_index] = frame;
        }
        if (outer_to_decr)
            frame = outer_to_decr; /* and loop */
        else
            break;
    }
    return NULL;
}
コード例 #8
0
ファイル: deopt.c プロジェクト: nanis/MoarVM
/* 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. */
            if (usf->body.num_locals)
                memcpy(uf->work, f->work + cand->inlines[i].locals_start,
                    usf->body.num_locals * sizeof(MVMRegister));
            if (usf->body.num_lexicals)
                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_by_frame(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_by_frame(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_by_frame(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;
        }
    }
}
コード例 #9
0
ファイル: frame.c プロジェクト: mj41/MoarVM
/* Removes a single frame, as part of a return or unwind. Done after any exit
 * handler has already been run. */
static MVMuint64 remove_one_frame(MVMThreadContext *tc, MVMuint8 unwind) {
    MVMFrame *returner = tc->cur_frame;
    MVMFrame *caller   = returner->caller;

    /* Some cleanup we only need do if we're not a frame involved in a
     * continuation (otherwise we need to allow for multi-shot
     * re-invocation). */
    if (!returner->in_continuation) {
        /* Arguments buffer no longer in use (saves GC visiting it). */
        returner->cur_args_callsite = NULL;

        /* Clear up argument processing leftovers, if any. */
        if (returner->work) {
            MVM_args_proc_cleanup_for_cache(tc, &returner->params);
        }

        /* Clear up any continuation tags. */
        if (returner->continuation_tags) {
            MVMContinuationTag *tag = returner->continuation_tags;
            while (tag) {
                MVMContinuationTag *next = tag->next;
                free(tag);
                tag = next;
            }
            returner->continuation_tags = NULL;
        }

        /* Signal to the GC to ignore ->work */
        returner->tc = NULL;

        /* Unless we need to keep the caller chain in place, clear it up. */
        if (caller) {
            if (!returner->keep_caller) {
                MVM_frame_dec_ref(tc, caller);
                returner->caller = NULL;
            }
            else if (unwind) {
                caller->keep_caller = 1;
            }
        }
    }

#if MVM_HLL_PROFILE_CALLS
    tc->profile_data[returner->profile_index].duration_nanos =
        MVM_platform_now() -
        tc->profile_data[returner->profile_index].duration_nanos;
#endif

    /* Decrement the frame's ref-count by the 1 it got by virtue of being the
     * currently executing frame. */
    MVM_frame_dec_ref(tc, returner);

    /* Switch back to the caller frame if there is one. */
    if (caller && returner != tc->thread_entry_frame) {
        tc->cur_frame = caller;
        *(tc->interp_cur_op) = caller->return_address;
        *(tc->interp_bytecode_start) = caller->effective_bytecode;
        *(tc->interp_reg_base) = caller->work;
        *(tc->interp_cu) = caller->static_info->body.cu;

        /* Handle any special return hooks. */
        if (caller->special_return || caller->special_unwind) {
            MVMSpecialReturn sr = caller->special_return;
            MVMSpecialReturn su = caller->special_unwind;
            caller->special_return = NULL;
            caller->special_unwind = NULL;
            if (unwind && su)
                su(tc, caller->special_return_data);
            else if (!unwind && sr)
                sr(tc, caller->special_return_data);
            caller->mark_special_return_data = NULL;
        }

        return 1;
    }
    else {
        tc->cur_frame = NULL;
        return 0;
    }
}