/* 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; }
/* 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_exception_throw_adhoc(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; }