/* Adds a static frame table index reference to the collectable snapshot entry, * either using an existing table entry or adding a new one. */ static void set_static_frame_index(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMHeapSnapshotCollectable *col, MVMStaticFrame *sf) { MVMuint64 name_idx = get_vm_string_index(tc, ss, sf->body.name); MVMuint64 cuid_idx = get_vm_string_index(tc, ss, sf->body.cuuid); MVMCompUnit *cu = sf->body.cu; MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &(sf->body), 0); MVMuint64 line = ann ? ann->line_number : 1; MVMuint64 file_idx = ann && ann->filename_string_heap_index < cu->body.num_strings ? get_vm_string_index(tc, ss, MVM_cu_string(tc, cu, ann->filename_string_heap_index)) : get_vm_string_index(tc, ss, cu->body.filename); MVMuint64 i; MVMHeapSnapshotStaticFrame *s; for (i = 0; i < ss->col->num_static_frames; i++) { s = &(ss->col->static_frames[i]); if (s->name == name_idx && s->cuid == cuid_idx && s->line == line && s->file == file_idx) { col->type_or_frame_index = i; MVM_free(ann); return; } } MVM_free(ann); grow_storage(&(ss->col->static_frames), &(ss->col->num_static_frames), &(ss->col->alloc_static_frames), sizeof(MVMHeapSnapshotStaticFrame)); s = &(ss->col->static_frames[ss->col->num_static_frames]); s->name = name_idx; s->cuid = cuid_idx; s->line = line; s->file = file_idx; col->type_or_frame_index = ss->col->num_static_frames; ss->col->num_static_frames++; }
static void getstringfrom_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { MVMCompUnit *dep = (MVMCompUnit *)g->spesh_slots[ins->operands[1].lit_i16]; MVMuint32 idx = ins->operands[2].lit_ui32; MVMString *str = MVM_cu_string(tc, dep, idx); MVMSpeshFacts *tgt_facts = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]; tgt_facts->value.s = str; tgt_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; }
/* Reads a string index, looks up the string and returns it. Bounds * checks the string heap index too. */ static MVMString * get_heap_string(MVMThreadContext *tc, MVMCompUnit *cu, ReaderState *rs, MVMuint8 *buffer, size_t offset) { MVMuint32 heap_index = read_int32(buffer, offset); if (heap_index >= cu->body.num_strings) { if (rs) cleanup_all(tc, rs); MVM_exception_throw_adhoc(tc, "String heap index beyond end of string heap"); } return MVM_cu_string(tc, cu, heap_index); }
/* Loads the SC dependencies list. */ static void deserialize_sc_deps(MVMThreadContext *tc, MVMCompUnit *cu, ReaderState *rs) { MVMCompUnitBody *cu_body = &cu->body; MVMuint32 i, sh_idx; MVMuint8 *pos; /* Allocate SC lists in compilation unit. */ cu_body->scs = MVM_malloc(rs->expected_scs * sizeof(MVMSerializationContext *)); cu_body->scs_to_resolve = MVM_malloc(rs->expected_scs * sizeof(MVMSerializationContextBody *)); cu_body->sc_handle_idxs = MVM_malloc(rs->expected_scs * sizeof(MVMint32)); cu_body->num_scs = rs->expected_scs; /* Resolve all the things. */ pos = rs->sc_seg; for (i = 0; i < rs->expected_scs; i++) { MVMSerializationContextBody *scb; MVMString *handle; /* Grab string heap index. */ ensure_can_read(tc, cu, rs, pos, 4); sh_idx = read_int32(pos, 0); pos += 4; /* Resolve to string. */ if (sh_idx >= cu_body->num_strings) { cleanup_all(tc, rs); MVM_exception_throw_adhoc(tc, "String heap index beyond end of string heap"); } cu_body->sc_handle_idxs[i] = sh_idx; handle = MVM_cu_string(tc, cu, sh_idx); /* See if we can resolve it. */ uv_mutex_lock(&tc->instance->mutex_sc_weakhash); MVM_string_flatten(tc, handle); MVM_HASH_GET(tc, tc->instance->sc_weakhash, handle, scb); if (scb && scb->sc) { cu_body->scs_to_resolve[i] = NULL; MVM_ASSIGN_REF(tc, &(cu->common.header), cu_body->scs[i], scb->sc); } else { if (!scb) { scb = MVM_calloc(1, sizeof(MVMSerializationContextBody)); scb->handle = handle; MVM_HASH_BIND(tc, tc->instance->sc_weakhash, handle, scb); MVM_sc_add_all_scs_entry(tc, scb); } cu_body->scs_to_resolve[i] = scb; cu_body->scs[i] = NULL; } uv_mutex_unlock(&tc->instance->mutex_sc_weakhash); } }
static void dump_fileinfo(MVMThreadContext *tc, DumpStr *ds, MVMStaticFrame *sf) { MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &sf->body, 0); MVMCompUnit *cu = sf->body.cu; MVMint32 str_idx = ann ? ann->filename_string_heap_index : 0; MVMint32 line_nr = ann ? ann->line_number : 1; MVMString *filename = cu->body.filename; char *filename_utf8 = "<unknown>"; if (ann && str_idx < cu->body.num_strings) { filename = MVM_cu_string(tc, cu, str_idx); } if (filename) filename_utf8 = MVM_string_utf8_encode_C_string(tc, filename); appendf(ds, "%s:%d", filename_utf8, line_nr); if (filename) MVM_free(filename_utf8); MVM_free(ann); }
/* constant ops on literals give us a specialize-time-known value */ static void literal_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) { MVMSpeshFacts *tgt_facts = &g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]; switch (ins->info->opcode) { case MVM_OP_const_i64: tgt_facts->value.i = ins->operands[1].lit_i64; break; case MVM_OP_const_i32: tgt_facts->value.i = ins->operands[1].lit_i32; break; case MVM_OP_const_i16: tgt_facts->value.i = ins->operands[1].lit_i16; break; case MVM_OP_const_i8: tgt_facts->value.i = ins->operands[1].lit_i8; break; case MVM_OP_const_n32: tgt_facts->value.n = ins->operands[1].lit_n32; break; case MVM_OP_const_n64: tgt_facts->value.n = ins->operands[1].lit_n64; break; case MVM_OP_const_i64_32: tgt_facts->value.i = ins->operands[1].lit_i32; break; case MVM_OP_const_i64_16: tgt_facts->value.i = ins->operands[1].lit_i16; break; case MVM_OP_const_s: tgt_facts->value.s = MVM_cu_string(tc, g->sf->body.cu, ins->operands[1].lit_str_idx); break; default: return; } tgt_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; }
/* Loads the extension op records. */ static MVMExtOpRecord * deserialize_extop_records(MVMThreadContext *tc, MVMCompUnit *cu, ReaderState *rs) { MVMExtOpRecord *extops; MVMuint32 num = rs->expected_extops; MVMuint8 *pos; MVMuint32 i; if (num == 0) return NULL; extops = MVM_calloc(num, sizeof *extops); pos = rs->extop_seg; for (i = 0; i < num; i++) { MVMuint32 name_idx; MVMuint16 operand_bytes = 0; MVMuint8 *operand_descriptor = extops[i].operand_descriptor; /* Read name string index. */ ensure_can_read(tc, cu, rs, pos, 4); name_idx = read_int32(pos, 0); pos += 4; /* Lookup name string. */ if (name_idx >= cu->body.num_strings) { cleanup_all(tc, rs); MVM_exception_throw_adhoc(tc, "String heap index beyond end of string heap"); } extops[i].name = MVM_cu_string(tc, cu, name_idx); /* Read operand descriptor. */ ensure_can_read(tc, cu, rs, pos, 8); memcpy(operand_descriptor, pos, 8); pos += 8; /* Validate operand descriptor. * TODO: Unify with validation in MVM_ext_register_extop? */ { MVMuint8 j = 0; for(; j < 8; j++) { MVMuint8 flags = operand_descriptor[j]; if (!flags) break; switch (flags & MVM_operand_rw_mask) { case MVM_operand_literal: goto check_literal; case MVM_operand_read_reg: case MVM_operand_write_reg: operand_bytes += 2; goto check_reg; case MVM_operand_read_lex: case MVM_operand_write_lex: operand_bytes += 4; goto check_reg; default: goto fail; } check_literal: switch (flags & MVM_operand_type_mask) { case MVM_operand_int8: operand_bytes += 1; continue; case MVM_operand_int16: operand_bytes += 2; continue; case MVM_operand_int32: operand_bytes += 4; continue; case MVM_operand_int64: operand_bytes += 8; continue; case MVM_operand_num32: operand_bytes += 4; continue; case MVM_operand_num64: operand_bytes += 8; continue; case MVM_operand_str: operand_bytes += 2; continue; case MVM_operand_coderef: operand_bytes += 2; continue; case MVM_operand_ins: case MVM_operand_callsite: default: goto fail; } check_reg: switch (flags & MVM_operand_type_mask) { case MVM_operand_int8: case MVM_operand_int16: case MVM_operand_int32: case MVM_operand_int64: case MVM_operand_num32: case MVM_operand_num64: case MVM_operand_str: case MVM_operand_obj: case MVM_operand_type_var: case MVM_operand_uint8: case MVM_operand_uint16: case MVM_operand_uint32: case MVM_operand_uint64: continue; default: goto fail; } fail: cleanup_all(tc, rs); MVM_exception_throw_adhoc(tc, "Invalid operand descriptor"); } } extops[i].operand_bytes = operand_bytes; } return extops; }
/* Dumps a call graph node. */ static MVMObject * dump_call_graph_node(MVMThreadContext *tc, ProfDumpStrs *pds, const MVMProfileCallNode *pcn) { MVMObject *node_hash = new_hash(tc); MVMuint32 i; /* Let's see if we're dealing with a native call or a regular moar call */ if (pcn->sf) { /* Try to resolve the code filename and line number. */ MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &(pcn->sf->body), 0); MVMint32 fshi = annot ? (MVMint32)annot->filename_string_heap_index : -1; /* Add name of code object. */ MVM_repr_bind_key_o(tc, node_hash, pds->name, box_s(tc, pcn->sf->body.name)); /* Add line number and file name. */ if (fshi >= 0 && fshi < pcn->sf->body.cu->body.num_strings) MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, MVM_cu_string(tc, pcn->sf->body.cu, fshi))); else if (pcn->sf->body.cu->body.filename) MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, pcn->sf->body.cu->body.filename)); else MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, tc->instance->str_consts.empty)); MVM_repr_bind_key_o(tc, node_hash, pds->line, box_i(tc, annot ? (MVMint32)annot->line_number : -1)); MVM_free(annot); /* Use static frame memory address to get a unique ID. */ MVM_repr_bind_key_o(tc, node_hash, pds->id, box_i(tc, (MVMint64)pcn->sf)); } else { MVMString *function_name_string = MVM_string_utf8_c8_decode(tc, tc->instance->VMString, pcn->native_target_name, strlen(pcn->native_target_name)); MVM_repr_bind_key_o(tc, node_hash, pds->name, box_s(tc, function_name_string)); MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, pds->native_lib)); MVM_repr_bind_key_o(tc, node_hash, pds->line, box_i(tc, -2)); /* Use the address of the name string as unique ID. a hack, but oh well. */ MVM_repr_bind_key_o(tc, node_hash, pds->id, box_i(tc, (MVMint64)pcn->native_target_name)); } /* Entry counts. */ if (pcn->total_entries) MVM_repr_bind_key_o(tc, node_hash, pds->entries, box_i(tc, pcn->total_entries)); if (pcn->specialized_entries) MVM_repr_bind_key_o(tc, node_hash, pds->spesh_entries, box_i(tc, pcn->specialized_entries)); if (pcn->jit_entries) MVM_repr_bind_key_o(tc, node_hash, pds->jit_entries, box_i(tc, pcn->jit_entries)); if (pcn->inlined_entries) MVM_repr_bind_key_o(tc, node_hash, pds->inlined_entries, box_i(tc, pcn->inlined_entries)); /* Total (inclusive) time. */ MVM_repr_bind_key_o(tc, node_hash, pds->inclusive_time, box_i(tc, pcn->total_time / 1000)); /* OSR and deopt counts. */ if (pcn->osr_count) MVM_repr_bind_key_o(tc, node_hash, pds->osr, box_i(tc, pcn->osr_count)); if (pcn->deopt_one_count) MVM_repr_bind_key_o(tc, node_hash, pds->deopt_one, box_i(tc, pcn->deopt_one_count)); if (pcn->deopt_all_count) MVM_repr_bind_key_o(tc, node_hash, pds->deopt_all, box_i(tc, pcn->deopt_all_count)); /* Visit successors in the call graph, dumping them and working out the * exclusive time. */ if (pcn->num_succ) { MVMObject *callees = new_array(tc); MVMuint64 exclusive_time = pcn->total_time; for (i = 0; i < pcn->num_succ; i++) { MVM_repr_push_o(tc, callees, dump_call_graph_node(tc, pds, pcn->succ[i])); exclusive_time -= pcn->succ[i]->total_time; } MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time, box_i(tc, exclusive_time / 1000)); MVM_repr_bind_key_o(tc, node_hash, pds->callees, callees); } else { MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time, box_i(tc, pcn->total_time / 1000)); } if (pcn->num_alloc) { /* Emit allocations. */ MVMObject *alloc_list = new_array(tc); MVM_repr_bind_key_o(tc, node_hash, pds->allocations, alloc_list); for (i = 0; i < pcn->num_alloc; i++) { MVMObject *alloc_info = new_hash(tc); MVMProfileAllocationCount *alloc = &pcn->alloc[i]; MVMObject *type = pcn->alloc[i].type; MVM_repr_bind_key_o(tc, alloc_info, pds->id, box_i(tc, (MVMint64)type)); MVM_repr_bind_key_o(tc, alloc_info, pds->type, type); if (alloc->allocations_spesh) MVM_repr_bind_key_o(tc, alloc_info, pds->spesh, box_i(tc, alloc->allocations_spesh)); if (alloc->allocations_jit) MVM_repr_bind_key_o(tc, alloc_info, pds->jit, box_i(tc, alloc->allocations_jit)); MVM_repr_bind_key_o(tc, alloc_info, pds->count, box_i(tc, alloc->allocations_interp + alloc->allocations_spesh + alloc->allocations_jit)); MVM_repr_push_o(tc, alloc_list, alloc_info); } } return node_hash; }
/* Dumps a basic block. */ static void dump_bb(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g, MVMSpeshBB *bb, SpeshGraphSizeStats *stats, InlineIndexStack *inline_stack) { MVMSpeshIns *cur_ins; MVMint64 i; MVMint32 size = 0; /* Heading. */ appendf(ds, " BB %d (%p):\n", bb->idx, bb); if (bb->inlined) { append(ds, " Inlined\n"); } { /* Also, we have a line number */ MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); MVMuint32 line_number; if (bbba) { line_number = bbba->line_number; MVM_free(bbba); } else { line_number = -1; } appendf(ds, " line: %d (pc %d)\n", line_number, bb->initial_pc); } /* Instructions. */ append(ds, " Instructions:\n"); cur_ins = bb->first_ins; while (cur_ins) { MVMSpeshAnn *ann = cur_ins->annotations; MVMuint32 line_number; MVMuint32 pop_inlines = 0; MVMuint32 num_comments = 0; while (ann) { /* These four annotations carry a deopt index that we can find a * corresponding line number for */ if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS || ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS || ann->type == MVM_SPESH_ANN_DEOPT_INLINE || ann->type == MVM_SPESH_ANN_DEOPT_OSR) { MVMBytecodeAnnotation *ba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, g->deopt_addrs[2 * ann->data.deopt_idx]); if (ba) { line_number = ba->line_number; MVM_free(ba); } else { line_number = -1; } } switch (ann->type) { case MVM_SPESH_ANN_FH_START: appendf(ds, " [Annotation: FH Start (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_FH_END: appendf(ds, " [Annotation: FH End (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_FH_GOTO: appendf(ds, " [Annotation: FH Goto (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_DEOPT_ONE_INS: appendf(ds, " [Annotation: INS Deopt One (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_DEOPT_ALL_INS: appendf(ds, " [Annotation: INS Deopt All (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_INLINE_START: appendf(ds, " [Annotation: Inline Start (%d)]\n", ann->data.inline_idx); push_inline(tc, inline_stack, ann->data.inline_idx); break; case MVM_SPESH_ANN_INLINE_END: appendf(ds, " [Annotation: Inline End (%d)]\n", ann->data.inline_idx); pop_inlines++; break; case MVM_SPESH_ANN_DEOPT_INLINE: appendf(ds, " [Annotation: INS Deopt Inline (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_DEOPT_OSR: appendf(ds, " [Annotation: INS Deopt OSR (idx %d -> pc %d); line %d]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_LINENO: { char *cstr; MVMCompUnit *cu = get_current_cu(tc, g, inline_stack); if (cu->body.num_strings < ann->data.lineno.filename_string_index) { appendf(ds, " [Annotation: Line Number: <out of bounds>:%d]\n", ann->data.lineno.line_number); } else { cstr = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, get_current_cu(tc, g, inline_stack), ann->data.lineno.filename_string_index)); appendf(ds, " [Annotation: Line Number: %s:%d]\n", cstr, ann->data.lineno.line_number); MVM_free(cstr); } break; } case MVM_SPESH_ANN_LOGGED: appendf(ds, " [Annotation: Logged (bytecode offset %d)]\n", ann->data.bytecode_offset); break; case MVM_SPESH_ANN_DEOPT_SYNTH: appendf(ds, " [Annotation: INS Deopt Synth (idx %d)]\n", ann->data.deopt_idx); break; case MVM_SPESH_ANN_COMMENT: num_comments++; break; default: appendf(ds, " [Annotation: %d (unknown)]\n", ann->type); } ann = ann->next; } while (pop_inlines--) pop_inline(tc, inline_stack); if (num_comments > 1) { ann = cur_ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_COMMENT) { appendf(ds, " # %s\n", ann->data.comment); } ann = ann->next; } } appendf(ds, " %-15s ", cur_ins->info->name); if (cur_ins->info->opcode == MVM_SSA_PHI) { for (i = 0; i < cur_ins->info->num_operands; i++) { MVMint16 orig = cur_ins->operands[i].reg.orig; MVMint16 regi = cur_ins->operands[i].reg.i; if (i) append(ds, ", "); if (orig < 10) append(ds, " "); if (regi < 10) append(ds, " "); appendf(ds, "r%d(%d)", orig, regi); } } else { /* Count the opcode itself */ size += 2; for (i = 0; i < cur_ins->info->num_operands; i++) { if (i) append(ds, ", "); switch (cur_ins->info->operands[i] & MVM_operand_rw_mask) { case MVM_operand_read_reg: case MVM_operand_write_reg: { MVMint16 orig = cur_ins->operands[i].reg.orig; MVMint16 regi = cur_ins->operands[i].reg.i; if (orig < 10) append(ds, " "); if (regi < 10) append(ds, " "); appendf(ds, "r%d(%d)", orig, regi); size += 4; break; } case MVM_operand_read_lex: case MVM_operand_write_lex: { MVMStaticFrameBody *cursor = &g->sf->body; MVMuint32 ascension; appendf(ds, "lex(idx=%d,outers=%d", cur_ins->operands[i].lex.idx, cur_ins->operands[i].lex.outers); for (ascension = 0; ascension < cur_ins->operands[i].lex.outers; ascension++, cursor = &cursor->outer->body) { }; if (cursor->fully_deserialized) { if (cur_ins->operands[i].lex.idx < cursor->num_lexicals) { char *cstr = MVM_string_utf8_encode_C_string(tc, cursor->lexical_names_list[cur_ins->operands[i].lex.idx]->key); appendf(ds, ",%s)", cstr); MVM_free(cstr); } else { append(ds, ",<out of bounds>)"); } } else { append(ds, ",<pending deserialization>)"); } size += 4; break; } case MVM_operand_literal: { MVMuint32 type = cur_ins->info->operands[i] & MVM_operand_type_mask; switch (type) { case MVM_operand_ins: { MVMint32 bb_idx = cur_ins->operands[i].ins_bb->idx; if (bb_idx < 100) append(ds, " "); if (bb_idx < 10) append(ds, " "); appendf(ds, "BB(%d)", bb_idx); size += 4; break; } case MVM_operand_int8: appendf(ds, "liti8(%"PRId8")", cur_ins->operands[i].lit_i8); size += 2; break; case MVM_operand_int16: appendf(ds, "liti16(%"PRId16")", cur_ins->operands[i].lit_i16); size += 2; break; case MVM_operand_int32: appendf(ds, "liti32(%"PRId32")", cur_ins->operands[i].lit_i32); size += 4; break; case MVM_operand_uint32: appendf(ds, "litui32(%"PRIu32")", cur_ins->operands[i].lit_ui32); size += 4; break; case MVM_operand_int64: appendf(ds, "liti64(%"PRId64")", cur_ins->operands[i].lit_i64); size += 8; break; case MVM_operand_num32: appendf(ds, "litn32(%f)", cur_ins->operands[i].lit_n32); size += 4; break; case MVM_operand_num64: appendf(ds, "litn64(%g)", cur_ins->operands[i].lit_n64); size += 8; break; case MVM_operand_str: { char *cstr = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, cur_ins->operands[i].lit_str_idx)); appendf(ds, "lits(%s)", cstr); MVM_free(cstr); size += 8; break; } case MVM_operand_callsite: { MVMCallsite *callsite = g->sf->body.cu->body.callsites[cur_ins->operands[i].callsite_idx]; appendf(ds, "callsite(%p, %d arg, %d pos, %s, %s)", callsite, callsite->arg_count, callsite->num_pos, callsite->has_flattening ? "flattening" : "nonflattening", callsite->is_interned ? "interned" : "noninterned"); size += 2; break; } case MVM_operand_spesh_slot: appendf(ds, "sslot(%"PRId16")", cur_ins->operands[i].lit_i16); size += 2; break; case MVM_operand_coderef: { MVMCodeBody *body = &((MVMCode*)g->sf->body.cu->body.coderefs[cur_ins->operands[i].coderef_idx])->body; MVMBytecodeAnnotation *anno = MVM_bytecode_resolve_annotation(tc, &body->sf->body, 0); append(ds, "coderef("); if (anno) { char *filestr = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, anno->filename_string_heap_index)); appendf(ds, "%s:%d%s)", filestr, anno->line_number, body->outer ? " (closure)" : ""); MVM_free(filestr); } else { append(ds, "??\?)"); } size += 2; MVM_free(anno); break; } default: append(ds, "<nyi(lit)>"); } break; } default: append(ds, "<nyi>"); } } if (cur_ins->info->opcode == MVM_OP_wval || cur_ins->info->opcode == MVM_OP_wval_wide) { /* We can try to find out what the debug_name of this thing is. */ MVMint16 dep = cur_ins->operands[1].lit_i16; MVMint64 idx; MVMCollectable *result = NULL; MVMSerializationContext *sc; char *debug_name = NULL; const char *repr_name = NULL; if (cur_ins->info->opcode == MVM_OP_wval) { idx = cur_ins->operands[2].lit_i16; } else { idx = cur_ins->operands[2].lit_i64; } sc = MVM_sc_get_sc(tc, g->sf->body.cu, dep); if (sc) result = (MVMCollectable *)MVM_sc_try_get_object(tc, sc, idx); if (result) { if (result->flags & MVM_CF_STABLE) { debug_name = MVM_6model_get_stable_debug_name(tc, (MVMSTable *)result); repr_name = ((MVMSTable *)result)->REPR->name; } else { debug_name = MVM_6model_get_debug_name(tc, (MVMObject *)result); repr_name = REPR(result)->name; } if (debug_name) { appendf(ds, " (%s: %s)", repr_name, debug_name); } else { appendf(ds, " (%s: ?)", repr_name); } } else { appendf(ds, " (not deserialized)"); } } } if (num_comments == 1) { ann = cur_ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_COMMENT) { appendf(ds, " # %s", ann->data.comment); break; } ann = ann->next; } } append(ds, "\n"); cur_ins = cur_ins->next; } if (stats) { if (bb->inlined) stats->inlined_size += size; stats->total_size += size; } /* Predecessors and successors. */ append(ds, " Successors: "); for (i = 0; i < bb->num_succ; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->succ[i]->idx); append(ds, "\n Predecessors: "); for (i = 0; i < bb->num_pred; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->pred[i]->idx); append(ds, "\n Dominance children: "); for (i = 0; i < bb->num_children; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->children[i]->idx); append(ds, "\n\n"); }
static void instrument_graph_with_breakpoints(MVMThreadContext *tc, MVMSpeshGraph *g) { MVMSpeshBB *bb = g->entry->linear_next; MVMuint16 array_slot = 0; MVMint32 last_line_number = -2; MVMint32 last_filename = -1; char *filename_buf = NULL; while (bb) { MVMSpeshIns *ins = bb->first_ins; MVMSpeshIns *breakpoint_ins; MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); MVMint64 line_number = -1; MVMint64 filename_string_index = -1; MVMuint32 file_bp_idx; if (bbba) { line_number = bbba->line_number; filename_string_index = bbba->filename_string_heap_index; MVM_free(bbba); } else { line_number = -1; bb = bb->linear_next; continue; } /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */ while (ins && ins->info->opcode == MVM_SSA_PHI) { ins = ins->next; } if (!ins) ins = bb->last_ins; /* Jumplists require the target BB to start in the goto op. * We must not break this, or we cause the interpreter to derail */ if (bb->last_ins->info->opcode == MVM_OP_jumplist) { MVMint16 to_skip = bb->num_succ; for (; to_skip > 0; to_skip--) { bb = bb->linear_next; } continue; } if (line_number >= 0) { breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); breakpoint_ins->info = MVM_op_get_op(MVM_OP_breakpoint); breakpoint_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand)); if (last_filename != filename_string_index) { if (filename_buf) MVM_free(filename_buf); filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index)); } MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx); breakpoint_ins->operands[0].lit_i32 = file_bp_idx; breakpoint_ins->operands[1].lit_i32 = line_number; last_filename = filename_string_index; MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, breakpoint_ins); } /* Now go through instructions to see if any are annotated with a * precise filename/lineno as well. */ while (ins) { MVMSpeshAnn *ann = ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_LINENO) { /* We are very likely to have one instruction here that has * the same annotation as the bb itself. We skip that one.*/ if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) { break; } line_number = ann->data.lineno.line_number; filename_string_index = ann->data.lineno.filename_string_index; /*breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));*/ /*breakpoint_ins->info = MVM_op_get_op(MVM_OP_breakpoint);*/ /*breakpoint_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));*/ if (last_filename != filename_string_index) { if (filename_buf) MVM_free(filename_buf); filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index)); } MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx); /*breakpoint_ins->operands[0].lit_i32 = file_bp_idx;*/ /*breakpoint_ins->operands[1].lit_i32 = ann->data.lineno.line_number;*/ /* XXX insert breakpoint op here, too, maybe? */ break; } ann = ann->next; } ins = ins->next; } bb = bb->linear_next; } if (filename_buf) MVM_free(filename_buf); }
/* Visits the blocks in dominator tree order, recursively. */ static void add_bb_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshPlanned *p) { MVMint32 i, is_phi; /* Look for instructions that provide or propagate facts. */ MVMSpeshIns *ins = bb->first_ins; while (ins) { /* See if there's deopt and logged annotations, and if so add logged * facts and guards. */ MVMSpeshAnn *ann = ins->annotations; MVMSpeshAnn *ann_deopt_one = NULL; MVMSpeshAnn *ann_logged = NULL; while (ann) { switch (ann->type) { case MVM_SPESH_ANN_DEOPT_ONE_INS: ann_deopt_one = ann; break; case MVM_SPESH_ANN_LOGGED: ann_logged = ann; } ann = ann->next; } if (p && ann_deopt_one && ann_logged && ins->info->opcode != MVM_OP_speshresolve) log_facts(tc, g, bb, ins, p, ann_deopt_one, ann_logged); /* Look for ops that are fact-interesting. */ switch (ins->info->opcode) { case MVM_OP_set: copy_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].reg.orig, ins->operands[1].reg.i); break; case MVM_OP_create: create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].reg.orig, ins->operands[1].reg.i); break; case MVM_OP_sp_fastcreate: case MVM_OP_sp_fastbox_i: case MVM_OP_sp_fastbox_bi: case MVM_OP_sp_fastbox_i_ic: case MVM_OP_sp_fastbox_bi_ic: case MVM_OP_sp_add_I: case MVM_OP_sp_sub_I: case MVM_OP_sp_mul_I: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ((MVMSTable *)g->spesh_slots[ins->operands[2].lit_i16])->WHAT); break; case MVM_OP_clone: copy_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].reg.orig, ins->operands[1].reg.i); break; case MVM_OP_box_s: case MVM_OP_box_i: case MVM_OP_box_n: { MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]); create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[2].reg.orig, ins->operands[2].reg.i); target_facts->flags |= MVM_SPESH_FACT_KNOWN_BOX_SRC; break; } case MVM_OP_coerce_sI: { MVMSpeshFacts *target_facts = &(g->facts[ins->operands[0].reg.orig][ins->operands[0].reg.i]); create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[2].reg.orig, ins->operands[2].reg.i); break; } case MVM_OP_add_I: case MVM_OP_sub_I: case MVM_OP_mul_I: case MVM_OP_div_I: case MVM_OP_mod_I: create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[3].reg.orig, ins->operands[3].reg.i); break; case MVM_OP_neg_I: case MVM_OP_abs_I: create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[2].reg.orig, ins->operands[2].reg.i); break; case MVM_OP_nativecallcast: create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[2].reg.orig, ins->operands[2].reg.i); break; case MVM_OP_trunc_u16: case MVM_OP_trunc_i16: trunc_i16_facts(tc, g, ins); break; case MVM_OP_coerce_ui: case MVM_OP_coerce_iu: trunc_i16_facts(tc, g, ins); break; case MVM_OP_bootint: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTInt); break; case MVM_OP_bootnum: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTNum); break; case MVM_OP_bootstr: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTStr); break; case MVM_OP_bootarray: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTArray); break; case MVM_OP_bootintarray: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTIntArray); break; case MVM_OP_bootnumarray: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTNumArray); break; case MVM_OP_bootstrarray: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTStrArray); break; case MVM_OP_boothash: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTHash); break; case MVM_OP_hllboolfor: { MVMSpeshOperand *name_op = &ins->operands[1]; MVMSpeshFacts *namefacts = &g->facts[name_op->reg.orig][name_op->reg.i]; if (namefacts->writer && namefacts->writer->info->opcode == MVM_OP_const_s) { MVMString *hllname = MVM_cu_string(tc, g->sf->body.cu, ins->operands[1].lit_str_idx); MVMHLLConfig *hll = MVM_hll_get_config_for(tc, hllname); if (hll->true_value && hll->false_value && STABLE(hll->true_value)->WHAT == STABLE(hll->false_value)->WHAT) create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, STABLE(hll->true_value)->WHAT); } break; } case MVM_OP_hllbool: { MVMHLLConfig *hll = g->sf->body.cu->body.hll_config; if (hll->true_value && hll->false_value && STABLE(hll->true_value)->WHAT == STABLE(hll->false_value)->WHAT) create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, STABLE(hll->true_value)->WHAT); break; } case MVM_OP_hllboxtype_i: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->int_box_type); break; case MVM_OP_hllboxtype_n: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->num_box_type); break; case MVM_OP_hllboxtype_s: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->str_box_type); break; case MVM_OP_hlllist: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->slurpy_array_type); break; case MVM_OP_hllhash: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->slurpy_hash_type); break; case MVM_OP_decont: decont_facts(tc, g, ins, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].reg.orig, ins->operands[1].reg.i); break; case MVM_OP_wval: wval_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].lit_i16, ins->operands[2].lit_i16); break; case MVM_OP_wval_wide: wval_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].lit_i16, ins->operands[2].lit_i64); break; case MVM_OP_sp_getwvalfrom: wvalfrom_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].lit_i16, ins->operands[2].lit_i64); break; case MVM_OP_sp_getspeshslot: object_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, (MVMObject *)g->spesh_slots[ins->operands[1].lit_i16]); break; case MVM_OP_iter: iter_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[1].reg.orig, ins->operands[1].reg.i); break; case MVM_OP_newexception: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, tc->instance->boot_types.BOOTException); break; case MVM_OP_getlexref_i: case MVM_OP_getlexref_i32: case MVM_OP_getlexref_i16: case MVM_OP_getlexref_i8: case MVM_OP_getlexref_u32: case MVM_OP_getlexref_u16: case MVM_OP_getlexref_u8: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->int_lex_ref); break; case MVM_OP_getlexref_n: case MVM_OP_getlexref_n32: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->num_lex_ref); break; case MVM_OP_getlexref_s: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->str_lex_ref); break; case MVM_OP_getattrref_i: case MVM_OP_getattrsref_i: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->int_attr_ref); break; case MVM_OP_getattrref_n: case MVM_OP_getattrsref_n: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->num_attr_ref); break; case MVM_OP_getattrref_s: case MVM_OP_getattrsref_s: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->str_attr_ref); break; case MVM_OP_atposref_i: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->int_pos_ref); break; case MVM_OP_atposref_n: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->num_pos_ref); break; case MVM_OP_atposref_s: create_facts_with_type(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, g->sf->body.cu->body.hll_config->str_pos_ref); break; case MVM_OP_const_i64: case MVM_OP_const_i32: case MVM_OP_const_i16: case MVM_OP_const_i8: case MVM_OP_const_n64: case MVM_OP_const_n32: case MVM_OP_const_i64_32: case MVM_OP_const_i64_16: case MVM_OP_const_s: literal_facts(tc, g, ins); break; case MVM_OP_sp_getstringfrom: getstringfrom_facts(tc, g, ins); break; case MVM_OP_encode: create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[3].reg.orig, ins->operands[3].reg.i); break; case MVM_OP_encoderep: create_facts(tc, g, ins->operands[0].reg.orig, ins->operands[0].reg.i, ins->operands[4].reg.orig, ins->operands[4].reg.i); break; case MVM_OP_setdispatcher: case MVM_OP_setdispatcherfor: g->sets_dispatcher = 1; break; case MVM_OP_sp_guard: case MVM_OP_sp_guardconc: case MVM_OP_sp_guardtype: case MVM_OP_sp_guardobj: case MVM_OP_sp_guardjustconc: case MVM_OP_sp_guardjusttype: sp_guard_facts(tc, g, bb, ins); break; default: if (ins->info->opcode == (MVMuint16)-1) discover_extop(tc, g, ins); } ins = ins->next; } /* Visit children. */ for (i = 0; i < bb->num_children; i++) add_bb_facts(tc, g, bb->children[i], p); }
static void bytecode_dump_frame_internal(MVMThreadContext *tc, MVMStaticFrame *frame, MVMSpeshCandidate *maybe_candidate, MVMuint8 *frame_cur_op, char ***frame_lexicals, char **oo, MVMuint32 *os, MVMuint32 *ol) { /* since "references" are not a thing in C, keep a local copy of these * and update the passed-in pointers at the end of the function */ char *o = *oo; MVMuint32 s = *os; MVMuint32 l = *ol; MVMuint32 i, j, k; /* mostly stolen from validation.c */ MVMStaticFrame *static_frame = frame; MVMuint32 bytecode_size = maybe_candidate ? maybe_candidate->bytecode_size : static_frame->body.bytecode_size; MVMuint8 *bytecode_start = maybe_candidate ? maybe_candidate->bytecode : static_frame->body.bytecode; MVMuint8 *bytecode_end = bytecode_start + bytecode_size; /* current position in the bytestream */ MVMuint8 *cur_op = bytecode_start; /* positions in the bytestream that are starts of ops and goto targets */ MVMuint8 *labels = MVM_calloc(1, bytecode_size); MVMuint32 *jumps = MVM_calloc(1, sizeof(MVMuint32) * bytecode_size); char **lines = MVM_malloc(sizeof(char *) * bytecode_size); MVMuint32 *linelocs = MVM_malloc(sizeof(MVMuint32) * bytecode_size); MVMuint32 lineno = 0; MVMuint32 lineloc; MVMuint16 op_num; const MVMOpInfo *op_info; MVMuint32 operand_size = 0; unsigned char op_rw; unsigned char op_type; unsigned char op_flags; MVMOpInfo tmp_extop_info; /* stash the outer output buffer */ MVMuint32 sP = s; MVMuint32 lP = l; char *oP = o; char *tmpstr; char mark_this_line = 0; MVMCompUnit *cu = static_frame->body.cu; while (cur_op < bytecode_end - 1) { /* allocate a line buffer */ s = 200; l = 0; o = MVM_calloc(s, sizeof(char)); lineloc = cur_op - bytecode_start; /* mark that this line starts at this point in the bytestream */ linelocs[lineno] = lineloc; /* mark that this point in the bytestream is an op boundary */ labels[lineloc] |= MVM_val_op_boundary; mark_this_line = 0; if (frame_cur_op) { if (frame_cur_op == cur_op || frame_cur_op == cur_op + 2) { mark_this_line = 1; } } if (mark_this_line) { a("-> "); } else { a(" "); } op_num = *((MVMint16 *)cur_op); cur_op += 2; if (op_num < MVM_OP_EXT_BASE) { op_info = MVM_op_get_op(op_num); if (op_info) a("%-18s ", op_info->name); else a("invalid OP "); } else { MVMint16 ext_op_num = op_num - MVM_OP_EXT_BASE; if (0 <= ext_op_num && ext_op_num < cu->body.num_extops) { MVMExtOpRecord r = cu->body.extops[ext_op_num]; MVMuint8 j; memset(&tmp_extop_info, 0, sizeof(MVMOpInfo)); tmp_extop_info.name = MVM_string_utf8_encode_C_string(tc, r.name); memcpy(tmp_extop_info.operands, r.operand_descriptor, 8); for (j = 0; j < 8; j++) if (tmp_extop_info.operands[j]) tmp_extop_info.num_operands++; else break; op_info = &tmp_extop_info; a("%-12s ", tmp_extop_info.name); MVM_free((void *)tmp_extop_info.name); tmp_extop_info.name = NULL; } else { a("Extension op %d out of range", (int)op_num); } } if (!op_info) continue; for (i = 0; i < op_info->num_operands; i++) { if (i) a(", "); op_flags = op_info->operands[i]; op_rw = op_flags & MVM_operand_rw_mask; op_type = op_flags & MVM_operand_type_mask; if (op_rw == MVM_operand_literal) { switch (op_type) { case MVM_operand_int8: operand_size = 1; a("%"PRId8, GET_I8(cur_op, 0)); break; case MVM_operand_int16: operand_size = 2; a("%"PRId16, GET_I16(cur_op, 0)); break; case MVM_operand_int32: operand_size = 4; a("%"PRId32, GET_I32(cur_op, 0)); break; case MVM_operand_int64: operand_size = 8; a("%"PRId64, MVM_BC_get_I64(cur_op, 0)); break; case MVM_operand_uint8: operand_size = 1; a("%"PRIu8, GET_I8(cur_op, 0)); break; case MVM_operand_uint16: operand_size = 2; a("%"PRIu16, GET_I16(cur_op, 0)); break; case MVM_operand_uint32: operand_size = 4; a("%"PRIu32, GET_I32(cur_op, 0)); break; case MVM_operand_uint64: operand_size = 8; a("%"PRIu64, MVM_BC_get_I64(cur_op, 0)); break; case MVM_operand_num32: operand_size = 4; a("%f", GET_N32(cur_op, 0)); break; case MVM_operand_num64: operand_size = 8; a("%f", MVM_BC_get_N64(cur_op, 0)); break; case MVM_operand_callsite: operand_size = 2; a("Callsite_%"PRIu16, GET_UI16(cur_op, 0)); break; case MVM_operand_coderef: operand_size = 2; a("Frame_%"PRIu16, GET_UI16(cur_op, 0)); break; case MVM_operand_str: operand_size = 4; if (GET_UI32(cur_op, 0) < cu->body.num_strings) { tmpstr = MVM_string_utf8_encode_C_string( tc, MVM_cu_string(tc, cu, GET_UI32(cur_op, 0))); /* XXX C-string-literal escape the \ and ' and line breaks and non-ascii someday */ a("'%s'", tmpstr); MVM_free(tmpstr); } else a("invalid string index: %d", GET_UI32(cur_op, 0)); break; case MVM_operand_ins: operand_size = 4; /* luckily all the ins operands are at the end of op operands, so I can wait to resolve the label to the end. */ if (GET_UI32(cur_op, 0) < bytecode_size) { labels[GET_UI32(cur_op, 0)] |= MVM_val_branch_target; jumps[lineno] = GET_UI32(cur_op, 0); } break; case MVM_operand_obj: /* not sure what a literal object is */ operand_size = 4; break; case MVM_operand_spesh_slot: operand_size = 2; a("sslot(%d)", GET_UI16(cur_op, 0)); break; default: fprintf(stderr, "what is an operand of type %d??\n", op_type); abort(); /* never reached, silence compiler warnings */ } } else if (op_rw == MVM_operand_read_reg || op_rw == MVM_operand_write_reg) { /* register operand */ MVMuint8 frame_has_inlines = maybe_candidate && maybe_candidate->num_inlines ? 1 : 0; MVMuint16 *local_types = frame_has_inlines ? maybe_candidate->local_types : frame->body.local_types; MVMuint16 num_locals = frame_has_inlines ? maybe_candidate->num_locals : frame->body.num_locals; operand_size = 2; a("loc_%u_%s", GET_REG(cur_op, 0), get_typename(local_types[GET_REG(cur_op, 0)])); } else if (op_rw == MVM_operand_read_lex || op_rw == MVM_operand_write_lex) { /* lexical operand */ MVMuint16 idx, frames, m; MVMStaticFrame *applicable_frame = static_frame; operand_size = 4; idx = GET_UI16(cur_op, 0); frames = GET_UI16(cur_op, 2); m = frames; while (m > 0) { applicable_frame = applicable_frame->body.outer; m--; } /* inefficient, I know. should use a hash. */ for (m = 0; m < cu->body.num_frames; m++) { if (get_frame(tc, cu, m) == applicable_frame) { char *lexname = frame_lexicals ? frame_lexicals[m][idx] : "lex??"; a("lex_Frame_%u_%s_%s", m, lexname, get_typename(applicable_frame->body.lexical_types[idx])); } } } cur_op += operand_size; } lines[lineno++] = o; } { MVMuint32 *linelabels = MVM_calloc(lineno, sizeof(MVMuint32)); MVMuint32 byte_offset = 0; MVMuint32 line_number = 0; MVMuint32 label_number = 1; MVMuint32 *annotations = MVM_calloc(lineno, sizeof(MVMuint32)); for (; byte_offset < bytecode_size; byte_offset++) { if (labels[byte_offset] & MVM_val_branch_target) { /* found a byte_offset where a label should be. now crawl up through the lines to find which line starts there */ while (line_number < lineno && linelocs[line_number] != byte_offset) line_number++; if (line_number < lineno) linelabels[line_number] = label_number++; } } o = oP; l = lP; s = sP; i = 0; /* resolve annotation line numbers */ for (j = 0; j < frame->body.num_annotations; j++) { MVMuint32 ann_offset = GET_UI32(frame->body.annotations_data, j*12); for (; i < lineno; i++) { if (linelocs[i] == ann_offset) { annotations[i] = j + 1; break; } } } for (j = 0; j < lineno; j++) { if (annotations[j]) { MVMuint16 shi = GET_UI16(frame->body.annotations_data + 4, (annotations[j] - 1)*12); tmpstr = MVM_string_utf8_encode_C_string( tc, MVM_cu_string(tc, cu, shi < cu->body.num_strings ? shi : 0)); a(" annotation: %s:%u\n", tmpstr, GET_UI32(frame->body.annotations_data, (annotations[j] - 1)*12 + 8)); MVM_free(tmpstr); } if (linelabels[j]) a(" label_%u:\n", linelabels[j]); a("%05d %s", j, lines[j]); MVM_free(lines[j]); if (jumps[j]) { /* horribly inefficient for large frames. again, should use a hash */ line_number = 0; while (line_number < lineno && linelocs[line_number] != jumps[j]) line_number++; if (line_number < lineno) a("label_%u(%05u)", linelabels[line_number], line_number); else a("label (invalid: %05u)", jumps[j]); } a("\n"); } MVM_free(lines); MVM_free(jumps); MVM_free(linelocs); MVM_free(linelabels); MVM_free(labels); MVM_free(annotations); } *oo = o; *os = s; *ol = l; }
char * MVM_bytecode_dump(MVMThreadContext *tc, MVMCompUnit *cu) { MVMuint32 s = 1024; MVMuint32 l = 0; MVMuint32 i, j, k; char *o = MVM_calloc(s, sizeof(char)); char ***frame_lexicals = MVM_malloc(sizeof(char **) * cu->body.num_frames); MVMString *name = MVM_string_utf8_decode(tc, tc->instance->VMString, "", 0); a("\nMoarVM dump of binary compilation unit:\n\n"); for (k = 0; k < cu->body.num_scs; k++) { char *tmpstr = MVM_string_utf8_encode_C_string( tc, MVM_cu_string(tc, cu, cu->body.sc_handle_idxs[k])); a(" SC_%u : %s\n", k, tmpstr); MVM_free(tmpstr); } for (k = 0; k < cu->body.num_callsites; k++) { MVMCallsite *callsite = cu->body.callsites[k]; MVMuint16 arg_count = callsite->arg_count; MVMuint16 nameds_count = 0; a(" Callsite_%u :\n", k); a(" num_pos: %d\n", callsite->num_pos); a(" arg_count: %u\n", arg_count); for (j = 0, i = 0; j < arg_count; j++) { MVMCallsiteEntry csitee = callsite->arg_flags[i++]; a(" Arg %u :", i); if (csitee & MVM_CALLSITE_ARG_NAMED) { if (callsite->arg_names) { char *arg_name = MVM_string_utf8_encode_C_string(tc, callsite->arg_names[nameds_count++]); a(" named(%s)", arg_name); MVM_free(arg_name); } else { a(" named"); } j++; } else if (csitee & MVM_CALLSITE_ARG_FLAT_NAMED) { a(" flatnamed"); } else if (csitee & MVM_CALLSITE_ARG_FLAT) { a(" flat"); } else a(" positional"); if (csitee & MVM_CALLSITE_ARG_OBJ) a(" obj"); else if (csitee & MVM_CALLSITE_ARG_INT) a(" int"); else if (csitee & MVM_CALLSITE_ARG_NUM) a(" num"); else if (csitee & MVM_CALLSITE_ARG_STR) a(" str"); if (csitee & MVM_CALLSITE_ARG_FLAT) a(" flat"); a("\n"); } } for (k = 0; k < cu->body.num_frames; k++) MVM_bytecode_finish_frame(tc, cu, get_frame(tc, cu, k), 1); for (k = 0; k < cu->body.num_frames; k++) { MVMStaticFrame *frame = get_frame(tc, cu, k); MVMLexicalRegistry *current; char **lexicals; if (!frame->body.fully_deserialized) { MVM_bytecode_finish_frame(tc, cu, frame, 1); } lexicals = (char **)MVM_malloc(sizeof(char *) * frame->body.num_lexicals); frame_lexicals[k] = lexicals; HASH_ITER(tc, hash_handle, frame->body.lexical_names, current, { name->body.storage.blob_32 = (MVMint32 *)current->hash_handle.key; name->body.num_graphs = (MVMuint32)current->hash_handle.keylen / sizeof(MVMGrapheme32); lexicals[current->value] = MVM_string_utf8_encode_C_string(tc, name); });