#include "moar.h" /* Provided spesh is enabled, set up specialization data logging for the * current thread. */ void MVM_spesh_log_initialize_thread(MVMThreadContext *tc, MVMint32 main_thread) { if (tc->instance->spesh_enabled) { tc->spesh_log = MVM_spesh_log_create(tc, tc->thread_obj); tc->spesh_log_quota = main_thread ? MVM_SPESH_LOG_QUOTA_MAIN_THREAD : MVM_SPESH_LOG_QUOTA; } } /* Creates a spesh log for the specified target thread. */ MVMSpeshLog * MVM_spesh_log_create(MVMThreadContext *tc, MVMThread *target_thread) { MVMSpeshLog *result; MVMROOT(tc, target_thread, { result = (MVMSpeshLog *)MVM_repr_alloc_init(tc, tc->instance->SpeshLog); MVM_ASSIGN_REF(tc, &(result->common.header), result->body.thread, target_thread); }); return result; } /* Increments the used count and - if it hits the limit - sends the log off * to the worker thread and NULLs it out. */ static void send_log(MVMThreadContext *tc, MVMSpeshLog *sl) { if (tc->instance->spesh_blocking) { uv_mutex_t *block_mutex; uv_cond_t *block_condvar; block_mutex = sl->body.block_mutex = MVM_malloc(sizeof(uv_mutex_t)); uv_mutex_init(sl->body.block_mutex); block_condvar = sl->body.block_condvar = MVM_malloc(sizeof(uv_cond_t)); uv_cond_init(sl->body.block_condvar); uv_mutex_lock(sl->body.block_mutex); MVMROOT(tc, sl, { MVM_repr_push_o(tc, tc->instance->spesh_queue, (MVMObject *)sl); MVM_gc_mark_thread_blocked(tc); while (!MVM_load(&(sl->body.completed))) uv_cond_wait(block_condvar, block_mutex); MVM_gc_mark_thread_unblocked(tc); }); uv_mutex_unlock(sl->body.block_mutex); } else { MVM_repr_push_o(tc, tc->instance->spesh_queue, (MVMObject *)sl); } if (MVM_decr(&(tc->spesh_log_quota)) > 1) { tc->spesh_log = MVM_spesh_log_create(tc, tc->thread_obj); } else { MVM_telemetry_timestamp(tc, "ran out of spesh log quota"); tc->spesh_log = NULL; } } static void commit_entry(MVMThreadContext *tc, MVMSpeshLog *sl) { sl->body.used++; if (sl->body.used == sl->body.limit) send_log(tc, sl); } /* Handles the case where we enter a new compilation unit and have either no * spesh log or a spesh log that's quite full. This might hinder us in getting * enough data recorded for a tight outer loop in a benchmark. Either grant a * bonus log or send the log early so we can have a fresh one. */ void MVM_spesh_log_new_compunit(MVMThreadContext *tc) { if (tc->num_compunit_extra_logs < 5) { if (tc->spesh_log) if (tc->spesh_log->body.used > tc->spesh_log->body.limit / 4) send_log(tc, tc->spesh_log); if (!tc->spesh_log) { if (MVM_incr(&(tc->spesh_log_quota)) == 0) { tc->spesh_log = MVM_spesh_log_create(tc, tc->thread_obj); tc->spesh_log->body.was_compunit_bumped = 1; MVM_incr(&(tc->num_compunit_extra_logs)); } } } } /* Log the entry to a call frame along with the parameters. */ static void log_param_type(MVMThreadContext *tc, MVMint32 cid, MVMuint16 arg_idx, MVMObject *value, MVMSpeshLogEntryKind kind, MVMint32 rw_cont) { MVMSpeshLog *sl = tc->spesh_log; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = kind; entry->id = cid; MVM_ASSIGN_REF(tc, &(sl->common.header), entry->param.type, value->st->WHAT); entry->param.flags = IS_CONCRETE(value) ? MVM_SPESH_LOG_TYPE_FLAG_CONCRETE : 0; if (rw_cont) entry->param.flags |= MVM_SPESH_LOG_TYPE_FLAG_RW_CONT; entry->param.arg_idx = arg_idx; commit_entry(tc, sl); } static void log_parameter(MVMThreadContext *tc, MVMint32 cid, MVMuint16 arg_idx, MVMObject *param) { MVMContainerSpec const *cs = STABLE(param)->container_spec; MVMROOT(tc, param, { log_param_type(tc, cid, arg_idx, param, MVM_SPESH_LOG_PARAMETER, cs && IS_CONCRETE(param) && cs->fetch_never_invokes ? cs->can_store(tc, param) : 0); }); if (tc->spesh_log && IS_CONCRETE(param)) { if (cs && cs->fetch_never_invokes && REPR(param)->ID != MVM_REPR_ID_NativeRef) { MVMRegister r; cs->fetch(tc, param, &r); log_param_type(tc, cid, arg_idx, r.o, MVM_SPESH_LOG_PARAMETER_DECONT, 0); } } } void MVM_spesh_log_entry(MVMThreadContext *tc, MVMint32 cid, MVMStaticFrame *sf, MVMArgs args) { MVMSpeshLog *sl = tc->spesh_log; if (sl) { /* Log the entry itself. */ MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_ENTRY; entry->id = cid; MVM_ASSIGN_REF(tc, &(sl->common.header), entry->entry.sf, sf); MVMCallsite *cs = args.callsite; entry->entry.cs = cs->is_interned ? cs : NULL; commit_entry(tc, sl); /* Log each parameter in the args buffer. */ if (cs->is_interned) { MVMuint32 i; for (i = 0; i < cs->flag_count; i++) { if (!tc->spesh_log) break; if (cs->arg_flags[i] & MVM_CALLSITE_ARG_OBJ) log_parameter(tc, cid, i, args.source[args.map[i]].o); } } } } /* Log an OSR point being hit. */ void MVM_spesh_log_osr(MVMThreadContext *tc) { MVMSpeshLog *sl = tc->spesh_log; MVMint32 cid = tc->cur_frame->spesh_correlation_id; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_OSR; entry->id = cid; entry->osr.bytecode_offset = (*(tc->interp_cur_op) - *(tc->interp_bytecode_start)) - 2; commit_entry(tc, sl); } /* Log a type. */ void MVM_spesh_log_type(MVMThreadContext *tc, MVMObject *value) { MVMSpeshLog *sl = tc->spesh_log; MVMint32 cid = tc->cur_frame->spesh_correlation_id; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_TYPE; entry->id = cid; MVM_ASSIGN_REF(tc, &(sl->common.header), entry->type.type, value->st->WHAT); entry->type.flags = IS_CONCRETE(value) ? MVM_SPESH_LOG_TYPE_FLAG_CONCRETE : 0; entry->type.bytecode_offset = (*(tc->interp_cur_op) - *(tc->interp_bytecode_start)) - 2; commit_entry(tc, sl); } void MVM_spesh_log_type_at(MVMThreadContext *tc, MVMObject *value, MVMuint8 *prev_op) { MVMSpeshLog *sl = tc->spesh_log; MVMint32 cid = tc->cur_frame->spesh_correlation_id; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_TYPE; entry->id = cid; MVM_ASSIGN_REF(tc, &(sl->common.header), entry->type.type, value->st->WHAT); entry->type.flags = IS_CONCRETE(value) ? MVM_SPESH_LOG_TYPE_FLAG_CONCRETE : 0; entry->type.bytecode_offset = (prev_op - *(tc->interp_bytecode_start)) - 2; commit_entry(tc, sl); } /* Log a decont, only those that did not invoke. */ void MVM_spesh_log_decont(MVMThreadContext *tc, MVMuint8 *prev_op, MVMObject *value) { MVMSpeshLog *sl = tc->spesh_log; MVMint32 cid = tc->cur_frame->spesh_correlation_id; if (prev_op + 4 == *(tc->interp_cur_op)) { MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_TYPE; entry->id = cid; MVM_ASSIGN_REF(tc, &(sl->common.header), entry->type.type, value->st->WHAT); entry->type.flags = IS_CONCRETE(value) ? MVM_SPESH_LOG_TYPE_FLAG_CONCRETE : 0; entry->type.bytecode_offset = (prev_op - *(tc->interp_bytecode_start)) - 2; commit_entry(tc, sl); } } /* Log the target of an invocation; we log the static frame and whether the * outer of the code object is the current frame. */ void MVM_spesh_log_bytecode_target(MVMThreadContext *tc, MVMint32 cid, MVMuint32 bytecode_offset, MVMCode *target) { MVMSpeshLog *sl = tc->spesh_log; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_INVOKE; entry->id = cid; MVM_ASSIGN_REF(tc, &(sl->common.header), entry->invoke.sf, target->body.sf); entry->invoke.caller_is_outer = target->body.outer == tc->cur_frame; entry->invoke.bytecode_offset = bytecode_offset; commit_entry(tc, sl); } /* Log the type returned to a frame after an invocation. */ void MVM_spesh_log_return_type(MVMThreadContext *tc, MVMObject *value) { MVMSpeshLog *sl = tc->spesh_log; MVMFrame *caller = tc->cur_frame->caller; MVMint32 cid = caller->spesh_correlation_id; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_RETURN; entry->id = cid; if (value && tc->stack_top->prev->kind != MVM_CALLSTACK_RECORD_DISPATCH_RECORD) { MVM_ASSIGN_REF(tc, &(sl->common.header), entry->type.type, value->st->WHAT); entry->type.flags = IS_CONCRETE(value) ? MVM_SPESH_LOG_TYPE_FLAG_CONCRETE : 0; } else { entry->type.type = NULL; entry->type.flags = 0; } entry->type.bytecode_offset = (caller->return_address - 2) - caller->static_info->body.bytecode; commit_entry(tc, sl); } /* Logs the return from logged to unlogged code, for the purpose of stack * tracking. */ void MVM_spesh_log_return_to_unlogged(MVMThreadContext *tc) { MVMSpeshLog *sl = tc->spesh_log; MVMint32 cid = tc->cur_frame->spesh_correlation_id; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_RETURN_TO_UNLOGGED; entry->id = cid; commit_entry(tc, sl); } /* Log the result of a dispatch. */ void MVM_spesh_log_dispatch_resolution_for_correlation_id(MVMThreadContext *tc, MVMint32 cid, MVMuint32 bytecode_offset, MVMuint16 result_index) { MVMSpeshLog *sl = tc->spesh_log; MVMSpeshLogEntry *entry = &(sl->body.entries[sl->body.used]); entry->kind = MVM_SPESH_LOG_DISPATCH_RESOLUTION; entry->id = cid; entry->dispatch.bytecode_offset = bytecode_offset; entry->dispatch.result_index = result_index; commit_entry(tc, sl); }