/* Initializes a new thread context. Note that this doesn't set up a * thread itself, it just creates the data structure that exists in * MoarVM per thread. */ MVMThreadContext * MVM_tc_create(MVMThreadContext *parent, MVMInstance *instance) { MVMThreadContext *tc = MVM_calloc(1, sizeof(MVMThreadContext)); MVMint32 i; /* Associate with VM instance. */ tc->instance = instance; /* Set up GC nursery. We only allocate tospace initially, and allocate * fromspace the first time this thread GCs, provided it ever does. */ tc->nursery_tospace_size = MVM_gc_new_thread_nursery_size(instance); tc->nursery_tospace = MVM_calloc(1, tc->nursery_tospace_size); tc->nursery_alloc = tc->nursery_tospace; tc->nursery_alloc_limit = (char *)tc->nursery_alloc + tc->nursery_tospace_size; /* Set up temporary root handling. */ tc->num_temproots = 0; tc->alloc_temproots = MVM_TEMP_ROOT_BASE_ALLOC; tc->temproots = MVM_malloc(sizeof(MVMCollectable **) * tc->alloc_temproots); /* Set up intergenerational root handling. */ tc->num_gen2roots = 0; tc->alloc_gen2roots = 64; tc->gen2roots = MVM_malloc(sizeof(MVMCollectable *) * tc->alloc_gen2roots); /* Set up the second generation allocator. */ tc->gen2 = MVM_gc_gen2_create(instance); /* The fixed size allocator also keeps pre-thread state. */ MVM_fixed_size_create_thread(tc); /* Allocate an initial call stack region for the thread. */ MVM_callstack_region_init(tc); /* Initialize random number generator state. */ MVM_proc_seed(tc, (MVM_platform_now() / 10000) * MVM_proc_getpid(tc)); /* Allocate temporary big integers. */ for (i = 0; i < MVM_NUM_TEMP_BIGINTS; i++) { tc->temp_bigints[i] = MVM_malloc(sizeof(mp_int)); mp_init(tc->temp_bigints[i]); } /* Initialize frame sequence numbers */ tc->next_frame_nr = 0; tc->current_frame_nr = 0; /* Initialize last_payload, so we can be sure it's never NULL and don't * need to check. */ tc->last_payload = instance->VMNull; /* Initialize plugin_guard_args so we never have to do a NULL check */ tc->plugin_guard_args = instance->VMNull; /* Note that these two assignments above are repeated in * MVM_6model_bootstrap because VMNull doesn't exist yet when the very * first tc is created. */ return tc; }
/* Initializes a new thread context. Note that this doesn't set up a * thread itself, it just creates the data structure that exists in * MoarVM per thread. */ MVMThreadContext * MVM_tc_create(MVMThreadContext *parent, MVMInstance *instance) { MVMThreadContext *tc = MVM_calloc(1, sizeof(MVMThreadContext)); /* Associate with VM instance. */ tc->instance = instance; /* Use default loop for main thread; create a new one for others. */ if (instance->main_thread) { int r; tc->loop = MVM_calloc(1, sizeof(uv_loop_t)); r = uv_loop_init(tc->loop); if (r < 0) { MVM_free(tc->loop); MVM_free(tc); MVM_exception_throw_adhoc(parent, "Could not create a new Thread: %s", uv_strerror(r)); } } else { tc->loop = uv_default_loop(); } /* Set up GC nursery. We only allocate tospace initially, and allocate * fromspace the first time this thread GCs, provided it ever does. */ tc->nursery_tospace = MVM_calloc(1, MVM_NURSERY_SIZE); tc->nursery_alloc = tc->nursery_tospace; tc->nursery_alloc_limit = (char *)tc->nursery_alloc + MVM_NURSERY_SIZE; /* Set up temporary root handling. */ tc->num_temproots = 0; tc->alloc_temproots = MVM_TEMP_ROOT_BASE_ALLOC; tc->temproots = MVM_malloc(sizeof(MVMCollectable **) * tc->alloc_temproots); /* Set up intergenerational root handling. */ tc->num_gen2roots = 0; tc->alloc_gen2roots = 64; tc->gen2roots = MVM_malloc(sizeof(MVMCollectable *) * tc->alloc_gen2roots); /* Set up the second generation allocator. */ tc->gen2 = MVM_gc_gen2_create(instance); /* Allocate an initial call stack region for the thread. */ MVM_callstack_region_init(tc); /* Initialize random number generator state. */ MVM_proc_seed(tc, (MVM_platform_now() / 10000) * MVM_proc_getpid(tc)); /* Initialize frame sequence numbers */ tc->next_frame_nr = 0; tc->current_frame_nr = 0; /* Initialize last_payload, so we can be sure it's never NULL and don't * need to check. */ tc->last_payload = instance->VMNull; return tc; }
/* Creates a new decoding stream. */ MVMDecodeStream * MVM_string_decodestream_create(MVMThreadContext *tc, MVMint32 encoding, MVMint64 abs_byte_pos) { MVMDecodeStream *ds = MVM_calloc(1, sizeof(MVMDecodeStream)); ds->encoding = encoding; ds->abs_byte_pos = abs_byte_pos; MVM_unicode_normalizer_init(tc, &(ds->norm), MVM_NORMALIZE_NFG); return ds; }
/* Make a copy of the callsite. */ MVMCallsite * MVM_args_copy_callsite(MVMThreadContext *tc, MVMArgProcContext *ctx) { MVMCallsite *res = MVM_calloc(1, sizeof(MVMCallsite)); MVMCallsiteEntry *flags = NULL; MVMCallsiteEntry *src_flags; MVMint32 fsize; if (ctx->arg_flags) { fsize = ctx->flag_count; src_flags = ctx->arg_flags; } else { fsize = ctx->callsite->flag_count; src_flags = ctx->callsite->arg_flags; } if (fsize) { flags = MVM_malloc(fsize * sizeof(MVMCallsiteEntry)); memcpy(flags, src_flags, fsize * sizeof(MVMCallsiteEntry)); } res->flag_count = fsize; res->arg_flags = flags; res->arg_count = ctx->arg_count; res->num_pos = ctx->num_pos; return res; }
void * MVM_region_alloc(MVMThreadContext *tc, MVMRegionAlloc *al, size_t bytes) { char *result = NULL; #if !defined(MVM_CAN_UNALIGNED_INT64) || !defined(MVM_CAN_UNALIGNED_NUM64) /* Round up size to next multiple of 8, to ensure alignment. */ bytes = (bytes + 7) & ~7; #endif if (al->block != NULL && (al->block->alloc + bytes) < al->block->limit) { result = al->block->alloc; al->block->alloc += bytes; } else { /* No block, or block was full. Add another. */ MVMRegionBlock *block = MVM_malloc(sizeof(MVMRegionBlock)); size_t buffer_size = al->block == NULL ? MVM_REGIONALLOC_FIRST_MEMBLOCK_SIZE : MVM_REGIONALLOC_MEMBLOCK_SIZE; if (buffer_size < bytes) buffer_size = bytes; block->buffer = MVM_calloc(1, buffer_size); block->alloc = block->buffer; block->limit = block->buffer + buffer_size; block->prev = al->block; al->block = block; /* Now allocate out of it. */ result = block->alloc; block->alloc += bytes; } return result; }
/* Opens a file, returning a synchronous file handle. */ MVMObject * MVM_file_open_fh(MVMThreadContext *tc, MVMString *filename, MVMString *mode) { char * const fname = MVM_string_utf8_encode_C_string(tc, filename); char * const fmode = MVM_string_utf8_encode_C_string(tc, mode); MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOFileData * const data = MVM_calloc(1, sizeof(MVMIOFileData)); uv_fs_t req; uv_file fd; /* Resolve mode description to flags. */ int flag; if (!resolve_open_mode(&flag, fmode)) { char *waste[] = { fmode, NULL }; MVM_free(fname); MVM_exception_throw_adhoc_free(tc, waste, "Invalid open mode: %s", fmode); } MVM_free(fmode); /* Try to open the file. */ if ((fd = uv_fs_open(tc->loop, &req, (const char *)fname, flag, DEFAULT_MODE, NULL)) < 0) { char *waste[] = { fname, NULL }; MVM_exception_throw_adhoc_free(tc, waste, "Failed to open file %s: %s", fname, uv_strerror(req.result)); } /* Set up handle. */ data->fd = fd; data->filename = fname; data->encoding = MVM_encoding_type_utf8; result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; }
/* Log that we're entering a new frame. */ void MVM_profile_log_enter(MVMThreadContext *tc, MVMStaticFrame *sf, MVMuint64 mode) { MVMProfileThreadData *ptd = get_thread_data(tc); /* Try to locate the entry node, if it's in the call graph already. */ MVMProfileCallNode *pcn = NULL; MVMuint32 i; if (ptd->current_call) for (i = 0; i < ptd->current_call->num_succ; i++) if (ptd->current_call->succ[i]->sf == sf) pcn = ptd->current_call->succ[i]; /* If we didn't find a call graph node, then create one and add it to the * graph. */ if (!pcn) { pcn = MVM_calloc(1, sizeof(MVMProfileCallNode)); pcn->sf = sf; if (ptd->current_call) { MVMProfileCallNode *pred = ptd->current_call; pcn->pred = pred; if (pred->num_succ == pred->alloc_succ) { pred->alloc_succ += 8; pred->succ = MVM_realloc(pred->succ, pred->alloc_succ * sizeof(MVMProfileCallNode *)); } pred->succ[pred->num_succ] = pcn; pred->num_succ++; } else { if (!ptd->call_graph) ptd->call_graph = pcn; } } /* Increment entry counts. */ pcn->total_entries++; switch (mode) { case MVM_PROFILE_ENTER_SPESH: pcn->specialized_entries++; break; case MVM_PROFILE_ENTER_SPESH_INLINE: pcn->specialized_entries++; pcn->inlined_entries++; break; case MVM_PROFILE_ENTER_JIT: pcn->jit_entries++; break; case MVM_PROFILE_ENTER_JIT_INLINE: pcn->jit_entries++; pcn->inlined_entries++; break; } pcn->entry_mode = mode; /* Log entry time; clear skip time. */ pcn->cur_entry_time = uv_hrtime(); pcn->cur_skip_time = 0; /* The current call graph node becomes this one. */ ptd->current_call = pcn; }
MVMObject * MVM_io_socket_create(MVMThreadContext *tc, MVMint64 listen) { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOSyncSocketData * const data = MVM_calloc(1, sizeof(MVMIOSyncSocketData)); result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; }
static MVMObject * socket_accept(MVMThreadContext *tc, MVMOSHandle *h) { MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; Socket s; unsigned int interval_id = MVM_telemetry_interval_start(tc, "syncsocket accept"); do { MVM_gc_mark_thread_blocked(tc); s = accept(data->handle, NULL, NULL); MVM_gc_mark_thread_unblocked(tc); } while(s == -1 && errno == EINTR); if (MVM_IS_SOCKET_ERROR(s)) { MVM_telemetry_interval_stop(tc, interval_id, "syncsocket accept failed"); throw_error(tc, s, "accept socket connection"); } else { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOSyncSocketData * const data = MVM_calloc(1, sizeof(MVMIOSyncSocketData)); data->handle = s; result->body.ops = &op_table; result->body.data = data; MVM_telemetry_interval_stop(tc, interval_id, "syncsocket accept succeeded"); return (MVMObject *)result; } }
/* Allocate a piece of memory from the spesh graph's buffer. Deallocated when * the spesh graph is. */ void * MVM_spesh_alloc(MVMThreadContext *tc, MVMSpeshGraph *g, size_t bytes) { char *result = NULL; #if !defined(MVM_CAN_UNALIGNED_INT64) || !defined(MVM_CAN_UNALIGNED_NUM64) /* Round up size to next multiple of 8, to ensure alignment. */ bytes = (bytes + 7) & ~7; #endif if (g->mem_block) { MVMSpeshMemBlock *block = g->mem_block; if (block->alloc + bytes < block->limit) { result = block->alloc; block->alloc += bytes; } } if (!result) { /* No block, or block was full. Add another. */ MVMSpeshMemBlock *block = MVM_malloc(sizeof(MVMSpeshMemBlock)); block->buffer = MVM_calloc(MVM_SPESH_MEMBLOCK_SIZE, 1); block->alloc = block->buffer; block->limit = block->buffer + MVM_SPESH_MEMBLOCK_SIZE; block->prev = g->mem_block; g->mem_block = block; /* Now allocate out of it. */ if (bytes > MVM_SPESH_MEMBLOCK_SIZE) { MVM_spesh_graph_destroy(tc, g); MVM_exception_throw_adhoc(tc, "MVM_spesh_alloc: requested oversized block"); } result = block->alloc; block->alloc += bytes; } return result; }
/* Gets the current thread's profiling data structure, creating it if needed. */ static MVMProfileThreadData * get_thread_data(MVMThreadContext *tc) { if (!tc->prof_data) { tc->prof_data = MVM_calloc(1, sizeof(MVMProfileThreadData)); tc->prof_data->start_time = uv_hrtime(); } return tc->prof_data; }
/* Finishes up reading and exploding of a frame. */ void MVM_bytecode_finish_frame(MVMThreadContext *tc, MVMCompUnit *cu, MVMStaticFrame *sf, MVMint32 dump_only) { MVMuint32 j; MVMuint8 *pos; MVMuint16 slvs; /* Ensure we've not already done this. */ if (sf->body.fully_deserialized) return; /* Acquire the update mutex on the CompUnit. */ MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)cu->body.update_mutex); /* Ensure no other thread has done this for us in the mean time. */ if (sf->body.fully_deserialized) { MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)cu->body.update_mutex); return; } /* Locate start of frame body. */ pos = sf->body.frame_data_pos; /* Get the number of static lex values we'll need to apply. */ slvs = read_int16(pos, 40); /* Skip past header. */ pos += FRAME_HEADER_SIZE; /* Read the local types. */ if (sf->body.num_locals) { sf->body.local_types = MVM_malloc(sizeof(MVMuint16) * sf->body.num_locals); for (j = 0; j < sf->body.num_locals; j++) sf->body.local_types[j] = read_int16(pos, 2 * j); pos += 2 * sf->body.num_locals; } /* Read the lexical types. */ if (sf->body.num_lexicals) { /* Allocate names hash and types list. */ sf->body.lexical_types = MVM_malloc(sizeof(MVMuint16) * sf->body.num_lexicals); /* Read in data. */ if (sf->body.num_lexicals) { sf->body.lexical_names_list = MVM_malloc(sizeof(MVMLexicalRegistry *) * sf->body.num_lexicals); } for (j = 0; j < sf->body.num_lexicals; j++) { MVMString *name = get_heap_string(tc, cu, NULL, pos, 6 * j + 2); MVMLexicalRegistry *entry = MVM_calloc(1, sizeof(MVMLexicalRegistry)); MVM_ASSIGN_REF(tc, &(sf->common.header), entry->key, name); sf->body.lexical_names_list[j] = entry; entry->value = j; sf->body.lexical_types[j] = read_int16(pos, 6 * j); MVM_string_flatten(tc, name); MVM_HASH_BIND(tc, sf->body.lexical_names, name, entry) } pos += 6 * sf->body.num_lexicals; }
/* Creates the allocator data structure with bins. */ MVMFixedSizeAlloc * MVM_fixed_size_create(MVMThreadContext *tc) { int init_stat; MVMFixedSizeAlloc *al = MVM_malloc(sizeof(MVMFixedSizeAlloc)); al->size_classes = MVM_calloc(MVM_FSA_BINS, sizeof(MVMFixedSizeAllocSizeClass)); if ((init_stat = uv_mutex_init(&(al->complex_alloc_mutex))) < 0) MVM_exception_throw_adhoc(tc, "Failed to initialize mutex: %s", uv_strerror(init_stat)); return al; }
/* Opens a file, returning a synchronous file handle. */ MVMObject * MVM_file_handle_from_fd(MVMThreadContext *tc, uv_file fd) { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOFileData * const data = MVM_calloc(1, sizeof(MVMIOFileData)); data->fd = fd; data->encoding = MVM_encoding_type_utf8; result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; }
/* Wraps a libuv stream (likely, libuv pipe or TTY) up in a sync stream. */ MVMObject * MVM_io_syncstream_from_uvstream(MVMThreadContext *tc, uv_stream_t *handle) { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOSyncStreamData * const data = MVM_calloc(1, sizeof(MVMIOSyncStreamData)); data->handle = handle; data->encoding = MVM_encoding_type_utf8; data->sep = '\n'; result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; }
MVMObject * MVM_io_socket_create(MVMThreadContext *tc, MVMint64 listen) { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOSyncSocketData * const data = MVM_calloc(1, sizeof(MVMIOSyncSocketData)); data->ss.handle = NULL; data->ss.encoding = MVM_encoding_type_utf8; MVM_string_decode_stream_sep_default(tc, &(data->ss.sep_spec)); result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; }
/* Adds another char result buffer into the decoding stream. */ void MVM_string_decodestream_add_chars(MVMThreadContext *tc, MVMDecodeStream *ds, MVMGrapheme32 *chars, MVMint32 length) { MVMDecodeStreamChars *new_chars = MVM_calloc(1, sizeof(MVMDecodeStreamChars)); new_chars->chars = chars; new_chars->length = length; if (ds->chars_tail) ds->chars_tail->next = new_chars; ds->chars_tail = new_chars; if (!ds->chars_head) ds->chars_head = new_chars; }
/* Creates a new decoding stream. */ MVMDecodeStream * MVM_string_decodestream_create(MVMThreadContext *tc, MVMint32 encoding, MVMint64 abs_byte_pos, MVMint32 translate_newlines) { MVMDecodeStream *ds = MVM_calloc(1, sizeof(MVMDecodeStream)); ds->encoding = encoding; ds->abs_byte_pos = abs_byte_pos; MVM_unicode_normalizer_init(tc, &(ds->norm), MVM_NORMALIZE_NFG); if (translate_newlines) MVM_unicode_normalizer_translate_newlines(tc, &(ds->norm)); ds->result_size_guess = 64; return ds; }
/* Opens a file, returning a synchronous file handle. */ MVMObject * MVM_file_open_fh(MVMThreadContext *tc, MVMString *filename, MVMString *mode) { char * const fname = MVM_string_utf8_c8_encode_C_string(tc, filename); int fd; int flag; STAT statbuf; /* Resolve mode description to flags. */ char * const fmode = MVM_string_utf8_encode_C_string(tc, mode); if (!resolve_open_mode(&flag, fmode)) { char *waste[] = { fname, fmode, NULL }; MVM_exception_throw_adhoc_free(tc, waste, "Invalid open mode for file %s: %s", fname, fmode); } MVM_free(fmode); /* Try to open the file. */ #ifdef _WIN32 flag |= _O_BINARY; #endif if ((fd = open((const char *)fname, flag, DEFAULT_MODE)) == -1) { char *waste[] = { fname, NULL }; const char *err = strerror(errno); MVM_exception_throw_adhoc_free(tc, waste, "Failed to open file %s: %s", fname, err); } /* Check that we didn't open a directory by accident. If fstat fails, just move on: Most of the documented error cases should already have triggered when opening the file, and we can't do anything about the others; a failure also does not necessarily imply that the file descriptor cannot be used for reading/writing. */ if (fstat(fd, &statbuf) == 0 && (statbuf.st_mode & S_IFMT) == S_IFDIR) { char *waste[] = { fname, NULL }; if (close(fd) == -1) { const char *err = strerror(errno); MVM_exception_throw_adhoc_free(tc, waste, "Tried to open directory %s, which we failed to close: %s", fname, err); } MVM_exception_throw_adhoc_free(tc, waste, "Tried to open directory %s", fname); } /* Set up handle. */ MVM_free(fname); { MVMIOFileData * const data = MVM_calloc(1, sizeof(MVMIOFileData)); MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); data->fd = fd; data->seekable = MVM_platform_lseek(fd, 0, SEEK_CUR) != -1; result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; } }
/* Opens a file, returning a synchronous file handle. */ MVMObject * MVM_file_handle_from_fd(MVMThreadContext *tc, int fd) { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOFileData * const data = MVM_calloc(1, sizeof(MVMIOFileData)); data->fd = fd; data->seekable = MVM_platform_lseek(fd, 0, SEEK_CUR) != -1; result->body.ops = &op_table; result->body.data = data; #ifdef _WIN32 _setmode(fd, _O_BINARY); #endif return (MVMObject *)result; }
/* Adds another byte buffer into the decoding stream. */ void MVM_string_decodestream_add_bytes(MVMThreadContext *tc, MVMDecodeStream *ds, char *bytes, MVMint32 length) { if (length > 0) { MVMDecodeStreamBytes *new_bytes = MVM_calloc(1, sizeof(MVMDecodeStreamBytes)); new_bytes->bytes = bytes; new_bytes->length = length; if (ds->bytes_tail) ds->bytes_tail->next = new_bytes; ds->bytes_tail = new_bytes; if (!ds->bytes_head) ds->bytes_head = new_bytes; } }
/* Creates a sync pipe handle. */ MVMObject * MVM_io_syncpipe(MVMThreadContext *tc) { MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); MVMIOSyncPipeData * const data = MVM_calloc(1, sizeof(MVMIOSyncPipeData)); uv_pipe_t *handle = MVM_malloc(sizeof(uv_pipe_t)); uv_pipe_init(tc->loop, handle, 0); data->ss.handle = (uv_stream_t *)handle; data->ss.encoding = MVM_encoding_type_utf8; MVM_string_decode_stream_sep_default(tc, &(data->ss.sep_spec)); result->body.ops = &op_table; result->body.data = data; return (MVMObject *)result; }
/* 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); } }
/* Log that we've entered a native routine */ void MVM_profile_log_enter_native(MVMThreadContext *tc, MVMObject *nativecallsite) { MVMProfileThreadData *ptd = get_thread_data(tc); MVMProfileCallNode *pcn = NULL; MVMNativeCallBody *callbody; MVMuint32 i; /* We locate the right call node by looking at sf being NULL and the * native_target_name matching our intended target. */ callbody = MVM_nativecall_get_nc_body(tc, nativecallsite); if (ptd->current_call) for (i = 0; i < ptd->current_call->num_succ; i++) if (ptd->current_call->succ[i]->sf == NULL) if (strcmp(callbody->sym_name, ptd->current_call->succ[i]->native_target_name) == 0) { pcn = ptd->current_call->succ[i]; break; } /* If we didn't find a call graph node, then create one and add it to the * graph. */ if (!pcn) { pcn = MVM_calloc(1, sizeof(MVMProfileCallNode)); pcn->native_target_name = callbody->sym_name; if (ptd->current_call) { MVMProfileCallNode *pred = ptd->current_call; pcn->pred = pred; if (pred->num_succ == pred->alloc_succ) { pred->alloc_succ += 8; pred->succ = MVM_realloc(pred->succ, pred->alloc_succ * sizeof(MVMProfileCallNode *)); } pred->succ[pred->num_succ] = pcn; pred->num_succ++; } else { if (!ptd->call_graph) ptd->call_graph = pcn; } } /* Increment entry counts. */ pcn->total_entries++; pcn->entry_mode = 0; /* Log entry time; clear skip time. */ pcn->cur_entry_time = uv_hrtime(); pcn->cur_skip_time = 0; /* The current call graph node becomes this one. */ ptd->current_call = pcn; }
void MVM_load_bytecode(MVMThreadContext *tc, MVMString *filename) { MVMCompUnit *cu; MVMLoadedCompUnitName *loaded_name; /* Work out actual filename to use, taking --libpath into account. */ filename = MVM_file_in_libpath(tc, filename); /* See if we already loaded this. */ uv_mutex_lock(&tc->instance->mutex_loaded_compunits); MVM_string_flatten(tc, filename); MVM_HASH_GET(tc, tc->instance->loaded_compunits, filename, loaded_name); if (loaded_name) { /* already loaded */ uv_mutex_unlock(&tc->instance->mutex_loaded_compunits); return; } /* Otherwise, load from disk. */ MVMROOT(tc, filename, { char *c_filename = MVM_string_utf8_c8_encode_C_string(tc, filename); /* XXX any exception from MVM_cu_map_from_file needs to be handled * and c_filename needs to be freed */ cu = MVM_cu_map_from_file(tc, c_filename); MVM_free(c_filename); cu->body.filename = filename; /* If there's a deserialization frame, need to run that. */ if (cu->body.deserialize_frame) { /* Set up special return to delegate to running the load frame, * if any. */ tc->cur_frame->return_value = NULL; tc->cur_frame->return_type = MVM_RETURN_VOID; tc->cur_frame->special_return = run_load; tc->cur_frame->special_return_data = cu; tc->cur_frame->mark_special_return_data = mark_sr_data; /* Invoke the deserialization frame and return to the runloop. */ MVM_frame_invoke(tc, cu->body.deserialize_frame, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS), NULL, NULL, NULL, -1); } else { /* No deserialize frame, so do load frame instead. */ run_load(tc, cu); } loaded_name = MVM_calloc(1, sizeof(MVMLoadedCompUnitName)); loaded_name->filename = filename; MVM_HASH_BIND(tc, tc->instance->loaded_compunits, filename, loaded_name); });
/* Adds another byte buffer into the decoding stream. */ void MVM_string_decodestream_add_bytes(MVMThreadContext *tc, MVMDecodeStream *ds, char *bytes, MVMint32 length) { if (length > 0) { MVMDecodeStreamBytes *new_bytes = MVM_calloc(1, sizeof(MVMDecodeStreamBytes)); new_bytes->bytes = bytes; new_bytes->length = length; if (ds->bytes_tail) ds->bytes_tail->next = new_bytes; ds->bytes_tail = new_bytes; if (!ds->bytes_head) ds->bytes_head = new_bytes; } else { /* It's empty, so free the buffer right away and don't add. */ MVM_free(bytes); } }
MVMObject * MVM_args_save_capture(MVMThreadContext *tc, MVMFrame *frame) { MVMObject *cc_obj; MVMROOT(tc, frame, { MVMCallCapture *cc = (MVMCallCapture *) (cc_obj = MVM_repr_alloc_init(tc, tc->instance->CallCapture)); /* Copy the arguments. */ MVMuint32 arg_size = frame->params.arg_count * sizeof(MVMRegister); MVMRegister *args = MVM_malloc(arg_size); memcpy(args, frame->params.args, arg_size); /* Set up the call capture, copying the callsite. */ cc->body.apc = (MVMArgProcContext *)MVM_calloc(1, sizeof(MVMArgProcContext)); MVM_args_proc_init(tc, cc->body.apc, MVM_args_copy_uninterned_callsite(tc, &frame->params), args); });
/* Adds instrumented versions of the unspecialized bytecode. */ static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf) { MVMSpeshCode *sc; MVMStaticFrameInstrumentation *ins; MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0); instrument_graph(tc, sg); sc = MVM_spesh_codegen(tc, sg); ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation)); ins->instrumented_bytecode = sc->bytecode; ins->instrumented_handlers = sc->handlers; ins->instrumented_bytecode_size = sc->bytecode_size; ins->uninstrumented_bytecode = sf->body.bytecode; ins->uninstrumented_handlers = sf->body.handlers; ins->uninstrumented_bytecode_size = sf->body.bytecode_size; sf->body.instrumentation = ins; MVM_spesh_graph_destroy(tc, sg); MVM_free(sc); }
void MVM_spesh_log_add_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMint32 osr) { MVMSpeshBB *bb; /* We've no log slots so far. */ g->num_log_slots = 0; /* Work through the code, adding logging instructions where needed. */ bb = g->entry; while (bb) { MVMSpeshIns *ins = bb->first_ins; while (ins) { switch (ins->info->opcode) { case MVM_OP_getlex: if (g->sf->body.local_types[ins->operands[0].reg.orig] == MVM_reg_obj) insert_log(tc, g, bb, ins, 0); break; case MVM_OP_getlex_no: case MVM_OP_getattr_o: case MVM_OP_getattrs_o: case MVM_OP_getlexstatic_o: case MVM_OP_getlexperinvtype_o: insert_log(tc, g, bb, ins, 0); break; case MVM_OP_invoke_o: insert_log(tc, g, bb, ins, 1); break; case MVM_OP_osrpoint: if (osr) ins->info = MVM_op_get_op(MVM_OP_sp_osrfinalize); else MVM_spesh_manipulate_delete_ins(tc, g, bb, ins); break; } ins = ins->next; } bb = bb->linear_next; } /* Allocate space for logging storage. */ g->log_slots = g->num_log_slots ? MVM_calloc(g->num_log_slots * MVM_SPESH_LOG_RUNS, sizeof(MVMCollectable *)) : NULL; }
/* Adds work to list of items to pass over to another thread, and if we * reach the pass threshold then does the passing. */ static void pass_work_item(MVMThreadContext *tc, WorkToPass *wtp, MVMCollectable **item_ptr) { ThreadWork *target_info = NULL; MVMuint32 target = (*item_ptr)->owner; MVMuint32 j; MVMInstance *i = tc->instance; /* Find any existing thread work passing list for the target. */ if (target == 0) MVM_panic(MVM_exitcode_gcnursery, "Internal error: zeroed target thread ID in work pass"); for (j = 0; j < wtp->num_target_threads; j++) { if (wtp->target_work[j].target == target) { target_info = &wtp->target_work[j]; break; } } /* If there's no entry for this target, create one. */ if (target_info == NULL) { wtp->num_target_threads++; wtp->target_work = MVM_realloc(wtp->target_work, wtp->num_target_threads * sizeof(ThreadWork)); target_info = &wtp->target_work[wtp->num_target_threads - 1]; target_info->target = target; target_info->work = NULL; } /* See if there's a currently active list; create it if not. */ if (!target_info->work) { target_info->work = MVM_calloc(sizeof(MVMGCPassedWork), 1); } /* Add this item to the work list. */ target_info->work->items[target_info->work->num_items] = item_ptr; target_info->work->num_items++; /* If we've hit the limit, pass this work to the target thread. */ if (target_info->work->num_items == MVM_GC_PASS_WORK_SIZE) { push_work_to_thread_in_tray(tc, target, target_info->work); target_info->work = NULL; } }