Ejemplo n.º 1
0
/* Processes the current worklist. */
static void process_worklist(MVMThreadContext *tc, MVMGCWorklist *worklist, WorkToPass *wtp, MVMuint8 gen) {
    MVMGen2Allocator  *gen2;
    MVMCollectable   **item_ptr;
    MVMCollectable    *new_addr;
    MVMuint32          gen2count;

    /* Grab the second generation allocator; we may move items into the
     * old generation. */
    gen2 = tc->gen2;

    MVM_gc_worklist_mark_frame_roots(tc, worklist);

    while ((item_ptr = MVM_gc_worklist_get(tc, worklist))) {
        /* Dereference the object we're considering. */
        MVMCollectable *item = *item_ptr;
        MVMuint8 item_gen2;
        MVMuint8 to_gen2 = 0;

        /* If the item is NULL, that's fine - it's just a null reference and
         * thus we've no object to consider. */
        if (item == NULL)
            continue;

        /* If it's in the second generation and we're only doing a nursery,
         * collection, we have nothing to do. */
        item_gen2 = item->flags & MVM_CF_SECOND_GEN;
        if (item_gen2) {
            if (gen == MVMGCGenerations_Nursery)
                continue;
            if (item->flags & MVM_CF_GEN2_LIVE) {
                /* gen2 and marked as live. */
                continue;
            }
        } else if (item->flags & MVM_CF_FORWARDER_VALID) {
            /* If the item was already seen and copied, then it will have a
             * forwarding address already. Just update this pointer to the
             * new address and we're done. */
            assert(*item_ptr != item->sc_forward_u.forwarder);
            if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) {
                if (*item_ptr != item->sc_forward_u.forwarder) {
                    GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from %p to forwarder %p\n", item_ptr, item, item->sc_forward_u.forwarder);
                }
                else {
                    GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : already visited handle %p to forwarder %p\n", item_ptr, item->sc_forward_u.forwarder);
                }
            }
            *item_ptr = item->sc_forward_u.forwarder;
            continue;
        } else {
            /* If the pointer is already into tospace (the bit we've already
               copied into), we already updated it, so we're done. */
            if (item >= (MVMCollectable *)tc->nursery_tospace && item < (MVMCollectable *)tc->nursery_alloc) {
                continue;
            }
        }

        /* If it's owned by a different thread, we need to pass it over to
         * the owning thread. */
        if (item->owner != tc->thread_id) {
            GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : sending a handle %p to object %p to thread %d\n", item_ptr, item, item->owner);
            pass_work_item(tc, wtp, item_ptr);
            continue;
        }

        /* If it's in to-space but *ahead* of our copy offset then it's an
           out-of-date pointer and we have some kind of corruption. */
        if (item >= (MVMCollectable *)tc->nursery_alloc && item < (MVMCollectable *)tc->nursery_alloc_limit)
            MVM_panic(1, "Heap corruption detected: pointer %p to past fromspace", item);

        /* At this point, we didn't already see the object, which means we
         * need to take some action. Go on the generation... */
        if (item_gen2) {
            assert(!(item->flags & MVM_CF_FORWARDER_VALID));
            /* It's in the second generation. We'll just mark it. */
            new_addr = item;
            if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT)) {
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : handle %p was already %p\n", item_ptr, new_addr);
            }
            item->flags |= MVM_CF_GEN2_LIVE;
            assert(*item_ptr == new_addr);
        } else {
            /* Catch NULL stable (always sign of trouble) in debug mode. */
            if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT) && !STABLE(item)) {
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : found a zeroed handle %p to object %p\n", item_ptr, item);
                printf("%d", ((MVMCollectable *)1)->owner);
            }

            /* Did we see it in the nursery before, or should we move it to
             * gen2 anyway since it a persistent ID was requested? */
            if (item->flags & (MVM_CF_NURSERY_SEEN | MVM_CF_HAS_OBJECT_ID)) {
                /* Yes; we should move it to the second generation. Allocate
                 * space in the second generation. */
                to_gen2 = 1;
                new_addr = item->flags & MVM_CF_HAS_OBJECT_ID
                    ? MVM_gc_object_id_use_allocation(tc, item)
                    : MVM_gc_gen2_allocate(gen2, item->size);

                /* Add on to the promoted amount (used by profiler). */
                tc->gc_promoted_bytes += item->size;

                /* Copy the object to the second generation and mark it as
                 * living there. */
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : copying an object %p of size %d to gen2 %p\n",
                    item, item->size, new_addr);
                memcpy(new_addr, item, item->size);
                new_addr->flags ^= MVM_CF_NURSERY_SEEN;
                new_addr->flags |= MVM_CF_SECOND_GEN;

                /* If it references frames or static frames, we need to keep
                 * on visiting it. */
                if (!(new_addr->flags & (MVM_CF_TYPE_OBJECT | MVM_CF_STABLE))) {
                    MVMObject *new_obj_addr = (MVMObject *)new_addr;
                    if (REPR(new_obj_addr)->refs_frames)
                        MVM_gc_root_gen2_add(tc, (MVMCollectable *)new_obj_addr);
                }

                /* If we're going to sweep the second generation, also need
                 * to mark it as live. */
                if (gen == MVMGCGenerations_Both)
                    new_addr->flags |= MVM_CF_GEN2_LIVE;
            }
            else {
                /* No, so it will live in the nursery for another GC
                 * iteration. Allocate space in the nursery. */
                new_addr = (MVMCollectable *)tc->nursery_alloc;
                tc->nursery_alloc = (char *)tc->nursery_alloc + item->size;
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : copying an object %p (reprid %d) of size %d to tospace %p\n",
                    item, REPR(item)->ID, item->size, new_addr);

                /* Copy the object to tospace and mark it as seen in the
                 * nursery (so the next time around it will move to the
                 * older generation, if it survives). */
                memcpy(new_addr, item, item->size);
                new_addr->flags |= MVM_CF_NURSERY_SEEN;
            }

            /* Store the forwarding pointer and update the original
             * reference. */
            if (MVM_GC_DEBUG_ENABLED(MVM_GC_DEBUG_COLLECT) && new_addr != item) {
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_COLLECT, "Thread %d run %d : updating handle %p from referent %p (reprid %d) to %p\n", item_ptr, item, REPR(item)->ID, new_addr);
            }
            *item_ptr = new_addr;
            item->sc_forward_u.forwarder = new_addr;
            /* Set the flag on the copy of item *in fromspace* to mark that the
               forwarder pointer is valid. */
            item->flags |= MVM_CF_FORWARDER_VALID;
        }

        /* make sure any rooted frames mark their stuff */
        MVM_gc_worklist_mark_frame_roots(tc, worklist);

        /* Finally, we need to mark the collectable (at its moved address).
         * Track how many items we had before we mark it, in case we need
         * to write barrier them post-move to uphold the generational
         * invariant. */
        gen2count = worklist->items;
        MVM_gc_mark_collectable(tc, worklist, new_addr);

        /* make sure any rooted frames mark their stuff */
        MVM_gc_worklist_mark_frame_roots(tc, worklist);

        /* In moving an object to generation 2, we may have left it pointing
         * to nursery objects. If so, make sure it's in the gen2 roots. */
        if (to_gen2) {
            MVMCollectable **j;
            MVMuint32 max = worklist->items, k;

            for (k = gen2count; k < max; k++) {
                j = worklist->list[k];
                if (*j)
                    MVM_gc_write_barrier(tc, new_addr, *j);
            }
        }
    }
}
Ejemplo n.º 2
0
/* Tries to generate a specialization of the bytecode, for the given callsite
 * and argument tuple. */
MVMSpeshCandidate * MVM_spesh_candidate_generate(MVMThreadContext *tc,
        MVMStaticFrame *static_frame, MVMCallsite *callsite, MVMRegister *args) {
    MVMSpeshCandidate *result;
    MVMSpeshGuard *guards;
    MVMSpeshCode *sc;
    MVMint32 num_spesh_slots, num_guards, *deopts, num_deopts;
    MVMCollectable **spesh_slots;
    char *before, *after;

    /* Generate the specialization. */
    MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, static_frame);
    if (tc->instance->spesh_log_fh)
        before = MVM_spesh_dump(tc, sg);
    MVM_spesh_args(tc, sg, callsite, args);
    MVM_spesh_facts_discover(tc, sg);
    MVM_spesh_optimize(tc, sg);
    if (tc->instance->spesh_log_fh)
        after = MVM_spesh_dump(tc, sg);
    sc = MVM_spesh_codegen(tc, sg);
    num_spesh_slots = sg->num_spesh_slots;
    spesh_slots = sg->spesh_slots;
    num_guards = sg->num_guards;
    guards = sg->guards;
    num_deopts = sg->num_deopt_addrs;
    deopts = sg->deopt_addrs;
    MVM_spesh_graph_destroy(tc, sg);

    /* Now try to add it. Note there's a slim chance another thread beat us
     * to doing so. Also other threads can read the specializations without
     * lock, so make absolutely sure we increment the count of them after we
     * add the new one. */
    result    = NULL;
    uv_mutex_lock(&tc->instance->mutex_spesh_install);
    if (static_frame->body.num_spesh_candidates < MVM_SPESH_LIMIT) {
        MVMint32 num_spesh = static_frame->body.num_spesh_candidates;
        MVMint32 i;
        for (i = 0; i < num_spesh; i++) {
            MVMSpeshCandidate *compare = &static_frame->body.spesh_candidates[i];
            if (compare->cs == callsite && compare->num_guards == num_guards &&
                memcmp(compare->guards, guards, num_guards * sizeof(MVMSpeshGuard)) == 0) {
                /* Beaten! */
                result = &static_frame->body.spesh_candidates[i];
                break;
            }
        }
        if (!result) {
            if (!static_frame->body.spesh_candidates)
                static_frame->body.spesh_candidates = malloc(
                    MVM_SPESH_LIMIT * sizeof(MVMSpeshCandidate));
            result                  = &static_frame->body.spesh_candidates[num_spesh];
            result->cs              = callsite;
            result->num_guards      = num_guards;
            result->guards          = guards;
            result->bytecode        = sc->bytecode;
            result->bytecode_size   = sc->bytecode_size;
            result->handlers        = sc->handlers;
            result->num_spesh_slots = num_spesh_slots;
            result->spesh_slots     = spesh_slots;
            result->num_deopts      = num_deopts;
            result->deopts          = deopts;
            MVM_barrier();
            static_frame->body.num_spesh_candidates++;
            if (static_frame->common.header.flags & MVM_CF_SECOND_GEN)
                if (!(static_frame->common.header.flags & MVM_CF_IN_GEN2_ROOT_LIST))
                    MVM_gc_root_gen2_add(tc, (MVMCollectable *)static_frame);
            if (tc->instance->spesh_log_fh) {
                char *c_name = MVM_string_utf8_encode_C_string(tc, static_frame->body.name);
                char *c_cuid = MVM_string_utf8_encode_C_string(tc, static_frame->body.cuuid);
                fprintf(tc->instance->spesh_log_fh,
                    "Specialized '%s' (cuid: %s)\n\n", c_name, c_cuid);
                fprintf(tc->instance->spesh_log_fh,
                    "Before:\n%s\nAfter:\n%s\n\n========\n\n", before, after);
                free(before);
                free(after);
                free(c_name);
                free(c_cuid);
            }
        }
    }
    if (!result) {
        free(sc->bytecode);
        free(sc->handlers);
    }
    uv_mutex_unlock(&tc->instance->mutex_spesh_install);

    free(sc);
    return result;
}