MVMint32 MVM_6model_find_method_spesh(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMint32 ss_idx, MVMRegister *res) { /* Missed mono-morph; try cache-only lookup. */ MVMObject *meth = MVM_6model_find_method_cache_only(tc, obj, name); if (!MVM_is_null(tc, meth)) { /* Got it; cache. Must be careful due to threads * reading, races, etc. */ MVMStaticFrame *sf = tc->cur_frame->static_info; uv_mutex_lock(&tc->instance->mutex_spesh_install); if (!tc->cur_frame->effective_spesh_slots[ss_idx + 1]) { MVM_ASSIGN_REF(tc, &(sf->common.header), tc->cur_frame->effective_spesh_slots[ss_idx + 1], (MVMCollectable *)meth); MVM_barrier(); MVM_ASSIGN_REF(tc, &(sf->common.header), tc->cur_frame->effective_spesh_slots[ss_idx], (MVMCollectable *)STABLE(obj)); } uv_mutex_unlock(&tc->instance->mutex_spesh_install); res->o = meth; return 0; } else { /* Fully late-bound. */ MVM_6model_find_method(tc, obj, name, res); return 1; } }
void MVM_6model_can_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *HOW, *find_method, *code; MVMCallsite *findmeth_callsite; MVMint64 can_cached = MVM_6model_can_method_cache_only(tc, obj, name); if (can_cached == 0 || can_cached == 1) { res->i64 = can_cached; return; } /* If no method in cache and the cache is not authoritative, need to make * a late-bound call to find_method. */ HOW = MVM_6model_get_how(tc, STABLE(obj)); find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (MVM_is_null(tc, find_method)) { /* This'll count as a "no"... */ res->i64 = 0; return; } /* Set up the call, using the result register as the target. A little bad * as we're really talking about */ code = MVM_frame_find_invokee(tc, find_method, NULL); findmeth_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_FIND_METHOD); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, findmeth_callsite); tc->cur_frame->special_return = late_bound_can_return; tc->cur_frame->special_return_data = res; tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, findmeth_callsite, tc->cur_frame->args); }
/* Locates a method by name. Returns the method if it exists, or throws an * exception if it can not be found. */ void MVM_6model_find_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *cache, *HOW, *find_method, *code; if (MVM_is_null(tc, obj)) MVM_exception_throw_adhoc(tc, "Cannot call method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* First try to find it in the cache. If we find it, we have a result. * If we don't find it, but the cache is authoritative, then error. */ cache = STABLE(obj)->method_cache; if (cache && IS_CONCRETE(cache)) { MVMObject *meth = MVM_repr_at_key_o(tc, cache, name); if (!MVM_is_null(tc, meth)) { res->o = meth; return; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { MVMObject *handler = MVM_hll_current(tc)->method_not_found_error; if (!MVM_is_null(tc, handler)) { handler = MVM_frame_find_invokee(tc, handler, NULL); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &mnfe_callsite); tc->cur_frame->args[0].o = obj; tc->cur_frame->args[1].s = name; STABLE(handler)->invoke(tc, handler, &mnfe_callsite, tc->cur_frame->args); return; } else { MVM_exception_throw_adhoc(tc, "Cannot find method '%s'", MVM_string_utf8_encode_C_string(tc, name)); } } } /* Otherwise, need to call the find_method method. We make the assumption * that the invocant's meta-object's type is composed. */ HOW = STABLE(obj)->HOW; find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (MVM_is_null(tc, find_method)) MVM_exception_throw_adhoc(tc, "Cannot find method '%s': no method cache and no .^find_method", MVM_string_utf8_encode_C_string(tc, name)); /* Set up the call, using the result register as the target. */ code = MVM_frame_find_invokee(tc, find_method, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, &fm_callsite); tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, &fm_callsite, tc->cur_frame->args); }
void MVM_6model_find_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *cache, *HOW, *find_method, *code; MVMCallsite *findmeth_callsite; if (MVM_is_null(tc, obj)) MVM_exception_throw_adhoc(tc, "Cannot call method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* First try to find it in the cache. If we find it, we have a result. * If we don't find it, but the cache is authoritative, then error. */ cache = get_method_cache(tc, STABLE(obj)); if (cache && IS_CONCRETE(cache)) { MVMObject *meth = MVM_repr_at_key_o(tc, cache, name); if (!MVM_is_null(tc, meth)) { res->o = meth; return; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { die_over_missing_method(tc, obj, name); return; } } /* Otherwise, need to call the find_method method. We make the assumption * that the invocant's meta-object's type is composed. */ HOW = MVM_6model_get_how(tc, STABLE(obj)); find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (MVM_is_null(tc, find_method)) MVM_exception_throw_adhoc(tc, "Cannot find method '%s': no method cache and no .^find_method", MVM_string_utf8_encode_C_string(tc, name)); /* Set up the call, using the result register as the target. */ code = MVM_frame_find_invokee(tc, find_method, NULL); findmeth_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_FIND_METHOD); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, findmeth_callsite); { FindMethodSRData *fm = MVM_malloc(sizeof(FindMethodSRData)); fm->obj = obj; fm->name = name; fm->res = res; tc->cur_frame->special_return = late_bound_find_method_return; tc->cur_frame->special_return_data = fm; tc->cur_frame->mark_special_return_data = mark_find_method_sr_data; } tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, findmeth_callsite, tc->cur_frame->args); }
void MVM_coerce_smart_stringify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *strmeth; const MVMStorageSpec *ss; /* Handle null case. */ if (MVM_is_null(tc, obj)) { res_reg->s = tc->instance->str_consts.empty; return; } /* If it can unbox as a string, that wins right off. */ ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR && IS_CONCRETE(obj)) { res_reg->s = REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); return; } /* Check if there is a Str method. */ strmeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts.Str); if (!MVM_is_null(tc, strmeth)) { /* We need to do the invocation; just set it up with our result reg as * the one for the call. */ MVMObject *code = MVM_frame_find_invokee(tc, strmeth, NULL); MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_STR, inv_arg_callsite); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); return; } /* Otherwise, guess something appropriate. */ if (!IS_CONCRETE(obj)) res_reg->s = tc->instance->str_consts.empty; else { if (REPR(obj)->ID == MVM_REPR_ID_MVMException) res_reg->s = ((MVMException *)obj)->body.message; else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) res_reg->s = MVM_coerce_i_s(tc, REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) res_reg->s = MVM_coerce_n_s(tc, REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else MVM_exception_throw_adhoc(tc, "cannot stringify this"); } }
void MVM_coerce_smart_numify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *nummeth; /* Handle null case. */ if (MVM_is_null(tc, obj)) { res_reg->n64 = 0.0; return; } /* Check if there is a Num method. */ nummeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts.Num); if (!MVM_is_null(tc, nummeth)) { /* We need to do the invocation; just set it up with our result reg as * the one for the call. */ MVMObject *code = MVM_frame_find_invokee(tc, nummeth, NULL); MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_NUM, inv_arg_callsite); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); return; } /* Otherwise, guess something appropriate. */ if (!IS_CONCRETE(obj)) { res_reg->n64 = 0.0; } else { const MVMStorageSpec *ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) res_reg->n64 = (MVMnum64)REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) res_reg->n64 = REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) res_reg->n64 = MVM_coerce_s_n(tc, REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else if (REPR(obj)->ID == MVM_REPR_ID_MVMArray) res_reg->n64 = (MVMnum64)REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (REPR(obj)->ID == MVM_REPR_ID_MVMHash) res_reg->n64 = (MVMnum64)REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else MVM_exception_throw_adhoc(tc, "cannot numify this"); } }
void MVM_6model_can_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *cache, *HOW, *find_method, *code; if (!obj) MVM_exception_throw_adhoc(tc, "Cannot look for method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* First consider method cache. */ cache = STABLE(obj)->method_cache; if (cache && IS_CONCRETE(cache)) { MVMObject *meth = MVM_repr_at_key_o(tc, cache, name); if (meth) { res->i64 = 1; return; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { res->i64 = 0; return; } } /* If no method in cache and the cache is not authoritative, need to make * a late-bound call to find_method. */ HOW = STABLE(obj)->HOW; find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (find_method == NULL) { /* This'll count as a "no"... */ res->i64 = 0; return; } /* Set up the call, using the result register as the target. A little bad * as we're really talking about */ code = MVM_frame_find_invokee(tc, find_method, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, &fm_callsite); tc->cur_frame->special_return = late_bound_can_return; tc->cur_frame->special_return_data = res; tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, &fm_callsite, tc->cur_frame->args); }
/* Checks if an object has a given type, delegating to the type_check or * accepts_type methods as needed. */ static void do_accepts_type_check(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMRegister *res) { MVMObject *HOW = MVM_6model_get_how(tc, STABLE(type)); MVMObject *meth = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.accepts_type); if (!MVM_is_null(tc, meth)) { /* Set up the call, using the result register as the target. */ MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_INT, &tc_callsite); tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = type; tc->cur_frame->args[2].o = obj; STABLE(code)->invoke(tc, code, &tc_callsite, tc->cur_frame->args); return; } else { MVM_exception_throw_adhoc(tc, "Expected 'accepts_type' method, but none found in meta-object"); } }
void MVM_coerce_smart_stringify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *strmeth; /* Handle null case. */ if (!obj) { res_reg->s = tc->instance->str_consts->empty; return; } /* Check if there is a Str method. */ strmeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts->Str); if (strmeth) { /* We need to do the invocation; just set it up with our result reg as * the one for the call. */ MVMObject *code = MVM_frame_find_invokee(tc, strmeth); tc->cur_frame->return_value = res_reg; tc->cur_frame->return_type = MVM_RETURN_STR; tc->cur_frame->return_address = *(tc->interp_cur_op); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, get_inv_callsite(), tc->cur_frame->args); return; } /* Otherwise, guess something appropriate. */ if (!IS_CONCRETE(obj)) res_reg->s = tc->instance->str_consts->empty; else { MVMStorageSpec ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (REPR(obj)->ID == MVM_REPR_ID_MVMString) res_reg->s = (MVMString *)obj; else if (ss.can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) res_reg->s = REPR(obj)->box_funcs->get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (ss.can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) res_reg->s = MVM_coerce_i_s(tc, REPR(obj)->box_funcs->get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else if (ss.can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) res_reg->s = MVM_coerce_n_s(tc, REPR(obj)->box_funcs->get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else MVM_exception_throw_adhoc(tc, "cannot stringify this"); } }
void MVM_coerce_smart_stringify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *strmeth; const MVMStorageSpec *ss; /* Handle null case. */ if (MVM_is_null(tc, obj)) { res_reg->s = tc->instance->str_consts.empty; return; } /* If it can unbox as a string, that wins right off. */ ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR && IS_CONCRETE(obj)) { res_reg->s = REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); return; } /* Check if there is a Str method. */ MVMROOT(tc, obj, { strmeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts.Str); });
/* Performs optimization on a method lookup. If we know the type that we'll * be dispatching on, resolve it right off. If not, add a cache. */ static void optimize_method_lookup(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { /* See if we can resolve the method right off due to knowing the type. */ MVMSpeshFacts *obj_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); MVMint32 resolved = 0; if (obj_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE) { /* Try to resolve. */ MVMString *name = MVM_spesh_get_string(tc, g, ins->operands[2]); MVMObject *meth = MVM_6model_find_method_cache_only(tc, obj_facts->type, name); if (!MVM_is_null(tc, meth)) { /* Could compile-time resolve the method. Add it in a spesh slot. */ MVMint16 ss = MVM_spesh_add_spesh_slot(tc, g, (MVMCollectable *)meth); /* Tweak facts for the target, given we know the method. */ MVMSpeshFacts *meth_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); meth_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; meth_facts->value.o = meth; /* Update the instruction to grab the spesh slot. */ MVM_spesh_get_facts(tc, g, ins->operands[1])->usages--; ins->info = MVM_op_get_op(MVM_OP_sp_getspeshslot); ins->operands[1].lit_i16 = ss; resolved = 1; } } /* If not, add space to cache a single type/method pair, to save hash * lookups in the (common) monomorphic case, and rewrite to caching * version of the instruction. */ if (!resolved) { MVMSpeshOperand *orig_o = ins->operands; ins->info = MVM_op_get_op(MVM_OP_sp_findmeth); ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand)); memcpy(ins->operands, orig_o, 3 * sizeof(MVMSpeshOperand)); ins->operands[3].lit_i16 = MVM_spesh_add_spesh_slot(tc, g, NULL); MVM_spesh_add_spesh_slot(tc, g, NULL); } }
void MVM_6model_istype(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMRegister *res) { MVMObject **cache; MVMSTable *st; MVMint64 mode; /* Null never type-checks. */ if (MVM_is_null(tc, obj)) { res->i64 = 0; return; } st = STABLE(obj); mode = STABLE(type)->mode_flags & MVM_TYPE_CHECK_CACHE_FLAG_MASK; cache = st->type_check_cache; if (cache) { /* We have the cache, so just look for the type object we * want to be in there. */ MVMint64 elems = STABLE(obj)->type_check_cache_length; MVMint64 i; for (i = 0; i < elems; i++) { if (cache[i] == type) { res->i64 = 1; return; } } /* If the type check cache is definitive, we're done. */ if ((mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD) == 0 && (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) == 0) { res->i64 = 0; return; } } /* If we get here, need to call .^type_check on the value we're * checking, unless it's an accepts check. */ if (!cache || (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD)) { MVMObject *HOW = MVM_6model_get_how(tc, st); MVMObject *meth = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.type_check); if (!MVM_is_null(tc, meth)) { /* Set up the call, using the result register as the target. */ MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); MVMCallsite *typecheck_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TYPECHECK); MVM_args_setup_thunk(tc, res, MVM_RETURN_INT, typecheck_callsite); tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].o = type; if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) { AcceptsTypeSRData *atd = MVM_malloc(sizeof(AcceptsTypeSRData)); atd->obj = obj; atd->type = type; atd->res = res; tc->cur_frame->special_return = accepts_type_sr; tc->cur_frame->special_return_data = atd; tc->cur_frame->mark_special_return_data = mark_sr_data; } STABLE(code)->invoke(tc, code, typecheck_callsite, tc->cur_frame->args); return; } } /* If the flag to call .accepts_type on the target value is set, do so. */ if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) { do_accepts_type_check(tc, obj, type, res); } else { /* If all else fails... */ res->i64 = 0; } }