Пример #1
0
static void insert_log(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins, MVMint32 next_bb) {
    /* Add the entry. */
    MVMSpeshIns *log_ins         = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    log_ins->info                = MVM_op_get_op(MVM_OP_sp_log);
    log_ins->operands            = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
    log_ins->operands[0].reg     = ins->operands[0].reg;
    log_ins->operands[1].lit_i16 = g->num_log_slots;
    if (next_bb)
        MVM_spesh_manipulate_insert_ins(tc, bb->succ[0], NULL, log_ins);
    else
        MVM_spesh_manipulate_insert_ins(tc, bb, ins, log_ins);
    g->num_log_slots++;

    /* Steal the de-opt annotation into the log instruction, if it exists. */
    if (ins->annotations) {
        MVMSpeshAnn *prev_ann = NULL;
        MVMSpeshAnn *cur_ann  = ins->annotations;
        while (cur_ann) {
            if (cur_ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS) {
                if (prev_ann)
                    prev_ann->next = cur_ann->next;
                else
                    ins->annotations = cur_ann->next;
                cur_ann->next = NULL;
                log_ins->annotations = cur_ann;
                break;
            }
            prev_ann = cur_ann;
            cur_ann = cur_ann->next;
        }
    }
}
Пример #2
0
/* Handles a pos arg that needs boxing. */
static void pos_box(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                    MVMSpeshIns *ins, const MVMOpInfo *hlltype_op, const MVMOpInfo *box_op,
                    const MVMOpInfo *arg_op, MVMuint8 kind) {
    MVMSpeshOperand  temp_bt, temp_arg;
    MVMSpeshIns     *hlltype, *box;

    /* Add HLL type op. */
    temp_bt              = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_obj);
    hlltype              = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    hlltype->info        = hlltype_op;
    hlltype->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
    hlltype->operands[0] = temp_bt;
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, hlltype);

    /* Add box op. */
    temp_arg         = MVM_spesh_manipulate_get_temp_reg(tc, g, kind);
    box              = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    box->info        = box_op;
    box->operands    = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
    box->operands[0] = ins->operands[0];
    box->operands[1] = temp_arg;
    box->operands[2] = temp_bt;
    MVM_spesh_manipulate_insert_ins(tc, bb, hlltype, box);

    /* Update instruction to receive unboxed arg. */
    ins->info        = arg_op;
    ins->operands[0] = temp_arg;

    /* Release temporary registers. */
    MVM_spesh_manipulate_release_temp_reg(tc, g, temp_bt);
    MVM_spesh_manipulate_release_temp_reg(tc, g, temp_arg);
}
Пример #3
0
static void add_nativecall_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
    MVMSpeshIns *enter_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    MVMSpeshIns *exit_ins  = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));

    enter_ins->info        = MVM_op_get_op(MVM_OP_prof_enternative);
    enter_ins->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
    enter_ins->operands[0] = ins->operands[2];

    MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, enter_ins);

    exit_ins->info         = MVM_op_get_op(MVM_OP_prof_exit);

    MVM_spesh_manipulate_insert_ins(tc, bb, ins, exit_ins);
}
Пример #4
0
/* Adds an instruction to log an allocation. */
static void add_allocation_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
    MVMSpeshIns *alloc_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    alloc_ins->info        = MVM_op_get_op(MVM_OP_prof_allocated);
    alloc_ins->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
    alloc_ins->operands[0] = ins->operands[0];
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, alloc_ins);
}
Пример #5
0
/* Adds an instruction marking a name arg as being used (if we turned its
 * fetching into a positional). */
static MVMSpeshIns * add_named_used_ins(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                               MVMSpeshIns *ins, MVMint32 idx) {
    MVMSpeshIns *inserted_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
    MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ));
    inserted_ins->info        = MVM_op_get_op(MVM_OP_sp_namedarg_used);
    inserted_ins->operands    = operands;
    operands[0].lit_i16       = (MVMint16)idx;
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, inserted_ins);
    return inserted_ins;
}
Пример #6
0
/* Walk graph and insert write check instructions. */
static void prepend_ctw_check(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                              MVMSpeshIns *before_ins, MVMSpeshOperand check_reg,
                              MVMint16 guilty) {
    MVMSpeshIns *ctw_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    ctw_ins->info        = MVM_op_get_op(MVM_OP_ctw_check);
    ctw_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
    ctw_ins->operands[0] = check_reg;
    ctw_ins->operands[1].lit_i16 = guilty;
    MVM_spesh_manipulate_insert_ins(tc, bb, before_ins->prev, ctw_ins);
}
Пример #7
0
/* Handles a pos arg that needs unboxing. */
static void pos_unbox(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                      MVMSpeshIns *ins, const MVMOpInfo *unbox_op) {
    MVMSpeshOperand  temp  = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_obj);
    MVMSpeshIns     *unbox = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    unbox->info            = unbox_op;
    unbox->operands        = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
    unbox->operands[0]     = ins->operands[0];
    unbox->operands[1]     = temp;
    ins->info              = MVM_op_get_op(MVM_OP_sp_getarg_o);
    ins->operands[0]       = temp;
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, unbox);
    MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
}
Пример #8
0
/* boolification has a major indirection, which we can spesh away.
 * Afterwards, we may be able to spesh even further, so we defer
 * to other optimization methods. */
static void optimize_istrue_isfalse(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
    MVMuint8 negated_op;
    MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
    if (ins->info->opcode == MVM_OP_istrue) {
        negated_op = 0;
    } else if (ins->info->opcode == MVM_OP_isfalse) {
        negated_op = 1;
    } else {
        return;
    }

    /* Let's try to figure out the boolification spec. */
    if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) {
        MVMBoolificationSpec *bs = STABLE(facts->type)->boolification_spec;
        switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) {
            case MVM_BOOL_MODE_UNBOX_INT:
                /* We can just unbox the int and pretend it's a bool. */
                ins->info = MVM_op_get_op(MVM_OP_unbox_i);
                /* And then we might be able to optimize this even further. */
                optimize_repr_op(tc, g, bb, ins, 1);
                break;
            case MVM_BOOL_MODE_NOT_TYPE_OBJECT:
                /* This is the same as isconcrete. */
                ins->info = MVM_op_get_op(MVM_OP_isconcrete);
                /* And now defer another bit of optimization */
                optimize_isconcrete(tc, g, ins);
                break;
            default:
                return;
        }
        /* Now we can take care of the negation. */
        if (negated_op) {
            MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns ));
            MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2);

            /* This is a bit naughty with regards to the SSA form, but
             * we'll hopefully get away with it until we have a proper
             * way to get new registers crammed in the middle of things */
            new_ins->info = MVM_op_get_op(MVM_OP_not_i);
            new_ins->operands = operands;
            operands[0] = ins->operands[0];
            operands[1] = ins->operands[0];

            MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins);
        }
    }
}
Пример #9
0
static void return_to_box(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *return_bb,
                   MVMSpeshIns *return_ins, MVMSpeshOperand target,
                   MVMuint16 box_type_op, MVMuint16 box_op) {
    /* Create and insert boxing instruction after current return instruction. */
    MVMSpeshIns      *box_ins     = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    MVMSpeshOperand *box_operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
    box_ins->info                 = MVM_op_get_op(box_op);
    box_ins->operands             = box_operands;
    box_operands[0]               = target;
    box_operands[1]               = return_ins->operands[0];
    box_operands[2]               = target;
    MVM_spesh_manipulate_insert_ins(tc, return_bb, return_ins, box_ins);

    /* Now turn return instruction node into lookup of appropraite box
     * type. */
    return_ins->info        = MVM_op_get_op(box_type_op);
    return_ins->operands[0] = target;
}
Пример #10
0
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) {
    /* Insert entry instruction. */
    MVMSpeshBB *bb         = g->entry->linear_next;
    MVMSpeshIns *enter_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
    enter_ins->info        = MVM_op_get_op(MVM_OP_prof_enter);
    MVM_spesh_manipulate_insert_ins(tc, bb, NULL, enter_ins);

    /* Walk the code and insert profile logging instructions as needed. */
    while (bb) {
        MVMSpeshIns *ins = bb->first_ins;
        while (ins) {
            switch (ins->info->opcode) {
            case MVM_OP_return_i:
            case MVM_OP_return_n:
            case MVM_OP_return_s:
            case MVM_OP_return_o:
            case MVM_OP_return: {
                /* Log a normal exit prior to returning. */
                MVMSpeshIns *exit_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
                exit_ins->info        = MVM_op_get_op(MVM_OP_prof_exit);
                MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, exit_ins);

                /* If the return instruction is a goto target, move to the
                 * instrumentation instruction. */
                if (ins->annotations) {
                    MVMSpeshAnn *ann      = ins->annotations;
                    MVMSpeshAnn *prev_ann = NULL;
                    while (ann) {
                        if (ann->type == MVM_SPESH_ANN_FH_GOTO) {
                            if (prev_ann)
                                prev_ann->next = ann->next;
                            else
                                ins->annotations = ann->next;
                            exit_ins->annotations = ann;
                            ann->next = NULL;
                            break;
                        }
                        prev_ann = ann;
                        ann = ann->next;
                    }
                }

                break;
            }
            case MVM_OP_invoke_o:
            case MVM_OP_param_rp_o:
            case MVM_OP_param_op_o:
            case MVM_OP_param_rn_o:
            case MVM_OP_param_on_o:
            case MVM_OP_param_sp:
            case MVM_OP_param_sn:
            case MVM_OP_newexception:
            case MVM_OP_usecapture:
            case MVM_OP_savecapture:
            case MVM_OP_takeclosure:
            case MVM_OP_getattr_o:
            case MVM_OP_getattrs_o:
            case MVM_OP_sp_p6ogetvc_o:
            case MVM_OP_create:
            case MVM_OP_sp_fastcreate:
            case MVM_OP_clone:
            case MVM_OP_box_i:
            case MVM_OP_box_n:
            case MVM_OP_box_s:
            case MVM_OP_iter:
            case MVM_OP_add_I:
            case MVM_OP_sub_I:
            case MVM_OP_mul_I:
            case MVM_OP_div_I:
            case MVM_OP_mod_I:
            case MVM_OP_neg_I:
            case MVM_OP_abs_I:
            case MVM_OP_bor_I:
            case MVM_OP_bxor_I:
            case MVM_OP_band_I:
            case MVM_OP_bnot_I:
            case MVM_OP_blshift_I:
            case MVM_OP_brshift_I:
            case MVM_OP_pow_I:
            case MVM_OP_gcd_I:
            case MVM_OP_lcm_I:
            case MVM_OP_expmod_I:
            case MVM_OP_rand_I:
            case MVM_OP_coerce_nI:
            case MVM_OP_coerce_sI:
            case MVM_OP_radix_I: {
                add_allocation_logging(tc, g, bb, ins);
                break;
            }
            case MVM_OP_getlex:
            case MVM_OP_getlex_no:
            case MVM_OP_getlexstatic_o:
            case MVM_OP_getlexperinvtype_o:
            case MVM_OP_getlexouter:
            case MVM_OP_getlexrel:
            case MVM_OP_getlexreldyn:
            case MVM_OP_getlexrelcaller:
            case MVM_OP_getlexcaller:
            {
                /* We have to check if the target register is actually
                 * an object register. */
                if ((g->local_types && g->local_types[ins->operands[0].reg.orig] == MVM_reg_obj)
                    || (!g->local_types && g->sf->body.local_types[ins->operands[0].reg.orig] == MVM_reg_obj))
                    add_allocation_logging(tc, g, bb, ins);
                break;
            }
            case MVM_OP_getregref_i:
            case MVM_OP_getregref_n:
            case MVM_OP_getregref_s:
            case MVM_OP_getlexref_i:
            case MVM_OP_getlexref_n:
            case MVM_OP_getlexref_s:
            case MVM_OP_getlexref_ni:
            case MVM_OP_getlexref_nn:
            case MVM_OP_getlexref_ns:
            case MVM_OP_atposref_i:
            case MVM_OP_atposref_n:
            case MVM_OP_atposref_s:
            case MVM_OP_getattrref_i:
            case MVM_OP_getattrref_n:
            case MVM_OP_getattrref_s:
            case MVM_OP_getattrsref_i:
            case MVM_OP_getattrsref_n:
            case MVM_OP_getattrsref_s:
                add_allocation_logging(tc, g, bb, ins);
                break;
            case MVM_OP_nativecallinvoke:
                add_nativecall_logging(tc, g, bb, ins);
                break;
            default:
                /* See if it's an allocating extop. */
                if (ins->info->opcode == (MVMuint16)-1) {
                    MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
                    MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
                    MVMuint16       i;
                    for (i = 0; i < num_extops; i++) {
                        if (extops[i].info == ins->info) {
                            if (extops[i].allocating && extops[i].info->num_operands >= 1)
                                add_allocation_logging(tc, g, bb, ins);
                            break;
                        }
                    }
                }
                break;
            }
            ins = ins->next;
        }
        bb = bb->linear_next;
    }
}
Пример #11
0
/* Drives optimization of a call. */
static void optimize_call(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                          MVMSpeshIns *ins, MVMint32 callee_idx, MVMSpeshCallInfo *arg_info) {
    /* Ensure we know what we're going to be invoking. */
    MVMSpeshFacts *callee_facts = MVM_spesh_get_facts(tc, g, ins->operands[callee_idx]);
    if (callee_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) {
        MVMObject *code   = callee_facts->value.o;
        MVMObject *target = NULL;
        if (REPR(code)->ID == MVM_REPR_ID_MVMCode) {
            /* Already have a code object we know we'll call. */
            target = code;
        }
        else if (STABLE(code)->invocation_spec) {
            /* What kind of invocation will it be? */
            MVMInvocationSpec *is = STABLE(code)->invocation_spec;
            if (!MVM_is_null(tc, is->md_class_handle)) {
                /* Multi-dispatch. Check if this is a dispatch where we can
                 * use the cache directly. */
                MVMRegister dest;
                REPR(code)->attr_funcs.get_attribute(tc,
                    STABLE(code), code, OBJECT_BODY(code),
                    is->md_class_handle, is->md_valid_attr_name,
                    is->md_valid_hint, &dest, MVM_reg_int64);
                if (dest.i64) {
                    /* Yes. Try to obtain the cache. */
                    REPR(code)->attr_funcs.get_attribute(tc,
                        STABLE(code), code, OBJECT_BODY(code),
                        is->md_class_handle, is->md_cache_attr_name,
                        is->md_cache_hint, &dest, MVM_reg_obj);
                    if (!MVM_is_null(tc, dest.o)) {
                        MVMObject *found = MVM_multi_cache_find_spesh(tc, dest.o, arg_info);
                        if (found) {
                            /* Found it. Is it a code object already, or do we
                             * have futher unpacking to do? */
                            if (REPR(found)->ID == MVM_REPR_ID_MVMCode) {
                                target = found;
                            }
                            else if (STABLE(found)->invocation_spec) {
                                MVMInvocationSpec *m_is = STABLE(found)->invocation_spec;
                                if (!MVM_is_null(tc, m_is->class_handle)) {
                                    REPR(found)->attr_funcs.get_attribute(tc,
                                        STABLE(found), found, OBJECT_BODY(found),
                                        is->class_handle, is->attr_name,
                                        is->hint, &dest, MVM_reg_obj);
                                    if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode)
                                        target = dest.o;
                                }
                            }
                        }
                    }
                }
            }
            else if (!MVM_is_null(tc, is->class_handle)) {
                /* Single dispatch; retrieve the code object. */
                MVMRegister dest;
                REPR(code)->attr_funcs.get_attribute(tc,
                    STABLE(code), code, OBJECT_BODY(code),
                    is->class_handle, is->attr_name,
                    is->hint, &dest, MVM_reg_obj);
                if (REPR(dest.o)->ID == MVM_REPR_ID_MVMCode)
                    target = dest.o;
            }
        }

        /* If we resolved to something better than the code object, then add
         * the resolved item in a spesh slot and insert a lookup. */
        if (target && target != code && !((MVMCode *)target)->body.is_compiler_stub) {
            MVMSpeshIns *ss_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
            ss_ins->info        = MVM_op_get_op(MVM_OP_sp_getspeshslot);
            ss_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
            ss_ins->operands[0] = ins->operands[callee_idx];
            ss_ins->operands[1].lit_i16 = MVM_spesh_add_spesh_slot(tc, g,
                (MVMCollectable *)target);
            MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, ss_ins);
            /* XXX TODO: Do this differently so we can eliminate the original
             * lookup of the enclosing code object also. */
        }

        /* See if we can point the call at a particular specialization. */
        if (target) {
            MVMCode *target_code  = (MVMCode *)target;
            MVMint32 spesh_cand = try_find_spesh_candidate(tc, target_code, arg_info);
            if (spesh_cand >= 0) {
                /* Yes. Will we be able to inline? */
                MVMSpeshGraph *inline_graph = MVM_spesh_inline_try_get_graph(tc, g,
                    target_code, &target_code->body.sf->body.spesh_candidates[spesh_cand]);
                if (inline_graph) {
                    /* Yes, have inline graph, so go ahead and do it. */
                    /*char *c_name_i = MVM_string_utf8_encode_C_string(tc, target_code->body.sf->body.name);
                    char *c_cuid_i = MVM_string_utf8_encode_C_string(tc, target_code->body.sf->body.cuuid);
                    char *c_name_t = MVM_string_utf8_encode_C_string(tc, g->sf->body.name);
                    char *c_cuid_t = MVM_string_utf8_encode_C_string(tc, g->sf->body.cuuid);
                    printf("Can inline %s (%s) into %s (%s)\n",
                        c_name_i, c_cuid_i, c_name_t, c_cuid_t);
                    free(c_name_i);
                    free(c_cuid_i);
                    free(c_name_t);
                    free(c_cuid_t);*/
                    MVM_spesh_inline(tc, g, arg_info, bb, ins, inline_graph, target_code);
                }
                else {
                    /* Can't inline, so just identify candidate. */
                    MVMSpeshOperand *new_operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
                    if (ins->info->opcode == MVM_OP_invoke_v) {
                        new_operands[0]         = ins->operands[0];
                        new_operands[1].lit_i16 = spesh_cand;
                        ins->operands           = new_operands;
                        ins->info               = MVM_op_get_op(MVM_OP_sp_fastinvoke_v);
                    }
                    else {
                        new_operands[0]         = ins->operands[0];
                        new_operands[1]         = ins->operands[1];
                        new_operands[2].lit_i16 = spesh_cand;
                        ins->operands           = new_operands;
                        switch (ins->info->opcode) {
                        case MVM_OP_invoke_i:
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_i);
                            break;
                        case MVM_OP_invoke_n:
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_n);
                            break;
                        case MVM_OP_invoke_s:
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_s);
                            break;
                        case MVM_OP_invoke_o:
                            ins->info = MVM_op_get_op(MVM_OP_sp_fastinvoke_o);
                            break;
                        default:
                            MVM_exception_throw_adhoc(tc, "Spesh: unhandled invoke instruction");
                        }
                    }
                }
            }
        }
    }
}
Пример #12
0
static void instrument_graph_with_breakpoints(MVMThreadContext *tc, MVMSpeshGraph *g) {
    MVMSpeshBB *bb = g->entry->linear_next;
    MVMuint16 array_slot = 0;

    MVMint32 last_line_number = -2;
    MVMint32 last_filename = -1;

    char *filename_buf = NULL;

    while (bb) {
        MVMSpeshIns *ins = bb->first_ins;
        MVMSpeshIns *breakpoint_ins;

        MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc);
        MVMint64 line_number = -1;
        MVMint64 filename_string_index = -1;

        MVMuint32 file_bp_idx;

        if (bbba) {
            line_number = bbba->line_number;
            filename_string_index = bbba->filename_string_heap_index;
            MVM_free(bbba);
        } else {
            line_number = -1;
            bb = bb->linear_next;
            continue;
        }

        /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */
        while (ins && ins->info->opcode == MVM_SSA_PHI) {
            ins = ins->next;
        }
        if (!ins) ins = bb->last_ins;

        /* Jumplists require the target BB to start in the goto op.
         * We must not break this, or we cause the interpreter to derail */
        if (bb->last_ins->info->opcode == MVM_OP_jumplist) {
            MVMint16 to_skip = bb->num_succ;
            for (; to_skip > 0; to_skip--) {
                bb = bb->linear_next;
            }
            continue;
        }

        if (line_number >= 0) {
            breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
            breakpoint_ins->info        = MVM_op_get_op(MVM_OP_breakpoint);
            breakpoint_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));

            if (last_filename != filename_string_index) {
                if (filename_buf)
                    MVM_free(filename_buf);
                filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index));
            }

            MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx);

            breakpoint_ins->operands[0].lit_i32 = file_bp_idx;
            breakpoint_ins->operands[1].lit_i32 = line_number;

            last_filename = filename_string_index;

            MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, breakpoint_ins);
        }

        /* Now go through instructions to see if any are annotated with a
         * precise filename/lineno as well. */
        while (ins) {
            MVMSpeshAnn *ann = ins->annotations;

            while (ann) {
                if (ann->type == MVM_SPESH_ANN_LINENO) {
                    /* We are very likely to have one instruction here that has
                     * the same annotation as the bb itself. We skip that one.*/
                    if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) {
                        break;
                    }

                    line_number = ann->data.lineno.line_number;
                    filename_string_index = ann->data.lineno.filename_string_index;

                    /*breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));*/
                    /*breakpoint_ins->info        = MVM_op_get_op(MVM_OP_breakpoint);*/
                    /*breakpoint_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));*/

                    if (last_filename != filename_string_index) {
                        if (filename_buf)
                            MVM_free(filename_buf);
                        filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index));
                    }

                    MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx);

                    /*breakpoint_ins->operands[0].lit_i32 = file_bp_idx;*/
                    /*breakpoint_ins->operands[1].lit_i32 = ann->data.lineno.line_number;*/

                    /* XXX insert breakpoint op here, too, maybe? */

                    break;
                }

                ann = ann->next;
            }

            ins = ins->next;
        }

        bb = bb->linear_next;
    }

    if (filename_buf)
        MVM_free(filename_buf);
}
Пример #13
0
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) {
    MVMSpeshBB *bb = g->entry->linear_next;
    MVMuint16 array_slot = 0;

    MVMint32 last_line_number = -2;
    MVMint32 last_filename = -1;

    MVMuint16 allocd_slots  = g->num_bbs * 2;
    char *line_report_store = MVM_calloc(allocd_slots, sizeof(char));

    /* Since we don't know the right size for the line report store
     * up front, we will have to realloc it along the way. After that
     * we havee to fix up the arguments to the coverage log instructions */
    MVMuint32 fixup_alloc = g->num_bbs * 2;
    MVMuint32 fixup_elems = 0;
    MVMuint32 fixup_idx; /* for iterating over the fixup array */
    MVMSpeshIns **to_fixup = MVM_malloc(fixup_alloc * sizeof(MVMSpeshIns*));

    while (bb) {
        MVMSpeshIns *ins = bb->first_ins;
        MVMSpeshIns *log_ins;

        MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc);
        MVMint64 line_number;
        MVMint64 filename_string_index;
        if (bbba) {
            line_number = bbba->line_number;
            filename_string_index = bbba->filename_string_heap_index;
            MVM_free(bbba);
        } else {
            line_number = -1;
            bb = bb->linear_next;
            continue;
        }

        /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */
        while (ins && ins->info->opcode == MVM_SSA_PHI) {
            ins = ins->next;
        }
        if (!ins) ins = bb->last_ins;

        /* Jumplists require the target BB to start in the goto op.
         * We must not break this, or we cause the interpreter to derail */
        if (bb->last_ins->info->opcode == MVM_OP_jumplist) {
            MVMint16 to_skip = bb->num_succ;
            for (; to_skip > 0; to_skip--) {
                bb = bb->linear_next;
            }
            continue;
        }

        log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
        log_ins->info        = MVM_op_get_op(MVM_OP_coverage_log);
        log_ins->operands    = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));

        log_ins->operands[0].lit_str_idx = filename_string_index;
        log_ins->operands[1].lit_i32 = line_number;

        if (last_line_number == line_number && last_filename == filename_string_index) {
            /* Consecutive BBs with the same line number and filename should
             * share one "already reported" slot. */
            log_ins->operands[2].lit_i32 = array_slot;
        } else {
            log_ins->operands[2].lit_i32 = array_slot++;
            last_line_number = line_number;
            last_filename = filename_string_index;

            if (array_slot == allocd_slots) {
                allocd_slots *= 2;
                line_report_store = MVM_realloc(line_report_store, sizeof(char) * allocd_slots);
            }
        }

        to_fixup[fixup_elems++] = log_ins;
        if (fixup_elems == fixup_alloc) {
            fixup_alloc *= 2;
            to_fixup = MVM_realloc(to_fixup, sizeof(MVMSpeshIns*) * fixup_alloc);
        }
        MVM_spesh_manipulate_insert_ins(tc, bb, ins, log_ins);

        /* Now go through instructions to see if any are annotated with a
         * precise filename/lineno as well. */
        while (ins) {
            MVMSpeshAnn *ann = ins->annotations;

            while (ann) {
                if (ann->type == MVM_SPESH_ANN_LINENO) {
                    /* We are very likely to have one instruction here that has
                     * the same annotation as the bb itself. We skip that one.*/
                    if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) {
                        break;
                    }

                    log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
                    log_ins->info        = MVM_op_get_op(MVM_OP_coverage_log);
                    log_ins->operands    = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));

                    log_ins->operands[0].lit_str_idx = ann->data.lineno.filename_string_index;
                    log_ins->operands[1].lit_i32 = ann->data.lineno.line_number;
                    log_ins->operands[2].lit_i32 = array_slot++;

                    if (array_slot == allocd_slots) {
                        allocd_slots *= 2;
                        line_report_store = MVM_realloc(line_report_store, sizeof(char) * allocd_slots);
                    }

                    to_fixup[fixup_elems++] = log_ins;
                    if (fixup_elems == fixup_alloc) {
                        fixup_alloc *= 2;
                        to_fixup = MVM_realloc(to_fixup, sizeof(MVMSpeshIns*) * fixup_alloc);
                    }
                    break;
                }

                ann = ann->next;
            }

            ins = ins->next;
        }

        bb = bb->linear_next;
    }

    line_report_store = MVM_realloc(line_report_store, sizeof(char) * (array_slot + 1));

    for (fixup_idx = 0; fixup_idx < fixup_elems; fixup_idx++) {
        MVMSpeshIns *ins = to_fixup[fixup_idx];

        ins->operands[3].lit_i64 = (uintptr_t)line_report_store;
    }

    if (array_slot == 0) {
        MVM_free(line_report_store);
    }
    MVM_free(to_fixup);
}
Пример #14
0
/* Considers logged types and, if they are stable, adds facts and a guard. */
static void log_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                      MVMSpeshIns *ins, MVMSpeshPlanned *p,
                      MVMSpeshAnn *deopt_one_ann, MVMSpeshAnn *logged_ann) {
    /* See if we have stable type information. For now, we need consistent
     * types, since a mis-match will force a deopt. In the future we may be
     * able to do Basic Block Versioning inspired tricks, like producing two
     * different code paths ahead when there are a small number of options. */
    MVMObject *agg_type = NULL;
    MVMuint32 agg_type_count = 0;
    MVMuint32 agg_type_object = 0;
    MVMuint32 agg_concrete = 0;
    MVMuint32 i;
    for (i = 0; i < p->num_type_stats; i++) {
        MVMSpeshStatsByType *ts = p->type_stats[i];
        MVMuint32 j;
        for (j = 0; j < ts->num_by_offset; j++) {
            if (ts->by_offset[j].bytecode_offset == logged_ann->data.bytecode_offset) {
                /* Go over the logged types. */
                MVMuint32 num_types = ts->by_offset[j].num_types;
                MVMuint32 k;
                for (k = 0; k < num_types; k++) {
                    /* If it's inconsistent with the aggregated type so far,
                     * then first check if the type we're now seeing is either
                     * massively more popular or massively less popular. If
                     * massively less, disregard this one. If massively more,
                     * disregard the previous one. Otherwise, tot up the type
                     * object vs. concrete. */
                    MVMObject *cur_type = ts->by_offset[j].types[k].type;
                    MVMuint32 count = ts->by_offset[j].types[k].count;
                    if (agg_type) {
                        if (agg_type != cur_type) {
                            if (count > 100 * agg_type_count) {
                                /* This one is hugely more popular. */
                                agg_type = cur_type;
                                agg_type_count = 0;
                                agg_concrete = 0;
                                agg_type_object = 0;
                            }
                            else if (agg_type_count > 100 * count) {
                                /* This one is hugely less popular. */
                                continue;
                            }
                            else {
                                /* Unstable types. */
                                return;
                            }
                        }
                    }
                    else {
                        agg_type = cur_type;
                    }
                    agg_type_count += count;
                    if (ts->by_offset[j].types[k].type_concrete)
                        agg_concrete++;
                    else
                        agg_type_object++;
                }

                /* No need to consider searching after this offset. */
                break;
            }
        }
    }
    if (agg_type) {
        MVMSpeshIns *guard;
        MVMSpeshAnn *ann;
        MVMuint16 guard_op;

        /* Generate a new version. We'll use this version for the original,
         * unguarded, value, which we know is the instruction right before
         * the guard, so that makes things rather simple. Thus the facts we
         * will set go on the original register */
        MVMSpeshOperand guard_reg = ins->operands[0];
        MVMSpeshOperand preguard_reg = MVM_spesh_manipulate_new_version(tc, g,
                ins->operands[0].reg.orig);
        MVMSpeshFacts *pre_facts = &g->facts[preguard_reg.reg.orig][preguard_reg.reg.i];
        MVMSpeshFacts *facts = &g->facts[guard_reg.reg.orig][guard_reg.reg.i];
        ins->operands[0] = preguard_reg;
        pre_facts->writer = ins;

        /* Add facts and choose guard op. */
        facts->type = agg_type;
        facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
        if (agg_concrete && !agg_type_object) {
            facts->flags |= MVM_SPESH_FACT_CONCRETE;
            guard_op = MVM_OP_sp_guardconc;
        }
        else if (agg_type_object && !agg_concrete) {
            facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
            guard_op = MVM_OP_sp_guardtype;
        }
        else {
            guard_op = MVM_OP_sp_guard;
        }

        /* Insert guard instruction. */
        guard = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
        guard->info = MVM_op_get_op(guard_op);
        guard->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
        guard->operands[0] = guard_reg;
        guard->operands[1] = preguard_reg;
        guard->operands[2].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
            (MVMCollectable *)agg_type->st);
        guard->operands[3].lit_ui32 = g->deopt_addrs[2 * deopt_one_ann->data.deopt_idx];
        if (ins->next)
            MVM_spesh_manipulate_insert_ins(tc, bb, ins, guard);
        else
            MVM_spesh_manipulate_insert_ins(tc, bb->linear_next, NULL, guard);
        facts->writer = guard;
        MVM_spesh_usages_add_by_reg(tc, g, preguard_reg, guard);

        /* Move deopt annotation to the guard instruction. */
        ann = ins->annotations;
        if (ann == deopt_one_ann) {
            ins->annotations = ann->next;
        }
        else {
            while (ann) {
                if (ann->next == deopt_one_ann) {
                    ann->next = deopt_one_ann->next;
                    break;
                }
                ann = ann->next;
            }
        }
        deopt_one_ann->next = NULL;
        guard->annotations = deopt_one_ann;

        /* Copy deopt usages to the preguard register. */
        {
            MVMSpeshDeoptUseEntry *due = facts->usage.deopt_users;
            while (due) {
                MVM_spesh_usages_add_deopt_usage(tc, g, pre_facts, due->deopt_idx);
                due = due->next;
            }
        }

        /* Add entry in log guards table, and mark facts as depending on it. */
        if (g->num_log_guards % 16 == 0) {
            MVMSpeshLogGuard *orig_log_guards = g->log_guards;
            g->log_guards = MVM_spesh_alloc(tc, g,
                (g->num_log_guards + 16) * sizeof(MVMSpeshLogGuard));
            if (orig_log_guards)
                memcpy(g->log_guards, orig_log_guards,
                    g->num_log_guards * sizeof(MVMSpeshLogGuard));
        }
        g->log_guards[g->num_log_guards].ins = guard;
        g->log_guards[g->num_log_guards].bb = ins->next ? bb : bb->linear_next;
        facts->log_guards = MVM_spesh_alloc(tc, g, sizeof(MVMint32));
        facts->log_guards[0] = g->num_log_guards;
        facts->num_log_guards++;
        g->num_log_guards++;
    }
}