static void fix_wval(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMSpeshGraph *inlinee, MVMSpeshIns *to_fix) { /* Resolve object, then just put it into a spesh slot. (Could do some * smarter things like trying to see if the SC is referenced by both * compilation units, too.) */ MVMCompUnit *cu = inlinee->sf->body.cu; MVMint16 dep = to_fix->operands[1].lit_i16; MVMint64 idx = to_fix->info->opcode == MVM_OP_wval ? to_fix->operands[2].lit_i16 : to_fix->operands[2].lit_i64; if (dep >= 0 && dep < cu->body.num_scs) { MVMSerializationContext *sc = MVM_sc_get_sc(tc, cu, dep); if (sc) { MVMObject *obj = MVM_sc_get_object(tc, sc, idx); MVMint16 ss = MVM_spesh_add_spesh_slot(tc, inliner, (MVMCollectable *)obj); to_fix->info = MVM_op_get_op(MVM_OP_sp_getspeshslot); to_fix->operands[1].lit_i16 = ss; } else { MVM_oops(tc, "Spesh inline: SC not yet resolved; lookup failed"); } } else { MVM_oops(tc, "Spesh inline: invalid SC index found"); } }
/* Tweak the successor of a BB, also updating the target BBs pred. */ static void tweak_succ(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshBB *new_succ) { if (bb->num_succ == 0) { bb->succ = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *)); bb->num_succ = 1; } if (bb->num_succ == 1) bb->succ[0] = new_succ; else MVM_oops(tc, "Spesh inline: unexpected num_succ"); if (new_succ->num_pred == 0) { new_succ->pred = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB *)); new_succ->num_pred = 1; new_succ->pred[0] = bb; } else { MVMint32 found = 0; MVMint32 i; for (i = 0; i < new_succ->num_pred; i++) if (new_succ->pred[i]->idx + 1 == new_succ->idx) { new_succ->pred[i] = bb; found = 1; break; } if (!found) MVM_oops(tc, "Spesh inline: could not find appropriate pred to update\n"); } }
/* Does a lookup of information held about a synthetic. The synth parameter * must be a synthetic codepoint (that is, negative). The memory returned is * not to be freed by the caller; it also is only valid until the next GC * safe point. */ MVMNFGSynthetic * MVM_nfg_get_synthetic_info(MVMThreadContext *tc, MVMGrapheme32 synth) { MVMNFGState *nfg = tc->instance->nfg; MVMint32 synth_idx = -synth - 1; if (synth >= 0) MVM_oops(tc, "MVM_nfg_get_synthetic_info illegally called on a non-synthetic codepoint.\nRequested codepoint %i.", synth); if (synth_idx >= nfg->num_synthetics) MVM_oops(tc, "MVM_nfg_get_synthetic_info call requested a synthetic codepoint that does not exist.\nRequested synthetic %i when only %i have been created.", -synth, nfg->num_synthetics); return &(nfg->synthetics[synth_idx]); }
/* Finds the deopt index of the return. */ static MVMint32 return_deopt_idx(MVMThreadContext *tc, MVMSpeshIns *invoke_ins) { MVMSpeshAnn *ann = invoke_ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS) return ann->data.deopt_idx; ann = ann->next; } MVM_oops(tc, "Spesh inline: return_deopt_idx failed"); }
static MVMint32 find_deopt_target(MVMThreadContext *tc, MVMFrame *f, MVMint32 deopt_offset) { MVMint32 i; for (i = 0; i < f->spesh_cand->num_deopts * 2; i += 2) { if (f->spesh_cand->deopts[i + 1] == deopt_offset) { return f->spesh_cand->deopts[i]; } } MVM_oops(tc, "find_deopt_target failed for %s (%s)", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid)); }
/* Locates deopt index matching OSR point. */ static MVMint32 get_osr_deopt_index(MVMThreadContext *tc, MVMSpeshCandidate *cand) { /* Calculate offset. */ MVMint32 offset = (*(tc->interp_cur_op) - *(tc->interp_bytecode_start)); /* Locate it in the deopt table. */ MVMint32 i; for (i = 0; i < cand->num_deopts; i++) if (cand->deopts[2 * i] == offset) return i; /* If we couldn't locate it, something is really very wrong. */ MVM_oops(tc, "Spesh: get_osr_deopt_index failed"); }
static void rewrite_returns(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMSpeshGraph *inlinee, MVMSpeshBB *invoke_bb, MVMSpeshIns *invoke_ins) { /* Locate return instructions. */ MVMSpeshBB *bb = inlinee->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMuint16 opcode = ins->info->opcode; switch (opcode) { case MVM_OP_return: if (invoke_ins->info->opcode == MVM_OP_invoke_v) { MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins, invoke_bb->succ[0]); tweak_succ(tc, inliner, bb, invoke_bb->succ[0]); } else { MVM_oops(tc, "Spesh inline: return_v/invoke_[!v] mismatch"); } break; case MVM_OP_return_i: MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins, invoke_bb->succ[0]); tweak_succ(tc, inliner, bb, invoke_bb->succ[0]); rewrite_int_return(tc, inliner, bb, ins, invoke_bb, invoke_ins); break; case MVM_OP_return_n: MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins, invoke_bb->succ[0]); tweak_succ(tc, inliner, bb, invoke_bb->succ[0]); rewrite_num_return(tc, inliner, bb, ins, invoke_bb, invoke_ins); break; case MVM_OP_return_s: MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins, invoke_bb->succ[0]); tweak_succ(tc, inliner, bb, invoke_bb->succ[0]); rewrite_str_return(tc, inliner, bb, ins, invoke_bb, invoke_ins); break; case MVM_OP_return_o: MVM_spesh_manipulate_insert_goto(tc, inliner, bb, ins, invoke_bb->succ[0]); tweak_succ(tc, inliner, bb, invoke_bb->succ[0]); rewrite_obj_return(tc, inliner, bb, ins, invoke_bb, invoke_ins); break; } ins = ins->next; } bb = bb->linear_next; } }
/* De-optimizes the current frame by directly specifying the addresses */ void MVM_spesh_deopt_one_direct(MVMThreadContext *tc, MVMint32 deopt_offset, MVMint32 deopt_target) { MVMFrame *f = tc->cur_frame; if (tc->instance->profiling) MVM_profiler_log_deopt_one(tc); if (f->effective_bytecode != f->static_info->body.bytecode) { deopt_frame(tc, tc->cur_frame, deopt_offset, deopt_target); } else { MVM_oops(tc, "deopt_one_direct failed for %s (%s)", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid)); } }
static void rewrite_obj_return(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *return_bb, MVMSpeshIns *return_ins, MVMSpeshBB *invoke_bb, MVMSpeshIns *invoke_ins) { switch (invoke_ins->info->opcode) { case MVM_OP_invoke_v: MVM_spesh_manipulate_delete_ins(tc, g, return_bb, return_ins); break; case MVM_OP_invoke_o: return_to_set(tc, g, return_ins, invoke_ins->operands[0]); break; default: MVM_oops(tc, "Spesh inline: unhandled case of return_o"); } }
/* De-optimizes the currently executing frame, provided it is specialized and * at a valid de-optimization point. Typically used when a guard fails. */ void MVM_spesh_deopt_one(MVMThreadContext *tc) { MVMFrame *f = tc->cur_frame; if (tc->instance->profiling) MVM_profiler_log_deopt_one(tc); /*fprintf(stderr, "deopt_one requested in frame '%s' (cuid '%s')\n", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid));*/ if (f->effective_bytecode != f->static_info->body.bytecode) { MVMint32 deopt_offset = *(tc->interp_cur_op) - f->effective_bytecode; MVMint32 deopt_target = find_deopt_target(tc, f, deopt_offset); deopt_frame(tc, tc->cur_frame, deopt_offset, deopt_target); } else { MVM_oops(tc, "deopt_one failed for %s (%s)", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid)); } }
/* Ensures that a given compilation unit has access to the specified extop. */ static void demand_extop(MVMThreadContext *tc, MVMCompUnit *target_cu, MVMCompUnit *source_cu, const MVMOpInfo *info) { MVMExtOpRecord *extops; MVMuint16 i, num_extops; MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)target_cu->body.update_mutex); /* See if the target compunit already has the extop. */ extops = target_cu->body.extops; num_extops = target_cu->body.num_extops; for (i = 0; i < num_extops; i++) if (extops[i].info == info) { MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)target_cu->body.update_mutex); return; } /* If not, need to add it. Locate it in the source CU. */ extops = source_cu->body.extops; num_extops = source_cu->body.num_extops; for (i = 0; i < num_extops; i++) { if (extops[i].info == info) { MVMuint32 size = (target_cu->body.num_extops + 1) * sizeof(MVMExtOpRecord); target_cu->body.extops = target_cu->body.extops ? MVM_realloc(target_cu->body.extops, size) : MVM_malloc(size); memcpy(&target_cu->body.extops[target_cu->body.num_extops], &extops[i], sizeof(MVMExtOpRecord)); target_cu->body.num_extops++; MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)target_cu->body.update_mutex); return; } } /* Didn't find it; should be impossible. */ MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)target_cu->body.update_mutex); MVM_oops(tc, "Spesh: inline failed to find source CU extop entry"); }
/* Merges the inlinee's spesh graph into the inliner. */ static void merge_graph(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMSpeshGraph *inlinee, MVMCode *inlinee_code, MVMSpeshIns *invoke_ins) { MVMSpeshFacts **merged_facts; MVMuint16 *merged_fact_counts; MVMint32 i, total_inlines, orig_deopt_addrs; /* If the inliner and inlinee are from different compilation units, we * potentially have to fix up extra things. */ MVMint32 same_comp_unit = inliner->sf->body.cu == inlinee->sf->body.cu; /* Renumber the locals, lexicals, and basic blocks of the inlinee; also * re-write any indexes in annotations that need it. */ MVMSpeshBB *bb = inlinee->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMuint16 opcode = ins->info->opcode; MVMSpeshAnn *ann = ins->annotations; while (ann) { switch (ann->type) { case MVM_SPESH_ANN_FH_START: case MVM_SPESH_ANN_FH_END: case MVM_SPESH_ANN_FH_GOTO: ann->data.frame_handler_index += inliner->num_handlers; break; case MVM_SPESH_ANN_DEOPT_INLINE: ann->data.deopt_idx += inliner->num_deopt_addrs; break; case MVM_SPESH_ANN_INLINE_START: case MVM_SPESH_ANN_INLINE_END: ann->data.inline_idx += inliner->num_inlines; break; } ann = ann->next; } if (opcode == MVM_SSA_PHI) { for (i = 0; i < ins->info->num_operands; i++) ins->operands[i].reg.orig += inliner->num_locals; } else { for (i = 0; i < ins->info->num_operands; i++) { MVMuint8 flags = ins->info->operands[i]; switch (flags & MVM_operand_rw_mask) { case MVM_operand_read_reg: case MVM_operand_write_reg: ins->operands[i].reg.orig += inliner->num_locals; break; case MVM_operand_read_lex: case MVM_operand_write_lex: ins->operands[i].lex.idx += inliner->num_lexicals; break; default: { MVMuint32 type = flags & MVM_operand_type_mask; if (type == MVM_operand_spesh_slot) { ins->operands[i].lit_i16 += inliner->num_spesh_slots; } else if (type == MVM_operand_callsite) { if (!same_comp_unit) fix_callsite(tc, inliner, inlinee, &(ins->operands[i])); } else if (type == MVM_operand_coderef) { if (!same_comp_unit) fix_coderef(tc, inliner, inlinee, &(ins->operands[i])); } else if (type == MVM_operand_str) { if (!same_comp_unit) fix_str(tc, inliner, inlinee, &(ins->operands[i])); } break; } } } } ins = ins->next; } bb->idx += inliner->num_bbs - 1; /* -1 as we won't include entry */ bb->inlined = 1; bb = bb->linear_next; } /* Incorporate the basic blocks by concatening them onto the end of the * linear_next chain of the inliner; skip the inlinee's fake entry BB. */ bb = inliner->entry; while (bb) { if (!bb->linear_next) { /* Found the end; insert and we're done. */ bb->linear_next = inlinee->entry->linear_next; bb = NULL; } else { bb = bb->linear_next; } } /* Merge facts. */ merged_facts = MVM_spesh_alloc(tc, inliner, (inliner->num_locals + inlinee->num_locals) * sizeof(MVMSpeshFacts *)); memcpy(merged_facts, inliner->facts, inliner->num_locals * sizeof(MVMSpeshFacts *)); memcpy(merged_facts + inliner->num_locals, inlinee->facts, inlinee->num_locals * sizeof(MVMSpeshFacts *)); inliner->facts = merged_facts; merged_fact_counts = MVM_spesh_alloc(tc, inliner, (inliner->num_locals + inlinee->num_locals) * sizeof(MVMuint16)); memcpy(merged_fact_counts, inliner->fact_counts, inliner->num_locals * sizeof(MVMuint16)); memcpy(merged_fact_counts + inliner->num_locals, inlinee->fact_counts, inlinee->num_locals * sizeof(MVMuint16)); inliner->fact_counts = merged_fact_counts; /* Copy over spesh slots. */ for (i = 0; i < inlinee->num_spesh_slots; i++) MVM_spesh_add_spesh_slot(tc, inliner, inlinee->spesh_slots[i]); /* If they are from separate compilation units, make another pass through * to fix up on wvals. Note we can't do this in the first pass as we must * not modify the spesh slots once we've got started with the rewrites. * Now we've resolved all that, we're good to map wvals elsewhere into * some extra spesh slots. */ if (!same_comp_unit) { bb = inlinee->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMuint16 opcode = ins->info->opcode; if (opcode == MVM_OP_wval || opcode == MVM_OP_wval_wide) fix_wval(tc, inliner, inlinee, ins); ins = ins->next; } bb = bb->linear_next; } } /* Merge de-opt tables, if needed. */ orig_deopt_addrs = inliner->num_deopt_addrs; if (inlinee->num_deopt_addrs) { assert(inlinee->deopt_addrs != inliner->deopt_addrs); inliner->alloc_deopt_addrs += inlinee->alloc_deopt_addrs; if (inliner->deopt_addrs) inliner->deopt_addrs = MVM_realloc(inliner->deopt_addrs, inliner->alloc_deopt_addrs * sizeof(MVMint32) * 2); else inliner->deopt_addrs = MVM_malloc(inliner->alloc_deopt_addrs * sizeof(MVMint32) * 2); memcpy(inliner->deopt_addrs + inliner->num_deopt_addrs * 2, inlinee->deopt_addrs, inlinee->alloc_deopt_addrs * sizeof(MVMint32) * 2); inliner->num_deopt_addrs += inlinee->num_deopt_addrs; } /* Merge inlines table, and add us an entry too. */ total_inlines = inliner->num_inlines + inlinee->num_inlines + 1; inliner->inlines = inliner->num_inlines ? MVM_realloc(inliner->inlines, total_inlines * sizeof(MVMSpeshInline)) : MVM_malloc(total_inlines * sizeof(MVMSpeshInline)); memcpy(inliner->inlines + inliner->num_inlines, inlinee->inlines, inlinee->num_inlines * sizeof(MVMSpeshInline)); for (i = inliner->num_inlines; i < total_inlines - 1; i++) { inliner->inlines[i].locals_start += inliner->num_locals; inliner->inlines[i].lexicals_start += inliner->num_lexicals; inliner->inlines[i].return_deopt_idx += orig_deopt_addrs; } inliner->inlines[total_inlines - 1].code = inlinee_code; inliner->inlines[total_inlines - 1].g = inlinee; inliner->inlines[total_inlines - 1].locals_start = inliner->num_locals; inliner->inlines[total_inlines - 1].lexicals_start = inliner->num_lexicals; switch (invoke_ins->info->opcode) { case MVM_OP_invoke_v: inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_VOID; break; case MVM_OP_invoke_o: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_OBJ; break; case MVM_OP_invoke_i: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_INT; break; case MVM_OP_invoke_n: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_NUM; break; case MVM_OP_invoke_s: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_STR; break; default: MVM_oops(tc, "Spesh inline: unknown invoke instruction"); } inliner->inlines[total_inlines - 1].return_deopt_idx = return_deopt_idx(tc, invoke_ins); inliner->num_inlines = total_inlines; /* Create/update per-specialization local and lexical type maps. */ if (!inliner->local_types) { MVMint32 local_types_size = inliner->num_locals * sizeof(MVMuint16); inliner->local_types = MVM_malloc(local_types_size); memcpy(inliner->local_types, inliner->sf->body.local_types, local_types_size); } inliner->local_types = MVM_realloc(inliner->local_types, (inliner->num_locals + inlinee->num_locals) * sizeof(MVMuint16)); memcpy(inliner->local_types + inliner->num_locals, inlinee->local_types ? inlinee->local_types : inlinee->sf->body.local_types, inlinee->num_locals * sizeof(MVMuint16)); if (!inliner->lexical_types) { MVMint32 lexical_types_size = inliner->num_lexicals * sizeof(MVMuint16); inliner->lexical_types = MVM_malloc(lexical_types_size); memcpy(inliner->lexical_types, inliner->sf->body.lexical_types, lexical_types_size); } inliner->lexical_types = MVM_realloc(inliner->lexical_types, (inliner->num_lexicals + inlinee->num_lexicals) * sizeof(MVMuint16)); memcpy(inliner->lexical_types + inliner->num_lexicals, inlinee->lexical_types ? inlinee->lexical_types : inlinee->sf->body.lexical_types, inlinee->num_lexicals * sizeof(MVMuint16)); /* Merge handlers. */ if (inlinee->num_handlers) { MVMuint32 total_handlers = inliner->num_handlers + inlinee->num_handlers; if (inliner->handlers == inliner->sf->body.handlers) { /* Original handlers table; need a copy. */ MVMFrameHandler *new_handlers = MVM_malloc(total_handlers * sizeof(MVMFrameHandler)); memcpy(new_handlers, inliner->handlers, inliner->num_handlers * sizeof(MVMFrameHandler)); inliner->handlers = new_handlers; } else { /* Probably already did some inlines into this frame; resize. */ inliner->handlers = MVM_realloc(inliner->handlers, total_handlers * sizeof(MVMFrameHandler)); } memcpy(inliner->handlers + inliner->num_handlers, inlinee->handlers, inlinee->num_handlers * sizeof(MVMFrameHandler)); for (i = inliner->num_handlers; i < total_handlers; i++) { inliner->handlers[i].block_reg += inliner->num_locals; inliner->handlers[i].label_reg += inliner->num_locals; } } /* Update total locals, lexicals, basic blocks, and handlers of the * inliner. */ inliner->num_bbs += inlinee->num_bbs - 1; inliner->num_locals += inlinee->num_locals; inliner->num_lexicals += inlinee->num_lexicals; inliner->num_handlers += inlinee->num_handlers; }
/* Writes instructions within a basic block boundary. */ static void write_instructions(MVMThreadContext *tc, MVMSpeshGraph *g, SpeshWriterState *ws, MVMSpeshBB *bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMint32 i; /* Process any annotations. */ MVMSpeshAnn *ann = ins->annotations; MVMSpeshAnn *deopt_one_ann = NULL; MVMSpeshAnn *deopt_all_ann = NULL; MVMSpeshAnn *deopt_inline_ann = NULL; while (ann) { switch (ann->type) { case MVM_SPESH_ANN_FH_START: ws->handlers[ann->data.frame_handler_index].start_offset = ws->bytecode_pos; break; case MVM_SPESH_ANN_FH_END: ws->handlers[ann->data.frame_handler_index].end_offset = ws->bytecode_pos; break; case MVM_SPESH_ANN_FH_GOTO: ws->handlers[ann->data.frame_handler_index].goto_offset = ws->bytecode_pos; break; case MVM_SPESH_ANN_DEOPT_ONE_INS: deopt_one_ann = ann; break; case MVM_SPESH_ANN_DEOPT_ALL_INS: deopt_all_ann = ann; break; case MVM_SPESH_ANN_INLINE_START: g->inlines[ann->data.inline_idx].start = ws->bytecode_pos; break; case MVM_SPESH_ANN_INLINE_END: g->inlines[ann->data.inline_idx].end = ws->bytecode_pos; break; case MVM_SPESH_ANN_DEOPT_INLINE: deopt_inline_ann = ann; break; case MVM_SPESH_ANN_DEOPT_OSR: g->deopt_addrs[2 * ann->data.deopt_idx + 1] = ws->bytecode_pos; break; } ann = ann->next; } if (ins->info->opcode != MVM_SSA_PHI) { /* Real instruction, not a phi. Emit opcode. */ if (ins->info->opcode == (MVMuint16)-1) { /* Ext op; resolve. */ MVMExtOpRecord *extops = g->sf->body.cu->body.extops; MVMuint16 num_extops = g->sf->body.cu->body.num_extops; MVMint32 found = 0; for (i = 0; i < num_extops; i++) { if (extops[i].info == ins->info) { write_int16(ws, MVM_OP_EXT_BASE + i); found = 1; break; } } if (!found) MVM_oops(tc, "Spesh: failed to resolve extop in code-gen"); } else { /* Core op. */ write_int16(ws, ins->info->opcode); } /* Write out operands. */ for (i = 0; i < ins->info->num_operands; i++) { MVMuint8 flags = ins->info->operands[i]; MVMuint8 rw = flags & MVM_operand_rw_mask; switch (rw) { case MVM_operand_read_reg: case MVM_operand_write_reg: write_int16(ws, ins->operands[i].reg.orig); break; case MVM_operand_read_lex: case MVM_operand_write_lex: write_int16(ws, ins->operands[i].lex.idx); write_int16(ws, ins->operands[i].lex.outers); break; case MVM_operand_literal: { MVMuint8 type = flags & MVM_operand_type_mask; switch (type) { case MVM_operand_int8: write_int8(ws, ins->operands[i].lit_i8); break; case MVM_operand_int16: write_int16(ws, ins->operands[i].lit_i16); break; case MVM_operand_int32: write_int32(ws, ins->operands[i].lit_i32); break; case MVM_operand_int64: write_int64(ws, ins->operands[i].lit_i64); break; case MVM_operand_num32: write_num32(ws, ins->operands[i].lit_n32); break; case MVM_operand_num64: write_num64(ws, ins->operands[i].lit_n64); break; case MVM_operand_callsite: write_int16(ws, ins->operands[i].callsite_idx); break; case MVM_operand_coderef: write_int16(ws, ins->operands[i].coderef_idx); break; case MVM_operand_str: write_int32(ws, ins->operands[i].lit_str_idx); break; case MVM_operand_ins: { MVMint32 offset = ws->bb_offsets[ins->operands[i].ins_bb->idx]; if (offset >= 0) { /* Already know where it is, so just write it. */ write_int32(ws, offset); } else { /* Need to fix it up. */ if (ws->num_fixups == ws->alloc_fixups) { ws->alloc_fixups *= 2; ws->fixup_locations = MVM_realloc(ws->fixup_locations, ws->alloc_fixups * sizeof(MVMint32)); ws->fixup_bbs = MVM_realloc(ws->fixup_bbs, ws->alloc_fixups * sizeof(MVMSpeshBB *)); } ws->fixup_locations[ws->num_fixups] = ws->bytecode_pos; ws->fixup_bbs[ws->num_fixups] = ins->operands[i].ins_bb; write_int32(ws, 0); ws->num_fixups++; } break; } case MVM_operand_spesh_slot: write_int16(ws, ins->operands[i].lit_i16); break; default: MVM_oops(tc, "Spesh: unknown operand type %d in codegen (op %s)", (int)type, ins->info->name); } } break; default: MVM_oops(tc, "Spesh: unknown operand type in codegen"); } } } /* If there was a deopt point annotation, update table. */ if (deopt_one_ann) g->deopt_addrs[2 * deopt_one_ann->data.deopt_idx + 1] = ws->bytecode_pos; if (deopt_all_ann) g->deopt_addrs[2 * deopt_all_ann->data.deopt_idx + 1] = ws->bytecode_pos; if (deopt_inline_ann) g->deopt_addrs[2 * deopt_inline_ann->data.deopt_idx + 1] = ws->bytecode_pos; ins = ins->next; } }
/* Finalizes OSR. */ void MVM_spesh_osr_finalize(MVMThreadContext *tc) { /* Find deopt index using existing deopt table, for entering the updated * code later. */ MVMSpeshCandidate *specialized = tc->cur_frame->spesh_cand; MVMint32 osr_index = get_osr_deopt_finalize_index(tc, specialized); MVMJitCode *jc; /* Finish up the specialization. */ MVM_spesh_candidate_specialize(tc, tc->cur_frame->static_info, specialized); /* Resize work area if needed. */ if (specialized->num_locals > tc->cur_frame->static_info->body.num_locals) { /* Resize work area. */ MVMRegister *new_work = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, specialized->work_size); memcpy(new_work, tc->cur_frame->work, tc->cur_frame->static_info->body.num_locals * sizeof(MVMRegister)); MVM_fixed_size_free(tc, tc->instance->fsa, tc->cur_frame->allocd_work, tc->cur_frame->work); tc->cur_frame->work = new_work; tc->cur_frame->allocd_work = specialized->work_size; tc->cur_frame->args = tc->cur_frame->work + specialized->num_locals; } /* Resize environment if needed. */ if (specialized->num_lexicals > tc->cur_frame->static_info->body.num_lexicals) { MVMRegister *new_env = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, specialized->env_size); if (tc->cur_frame->allocd_env) { memcpy(new_env, tc->cur_frame->env, tc->cur_frame->static_info->body.num_lexicals * sizeof(MVMRegister)); MVM_fixed_size_free(tc, tc->instance->fsa, tc->cur_frame->allocd_env, tc->cur_frame->env); } tc->cur_frame->env = new_env; tc->cur_frame->allocd_env = specialized->env_size; } /* Sync frame with updates. */ tc->cur_frame->effective_bytecode = specialized->bytecode; tc->cur_frame->effective_handlers = specialized->handlers; tc->cur_frame->effective_spesh_slots = specialized->spesh_slots; tc->cur_frame->spesh_log_slots = NULL; tc->cur_frame->spesh_log_idx = -1; /* Sync interpreter with updates. */ jc = specialized->jitcode; if (jc && jc->num_deopts) { MVMint32 i; *(tc->interp_bytecode_start) = specialized->jitcode->bytecode; *(tc->interp_cur_op) = specialized->jitcode->bytecode; for (i = 0; i < jc->num_deopts; i++) { if (jc->deopts[i].idx == osr_index) { tc->cur_frame->jit_entry_label = jc->labels[jc->deopts[i].label]; break; } } if (i == jc->num_deopts) MVM_oops(tc, "JIT: Could not find OSR label"); if (tc->instance->profiling) MVM_profiler_log_osr(tc, 1); } else { *(tc->interp_bytecode_start) = specialized->bytecode; *(tc->interp_cur_op) = specialized->bytecode + specialized->deopts[2 * osr_index + 1]; if (tc->instance->profiling) MVM_profiler_log_osr(tc, 0); } *(tc->interp_reg_base) = tc->cur_frame->work; /* Tweak frame invocation count so future invocations will use the code * produced by OSR. */ tc->cur_frame->static_info->body.invocations += tc->cur_frame->static_info->body.spesh_threshold; }
/* Does the jump into the optimized code. */ void perform_osr(MVMThreadContext *tc, MVMSpeshCandidate *specialized) { MVMJitCode *jit_code; MVMint32 num_locals; /* Work out the OSR deopt index, to locate the entry point. */ MVMint32 osr_index = get_osr_deopt_index(tc, specialized); #if MVM_LOG_OSR fprintf(stderr, "Performing OSR of frame '%s' (cuid: %s) at index %d\n", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid), osr_index); #endif jit_code = specialized->jitcode; num_locals = jit_code && jit_code->local_types ? jit_code->num_locals : specialized->num_locals; /* Resize work area if needed. */ if (specialized->work_size > tc->cur_frame->allocd_work) { /* Resize work area. */ MVMRegister *new_work = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, specialized->work_size); MVMRegister *new_args = new_work + num_locals; memcpy(new_work, tc->cur_frame->work, tc->cur_frame->static_info->body.num_locals * sizeof(MVMRegister)); memcpy(new_args, tc->cur_frame->args, tc->cur_frame->static_info->body.cu->body.max_callsite_size * sizeof(MVMRegister)); MVM_fixed_size_free(tc, tc->instance->fsa, tc->cur_frame->allocd_work, tc->cur_frame->work); tc->cur_frame->work = new_work; tc->cur_frame->allocd_work = specialized->work_size; tc->cur_frame->args = new_args; #if MVM_LOG_OSR fprintf(stderr, "OSR resized work area of frame '%s' (cuid: %s)\n", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid)); #endif } else if (specialized->work_size > tc->cur_frame->static_info->body.work_size) { size_t keep_bytes = tc->cur_frame->static_info->body.num_locals * sizeof(MVMRegister); size_t to_null = specialized->work_size - keep_bytes; memset((char *)tc->cur_frame->work + keep_bytes, 0, to_null); } /* Resize environment if needed. */ if (specialized->num_lexicals > tc->cur_frame->static_info->body.num_lexicals) { MVMRegister *new_env = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, specialized->env_size); if (tc->cur_frame->allocd_env) { memcpy(new_env, tc->cur_frame->env, tc->cur_frame->static_info->body.num_lexicals * sizeof(MVMRegister)); MVM_fixed_size_free(tc, tc->instance->fsa, tc->cur_frame->allocd_env, tc->cur_frame->env); } tc->cur_frame->env = new_env; tc->cur_frame->allocd_env = specialized->env_size; #if MVM_LOG_OSR fprintf(stderr, "OSR resized environment of frame '%s' (cuid: %s)\n", MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.name), MVM_string_utf8_encode_C_string(tc, tc->cur_frame->static_info->body.cuuid)); #endif } else if (specialized->env_size > tc->cur_frame->static_info->body.env_size) { size_t keep_bytes = tc->cur_frame->static_info->body.num_lexicals * sizeof(MVMRegister); size_t to_null = specialized->env_size - keep_bytes; memset((char *)tc->cur_frame->env + keep_bytes, 0, to_null); } /* Set up frame to point to spesh candidate/slots. */ tc->cur_frame->effective_spesh_slots = specialized->spesh_slots; tc->cur_frame->spesh_cand = specialized; /* Move into the optimized (and maybe JIT-compiled) code. */ if (jit_code && jit_code->num_deopts) { MVMint32 i; *(tc->interp_bytecode_start) = jit_code->bytecode; *(tc->interp_cur_op) = jit_code->bytecode; for (i = 0; i < jit_code->num_deopts; i++) { if (jit_code->deopts[i].idx == osr_index) { tc->cur_frame->jit_entry_label = jit_code->labels[jit_code->deopts[i].label]; break; } } if (i == jit_code->num_deopts) MVM_oops(tc, "JIT: Could not find OSR label"); if (tc->instance->profiling) MVM_profiler_log_osr(tc, 1); } else { *(tc->interp_bytecode_start) = specialized->bytecode; *(tc->interp_cur_op) = specialized->bytecode + specialized->deopts[2 * osr_index + 1]; if (tc->instance->profiling) MVM_profiler_log_osr(tc, 0); } *(tc->interp_reg_base) = tc->cur_frame->work; }
static void pop_inline(MVMThreadContext *tc, InlineIndexStack *stack) { stack->cur_depth--; if (stack->cur_depth < -1) MVM_oops(tc, "Too many levels of inlining popped"); }
static void push_inline(MVMThreadContext *tc, InlineIndexStack *stack, MVMint32 idx) { if (stack->cur_depth == 63) MVM_oops(tc, "Too many levels of inlining to dump"); stack->cur_depth++; stack->inline_idx[stack->cur_depth] = idx; }
void MVM_jit_expr_load_operands(MVMThreadContext *tc, MVMJitExprTree *tree, MVMSpeshIns *ins, struct ValueDefinition *values, MVMint32 *operands) { MVMint32 i; for (i = 0; i < ins->info->num_operands; i++) { MVMSpeshOperand opr = ins->operands[i]; MVMint8 opr_kind = ins->info->operands[i]; switch(opr_kind & MVM_operand_rw_mask) { case MVM_operand_read_reg: if (values[opr.reg.orig].node >= 0) { operands[i] = values[opr.reg.orig].node; } else { MVMint32 addr = MVM_jit_expr_add_regaddr(tc, tree, opr.reg.orig); operands[i] = MVM_jit_expr_add_load(tc, tree, addr); values[opr.reg.orig].node = operands[i]; values[opr.reg.orig].addr = addr; values[opr.reg.orig].root = -1; /* load is not part of a root */ } break; case MVM_operand_write_reg: /* get address of register to write */ operands[i] = MVM_jit_expr_add_regaddr(tc, tree, opr.reg.orig); break; case MVM_operand_literal: operands[i] = MVM_jit_expr_add_const(tc, tree, opr, ins->info->operands[i]); break; case MVM_operand_read_lex: { MVMint32 addr = MVM_jit_expr_add_lexaddr(tc, tree, opr.lex.outers, opr.lex.idx); operands[i] = MVM_jit_expr_add_load(tc, tree, addr); break; } case MVM_operand_write_lex: operands[i] = MVM_jit_expr_add_lexaddr(tc, tree, opr.lex.outers, opr.lex.idx); break; default: continue; } if (operands[i] >= tree->nodes_num || operands[i] < 0) { MVM_oops(tc, "JIT: something is wrong with operand loading"); } } /* A HACK. * * dec_i and inc_i have a single operand that acts both as input and output. * This is marked only as an output operand, though. Thus, we load the * address here, and define the value later. However, if we have multiple of * these in sequence, each will load the old value from memory, disregarding * the value that an earlier operator has defined, i.e. losing the update. * That's a bug, and this tries to fix it, by forcing a 'split' between the * input and the output operand. */ if (ins_has_single_input_output_operand(ins)) { MVMuint16 reg = ins->operands[0].reg.orig; if (values[reg].node >= 0) { operands[1] = values[reg].node; } else { /* operands[0] has the address */ operands[1] = MVM_jit_expr_add_load(tc, tree, operands[0]); /* no need to insert it in the table since it will be directly * overwritten */ } } }
/* Re-writes argument passing and parameter taking instructions to simple * register set operations. */ static void rewrite_args(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMSpeshGraph *inlinee, MVMSpeshBB *invoke_bb, MVMSpeshCallInfo *call_info) { /* Look for param-taking instructions. Track what arg instructions we * use in the process. */ MVMSpeshBB *bb = inlinee->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMuint16 opcode = ins->info->opcode; MVMSpeshIns *next = ins->next; switch (opcode) { case MVM_OP_sp_getarg_o: case MVM_OP_sp_getarg_i: case MVM_OP_sp_getarg_n: case MVM_OP_sp_getarg_s: { MVMuint16 idx = ins->operands[1].lit_i16; MVMSpeshIns *arg_ins = call_info->arg_ins[idx]; switch (arg_ins->info->opcode) { case MVM_OP_arg_i: case MVM_OP_arg_n: case MVM_OP_arg_s: case MVM_OP_arg_o: /* Arg passer just becomes a set instruction; delete the * parameter-taking instruction. */ arg_ins->info = MVM_op_get_op(MVM_OP_set); arg_ins->operands[0] = ins->operands[0]; MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins); MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++; break; case MVM_OP_argconst_i: arg_ins->info = MVM_op_get_op(MVM_OP_const_i64); arg_ins->operands[0] = ins->operands[0]; MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins); MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++; break; case MVM_OP_argconst_n: arg_ins->info = MVM_op_get_op(MVM_OP_const_n64); arg_ins->operands[0] = ins->operands[0]; MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins); MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++; break; case MVM_OP_argconst_s: arg_ins->info = MVM_op_get_op(MVM_OP_const_s); arg_ins->operands[0] = ins->operands[0]; MVM_spesh_manipulate_delete_ins(tc, inliner, bb, ins); MVM_spesh_get_facts(tc, inliner, arg_ins->operands[0])->usages++; break; default: MVM_oops(tc, "Spesh inline: unhandled arg instruction %d", arg_ins->info->opcode); } break; } } ins = next; } bb = bb->linear_next; } /* Delete the prepargs instruction. */ MVM_spesh_manipulate_delete_ins(tc, inliner, invoke_bb, call_info->prepargs_ins); }
static void fix_coderef(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMSpeshGraph *inlinee, MVMSpeshOperand *to_fix) { MVM_oops(tc, "Spesh inline: fix_coderef NYI"); }
/* Generate bytecode from a spesh graph. */ MVMSpeshCode * MVM_spesh_codegen(MVMThreadContext *tc, MVMSpeshGraph *g) { MVMSpeshCode *res; MVMSpeshBB *bb; MVMint32 i, hanlen; /* Initialize writer state. */ SpeshWriterState *ws = MVM_malloc(sizeof(SpeshWriterState)); ws->bytecode_pos = 0; ws->bytecode_alloc = 1024; ws->bytecode = MVM_malloc(ws->bytecode_alloc); ws->bb_offsets = MVM_malloc(g->num_bbs * sizeof(MVMint32)); ws->num_fixups = 0; ws->alloc_fixups = 64; ws->fixup_locations = MVM_malloc(ws->alloc_fixups * sizeof(MVMint32)); ws->fixup_bbs = MVM_malloc(ws->alloc_fixups * sizeof(MVMSpeshBB *)); for (i = 0; i < g->num_bbs; i++) ws->bb_offsets[i] = -1; /* Create copy of handlers, and -1 all offsets so we can catch missing * updates. */ hanlen = g->num_handlers * sizeof(MVMFrameHandler); if (hanlen) { ws->handlers = MVM_malloc(hanlen); memcpy(ws->handlers, g->handlers, hanlen); for (i = 0; i < g->num_handlers; i++) { ws->handlers[i].start_offset = -1; ws->handlers[i].end_offset = -1; ws->handlers[i].goto_offset = -1; } } else { ws->handlers = NULL; } /* -1 all the deopt targets, so we'll easily catch those that don't get * mapped if we try to use them. Same for inlines. */ for (i = 0; i < g->num_deopt_addrs; i++) g->deopt_addrs[i * 2 + 1] = -1; for (i = 0; i < g->num_inlines; i++) { g->inlines[i].start = -1; g->inlines[i].end = -1; } /* Write out each of the basic blocks, in linear order. Skip the first, * dummy, block. */ bb = g->entry->linear_next; while (bb) { ws->bb_offsets[bb->idx] = ws->bytecode_pos; write_instructions(tc, g, ws, bb); bb = bb->linear_next; } /* Fixup labels we were too early for. */ for (i = 0; i < ws->num_fixups; i++) *((MVMuint32 *)(ws->bytecode + ws->fixup_locations[i])) = ws->bb_offsets[ws->fixup_bbs[i]->idx]; /* Ensure all handlers got fixed up. */ for (i = 0; i < g->num_handlers; i++) { if (ws->handlers[i].start_offset == -1 || ws->handlers[i].end_offset == -1 || ws->handlers[i].goto_offset == -1) MVM_oops(tc, "Spesh: failed to fix up handlers (%d, %d, %d)", (int)ws->handlers[i].start_offset, (int)ws->handlers[i].end_offset, (int)ws->handlers[i].goto_offset); } /* Ensure all inlines got fixed up. */ for (i = 0; i < g->num_inlines; i++) if (g->inlines[i].start == -1 || g->inlines[i].end == -1) MVM_oops(tc, "Spesh: failed to fix up inline %d", i); /* Produce result data structure. */ res = MVM_malloc(sizeof(MVMSpeshCode)); res->bytecode = ws->bytecode; res->bytecode_size = ws->bytecode_pos; res->handlers = ws->handlers; /* Cleanup. */ MVM_free(ws->bb_offsets); MVM_free(ws->fixup_locations); MVM_free(ws->fixup_bbs); MVM_free(ws); return res; }
/* Merges the inlinee's spesh graph into the inliner. */ static void merge_graph(MVMThreadContext *tc, MVMSpeshGraph *inliner, MVMSpeshGraph *inlinee, MVMCode *inlinee_code, MVMSpeshIns *invoke_ins) { MVMSpeshFacts **merged_facts; MVMuint16 *merged_fact_counts; MVMint32 i, total_inlines, orig_deopt_addrs; MVMSpeshBB *inlinee_first_bb = NULL, *inlinee_last_bb = NULL; MVMint32 active_handlers_at_invoke = 0; /* If the inliner and inlinee are from different compilation units, we * potentially have to fix up extra things. */ MVMint32 same_comp_unit = inliner->sf->body.cu == inlinee->sf->body.cu; /* Renumber the locals, lexicals, and basic blocks of the inlinee; also * re-write any indexes in annotations that need it. */ MVMSpeshBB *bb = inlinee->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMuint16 opcode = ins->info->opcode; MVMSpeshAnn *ann = ins->annotations; while (ann) { switch (ann->type) { case MVM_SPESH_ANN_FH_START: case MVM_SPESH_ANN_FH_END: case MVM_SPESH_ANN_FH_GOTO: ann->data.frame_handler_index += inliner->num_handlers; break; case MVM_SPESH_ANN_DEOPT_INLINE: ann->data.deopt_idx += inliner->num_deopt_addrs; break; case MVM_SPESH_ANN_INLINE_START: case MVM_SPESH_ANN_INLINE_END: ann->data.inline_idx += inliner->num_inlines; break; } ann = ann->next; } if (opcode == MVM_SSA_PHI) { for (i = 0; i < ins->info->num_operands; i++) ins->operands[i].reg.orig += inliner->num_locals; } else { for (i = 0; i < ins->info->num_operands; i++) { MVMuint8 flags = ins->info->operands[i]; switch (flags & MVM_operand_rw_mask) { case MVM_operand_read_reg: case MVM_operand_write_reg: ins->operands[i].reg.orig += inliner->num_locals; break; case MVM_operand_read_lex: case MVM_operand_write_lex: ins->operands[i].lex.idx += inliner->num_lexicals; break; default: { MVMuint32 type = flags & MVM_operand_type_mask; if (type == MVM_operand_spesh_slot) { ins->operands[i].lit_i16 += inliner->num_spesh_slots; } else if (type == MVM_operand_callsite) { if (!same_comp_unit) fix_callsite(tc, inliner, inlinee, &(ins->operands[i])); } else if (type == MVM_operand_coderef) { if (!same_comp_unit) fix_coderef(tc, inliner, inlinee, &(ins->operands[i])); } else if (type == MVM_operand_str) { if (!same_comp_unit) fix_str(tc, inliner, inlinee, &(ins->operands[i])); } break; } } } } ins = ins->next; } bb->idx += inliner->num_bbs - 1; /* -1 as we won't include entry */ bb->inlined = 1; if (!bb->linear_next) inlinee_last_bb = bb; bb = bb->linear_next; } /* Incorporate the basic blocks by concatening them onto the end of the * linear_next chain of the inliner; skip the inlinee's fake entry BB. */ bb = inliner->entry; while (bb) { if (!bb->linear_next) { /* Found the end; insert and we're done. */ bb->linear_next = inlinee_first_bb = inlinee->entry->linear_next; bb = NULL; } else { bb = bb->linear_next; } } /* Merge facts. */ merged_facts = MVM_spesh_alloc(tc, inliner, (inliner->num_locals + inlinee->num_locals) * sizeof(MVMSpeshFacts *)); memcpy(merged_facts, inliner->facts, inliner->num_locals * sizeof(MVMSpeshFacts *)); memcpy(merged_facts + inliner->num_locals, inlinee->facts, inlinee->num_locals * sizeof(MVMSpeshFacts *)); inliner->facts = merged_facts; merged_fact_counts = MVM_spesh_alloc(tc, inliner, (inliner->num_locals + inlinee->num_locals) * sizeof(MVMuint16)); memcpy(merged_fact_counts, inliner->fact_counts, inliner->num_locals * sizeof(MVMuint16)); memcpy(merged_fact_counts + inliner->num_locals, inlinee->fact_counts, inlinee->num_locals * sizeof(MVMuint16)); inliner->fact_counts = merged_fact_counts; /* Copy over spesh slots. */ for (i = 0; i < inlinee->num_spesh_slots; i++) MVM_spesh_add_spesh_slot(tc, inliner, inlinee->spesh_slots[i]); /* If they are from separate compilation units, make another pass through * to fix up on wvals. Note we can't do this in the first pass as we must * not modify the spesh slots once we've got started with the rewrites. * Now we've resolved all that, we're good to map wvals elsewhere into * some extra spesh slots. */ if (!same_comp_unit) { bb = inlinee->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMuint16 opcode = ins->info->opcode; if (opcode == MVM_OP_wval || opcode == MVM_OP_wval_wide) fix_wval(tc, inliner, inlinee, ins); ins = ins->next; } bb = bb->linear_next; } } /* Merge de-opt tables, if needed. */ orig_deopt_addrs = inliner->num_deopt_addrs; if (inlinee->num_deopt_addrs) { assert(inlinee->deopt_addrs != inliner->deopt_addrs); inliner->alloc_deopt_addrs += inlinee->alloc_deopt_addrs; if (inliner->deopt_addrs) inliner->deopt_addrs = MVM_realloc(inliner->deopt_addrs, inliner->alloc_deopt_addrs * sizeof(MVMint32) * 2); else inliner->deopt_addrs = MVM_malloc(inliner->alloc_deopt_addrs * sizeof(MVMint32) * 2); memcpy(inliner->deopt_addrs + inliner->num_deopt_addrs * 2, inlinee->deopt_addrs, inlinee->alloc_deopt_addrs * sizeof(MVMint32) * 2); inliner->num_deopt_addrs += inlinee->num_deopt_addrs; } /* Merge inlines table, and add us an entry too. */ total_inlines = inliner->num_inlines + inlinee->num_inlines + 1; inliner->inlines = inliner->num_inlines ? MVM_realloc(inliner->inlines, total_inlines * sizeof(MVMSpeshInline)) : MVM_malloc(total_inlines * sizeof(MVMSpeshInline)); memcpy(inliner->inlines + inliner->num_inlines, inlinee->inlines, inlinee->num_inlines * sizeof(MVMSpeshInline)); for (i = inliner->num_inlines; i < total_inlines - 1; i++) { inliner->inlines[i].locals_start += inliner->num_locals; inliner->inlines[i].lexicals_start += inliner->num_lexicals; inliner->inlines[i].return_deopt_idx += orig_deopt_addrs; } inliner->inlines[total_inlines - 1].code = inlinee_code; inliner->inlines[total_inlines - 1].g = inlinee; inliner->inlines[total_inlines - 1].locals_start = inliner->num_locals; inliner->inlines[total_inlines - 1].lexicals_start = inliner->num_lexicals; switch (invoke_ins->info->opcode) { case MVM_OP_invoke_v: inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_VOID; break; case MVM_OP_invoke_o: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_OBJ; break; case MVM_OP_invoke_i: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_INT; break; case MVM_OP_invoke_n: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_NUM; break; case MVM_OP_invoke_s: inliner->inlines[total_inlines - 1].res_reg = invoke_ins->operands[0].reg.orig; inliner->inlines[total_inlines - 1].res_type = MVM_RETURN_STR; break; default: MVM_oops(tc, "Spesh inline: unknown invoke instruction"); } inliner->inlines[total_inlines - 1].return_deopt_idx = return_deopt_idx(tc, invoke_ins); inliner->num_inlines = total_inlines; /* Create/update per-specialization local and lexical type maps. */ if (!inliner->local_types) { MVMint32 local_types_size = inliner->num_locals * sizeof(MVMuint16); inliner->local_types = MVM_malloc(local_types_size); memcpy(inliner->local_types, inliner->sf->body.local_types, local_types_size); } inliner->local_types = MVM_realloc(inliner->local_types, (inliner->num_locals + inlinee->num_locals) * sizeof(MVMuint16)); memcpy(inliner->local_types + inliner->num_locals, inlinee->local_types ? inlinee->local_types : inlinee->sf->body.local_types, inlinee->num_locals * sizeof(MVMuint16)); if (!inliner->lexical_types) { MVMint32 lexical_types_size = inliner->num_lexicals * sizeof(MVMuint16); inliner->lexical_types = MVM_malloc(lexical_types_size); memcpy(inliner->lexical_types, inliner->sf->body.lexical_types, lexical_types_size); } inliner->lexical_types = MVM_realloc(inliner->lexical_types, (inliner->num_lexicals + inlinee->num_lexicals) * sizeof(MVMuint16)); memcpy(inliner->lexical_types + inliner->num_lexicals, inlinee->lexical_types ? inlinee->lexical_types : inlinee->sf->body.lexical_types, inlinee->num_lexicals * sizeof(MVMuint16)); /* Merge handlers from inlinee. */ if (inlinee->num_handlers) { MVMuint32 total_handlers = inliner->num_handlers + inlinee->num_handlers; resize_handlers_table(tc, inliner, total_handlers); memcpy(inliner->handlers + inliner->num_handlers, inlinee->handlers, inlinee->num_handlers * sizeof(MVMFrameHandler)); for (i = inliner->num_handlers; i < total_handlers; i++) { inliner->handlers[i].block_reg += inliner->num_locals; inliner->handlers[i].label_reg += inliner->num_locals; } } /* If the inliner has handlers in effect at the point of the call that we * are inlining, then we duplicate those and place them surrounding the * inlinee, but with the goto still pointing to the original location. * This means that we can still do a linear scan when searching for an * exception handler, and don't have to try the (costly and fiddly) matter * of trying to traverse the post-inlined call chain. */ if (inliner->sf->body.num_handlers) { /* Walk inliner looking for handlers in effect at the point we hit the * invoke instruction we're currently inlining; also record all of the * instructions where the handler "goto" annotation lives. */ MVMuint32 orig_handlers = inliner->sf->body.num_handlers; MVMuint8 *active = MVM_spesh_alloc(tc, inliner, orig_handlers); MVMSpeshIns **handler_goto_ins = MVM_spesh_alloc(tc, inliner, orig_handlers * sizeof(MVMSpeshIns *)); MVMint32 found_invoke = 0; bb = inliner->entry; while (bb && !bb->inlined) { MVMSpeshIns *ins = bb->first_ins; while (ins) { MVMSpeshAnn *ann = ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_FH_GOTO) { if (ann->data.frame_handler_index < orig_handlers) handler_goto_ins[ann->data.frame_handler_index] = ins; } else if (!found_invoke) { /* Only update these to the point we found the invoke * being inlined, so it serves as a snapshot of what * is active. */ if (ann->type == MVM_SPESH_ANN_FH_START) active[ann->data.frame_handler_index] = 1; else if (ann->type == MVM_SPESH_ANN_FH_END) active[ann->data.frame_handler_index] = 0; } ann = ann->next; } if (ins == invoke_ins) { /* Found it; see if we have any handlers active. If so, we * will continue walking to collect goto annotations. */ found_invoke = 1; for (i = 0; i < orig_handlers; i++) active_handlers_at_invoke += active[i]; if (!active_handlers_at_invoke) break; } ins = ins->next; } if (found_invoke && !active_handlers_at_invoke) break; bb = bb->linear_next; } /* If we found handlers active at the point of invoke, duplicate them * in the handlers table and add annotations. */ if (active_handlers_at_invoke) { MVMuint32 insert_pos = inliner->num_handlers + inlinee->num_handlers; resize_handlers_table(tc, inliner, insert_pos + active_handlers_at_invoke); for (i = orig_handlers - 1; i >= 0; i--) { if (active[i]) { /* Add handler start annotation to first inlinee instruction. */ MVMSpeshAnn *new_ann = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn)); new_ann->type = MVM_SPESH_ANN_FH_START; new_ann->data.frame_handler_index = insert_pos; new_ann->next = inlinee_first_bb->first_ins->annotations; inlinee_first_bb->first_ins->annotations = new_ann; /* Add handler end annotation to last inlinee instruction. */ new_ann = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn)); new_ann->type = MVM_SPESH_ANN_FH_END; new_ann->data.frame_handler_index = insert_pos; new_ann->next = inlinee_last_bb->last_ins->annotations; inlinee_last_bb->last_ins->annotations = new_ann; /* Add handler goto annotation to original target in inliner. */ new_ann = MVM_spesh_alloc(tc, inliner, sizeof(MVMSpeshAnn)); new_ann->type = MVM_SPESH_ANN_FH_GOTO; new_ann->data.frame_handler_index = insert_pos; new_ann->next = handler_goto_ins[i]->annotations; handler_goto_ins[i]->annotations = new_ann; /* Copy handler entry to new slot. */ memcpy(inliner->handlers + insert_pos, inliner->handlers + i, sizeof(MVMFrameHandler)); insert_pos++; } } } } /* Update total locals, lexicals, basic blocks, and handlers of the * inliner. */ inliner->num_bbs += inlinee->num_bbs - 1; inliner->num_locals += inlinee->num_locals; inliner->num_lexicals += inlinee->num_lexicals; inliner->num_handlers += inlinee->num_handlers + active_handlers_at_invoke; }