/* If we have to deopt inside of a frame containing inlines, and we're in * an inlined frame at the point we hit deopt, we need to undo the inlining * by switching all levels of inlined frame out for a bunch of frames that * are running the de-optimized code. We may, of course, be in the original, * non-inline, bit of the code - in which case we've nothing to do. */ static void uninline(MVMThreadContext *tc, MVMFrame *f, MVMSpeshCandidate *cand, MVMint32 offset, MVMint32 deopt_offset, MVMFrame *callee) { MVMFrame *last_uninlined = NULL; MVMuint16 last_res_reg; MVMReturnType last_res_type; MVMuint32 last_return_deopt_idx; MVMint32 i; for (i = 0; i < cand->num_inlines; i++) { if (offset >= cand->inlines[i].start && offset < cand->inlines[i].end) { /* Create the frame. */ MVMCode *ucode = (MVMCode *)f->work[cand->inlines[i].code_ref_reg].o; MVMStaticFrame *usf = cand->inlines[i].sf; MVMFrame *uf; if (REPR(ucode)->ID != MVM_REPR_ID_MVMCode) MVM_panic(1, "Deopt: did not find code object when uninlining"); MVMROOT(tc, f, { MVMROOT(tc, callee, { MVMROOT(tc, last_uninlined, { MVMROOT(tc, usf, { uf = MVM_frame_create_for_deopt(tc, usf, ucode); }); }); });
/* If we have to deopt inside of a frame containing inlines, and we're in * an inlined frame at the point we hit deopt, we need to undo the inlining * by switching all levels of inlined frame out for a bunch of frames that * are running the de-optimized code. We may, of course, be in the original, * non-inline, bit of the code - in which case we've nothing to do. */ static void uninline(MVMThreadContext *tc, MVMFrame *f, MVMSpeshCandidate *cand, MVMint32 offset, MVMint32 deopt_offset, MVMFrame *callee) { MVMFrame *last_uninlined = NULL; MVMuint16 last_res_reg; MVMReturnType last_res_type; MVMuint32 last_return_deopt_idx; MVMint32 i; for (i = 0; i < cand->num_inlines; i++) { if (offset >= cand->inlines[i].start && offset < cand->inlines[i].end) { /* Create the frame. */ MVMCode *ucode = cand->inlines[i].code; MVMStaticFrame *usf = ucode->body.sf; MVMFrame *uf = MVM_frame_create_for_deopt(tc, usf, ucode); /*fprintf(stderr, "Recreated frame '%s' (cuid '%s')\n", MVM_string_utf8_encode_C_string(tc, usf->body.name), MVM_string_utf8_encode_C_string(tc, usf->body.cuuid));*/ /* Copy the locals and lexicals into place. */ if (usf->body.num_locals) memcpy(uf->work, f->work + cand->inlines[i].locals_start, usf->body.num_locals * sizeof(MVMRegister)); if (usf->body.num_lexicals) memcpy(uf->env, f->env + cand->inlines[i].lexicals_start, usf->body.num_lexicals * sizeof(MVMRegister)); /* Did we already uninline a frame? */ if (last_uninlined) { /* Yes; multi-level un-inline. Switch it back to deopt'd * code. */ uf->effective_bytecode = uf->static_info->body.bytecode; uf->effective_handlers = uf->static_info->body.handlers; uf->effective_spesh_slots = NULL; uf->spesh_cand = NULL; /* Set up the return location. */ uf->return_address = uf->static_info->body.bytecode + cand->deopts[2 * last_return_deopt_idx]; /* Set result type and register. */ uf->return_type = last_res_type; if (last_res_type == MVM_RETURN_VOID) uf->return_value = NULL; else uf->return_value = uf->work + last_res_reg; /* Set up last uninlined's caller to us. */ last_uninlined->caller = MVM_frame_inc_ref_by_frame(tc, uf); } else { /* First uninlined frame. Are we in the middle of the call * stack (and thus in deopt_all)? */ if (callee) { /* Tweak the callee's caller to the uninlined frame, not * the frame holding the inlinings. */ MVMFrame *orig_caller = callee->caller; callee->caller = MVM_frame_inc_ref_by_frame(tc, uf); MVM_frame_dec_ref(tc, orig_caller); /* Copy over the return location. */ uf->return_address = uf->effective_bytecode + deopt_offset; /* Set result type and register. */ uf->return_type = f->return_type; if (uf->return_type == MVM_RETURN_VOID) { uf->return_value = NULL; } else { MVMuint16 orig_reg = (MVMuint16)(f->return_value - f->work); MVMuint16 ret_reg = orig_reg - cand->inlines[i].locals_start; uf->return_value = uf->work + ret_reg; } } else { /* No, it's the deopt_one case, so this is where we'll point * the interpreter. */ tc->cur_frame = uf; *(tc->interp_cur_op) = uf->effective_bytecode + deopt_offset; *(tc->interp_bytecode_start) = uf->effective_bytecode; *(tc->interp_reg_base) = uf->work; *(tc->interp_cu) = usf->body.cu; } } /* Update tracking variables for last uninline. Note that we know * an inline ends with a goto, which is how we're able to find a * return address offset. */ last_uninlined = uf; last_res_reg = cand->inlines[i].res_reg; last_res_type = cand->inlines[i].res_type; last_return_deopt_idx = cand->inlines[i].return_deopt_idx; } } if (last_uninlined) { /* Set return address, which we need to resolve to the deopt'd one. */ f->return_address = f->static_info->body.bytecode + cand->deopts[2 * last_return_deopt_idx]; /* Set result type and register. */ f->return_type = last_res_type; if (last_res_type == MVM_RETURN_VOID) f->return_value = NULL; else f->return_value = f->work + last_res_reg; /* Set up inliner as the caller, given we now have a direct inline. */ last_uninlined->caller = MVM_frame_inc_ref_by_frame(tc, f); } else { /* Weren't in an inline after all. What kind of deopt? */ if (callee) { /* Deopt all. Move return address. */ f->return_address = f->effective_bytecode + deopt_offset; } else { /* Deopt one. Move interpreter. */ *(tc->interp_cur_op) = f->static_info->body.bytecode + deopt_offset; *(tc->interp_bytecode_start) = f->static_info->body.bytecode; } } }