#include "moar.h" /* Gets the statistics for a static frame, creating them if needed. */ static MVMSpeshStats * stats_for(MVMThreadContext *tc, MVMStaticFrame *sf) { MVMStaticFrameSpesh *spesh = sf->body.spesh; if (!spesh->body.spesh_stats) spesh->body.spesh_stats = MVM_calloc(1, sizeof(MVMSpeshStats)); return spesh->body.spesh_stats; } /* Gets the stats by callsite, adding it if it's missing. */ static MVMuint32 by_callsite_idx(MVMThreadContext *tc, MVMSpeshStats *ss, MVMCallsite *cs) { /* See if we already have it. */ MVMuint32 found; MVMuint32 n = ss->num_by_callsite; for (found = 0; found < n; found++) if (ss->by_callsite[found].cs == cs) return found; /* If not, we need a new record. */ found = ss->num_by_callsite; ss->num_by_callsite++; ss->by_callsite = MVM_realloc(ss->by_callsite, ss->num_by_callsite * sizeof(MVMSpeshStatsByCallsite)); memset(&(ss->by_callsite[found]), 0, sizeof(MVMSpeshStatsByCallsite)); ss->by_callsite[found].cs = cs; return found; } /* Checks if a type tuple is incomplete (no types logged for some passed * objects, or no decont type logged for a container type). */ static MVMint32 incomplete_type_tuple(MVMThreadContext *tc, MVMCallsite *cs, MVMSpeshStatsType *arg_types) { MVMuint32 i; for (i = 0; i < cs->flag_count; i++) { if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ) { MVMObject *type = arg_types[i].type; if (!type) return 1; if (arg_types[i].type_concrete && type->st->container_spec) if (!arg_types[i].decont_type && REPR(type)->ID != MVM_REPR_ID_NativeRef) return 1; } } return 0; } /* Returns true if the callsite has no object arguments, false otherwise. */ static MVMint32 cs_without_object_args(MVMThreadContext *tc, MVMCallsite *cs) { MVMuint32 i; for (i = 0; i < cs->flag_count; i++) if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ) return 0; return 1; } /* Gets the stats by type, adding it if it's missing. Frees arg_types. Returns * the index in the by type array, or -1 if unresolved. */ static MVMint32 by_type(MVMThreadContext *tc, MVMSpeshStats *ss, MVMuint32 callsite_idx, MVMSpeshStatsType *arg_types) { /* Resolve type by callsite level info. If this is the no-callsite * specialization there is nothing further to do. */ MVMSpeshStatsByCallsite *css = &(ss->by_callsite[callsite_idx]); MVMCallsite *cs = css->cs; if (!cs) { MVM_free(arg_types); return -1; } /* Otherwise if there's no object args, then we'll use a single "by type" * specialization, so we can have data tracked by offset at least. */ else if (cs_without_object_args(tc, cs)) { if (css->num_by_type == 0) { css->num_by_type++; css->by_type = MVM_calloc(1, sizeof(MVMSpeshStatsByType)); css->by_type[0].arg_types = arg_types; } else { MVM_free(arg_types); } return 0; } /* Maybe the type tuple is incomplete, maybe because the log buffer ended * prior to having all the type information. Discard. */ else if (incomplete_type_tuple(tc, cs, arg_types)) { MVM_free(arg_types); return -1; } /* Can produce by-type stats. */ else { /* See if we already have it. */ size_t args_length = cs->flag_count * sizeof(MVMSpeshStatsType); MVMuint32 found; MVMuint32 n = css->num_by_type; for (found = 0; found < n; found++) { if (memcmp(css->by_type[found].arg_types, arg_types, args_length) == 0) { MVM_free(arg_types); return found; } } /* If not, we need a new record. */ found = css->num_by_type; css->num_by_type++; css->by_type = MVM_realloc(css->by_type, css->num_by_type * sizeof(MVMSpeshStatsByType)); memset(&(css->by_type[found]), 0, sizeof(MVMSpeshStatsByType)); css->by_type[found].arg_types = arg_types; return found; } } /* Get the stats by offset entry, adding it if it's missing. */ static MVMSpeshStatsByOffset * by_offset(MVMThreadContext *tc, MVMSpeshStatsByType *tss, MVMuint32 bytecode_offset) { /* See if we already have it. */ MVMuint32 found; MVMuint32 n = tss->num_by_offset; for (found = 0; found < n; found++) if (tss->by_offset[found].bytecode_offset == bytecode_offset) return &(tss->by_offset[found]); /* If not, we need a new record. */ found = tss->num_by_offset; tss->num_by_offset++; tss->by_offset = MVM_realloc(tss->by_offset, tss->num_by_offset * sizeof(MVMSpeshStatsByOffset)); memset(&(tss->by_offset[found]), 0, sizeof(MVMSpeshStatsByOffset)); tss->by_offset[found].bytecode_offset = bytecode_offset; return &(tss->by_offset[found]); } /* Adds/increments the count of a certain type seen at the given offset. */ static void add_type_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss, MVMStaticFrame *sf, MVMObject *type, MVMuint8 concrete) { /* If we have it already, increment the count. */ MVMuint32 found; MVMuint32 n = oss->num_types; for (found = 0; found < n; found++) { if (oss->types[found].type == type && oss->types[found].type_concrete == concrete) { oss->types[found].count++; return; } } /* Otherwise, add it to the list. */ found = oss->num_types; oss->num_types++; oss->types = MVM_realloc(oss->types, oss->num_types * sizeof(MVMSpeshStatsTypeCount)); MVM_ASSIGN_REF(tc, &(sf->body.spesh->common.header), oss->types[found].type, type); oss->types[found].type_concrete = concrete; oss->types[found].count = 1; } /* Adds/increments the count of a certain invocation target seen at the given * offset. */ static void add_invoke_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss, MVMStaticFrame *sf, MVMStaticFrame *target_sf, MVMint16 caller_is_outer) { /* If we have it already, increment the count. */ MVMuint32 found; MVMuint32 n = oss->num_invokes; for (found = 0; found < n; found++) { if (oss->invokes[found].sf == target_sf) { oss->invokes[found].count++; if (caller_is_outer) oss->invokes[found].caller_is_outer_count++; return; } } /* Otherwise, add it to the list. */ found = oss->num_invokes; oss->num_invokes++; oss->invokes = MVM_realloc(oss->invokes, oss->num_invokes * sizeof(MVMSpeshStatsInvokeCount)); MVM_ASSIGN_REF(tc, &(sf->body.spesh->common.header), oss->invokes[found].sf, target_sf); oss->invokes[found].count = 1; oss->invokes[found].caller_is_outer_count = caller_is_outer ? 1 : 0; } /* Adds/increments the count of a dispatch result seen at the given offset. */ static void add_dispatch_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss, MVMuint32 result_index) { /* If we have it already, increment the count. */ MVMuint32 found; MVMuint32 n = oss->num_dispatch_results; for (found = 0; found < n; found++) { if (oss->dispatch_results[found].result_index == result_index) { oss->dispatch_results[found].count++; return; } } /* Otherwise, add it to the list. */ found = oss->num_dispatch_results; oss->num_dispatch_results++; oss->dispatch_results = MVM_realloc(oss->dispatch_results, oss->num_dispatch_results * sizeof(MVMSpeshStatsDispatchResultCount)); oss->dispatch_results[found].result_index = result_index; oss->dispatch_results[found].count = 1; } /* Adds/increments the count of a type tuple seen at the given offset. */ static void add_type_tuple_at_offset(MVMThreadContext *tc, MVMSpeshStatsByOffset *oss, MVMStaticFrame *sf, MVMSpeshSimCallType *info) { /* Compute type tuple size. */ size_t tt_size = info->cs->flag_count * sizeof(MVMSpeshStatsType); /* If we have it already, increment the count. */ MVMuint32 found, i; MVMuint32 n = oss->num_type_tuples; for (found = 0; found < n; found++) { if (oss->type_tuples[found].cs == info->cs) { if (memcmp(oss->type_tuples[found].arg_types, info->arg_types, tt_size) == 0) { oss->type_tuples[found].count++; return; } } } /* Otherwise, add it to the list; copy type tuple to ease memory * management, but also need to write barrier any types. */ found = oss->num_type_tuples; oss->num_type_tuples++; oss->type_tuples = MVM_realloc(oss->type_tuples, oss->num_type_tuples * sizeof(MVMSpeshStatsTypeTupleCount)); oss->type_tuples[found].cs = info->cs; oss->type_tuples[found].arg_types = MVM_malloc(tt_size); memcpy(oss->type_tuples[found].arg_types, info->arg_types, tt_size); for (i = 0; i < info->cs->flag_count; i++) { if (info->arg_types[i].type) MVM_gc_write_barrier(tc, &(sf->body.spesh->common.header), &(info->arg_types[i].type->header)); if (info->arg_types[i].decont_type) MVM_gc_write_barrier(tc, &(sf->body.spesh->common.header), &(info->arg_types[i].decont_type->header)); } oss->type_tuples[found].count = 1; } /* Initializes the stack simulation. */ static void sim_stack_init(MVMThreadContext *tc, MVMSpeshSimStack *sims) { sims->used = 0; sims->limit = 32; sims->frames = MVM_malloc(sims->limit * sizeof(MVMSpeshSimStackFrame)); sims->depth = 0; } /* Pushes an entry onto the stack frame model. */ static void sim_stack_push(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMStaticFrame *sf, MVMSpeshStats *ss, MVMuint32 cid, MVMuint32 callsite_idx) { MVMSpeshSimStackFrame *frame; MVMCallsite *cs; if (sims->used == sims->limit) { sims->limit *= 2; sims->frames = MVM_realloc(sims->frames, sims->limit * sizeof(MVMSpeshSimStackFrame)); } frame = &(sims->frames[sims->used++]); frame->sf = sf; frame->ss = ss; frame->cid = cid; frame->callsite_idx = callsite_idx; frame->type_idx = -1; frame->arg_types = (cs = ss->by_callsite[callsite_idx].cs) ? MVM_calloc(cs->flag_count, sizeof(MVMSpeshStatsType)) : NULL; frame->offset_logs = NULL; frame->offset_logs_used = frame->offset_logs_limit = 0; frame->osr_hits = 0; frame->call_type_info = NULL; frame->call_type_info_used = frame->call_type_info_limit = 0; frame->last_invoke_offset = 0; frame->last_invoke_sf = NULL; sims->depth++; } /* Adds an entry to a sim frame's callsite type info list, for later * inclusion in the callsite stats. */ static void add_sim_call_type_info(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf, MVMuint32 bytecode_offset, MVMCallsite *cs, MVMSpeshStatsType *arg_types) { MVMSpeshSimCallType *info; if (simf->call_type_info_used == simf->call_type_info_limit) { simf->call_type_info_limit += 32; simf->call_type_info = MVM_realloc(simf->call_type_info, simf->call_type_info_limit * sizeof(MVMSpeshSimCallType)); } info = &(simf->call_type_info[simf->call_type_info_used++]); info->bytecode_offset = bytecode_offset; info->cs = cs; info->arg_types = arg_types; } /* Incorporate information collected into a sim stack frame. */ static void incorporate_stats(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf, MVMuint32 frame_depth, MVMSpeshSimStackFrame *caller, MVMObject *sf_updated) { MVMSpeshStatsByType *tss; MVMint32 first_type_hit = 0; /* Bump version if needed. */ if (simf->ss->last_update != tc->instance->spesh_stats_version) { simf->ss->last_update = tc->instance->spesh_stats_version; MVM_repr_push_o(tc, sf_updated, (MVMObject *)simf->sf); } /* Add OSR hits at callsite level and update depth. */ if (simf->osr_hits) { simf->ss->osr_hits += simf->osr_hits; simf->ss->by_callsite[simf->callsite_idx].osr_hits += simf->osr_hits; } if (frame_depth > simf->ss->by_callsite[simf->callsite_idx].max_depth) simf->ss->by_callsite[simf->callsite_idx].max_depth = frame_depth; /* See if there's a type tuple to attach type-based stats to. */ if (simf->type_idx < 0 && simf->arg_types) { simf->type_idx = by_type(tc, simf->ss, simf->callsite_idx, simf->arg_types); simf->arg_types = NULL; first_type_hit = 1; } tss = simf->type_idx >= 0 ? &(simf->ss->by_callsite[simf->callsite_idx].by_type[simf->type_idx]) : NULL; if (tss) { /* Incorporate data logged at offsets. */ MVMuint32 i; for (i = 0; i < simf->offset_logs_used; i++) { MVMSpeshLogEntry *e = simf->offset_logs[i]; switch (e->kind) { case MVM_SPESH_LOG_TYPE: case MVM_SPESH_LOG_RETURN: { MVMSpeshStatsByOffset *oss = by_offset(tc, tss, e->type.bytecode_offset); add_type_at_offset(tc, oss, simf->sf, e->type.type, e->type.flags & MVM_SPESH_LOG_TYPE_FLAG_CONCRETE); break; } case MVM_SPESH_LOG_INVOKE: { MVMSpeshStatsByOffset *oss = by_offset(tc, tss, e->invoke.bytecode_offset); add_invoke_at_offset(tc, oss, simf->sf, e->invoke.sf, e->invoke.caller_is_outer); break; } case MVM_SPESH_LOG_DISPATCH_RESOLUTION: { MVMSpeshStatsByOffset *oss = by_offset(tc, tss, e->dispatch.bytecode_offset); add_dispatch_at_offset(tc, oss, e->dispatch.result_index); break; } } } /* Incorporate callsite type stats (what type tuples did we make a * call with). */ for (i = 0; i < simf->call_type_info_used; i++) { MVMSpeshSimCallType *info = &(simf->call_type_info[i]); MVMSpeshStatsByOffset *oss = by_offset(tc, tss, info->bytecode_offset); add_type_tuple_at_offset(tc, oss, simf->sf, info); } /* Incorporate OSR hits and bump max depth. */ if (first_type_hit) tss->hits++; tss->osr_hits += simf->osr_hits; if (frame_depth > tss->max_depth) tss->max_depth = frame_depth; /* If the callee's last incovation matches the frame just invoked, * then log the type tuple against the callsite. */ if (caller && caller->last_invoke_sf == simf->sf) add_sim_call_type_info(tc, caller, caller->last_invoke_offset, simf->ss->by_callsite[simf->callsite_idx].cs, tss->arg_types); } /* Clear up offset logs and call type info; they're either incorproated or * to be tossed. Also zero OSR hits, so we don't over-inflate them if this * frame entry survives. */ MVM_free_null(simf->offset_logs); simf->offset_logs_used = simf->offset_logs_limit = 0; MVM_free_null(simf->call_type_info); simf->call_type_info_used = simf->call_type_info_limit = 0; simf->osr_hits = 0; } /* Pops the top frame from the sim stack. */ static void sim_stack_pop(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMObject *sf_updated) { MVMSpeshSimStackFrame *simf; MVMuint32 frame_depth; /* Pop off the simulated frame and incorporate logged data into the spesh * stats model. */ if (sims->used == 0) MVM_panic(1, "Spesh stats: cannot pop an empty simulation stack"); sims->used--; simf = &(sims->frames[sims->used]); frame_depth = sims->depth--; /* Incorporate logged data into the statistics model. */ incorporate_stats(tc, simf, frame_depth, sims->used > 0 ? &(sims->frames[sims->used - 1]) : NULL, sf_updated); } /* Gets the simulation stack frame for the specified correlation ID. If it is * not on the top, searches to see if it's further down. If it is, then pops * off the top to reach it. If it's not found at all, returns NULL and does * nothing to the simulation stack. */ static MVMSpeshSimStackFrame * sim_stack_find(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMuint32 cid, MVMObject *sf_updated) { MVMuint32 found_at = sims->used; while (found_at != 0) { found_at--; if (sims->frames[found_at].cid == cid) { MVMint32 pop = (sims->used - found_at) - 1; MVMint32 i; for (i = 0; i < pop; i++) sim_stack_pop(tc, sims, sf_updated); return &(sims->frames[found_at]); } } return NULL; } /* Pops all the frames in the stack simulation and frees the frames storage. */ static void sim_stack_teardown(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMObject *sf_updated) { while (sims->used) sim_stack_pop(tc, sims, sf_updated); MVM_free(sims->frames); } /* Gets the parameter type slot from a simulation frame. */ static MVMSpeshStatsType * param_type(MVMThreadContext *tc, MVMSpeshSimStackFrame *simf, MVMSpeshLogEntry *e) { if (simf->arg_types) { MVMuint16 idx = e->param.arg_idx; MVMCallsite *cs = simf->ss->by_callsite[simf->callsite_idx].cs; if (cs) { if (idx >= cs->flag_count) MVM_panic(1, "Spesh stats: argument flag index out of bounds"); if (cs->arg_flags[idx] & MVM_CALLSITE_ARG_OBJ) return &(simf->arg_types[idx]); } } return NULL; } /* Decides whether to save or free the simulation stack. */ static void save_or_free_sim_stack(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMThreadContext *save_on_tc, MVMObject *sf_updated) { MVMint32 first_survivor = -1; MVMuint32 i; MVMint32 j; if (save_on_tc) { for (i = 0; i < sims->used; i++) { MVMSpeshSimStackFrame *simf = &(sims->frames[i]); MVMuint32 age = tc->instance->spesh_stats_version - simf->ss->last_update; if (age < MVM_SPESH_STATS_MAX_AGE - 1) { first_survivor = i; break; } } } if (first_survivor >= 0) { /* Move survivors to the start. */ if (first_survivor > 0) { for (i = 0; i < (MVMuint32)first_survivor; i++) { MVM_free(sims->frames[i].offset_logs); MVM_free(sims->frames[i].call_type_info); } sims->used -= first_survivor; memmove(sims->frames, sims->frames + first_survivor, sims->used * sizeof(MVMSpeshSimStackFrame)); } /* Incorporate data from the rest into the stats model, clearing it * away. */ j = sims->used - 1; while (j >= 0) { incorporate_stats(tc, &(sims->frames[j]), first_survivor + j, j > 0 ? &(sims->frames[j - 1]) : NULL, sf_updated); j--; } /* Save frames for next time. */ save_on_tc->spesh_sim_stack = sims; } else { /* Everything on the simulated stack is too old; throw it away. */ sim_stack_teardown(tc, sims, sf_updated); MVM_free(sims); } } /* Receives a spesh log and updates static frame statistics. Each static frame * that is updated is pushed once into sf_updated. */ void MVM_spesh_stats_update(MVMThreadContext *tc, MVMSpeshLog *sl, MVMObject *sf_newly_seen, MVMObject *sf_updated, MVMuint64 *in_newly_seen, MVMuint64 *in_updated) { MVMuint32 i; MVMuint32 n = sl->body.used; MVMSpeshSimStack *sims; MVMThreadContext *log_from_tc = sl->body.thread->body.tc; MVMuint64 newly_seen = 0; MVMuint64 updated = 0; #if MVM_GC_DEBUG tc->in_spesh = 1; #endif /* See if we have a simulation stack left over from before; create a new * one if not. */ if (log_from_tc && log_from_tc->spesh_sim_stack) { /* Filter out those whose stats pointer is outdated. */ MVMuint32 insert_pos = 0; sims = log_from_tc->spesh_sim_stack; for (i = 0; i < sims->used; i++) { MVMSpeshStats *cur_stats = sims->frames[i].sf->body.spesh->body.spesh_stats; if (cur_stats == sims->frames[i].ss) { if (i != insert_pos) sims->frames[insert_pos] = sims->frames[i]; insert_pos++; } } sims->used = insert_pos; log_from_tc->spesh_sim_stack = NULL; } else { sims = MVM_malloc(sizeof(MVMSpeshSimStack)); sim_stack_init(tc, sims); } /* Process the log entries. */ for (i = 0; i < n; i++) { MVMSpeshLogEntry *e = &(sl->body.entries[i]); switch (e->kind) { case MVM_SPESH_LOG_ENTRY: { MVMSpeshStats *ss = stats_for(tc, e->entry.sf); MVMuint32 callsite_idx; if (ss->last_update == 0) { newly_seen++; MVM_repr_push_o(tc, sf_newly_seen, (MVMObject *)e->entry.sf); } else { updated++; } if (ss->last_update != tc->instance->spesh_stats_version) { ss->last_update = tc->instance->spesh_stats_version; MVM_repr_push_o(tc, sf_updated, (MVMObject *)e->entry.sf); } ss->hits++; callsite_idx = by_callsite_idx(tc, ss, e->entry.cs); ss->by_callsite[callsite_idx].hits++; sim_stack_push(tc, sims, e->entry.sf, ss, e->id, callsite_idx); break; } case MVM_SPESH_LOG_PARAMETER: { MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated); if (simf) { MVMSpeshStatsType *type_slot = param_type(tc, simf, e); if (type_slot) { MVM_ASSIGN_REF(tc, &(simf->sf->body.spesh->common.header), type_slot->type, e->param.type); type_slot->type_concrete = e->param.flags & MVM_SPESH_LOG_TYPE_FLAG_CONCRETE ? 1 : 0; type_slot->rw_cont = e->param.flags & MVM_SPESH_LOG_TYPE_FLAG_RW_CONT ? 1 : 0; } } break; } case MVM_SPESH_LOG_PARAMETER_DECONT: { MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated); if (simf) { MVMSpeshStatsType *type_slot = param_type(tc, simf, e); if (type_slot) { MVM_ASSIGN_REF(tc, &(simf->sf->body.spesh->common.header), type_slot->decont_type, e->param.type); type_slot->decont_type_concrete = e->param.flags & MVM_SPESH_LOG_TYPE_FLAG_CONCRETE; } } break; } case MVM_SPESH_LOG_TYPE: case MVM_SPESH_LOG_RETURN: case MVM_SPESH_LOG_INVOKE: case MVM_SPESH_LOG_DISPATCH_RESOLUTION: { /* We only incorporate these into the model later, and only * then if we need to. For now, just keep references to * them. */ MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated); if (simf && (e->kind != MVM_SPESH_LOG_RETURN || e->type.type)) { if (simf->offset_logs_used == simf->offset_logs_limit) { simf->offset_logs_limit += 32; simf->offset_logs = MVM_realloc(simf->offset_logs, simf->offset_logs_limit * sizeof(MVMSpeshLogEntry *)); } simf->offset_logs[simf->offset_logs_used++] = e; if (e->kind == MVM_SPESH_LOG_INVOKE) { simf->last_invoke_offset = e->invoke.bytecode_offset; simf->last_invoke_sf = e->invoke.sf; } } break; } case MVM_SPESH_LOG_OSR: { MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated); if (simf) simf->osr_hits++; break; } case MVM_SPESH_LOG_RETURN_TO_UNLOGGED: { MVMSpeshSimStackFrame *simf = sim_stack_find(tc, sims, e->id, sf_updated); if (simf) sim_stack_pop(tc, sims, sf_updated); break; } } } save_or_free_sim_stack(tc, sims, log_from_tc, sf_updated); if (in_newly_seen) *in_newly_seen = newly_seen; if (in_updated) *in_updated = updated; #if MVM_GC_DEBUG tc->in_spesh = 0; #endif } /* Takes an array of frames we recently updated the stats in. If they weren't * updated in a while, clears them out. */ void MVM_spesh_stats_cleanup(MVMThreadContext *tc, MVMObject *check_frames) { MVMint64 elems = MVM_repr_elems(tc, check_frames); MVMSTable *check_frames_st = STABLE(check_frames); void *check_frames_data = OBJECT_BODY(check_frames); MVMROOT(tc, check_frames, { MVMint64 insert_pos = 0; MVMint64 i; for (i = 0; i < elems; i++) { MVMRegister sf_reg; MVM_VMArray_at_pos(tc, check_frames_st, check_frames, check_frames_data, i, &sf_reg, MVM_reg_obj); MVMStaticFrame *sf = (MVMStaticFrame *)sf_reg.o; MVMROOT(tc, sf, { MVMStaticFrameSpesh *spesh = sf->body.spesh; MVMSpeshStats *ss = spesh->body.spesh_stats; MVMuint32 removed = 0; if (!ss) { /* No stats; already destroyed, don't keep this frame under * consideration. */ removed = 1; } else if (tc->instance->spesh_stats_version - ss->last_update > MVM_SPESH_STATS_MAX_AGE) { /* Do not mark thread blocked as the GC also tries to acquire * mutex_threads and it's held only briefly by all holders anyway */ uv_mutex_lock(&tc->instance->mutex_threads); MVMThread *current = tc->instance->threads; int found = 0; while (current && !found) { MVMThreadContext *cur_tc = current->body.tc; if (cur_tc) { MVMSpeshSimStack *sims = cur_tc->spesh_sim_stack; if (sims) { for (MVMuint32 j = 0; j < sims->used; j++) { MVMSpeshSimStackFrame *simf = &sims->frames[j]; if (simf->ss == ss) { found = 1; break; } } } } current = current->body.next; } uv_mutex_unlock(&tc->instance->mutex_threads); if (!found) { MVM_spesh_stats_destroy(tc, ss); MVM_free_null(spesh->body.spesh_stats); removed = 1; } } if (!removed) { sf_reg.o = (MVMObject *)sf; MVM_VMArray_bind_pos(tc, check_frames_st, check_frames, check_frames_data, insert_pos++, sf_reg, MVM_reg_obj); } }); } MVM_repr_pos_set_elems(tc, check_frames, insert_pos); }); } void MVM_spesh_stats_gc_mark(MVMThreadContext *tc, MVMSpeshStats *ss, MVMGCWorklist *worklist) { if (ss) { MVMuint32 i, j, k, l, m; for (i = 0; i < ss->num_by_callsite; i++) { MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]); for (j = 0; j < by_cs->num_by_type; j++) { MVMSpeshStatsByType *by_type = &(by_cs->by_type[j]); MVMuint32 num_types = by_cs->cs->flag_count; for (k = 0; k < num_types; k++) { MVM_gc_worklist_add(tc, worklist, &(by_type->arg_types[k].type)); MVM_gc_worklist_add(tc, worklist, &(by_type->arg_types[k].decont_type)); } for (k = 0; k < by_type->num_by_offset; k++) { MVMSpeshStatsByOffset *by_offset = &(by_type->by_offset[k]); for (l = 0; l < by_offset->num_types; l++) MVM_gc_worklist_add(tc, worklist, &(by_offset->types[l].type)); for (l = 0; l < by_offset->num_invokes; l++) MVM_gc_worklist_add(tc, worklist, &(by_offset->invokes[l].sf)); for (l = 0; l < by_offset->num_type_tuples; l++) { MVMSpeshStatsType *off_types = by_offset->type_tuples[l].arg_types; MVMuint32 num_off_types = by_offset->type_tuples[l].cs->flag_count; for (m = 0; m < num_off_types; m++) { MVM_gc_worklist_add(tc, worklist, &(off_types[m].type)); MVM_gc_worklist_add(tc, worklist, &(off_types[m].decont_type)); } } } } } } } void MVM_spesh_stats_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *snapshot, MVMSpeshStats *ss) { MVMuint64 cache_1 = 0; MVMuint64 cache_2 = 0; MVMuint64 cache_3 = 0; MVMuint64 cache_4 = 0; MVMuint64 cache_5 = 0; if (ss) { MVMuint32 i, j, k, l, m; for (i = 0; i < ss->num_by_callsite; i++) { MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]); for (j = 0; j < by_cs->num_by_type; j++) { MVMSpeshStatsByType *by_type = &(by_cs->by_type[j]); MVMuint32 num_types = by_cs->cs->flag_count; for (k = 0; k < num_types; k++) { MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, snapshot, (MVMCollectable*)(by_type->arg_types[k].type), "type", &cache_1); MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, snapshot, (MVMCollectable*)(by_type->arg_types[k].decont_type), "decont type", &cache_2); } for (k = 0; k < by_type->num_by_offset; k++) { MVMSpeshStatsByOffset *by_offset = &(by_type->by_offset[k]); for (l = 0; l < by_offset->num_types; l++) MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, snapshot, (MVMCollectable*)(by_offset->types[l].type), "type at offset", &cache_3); for (l = 0; l < by_offset->num_invokes; l++) MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, snapshot, (MVMCollectable*)(by_offset->invokes[l].sf), "invoke", &cache_4); for (l = 0; l < by_offset->num_type_tuples; l++) { MVMSpeshStatsType *off_types = by_offset->type_tuples[l].arg_types; MVMuint32 num_off_types = by_offset->type_tuples[l].cs->flag_count; for (m = 0; m < num_off_types; m++) { MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, snapshot, (MVMCollectable*)(off_types[m].type), "type tuple type", &cache_4); MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, snapshot, (MVMCollectable*)(off_types[m].decont_type), "type tuple deconted type", &cache_5); } } } } } } } void MVM_spesh_stats_destroy(MVMThreadContext *tc, MVMSpeshStats *ss) { if (ss) { MVMuint32 i, j, k, l; for (i = 0; i < ss->num_by_callsite; i++) { MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]); for (j = 0; j < by_cs->num_by_type; j++) { MVMSpeshStatsByType *by_type = &(by_cs->by_type[j]); for (k = 0; k < by_type->num_by_offset; k++) { MVMSpeshStatsByOffset *by_offset = &(by_type->by_offset[k]); MVM_free(by_offset->types); MVM_free(by_offset->invokes); for (l = 0; l < by_offset->num_type_tuples; l++) MVM_free(by_offset->type_tuples[l].arg_types); MVM_free(by_offset->type_tuples); MVM_free(by_offset->dispatch_results); } MVM_free(by_type->by_offset); MVM_free(by_type->arg_types); } MVM_free(by_cs->by_type); } MVM_free(ss->by_callsite); } } void MVM_spesh_sim_stack_gc_mark(MVMThreadContext *tc, MVMSpeshSimStack *sims, MVMGCWorklist *worklist) { MVMuint32 n = sims ? sims->used : 0; MVMuint32 i, j; for (i = 0; i < n; i++) { MVMSpeshSimStackFrame *simf = &(sims->frames[i]); MVM_gc_worklist_add(tc, worklist, &(simf->sf)); MVM_gc_worklist_add(tc, worklist, &(simf->last_invoke_sf)); if (simf->arg_types) { MVMCallsite *cs = simf->ss->by_callsite[simf->callsite_idx].cs; if (cs) { for (j = 0; j < cs->flag_count; j++) { if (cs->arg_flags[j] & MVM_CALLSITE_ARG_OBJ) { MVM_gc_worklist_add(tc, worklist, &(simf->arg_types[j].type)); MVM_gc_worklist_add(tc, worklist, &(simf->arg_types[j].decont_type)); } } } } } } void MVM_spesh_sim_stack_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMSpeshSimStack *sims) { MVMuint32 n = sims ? sims->used : 0; MVMuint32 i; MVMuint64 cache_1 = 0; MVMuint64 cache_2 = 0; for (i = 0; i < n; i++) { MVMSpeshSimStackFrame *simf = &(sims->frames[i]); MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, ss, (MVMCollectable*)(simf->sf), "staticframe", &cache_1); MVM_profile_heap_add_collectable_rel_const_cstr_cached(tc, ss, (MVMCollectable*)(simf->last_invoke_sf), "last invoked staticframe", &cache_2); } } void MVM_spesh_sim_stack_destroy(MVMThreadContext *tc, MVMSpeshSimStack *sims) { if (sims) { MVM_free(sims->frames); MVM_free(sims); } }