Exemplo n.º 1
0
/* Turns a decont into a set, if we know it's not needed. Also make sure we
 * propagate any needed information. */
static void optimize_decont(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
    if (obj_facts->flags & (MVM_SPESH_FACT_DECONTED | MVM_SPESH_FACT_TYPEOBJ)) {
        ins->info = MVM_op_get_op(MVM_OP_set);
        copy_facts(tc, g, ins->operands[0], ins->operands[1]);
    }
    else {
        MVMSpeshFacts *res_facts;
        if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
            MVMSTable *stable = STABLE(obj_facts->type);
            MVMContainerSpec const *contspec = stable->container_spec;
            if (contspec && contspec->fetch_never_invokes && contspec->spesh) {
                contspec->spesh(tc, stable, g, bb, ins);
            }
        }
        res_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]);
        if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
            res_facts->type   = obj_facts->decont_type;
            res_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
        }
        if (obj_facts->flags & MVM_SPESH_FACT_DECONT_CONCRETE)
            res_facts->flags |= MVM_SPESH_FACT_CONCRETE;
        else if (obj_facts->flags & MVM_SPESH_FACT_DECONT_TYPEOBJ)
            res_facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
    }
}
Exemplo n.º 2
0
/* Propagates information relating to decontainerization. */
static void decont_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins,
                         MVMuint16 out_orig, MVMuint16 out_i, MVMuint16 in_orig,
                         MVMuint16 in_i) {
    MVMSpeshFacts *out_facts = &(g->facts[out_orig][out_i]);
    MVMSpeshFacts *in_facts  = &(g->facts[in_orig][in_i]);

    /* If we know the original is decontainerized already, just copy its
     * info. */
    MVMint32 in_flags = in_facts->flags;
    if ((in_flags & MVM_SPESH_FACT_TYPEOBJ) ||
            ((in_flags & MVM_SPESH_FACT_KNOWN_TYPE) &&
            !in_facts->type->st->container_spec)) {
        copy_facts(tc, g, out_orig, out_i, in_orig, in_i);
        return;
    }

    /* We may also know the original was containerized, and have some facts
     * about its contents. */
    if (in_flags & MVM_SPESH_FACT_KNOWN_DECONT_TYPE) {
        out_facts->type = in_facts->decont_type;
        out_facts->flags |= MVM_SPESH_FACT_KNOWN_TYPE;
    }
    if (in_flags & MVM_SPESH_FACT_DECONT_CONCRETE)
        out_facts->flags |= MVM_SPESH_FACT_CONCRETE;
    else if (in_flags & MVM_SPESH_FACT_DECONT_TYPEOBJ)
        out_facts->flags |= MVM_SPESH_FACT_TYPEOBJ;
    if (in_flags & (MVM_SPESH_FACT_KNOWN_DECONT_TYPE |
                    MVM_SPESH_FACT_DECONT_CONCRETE |
                    MVM_SPESH_FACT_DECONT_TYPEOBJ))
        MVM_spesh_facts_depend(tc, g, out_facts, in_facts);
}
Exemplo n.º 3
0
/* Optimizes a hllize instruction away if the type is known and already in the
 * right HLL, by turning it into a set. */
static void optimize_hllize(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
    MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]);
    if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && obj_facts->type) {
        if (STABLE(obj_facts->type)->hll_owner == g->sf->body.cu->body.hll_config) {
            ins->info = MVM_op_get_op(MVM_OP_set);
            copy_facts(tc, g, ins->operands[0], ins->operands[1]);
        }
    }
}
Exemplo n.º 4
0
/* Visits the blocks in dominator tree order, recursively. */
static void optimize_bb(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb) {
    MVMSpeshCallInfo arg_info;
    MVMint32 i;

    /* Look for instructions that are interesting to optimize. */
    MVMSpeshIns *ins = bb->first_ins;
    while (ins) {
        switch (ins->info->opcode) {
        case MVM_OP_set:
            copy_facts(tc, g, ins->operands[0], ins->operands[1]);
            break;
        case MVM_OP_istrue:
        case MVM_OP_isfalse:
            optimize_istrue_isfalse(tc, g, bb, ins);
            break;
        case MVM_OP_if_i:
        case MVM_OP_unless_i:
        case MVM_OP_if_n:
        case MVM_OP_unless_n:
        case MVM_OP_if_o:
        case MVM_OP_unless_o:
            optimize_iffy(tc, g, ins, bb);
            break;
        case MVM_OP_prepargs:
            arg_info.cs = g->sf->body.cu->body.callsites[ins->operands[0].callsite_idx];
            arg_info.prepargs_ins = ins;
            break;
        case MVM_OP_arg_i:
        case MVM_OP_arg_n:
        case MVM_OP_arg_s:
        case MVM_OP_arg_o: {
            MVMint16 idx = ins->operands[0].lit_i16;
            if (idx < MAX_ARGS_FOR_OPT) {
                arg_info.arg_is_const[idx] = 0;
                arg_info.arg_facts[idx]    = MVM_spesh_get_facts(tc, g, ins->operands[1]);
                arg_info.arg_ins[idx]      = ins;
            }
            break;
        }
        case MVM_OP_argconst_i:
        case MVM_OP_argconst_n:
        case MVM_OP_argconst_s: {
            MVMint16 idx = ins->operands[0].lit_i16;
            if (idx < MAX_ARGS_FOR_OPT) {
                arg_info.arg_is_const[idx] = 1;
                arg_info.arg_ins[idx]      = ins;
            }
            break;
        }
        case MVM_OP_coerce_in:
            optimize_coerce(tc, g, bb, ins);
            break;
        case MVM_OP_invoke_v:
            optimize_call(tc, g, bb, ins, 0, &arg_info);
            break;
        case MVM_OP_invoke_i:
        case MVM_OP_invoke_n:
        case MVM_OP_invoke_s:
        case MVM_OP_invoke_o:
            optimize_call(tc, g, bb, ins, 1, &arg_info);
            break;
        case MVM_OP_islist:
        case MVM_OP_ishash:
        case MVM_OP_isint:
        case MVM_OP_isnum:
        case MVM_OP_isstr:
            optimize_is_reprid(tc, g, ins);
            break;
        case MVM_OP_findmeth:
            optimize_method_lookup(tc, g, ins);
            break;
        case MVM_OP_can:
        case MVM_OP_can_s:
            break; /* XXX This causes problems, Spesh: failed to fix up handlers (-1, 110, 110) */
            /* optimize_can_op(tc, g, bb, ins);
            break; */
        case MVM_OP_create:
            optimize_repr_op(tc, g, bb, ins, 1);
            break;
        case MVM_OP_isconcrete:
            optimize_isconcrete(tc, g, ins);
            break;
        case MVM_OP_istype:
            optimize_istype(tc, g, ins);
            break;
        case MVM_OP_bindattr_i:
        case MVM_OP_bindattr_n:
        case MVM_OP_bindattr_s:
        case MVM_OP_bindattr_o:
        case MVM_OP_bindattrs_i:
        case MVM_OP_bindattrs_n:
        case MVM_OP_bindattrs_s:
        case MVM_OP_bindattrs_o:
            optimize_repr_op(tc, g, bb, ins, 0);
            break;
        case MVM_OP_getattr_i:
        case MVM_OP_getattr_n:
        case MVM_OP_getattr_s:
        case MVM_OP_getattr_o:
        case MVM_OP_getattrs_i:
        case MVM_OP_getattrs_n:
        case MVM_OP_getattrs_s:
        case MVM_OP_getattrs_o:
            optimize_repr_op(tc, g, bb, ins, 1);
            break;
        case MVM_OP_box_i:
        case MVM_OP_box_n:
        case MVM_OP_box_s:
            optimize_repr_op(tc, g, bb, ins, 2);
            break;
        case MVM_OP_unbox_i:
        case MVM_OP_unbox_n:
        case MVM_OP_unbox_s:
            optimize_repr_op(tc, g, bb, ins, 1);
            break;
        case MVM_OP_elems:
            optimize_repr_op(tc, g, bb, ins, 1);
            break;
        case MVM_OP_hllize:
            optimize_hllize(tc, g, ins);
            break;
        case MVM_OP_decont:
            optimize_decont(tc, g, bb, ins);
            break;
        case MVM_OP_assertparamcheck:
            optimize_assertparamcheck(tc, g, bb, ins);
            break;
        case MVM_OP_getlexstatic_o:
            optimize_getlex_known(tc, g, bb, ins);
            break;
        case MVM_OP_getlexperinvtype_o:
            if (specialized_on_invocant(tc, g))
                optimize_getlex_known(tc, g, bb, ins);
            break;
        case MVM_OP_sp_log:
        case MVM_OP_sp_osrfinalize:
            /* Left-over log instruction that didn't become a guard, or OSR
             * finalize instruction; just delete it. */
            MVM_spesh_manipulate_delete_ins(tc, g, bb, ins);
            break;
        }
        ins = ins->next;
    }

    /* Visit children. */
    for (i = 0; i < bb->num_children; i++)
        optimize_bb(tc, g, bb->children[i]);
}
Exemplo n.º 5
0
/* Visits the blocks in dominator tree order, recursively. */
static void add_bb_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                         MVMSpeshPlanned *p) {
    MVMint32 i, is_phi;

    /* Look for instructions that provide or propagate facts. */
    MVMSpeshIns *ins = bb->first_ins;
    while (ins) {
        /* See if there's deopt and logged annotations, and if so add logged
         * facts and guards. */
        MVMSpeshAnn *ann = ins->annotations;
        MVMSpeshAnn *ann_deopt_one = NULL;
        MVMSpeshAnn *ann_logged = NULL;
        while (ann) {
            switch (ann->type) {
                case MVM_SPESH_ANN_DEOPT_ONE_INS:
                    ann_deopt_one = ann;
                    break;
                case MVM_SPESH_ANN_LOGGED:
                    ann_logged = ann;
            }
            ann = ann->next;
        }
        if (p && ann_deopt_one && ann_logged && ins->info->opcode != MVM_OP_speshresolve)
            log_facts(tc, g, bb, ins, p, ann_deopt_one, ann_logged);

        /* Look for ops that are fact-interesting. */
        switch (ins->info->opcode) {
        case MVM_OP_set:
            copy_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_create:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_sp_fastcreate:
        case MVM_OP_sp_fastbox_i:
        case MVM_OP_sp_fastbox_bi:
        case MVM_OP_sp_fastbox_i_ic:
        case MVM_OP_sp_fastbox_bi_ic:
        case MVM_OP_sp_add_I:
        case MVM_OP_sp_sub_I:
        case MVM_OP_sp_mul_I:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ((MVMSTable *)g->spesh_slots[ins->operands[2].lit_i16])->WHAT);
            break;
        case MVM_OP_clone:
            copy_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_box_s:
        case MVM_OP_box_i:
        case MVM_OP_box_n: {
                MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]);
                create_facts(tc, g,
                    ins->operands[0].reg.orig, ins->operands[0].reg.i,
                    ins->operands[2].reg.orig, ins->operands[2].reg.i);
                target_facts->flags |= MVM_SPESH_FACT_KNOWN_BOX_SRC;
                break;
            }
        case MVM_OP_coerce_sI: {
                MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]);
                create_facts(tc, g,
                    ins->operands[0].reg.orig, ins->operands[0].reg.i,
                    ins->operands[2].reg.orig, ins->operands[2].reg.i);
                break;
            }
        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:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[3].reg.orig, ins->operands[3].reg.i);
            break;
        case MVM_OP_neg_I:
        case MVM_OP_abs_I:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[2].reg.orig, ins->operands[2].reg.i);
            break;
        case MVM_OP_nativecallcast:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[2].reg.orig, ins->operands[2].reg.i);
            break;
        case MVM_OP_trunc_u16:
        case MVM_OP_trunc_i16:
            trunc_i16_facts(tc, g, ins);
            break;
        case MVM_OP_coerce_ui:
        case MVM_OP_coerce_iu:
            trunc_i16_facts(tc, g, ins);
            break;
        case MVM_OP_bootint:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTInt);
            break;
        case MVM_OP_bootnum:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTNum);
            break;
        case MVM_OP_bootstr:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTStr);
            break;
        case MVM_OP_bootarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTArray);
            break;
        case MVM_OP_bootintarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTIntArray);
            break;
        case MVM_OP_bootnumarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTNumArray);
            break;
        case MVM_OP_bootstrarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTStrArray);
            break;
        case MVM_OP_boothash:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTHash);
            break;
        case MVM_OP_hllboolfor: {
            MVMSpeshOperand *name_op = &ins->operands[1];
            MVMSpeshFacts *namefacts = &g->facts[name_op->reg.orig][name_op->reg.i];
            if (namefacts->writer && namefacts->writer->info->opcode == MVM_OP_const_s) {
                MVMString *hllname = MVM_cu_string(tc, g->sf->body.cu, ins->operands[1].lit_str_idx);
                MVMHLLConfig *hll = MVM_hll_get_config_for(tc, hllname);
                if (hll->true_value && hll->false_value && STABLE(hll->true_value)->WHAT == STABLE(hll->false_value)->WHAT)
                    create_facts_with_type(tc, g,
                        ins->operands[0].reg.orig, ins->operands[0].reg.i,
                        STABLE(hll->true_value)->WHAT);
            }
            break;
        }
        case MVM_OP_hllbool: {
            MVMHLLConfig *hll = g->sf->body.cu->body.hll_config;
            if (hll->true_value && hll->false_value && STABLE(hll->true_value)->WHAT == STABLE(hll->false_value)->WHAT)
                create_facts_with_type(tc, g,
                    ins->operands[0].reg.orig, ins->operands[0].reg.i,
                    STABLE(hll->true_value)->WHAT);
            break;
        }
        case MVM_OP_hllboxtype_i:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->int_box_type);
            break;
        case MVM_OP_hllboxtype_n:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->num_box_type);
            break;
        case MVM_OP_hllboxtype_s:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->str_box_type);
            break;
        case MVM_OP_hlllist:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->slurpy_array_type);
            break;
        case MVM_OP_hllhash:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->slurpy_hash_type);
            break;
        case MVM_OP_decont:
            decont_facts(tc, g, ins,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_wval:
            wval_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].lit_i16, ins->operands[2].lit_i16);
            break;
        case MVM_OP_wval_wide:
            wval_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].lit_i16, ins->operands[2].lit_i64);
            break;
        case MVM_OP_sp_getwvalfrom:
            wvalfrom_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].lit_i16, ins->operands[2].lit_i64);
            break;
        case MVM_OP_sp_getspeshslot:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                (MVMObject *)g->spesh_slots[ins->operands[1].lit_i16]);
            break;
        case MVM_OP_iter:
            iter_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_newexception:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTException);
            break;
        case MVM_OP_getlexref_i:
        case MVM_OP_getlexref_i32:
        case MVM_OP_getlexref_i16:
        case MVM_OP_getlexref_i8:
        case MVM_OP_getlexref_u32:
        case MVM_OP_getlexref_u16:
        case MVM_OP_getlexref_u8:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->int_lex_ref);
            break;
        case MVM_OP_getlexref_n:
        case MVM_OP_getlexref_n32:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->num_lex_ref);
            break;
        case MVM_OP_getlexref_s:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->str_lex_ref);
            break;
        case MVM_OP_getattrref_i:
        case MVM_OP_getattrsref_i:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->int_attr_ref);
            break;
        case MVM_OP_getattrref_n:
        case MVM_OP_getattrsref_n:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->num_attr_ref);
            break;
        case MVM_OP_getattrref_s:
        case MVM_OP_getattrsref_s:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->str_attr_ref);
            break;
        case MVM_OP_atposref_i:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->int_pos_ref);
            break;
        case MVM_OP_atposref_n:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->num_pos_ref);
            break;
        case MVM_OP_atposref_s:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->str_pos_ref);
            break;

        case MVM_OP_const_i64:
        case MVM_OP_const_i32:
        case MVM_OP_const_i16:
        case MVM_OP_const_i8:
        case MVM_OP_const_n64:
        case MVM_OP_const_n32:
        case MVM_OP_const_i64_32:
        case MVM_OP_const_i64_16:
        case MVM_OP_const_s:
            literal_facts(tc, g, ins);
            break;
        case MVM_OP_sp_getstringfrom:
            getstringfrom_facts(tc, g, ins);
            break;
        case MVM_OP_encode:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[3].reg.orig, ins->operands[3].reg.i);
            break;
        case MVM_OP_encoderep:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[4].reg.orig, ins->operands[4].reg.i);
            break;
        case MVM_OP_setdispatcher:
        case MVM_OP_setdispatcherfor:
            g->sets_dispatcher = 1;
            break;
        case MVM_OP_sp_guard:
        case MVM_OP_sp_guardconc:
        case MVM_OP_sp_guardtype:
        case MVM_OP_sp_guardobj:
        case MVM_OP_sp_guardjustconc:
        case MVM_OP_sp_guardjusttype:
            sp_guard_facts(tc, g, bb, ins);
            break;
        default:
            if (ins->info->opcode == (MVMuint16)-1)
                discover_extop(tc, g, ins);
        }
        ins = ins->next;
    }

    /* Visit children. */
    for (i = 0; i < bb->num_children; i++)
        add_bb_facts(tc, g, bb->children[i], p);
}
Exemplo n.º 6
0
Arquivo: facts.c Projeto: nanis/MoarVM
/* Visits the blocks in dominator tree order, recursively. */
static void add_bb_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
                         MVMint32 cur_deopt_idx) {
    MVMint32 i, is_phi;

    /* Look for instructions that provide or propagate facts. */
    MVMSpeshIns *ins = bb->first_ins;
    while (ins) {
        /* See if there's a deopt annotation, and sync cur_deopt_idx. */
        MVMSpeshAnn *ann = ins->annotations;
        while (ann) {
            if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS ||
                    ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS) {
                cur_deopt_idx = ann->data.deopt_idx;
                break;
            }
            ann = ann->next;
        }

        /* Look through operands for reads and writes. */
        is_phi = ins->info->opcode == MVM_SSA_PHI;
        for (i = 0; i < ins->info->num_operands; i++) {
            /* Reads need usage tracking; if the read is after a deopt point
             * relative to the write then give it an extra usage bump. */
            if ((is_phi && i > 0)
                || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_read_reg)) {
                MVMSpeshFacts *facts = &(g->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i]);
                facts->usages += facts->deopt_idx == cur_deopt_idx ? 1 : 2;
            }

            /* Writes need the current deopt index and the writing instruction
             * to be specified. */
            if ((is_phi && i == 0)
                || (!is_phi && (ins->info->operands[i] & MVM_operand_rw_mask) == MVM_operand_write_reg)) {
                MVMSpeshFacts *facts = &(g->facts[ins->operands[i].reg.orig][ins->operands[i].reg.i]);
                facts->deopt_idx = cur_deopt_idx;
                facts->writer    = ins;
            }
        }

        /* Look for ops that are fact-interesting. */
        switch (ins->info->opcode) {
        case MVM_OP_inc_i:
        case MVM_OP_inc_u:
        case MVM_OP_dec_i:
        case MVM_OP_dec_u:
            /* These all read as well as write a value, so bump usages. */
            g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i - 1].usages++;
            break;
        case MVM_OP_set:
            copy_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_create:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_box_s:
        case MVM_OP_box_i:
        case MVM_OP_box_n: {
                MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]);
                create_facts(tc, g,
                    ins->operands[0].reg.orig, ins->operands[0].reg.i,
                    ins->operands[2].reg.orig, ins->operands[2].reg.i);
                target_facts->flags |= MVM_SPESH_FACT_KNOWN_BOX_SRC;
                break;
            }
        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:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[3].reg.orig, ins->operands[3].reg.i);
            break;
        case MVM_OP_neg_I:
        case MVM_OP_abs_I:
            create_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[2].reg.orig, ins->operands[2].reg.i);
            break;
        case MVM_OP_bootint:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTInt);
            break;
        case MVM_OP_bootnum:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTNum);
            break;
        case MVM_OP_bootstr:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTStr);
            break;
        case MVM_OP_bootarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTArray);
            break;
        case MVM_OP_bootintarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTIntArray);
            break;
        case MVM_OP_bootnumarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTNumArray);
            break;
        case MVM_OP_bootstrarray:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTStrArray);
            break;
        case MVM_OP_boothash:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTHash);
            break;
        case MVM_OP_hllboxtype_i:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->int_box_type);
            break;
        case MVM_OP_hllboxtype_n:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->num_box_type);
            break;
        case MVM_OP_hllboxtype_s:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->str_box_type);
            break;
        case MVM_OP_hlllist:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->slurpy_array_type);
            break;
        case MVM_OP_hllhash:
            object_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                g->sf->body.cu->body.hll_config->slurpy_hash_type);
            break;
        case MVM_OP_decont:
            decont_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_wval:
            wval_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].lit_i16, ins->operands[2].lit_i16);
            break;
        case MVM_OP_wval_wide:
            wval_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].lit_i16, ins->operands[2].lit_i64);
            break;
        case MVM_OP_iter:
            iter_facts(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                ins->operands[1].reg.orig, ins->operands[1].reg.i);
            break;
        case MVM_OP_newexception:
            create_facts_with_type(tc, g,
                ins->operands[0].reg.orig, ins->operands[0].reg.i,
                tc->instance->boot_types.BOOTException);
            break;
        case MVM_OP_const_i64:
        case MVM_OP_const_i32:
        case MVM_OP_const_i16:
        case MVM_OP_const_i8:
        case MVM_OP_const_n64:
        case MVM_OP_const_n32:
        case MVM_OP_const_i64_32:
        case MVM_OP_const_i64_16:
        case MVM_OP_const_s:
            literal_facts(tc, g, ins);
            break;
        case MVM_OP_sp_log: {
            MVMuint16 po = ins->prev
                ? ins->prev->info->opcode
                : bb->pred[0]->last_ins->info->opcode;
            if (po != MVM_OP_getlexstatic_o && po != MVM_OP_getlexperinvtype_o)
                log_facts(tc, g, bb, ins);
            break;
        }
        default:
            if (ins->info->opcode == (MVMuint16)-1)
                discover_extop(tc, g, ins);
        }
        ins = ins->next;
    }

    /* Visit children. */
    for (i = 0; i < bb->num_children; i++)
        add_bb_facts(tc, g, bb->children[i], cur_deopt_idx);
}