Mercurial > hg > Members > anatofuz > MoarVM
diff src/core/nativecall.c @ 0:2cf249471370
convert mercurial for git
author | Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 08 May 2018 16:09:12 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/nativecall.c Tue May 08 16:09:12 2018 +0900 @@ -0,0 +1,1154 @@ +#include "moar.h" +#ifndef _WIN32 +#include <dlfcn.h> +#endif +#include <platform/threads.h> +#include <platform/time.h> + +/* Grabs a NativeCall body. */ +MVMNativeCallBody * MVM_nativecall_get_nc_body(MVMThreadContext *tc, MVMObject *obj) { + if (REPR(obj)->ID == MVM_REPR_ID_MVMNativeCall) + return (MVMNativeCallBody *)OBJECT_BODY(obj); + else + return (MVMNativeCallBody *)REPR(obj)->box_funcs.get_boxed_ref(tc, + STABLE(obj), obj, OBJECT_BODY(obj), MVM_REPR_ID_MVMNativeCall); +} + +/* Gets the flag for whether to free a string after a call or not. */ +static MVMint16 get_str_free_flag(MVMThreadContext *tc, MVMObject *info) { + MVMString *flag = tc->instance->str_consts.free_str; + if (MVM_repr_exists_key(tc, info, flag)) + if (!MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag))) + return MVM_NATIVECALL_ARG_NO_FREE_STR; + return MVM_NATIVECALL_ARG_FREE_STR; +} + +/* Gets the flag for whether an arg is rw or not. */ +static MVMint16 get_rw_flag(MVMThreadContext *tc, MVMObject *info) { + MVMString *flag = tc->instance->str_consts.rw; + if (MVM_repr_exists_key(tc, info, flag)) { + if (MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag))) + return MVM_NATIVECALL_ARG_RW; + } + return MVM_NATIVECALL_ARG_NO_RW; +} + +/* Gets the flag for whether an arg is rw or not. */ +static MVMint16 get_refresh_flag(MVMThreadContext *tc, MVMObject *info) { + MVMString *typeobj_str = tc->instance->str_consts.typeobj; + if (MVM_repr_exists_key(tc, info, typeobj_str)) { + MVMObject *typeobj = MVM_repr_at_key_o(tc, info, typeobj_str); + + if (REPR(typeobj)->ID == MVM_REPR_ID_MVMCArray) { + MVMCArrayREPRData *repr_data = (MVMCArrayREPRData *)STABLE(typeobj)->REPR_data; + + /* No need to refresh numbers. They're stored directly in the array. */ + if (repr_data->elem_kind == MVM_CARRAY_ELEM_KIND_NUMERIC) + return MVM_NATIVECALL_ARG_NO_REFRESH; + + return MVM_NATIVECALL_ARG_REFRESH; + } + } + + /* We don't know, so fail safe by assuming we have to refresh */ + return MVM_NATIVECALL_ARG_REFRESH; +} + +/* Takes a hash describing a type hands back an argument type code. */ +MVMint16 MVM_nativecall_get_arg_type(MVMThreadContext *tc, MVMObject *info, MVMint16 is_return) { + MVMString *typename = MVM_repr_get_str(tc, MVM_repr_at_key_o(tc, info, + tc->instance->str_consts.type)); + char *ctypename = MVM_string_utf8_encode_C_string(tc, typename); + MVMint16 result; + if (strcmp(ctypename, "void") == 0) { + if (!is_return) { + MVM_free(ctypename); + MVM_exception_throw_adhoc(tc, + "Cannot use 'void' type except for on native call return values"); + } + result = MVM_NATIVECALL_ARG_VOID; + } + else if (strcmp(ctypename, "char") == 0) + result = MVM_NATIVECALL_ARG_CHAR | get_rw_flag(tc, info); + else if (strcmp(ctypename, "short") == 0) + result = MVM_NATIVECALL_ARG_SHORT | get_rw_flag(tc, info); + else if (strcmp(ctypename, "int") == 0) + result = MVM_NATIVECALL_ARG_INT | get_rw_flag(tc, info); + else if (strcmp(ctypename, "long") == 0) + result = MVM_NATIVECALL_ARG_LONG | get_rw_flag(tc, info); + else if (strcmp(ctypename, "longlong") == 0) + result = MVM_NATIVECALL_ARG_LONGLONG | get_rw_flag(tc, info); + else if (strcmp(ctypename, "uchar") == 0) + result = MVM_NATIVECALL_ARG_UCHAR | get_rw_flag(tc, info); + else if (strcmp(ctypename, "ushort") == 0) + result = MVM_NATIVECALL_ARG_USHORT | get_rw_flag(tc, info); + else if (strcmp(ctypename, "uint") == 0) + result = MVM_NATIVECALL_ARG_UINT | get_rw_flag(tc, info); + else if (strcmp(ctypename, "ulong") == 0) + result = MVM_NATIVECALL_ARG_ULONG | get_rw_flag(tc, info); + else if (strcmp(ctypename, "ulonglong") == 0) + result = MVM_NATIVECALL_ARG_ULONGLONG | get_rw_flag(tc, info); + else if (strcmp(ctypename, "float") == 0) + result = MVM_NATIVECALL_ARG_FLOAT | get_rw_flag(tc, info); + else if (strcmp(ctypename, "double") == 0) + result = MVM_NATIVECALL_ARG_DOUBLE | get_rw_flag(tc, info); + else if (strcmp(ctypename, "asciistr") == 0) + result = MVM_NATIVECALL_ARG_ASCIISTR | get_str_free_flag(tc, info); + else if (strcmp(ctypename, "utf8str") == 0) + result = MVM_NATIVECALL_ARG_UTF8STR | get_str_free_flag(tc, info); + else if (strcmp(ctypename, "utf16str") == 0) + result = MVM_NATIVECALL_ARG_UTF16STR | get_str_free_flag(tc, info); + else if (strcmp(ctypename, "cstruct") == 0) + result = MVM_NATIVECALL_ARG_CSTRUCT; + else if (strcmp(ctypename, "cppstruct") == 0) + result = MVM_NATIVECALL_ARG_CPPSTRUCT; + else if (strcmp(ctypename, "cpointer") == 0) + result = MVM_NATIVECALL_ARG_CPOINTER | get_rw_flag(tc, info); + else if (strcmp(ctypename, "carray") == 0) + result = MVM_NATIVECALL_ARG_CARRAY | get_refresh_flag(tc, info); + else if (strcmp(ctypename, "cunion") == 0) + result = MVM_NATIVECALL_ARG_CUNION; + else if (strcmp(ctypename, "vmarray") == 0) + result = MVM_NATIVECALL_ARG_VMARRAY; + else if (strcmp(ctypename, "callback") == 0) + result = MVM_NATIVECALL_ARG_CALLBACK; + else { + char *waste[] = { ctypename, NULL }; + MVM_exception_throw_adhoc_free(tc, waste, "Unknown type '%s' used for native call", ctypename); + } + MVM_free(ctypename); + return result; +} + +MVMObject * MVM_nativecall_make_int(MVMThreadContext *tc, MVMObject *type, MVMint64 value) { + return type ? MVM_repr_box_int(tc, type, value) : NULL; +} + +MVMObject * MVM_nativecall_make_uint(MVMThreadContext *tc, MVMObject *type, MVMuint64 value) { + return type ? MVM_repr_box_int(tc, type, (MVMint64)value) : NULL; +} + +MVMObject * MVM_nativecall_make_num(MVMThreadContext *tc, MVMObject *type, MVMnum64 value) { + return type ? MVM_repr_box_num(tc, type, value) : NULL; +} + +MVMObject * MVM_nativecall_make_str(MVMThreadContext *tc, MVMObject *type, MVMint16 ret_type, char *cstring) { + MVMObject *result = type; + if (cstring && type) { + MVMString *value; + + MVM_gc_root_temp_push(tc, (MVMCollectable **)&type); + + switch (ret_type & MVM_NATIVECALL_ARG_TYPE_MASK) { + case MVM_NATIVECALL_ARG_ASCIISTR: + value = MVM_string_ascii_decode(tc, tc->instance->VMString, cstring, strlen(cstring)); + break; + case MVM_NATIVECALL_ARG_UTF8STR: + value = MVM_string_utf8_decode(tc, tc->instance->VMString, cstring, strlen(cstring)); + break; + case MVM_NATIVECALL_ARG_UTF16STR: + value = MVM_string_utf16_decode(tc, tc->instance->VMString, cstring, strlen(cstring)); + break; + default: + MVM_exception_throw_adhoc(tc, "Internal error: unhandled encoding"); + } + + MVM_gc_root_temp_pop(tc); + result = MVM_repr_box_str(tc, type, value); + if (ret_type & MVM_NATIVECALL_ARG_FREE_STR) + MVM_free(cstring); + } + + return result; +} + +/* Constructs a boxed result using a CStruct REPR type. */ +MVMObject * MVM_nativecall_make_cstruct(MVMThreadContext *tc, MVMObject *type, void *cstruct) { + MVMObject *result = type; + if (cstruct && type) { + MVMCStructREPRData *repr_data = (MVMCStructREPRData *)STABLE(type)->REPR_data; + if (REPR(type)->ID != MVM_REPR_ID_MVMCStruct) + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CStruct representation, but got a %s (%s)", REPR(type)->name, MVM_6model_get_debug_name(tc, type)); + result = REPR(type)->allocate(tc, STABLE(type)); + ((MVMCStruct *)result)->body.cstruct = cstruct; + if (repr_data->num_child_objs) + ((MVMCStruct *)result)->body.child_objs = MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *)); + } + return result; +} + +/* Constructs a boxed result using a CUnion REPR type. */ +MVMObject * MVM_nativecall_make_cunion(MVMThreadContext *tc, MVMObject *type, void *cunion) { + MVMObject *result = type; + if (cunion && type) { + MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)STABLE(type)->REPR_data; + if (REPR(type)->ID != MVM_REPR_ID_MVMCUnion) + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CUnion representation, but got a %s (%s)", REPR(type)->name, MVM_6model_get_debug_name(tc, type)); + result = REPR(type)->allocate(tc, STABLE(type)); + ((MVMCUnion *)result)->body.cunion = cunion; + if (repr_data->num_child_objs) + ((MVMCUnion *)result)->body.child_objs = MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *)); + } + return result; +} + +MVMObject * MVM_nativecall_make_cppstruct(MVMThreadContext *tc, MVMObject *type, void *cppstruct) { + MVMObject *result = type; + if (cppstruct && type) { + MVMCPPStructREPRData *repr_data = (MVMCPPStructREPRData *)STABLE(type)->REPR_data; + if (REPR(type)->ID != MVM_REPR_ID_MVMCPPStruct) + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CPPStruct representation, but got a %s (%s)", REPR(type)->name, MVM_6model_get_debug_name(tc, type)); + result = REPR(type)->allocate(tc, STABLE(type)); + ((MVMCPPStruct *)result)->body.cppstruct = cppstruct; + if (repr_data->num_child_objs) + ((MVMCPPStruct *)result)->body.child_objs = MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *)); + } + return result; +} + +/* Constructs a boxed result using a CPointer REPR type. */ +MVMObject * MVM_nativecall_make_cpointer(MVMThreadContext *tc, MVMObject *type, void *ptr) { + MVMObject *result = type; + if (ptr && type) { + if (REPR(type)->ID != MVM_REPR_ID_MVMCPointer) + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CPointer representation, but got a %s (%s)", REPR(type)->name, MVM_6model_get_debug_name(tc, type)); + result = REPR(type)->allocate(tc, STABLE(type)); + ((MVMCPointer *)result)->body.ptr = ptr; + } + return result; +} + +/* Constructs a boxed result using a CArray REPR type. */ +MVMObject * MVM_nativecall_make_carray(MVMThreadContext *tc, MVMObject *type, void *carray) { + MVMObject *result = type; + if (carray && type) { + if (REPR(type)->ID != MVM_REPR_ID_MVMCArray) + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CArray representation, but got a %s (%s)", REPR(type)->name, MVM_6model_get_debug_name(tc, type)); + result = REPR(type)->allocate(tc, STABLE(type)); + ((MVMCArray *)result)->body.storage = carray; + } + return result; +} + +signed char MVM_nativecall_unmarshal_char(MVMThreadContext *tc, MVMObject *value) { + return (signed char)MVM_repr_get_int(tc, value); +} + +signed short MVM_nativecall_unmarshal_short(MVMThreadContext *tc, MVMObject *value) { + return (signed short)MVM_repr_get_int(tc, value); +} + +signed int MVM_nativecall_unmarshal_int(MVMThreadContext *tc, MVMObject *value) { + return (signed int)MVM_repr_get_int(tc, value); +} + +signed long MVM_nativecall_unmarshal_long(MVMThreadContext *tc, MVMObject *value) { + return (signed long)MVM_repr_get_int(tc, value); +} + +signed long long MVM_nativecall_unmarshal_longlong(MVMThreadContext *tc, MVMObject *value) { + return (signed long long)MVM_repr_get_int(tc, value); +} + +unsigned char MVM_nativecall_unmarshal_uchar(MVMThreadContext *tc, MVMObject *value) { + return (unsigned char)MVM_repr_get_int(tc, value); +} + +unsigned short MVM_nativecall_unmarshal_ushort(MVMThreadContext *tc, MVMObject *value) { + return (unsigned short)MVM_repr_get_int(tc, value); +} + +unsigned int MVM_nativecall_unmarshal_uint(MVMThreadContext *tc, MVMObject *value) { + return (unsigned int)MVM_repr_get_int(tc, value); +} + +unsigned long MVM_nativecall_unmarshal_ulong(MVMThreadContext *tc, MVMObject *value) { + return (unsigned long)MVM_repr_get_int(tc, value); +} + +unsigned long long MVM_nativecall_unmarshal_ulonglong(MVMThreadContext *tc, MVMObject *value) { + return (unsigned long long)MVM_repr_get_int(tc, value); +} + +float MVM_nativecall_unmarshal_float(MVMThreadContext *tc, MVMObject *value) { + return (float)MVM_repr_get_num(tc, value); +} + +double MVM_nativecall_unmarshal_double(MVMThreadContext *tc, MVMObject *value) { + return (double)MVM_repr_get_num(tc, value); +} + +char * MVM_nativecall_unmarshal_string(MVMThreadContext *tc, MVMObject *value, MVMint16 type, MVMint16 *free) { + if (IS_CONCRETE(value)) { + MVMString *value_str = MVM_repr_get_str(tc, value); + + /* Encode string. */ + char *str; + switch (type & MVM_NATIVECALL_ARG_TYPE_MASK) { + case MVM_NATIVECALL_ARG_ASCIISTR: + str = MVM_string_ascii_encode_any(tc, value_str); + break; + case MVM_NATIVECALL_ARG_UTF16STR: + str = MVM_string_utf16_encode(tc, value_str, 0); + break; + default: + str = MVM_string_utf8_encode_C_string(tc, value_str); + } + + /* Set whether to free it or not. */ + if (free) { + if (REPR(value)->ID == MVM_REPR_ID_MVMCStr) + *free = 0; /* Manually managed. */ + else if (free && type & MVM_NATIVECALL_ARG_FREE_STR_MASK) + *free = 1; + else + *free = 0; + } + + return str; + } + else { + return NULL; + } +} + +void * MVM_nativecall_unmarshal_cstruct(MVMThreadContext *tc, MVMObject *value) { + if (!IS_CONCRETE(value)) + return NULL; + else if (REPR(value)->ID == MVM_REPR_ID_MVMCStruct) + return ((MVMCStruct *)value)->body.cstruct; + else + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CStruct representation, but got a %s (%s)", REPR(value)->name, MVM_6model_get_debug_name(tc, value)); +} + +void * MVM_nativecall_unmarshal_cppstruct(MVMThreadContext *tc, MVMObject *value) { + if (!IS_CONCRETE(value)) + return NULL; + else if (REPR(value)->ID == MVM_REPR_ID_MVMCPPStruct) + return ((MVMCPPStruct *)value)->body.cppstruct; + else + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CPPStruct representation, but got a %s (%s)", REPR(value)->name, MVM_6model_get_debug_name(tc, value)); +} + +void * MVM_nativecall_unmarshal_cpointer(MVMThreadContext *tc, MVMObject *value) { + if (!IS_CONCRETE(value)) + return NULL; + else if (REPR(value)->ID == MVM_REPR_ID_MVMCPointer) + return ((MVMCPointer *)value)->body.ptr; + else + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CPointer representation, but got a %s (%s)", REPR(value)->name, MVM_6model_get_debug_name(tc, value)); +} + +void * MVM_nativecall_unmarshal_carray(MVMThreadContext *tc, MVMObject *value) { + if (!IS_CONCRETE(value)) + return NULL; + else if (REPR(value)->ID == MVM_REPR_ID_MVMCArray) + return ((MVMCArray *)value)->body.storage; + else + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CArray representation, but got a %s (%s)", REPR(value)->name, MVM_6model_get_debug_name(tc, value)); +} + +void * MVM_nativecall_unmarshal_vmarray(MVMThreadContext *tc, MVMObject *value) { + if (!IS_CONCRETE(value)) + return NULL; + else if (REPR(value)->ID == MVM_REPR_ID_VMArray) { + MVMArrayBody *body = &((MVMArray *)value)->body; + MVMArrayREPRData *repr_data = (MVMArrayREPRData *)STABLE(value)->REPR_data; + size_t start_pos = body->start * repr_data->elem_size; + return ((char *)body->slots.any) + start_pos; + } + else + MVM_exception_throw_adhoc(tc, + "Native call expected object with Array representation, but got a %s (%s)", REPR(value)->name, MVM_6model_get_debug_name(tc, value)); +} + +void * MVM_nativecall_unmarshal_cunion(MVMThreadContext *tc, MVMObject *value) { + if (!IS_CONCRETE(value)) + return NULL; + else if (REPR(value)->ID == MVM_REPR_ID_MVMCUnion) + return ((MVMCUnion *)value)->body.cunion; + else + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CUnion representation, but got a %s (%s)", REPR(value)->name, MVM_6model_get_debug_name(tc, value)); +} + +#ifdef _WIN32 +static const char *dlerror(void) +{ + static char buf[32]; + DWORD dw = GetLastError(); + if (dw == 0) + return NULL; + snprintf(buf, 32, "error 0x%"PRIx32"", (MVMuint32)dw); + return buf; +} +#endif + +void init_c_call_node(MVMThreadContext *tc, MVMSpeshGraph *sg, MVMJitNode *node, void *func_ptr, MVMint16 num_args, MVMJitCallArg *args) { + node->type = MVM_JIT_NODE_CALL_C; + node->u.call.func_ptr = func_ptr; + if (0 < num_args) { + node->u.call.args = MVM_spesh_alloc(tc, sg, num_args * sizeof(MVMJitCallArg)); + memcpy(node->u.call.args, args, num_args * sizeof(MVMJitCallArg)); + } + else { + node->u.call.args = NULL; + } + node->u.call.num_args = num_args; + node->u.call.has_vargs = 0; + node->u.call.rv_mode = MVM_JIT_RV_VOID; + node->u.call.rv_idx = -1; +} + +void init_box_call_node(MVMThreadContext *tc, MVMSpeshGraph *sg, MVMJitNode *box_rv_node, void *func_ptr, MVMint16 restype, MVMint16 dst) { + MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, + { MVM_JIT_REG_DYNIDX, { 2 } }, + { MVM_JIT_SAVED_RV, { 0 } }}; + init_c_call_node(tc, sg, box_rv_node, func_ptr, 3, args); + box_rv_node->next = NULL; + if (dst == -1) { + box_rv_node->u.call.rv_mode = MVM_JIT_RV_DYNIDX; + box_rv_node->u.call.rv_idx = 0; + } + else { + box_rv_node->u.call.args[1].type = MVM_JIT_REG_VAL; + box_rv_node->u.call.args[1].v.reg = restype; + + box_rv_node->u.call.rv_mode = MVM_JIT_RV_PTR; + box_rv_node->u.call.rv_idx = dst; + } +} + +MVMJitGraph *MVM_nativecall_jit_graph_for_caller_code( + MVMThreadContext *tc, + MVMSpeshGraph *sg, + MVMNativeCallBody *body, + MVMint16 restype, + MVMint16 dst, + MVMSpeshIns **arg_ins +) { + MVMJitGraph *jg = MVM_spesh_alloc(tc, sg, sizeof(MVMJitGraph)); /* will actually calloc */ + MVMJitNode *block_gc_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + MVMJitNode *unblock_gc_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + MVMJitNode *call_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + MVMJitNode *save_rv_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + MVMJitNode *box_rv_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + MVMJitCode *jitcode = NULL; + MVMJitCallArg block_gc_args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } } }; + + jg->sg = sg; + jg->first_node = block_gc_node; + + save_rv_node->type = MVM_JIT_NODE_SAVE_RV; + save_rv_node->u.stack.slot = 0; + save_rv_node->next = unblock_gc_node; + + init_c_call_node(tc, sg, block_gc_node, &MVM_gc_mark_thread_blocked, 1, block_gc_args); + block_gc_node->next = call_node; + + init_c_call_node(tc, sg, unblock_gc_node, &MVM_gc_mark_thread_unblocked, 1, block_gc_args); + + init_c_call_node(tc, sg, call_node, body->entry_point, 0, NULL); /* we handle args manually */ + call_node->next = save_rv_node; + call_node->u.call.num_args = body->num_args; + jg->last_node = unblock_gc_node->next = box_rv_node; + + if (0 < body->num_args) { + MVMuint16 i = 0, str_arg_count = 0; + call_node->u.call.args = MVM_spesh_alloc(tc, sg, body->num_args * sizeof(MVMJitCallArg)); + for (i = 0; i < body->num_args; i++) { + if ((body->arg_types[i] & MVM_NATIVECALL_ARG_TYPE_MASK) == MVM_NATIVECALL_ARG_UTF8STR) { + MVMJitNode *unbox_str_node; + MVMJitNode *save_str_rv_node; + MVMJitNode *free_str_node; + + if (7 < ++str_arg_count) /* only got 7 empty slots in the stack scratch space */ + goto fail; + + save_str_rv_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + save_str_rv_node->type = MVM_JIT_NODE_SAVE_RV; + save_str_rv_node->u.stack.slot = str_arg_count; + save_str_rv_node->next = jg->first_node; + + unbox_str_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + { + MVMJitCallArg unbox_str_args[] = { + { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, + { + dst == -1 ? MVM_JIT_ARG_I64 : MVM_JIT_PARAM_I64 , + { dst == -1 ? i : arg_ins[i]->operands[1].reg.orig } + } + }; + init_c_call_node(tc, sg, unbox_str_node, &MVM_string_utf8_maybe_encode_C_string, 2, unbox_str_args); + } + unbox_str_node->next = save_str_rv_node; + jg->first_node = unbox_str_node; + + call_node->u.call.args[i].type = MVM_JIT_SAVED_RV; + call_node->u.call.args[i].v.lit_i64 = str_arg_count; + + if ((body->arg_types[i] & MVM_NATIVECALL_ARG_FREE_STR_MASK) != 0) { + MVMJitCallArg free_str_args[] = { + { MVM_JIT_SAVED_RV , { str_arg_count } } + }; + free_str_node = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + init_c_call_node(tc, sg, free_str_node, &MVM_free, 1, free_str_args); + free_str_node->next = unblock_gc_node->next; + unblock_gc_node->next = free_str_node; + } + } + } + for (i = 0; i < body->num_args; i++) { + MVMJitArgType arg_type; + int is_rw = ((body->arg_types[i] & MVM_NATIVECALL_ARG_RW_MASK) == MVM_NATIVECALL_ARG_RW); + + switch (body->arg_types[i] & MVM_NATIVECALL_ARG_TYPE_MASK) { + case MVM_NATIVECALL_ARG_CHAR: + case MVM_NATIVECALL_ARG_UCHAR: + case MVM_NATIVECALL_ARG_SHORT: + case MVM_NATIVECALL_ARG_USHORT: + case MVM_NATIVECALL_ARG_INT: + case MVM_NATIVECALL_ARG_UINT: + case MVM_NATIVECALL_ARG_LONG: + case MVM_NATIVECALL_ARG_ULONG: + case MVM_NATIVECALL_ARG_LONGLONG: + case MVM_NATIVECALL_ARG_ULONGLONG: + arg_type = dst == -1 + ? is_rw ? MVM_JIT_ARG_I64_RW : MVM_JIT_ARG_I64 + : is_rw ? MVM_JIT_PARAM_I64_RW : MVM_JIT_PARAM_I64; + break; + case MVM_NATIVECALL_ARG_CPOINTER: + if (is_rw) goto fail; + arg_type = dst == -1 ? MVM_JIT_ARG_PTR : MVM_JIT_PARAM_PTR; + break; + case MVM_NATIVECALL_ARG_CARRAY: + if (body->arg_types[i] & MVM_NATIVECALL_ARG_REFRESH_MASK) goto fail; + if (is_rw) goto fail; + arg_type = dst == -1 ? MVM_JIT_ARG_PTR : MVM_JIT_PARAM_PTR; + break; + case MVM_NATIVECALL_ARG_VMARRAY: + if (is_rw) goto fail; + arg_type = dst == -1 ? MVM_JIT_ARG_VMARRAY : MVM_JIT_PARAM_VMARRAY; + break; + case MVM_NATIVECALL_ARG_UTF8STR: + if (is_rw) goto fail; + continue; /* already handled */ + default: + goto fail; + } + call_node->u.call.args[i].type = arg_type; + call_node->u.call.args[i].v.lit_i64 = dst == -1 ? i : arg_ins[i]->operands[1].reg.orig; + } + } + + if (body->ret_type == MVM_NATIVECALL_ARG_CHAR + || body->ret_type == MVM_NATIVECALL_ARG_UCHAR + || body->ret_type == MVM_NATIVECALL_ARG_SHORT + || body->ret_type == MVM_NATIVECALL_ARG_USHORT + || body->ret_type == MVM_NATIVECALL_ARG_INT + || body->ret_type == MVM_NATIVECALL_ARG_UINT + || body->ret_type == MVM_NATIVECALL_ARG_LONG + || body->ret_type == MVM_NATIVECALL_ARG_ULONG + || body->ret_type == MVM_NATIVECALL_ARG_LONGLONG + || body->ret_type == MVM_NATIVECALL_ARG_ULONGLONG + ) { + init_box_call_node(tc, sg, box_rv_node, &MVM_nativecall_make_int, restype, dst); + } + else if (body->ret_type == MVM_NATIVECALL_ARG_CPOINTER) { + init_box_call_node(tc, sg, box_rv_node, &MVM_nativecall_make_cpointer, restype, dst); + } + else if (body->ret_type == MVM_NATIVECALL_ARG_UTF8STR) { + MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, + { MVM_JIT_REG_DYNIDX, { 2 } }, + { MVM_JIT_LITERAL, { MVM_NATIVECALL_ARG_UTF8STR } }, + { MVM_JIT_SAVED_RV, { 0 } }}; + init_c_call_node(tc, sg, box_rv_node, &MVM_nativecall_make_str, 4, args); + box_rv_node->next = NULL; + if (dst == -1) { + box_rv_node->u.call.rv_mode = MVM_JIT_RV_DYNIDX; + box_rv_node->u.call.rv_idx = 0; + } + else { + box_rv_node->u.call.args[1].type = MVM_JIT_REG_VAL; + box_rv_node->u.call.args[1].v.reg = restype; + + box_rv_node->u.call.rv_mode = MVM_JIT_RV_PTR; + box_rv_node->u.call.rv_idx = dst; + } + } + else if (body->ret_type == MVM_NATIVECALL_ARG_VOID) { + call_node->next = unblock_gc_node; + unblock_gc_node->next = NULL; + jg->last_node = unblock_gc_node; + } + else { + goto fail; + } + + return jg; +fail: + return NULL; +} + +MVMJitCode *create_caller_code(MVMThreadContext *tc, MVMNativeCallBody *body) { + MVMSpeshGraph *sg = MVM_calloc(1, sizeof(MVMSpeshGraph)); + MVMJitGraph *jg = MVM_nativecall_jit_graph_for_caller_code(tc, sg, body, -1, -1, NULL); + MVMJitCode *jitcode; + if (jg != NULL) { + MVMJitNode *entry_label = MVM_spesh_alloc(tc, sg, sizeof(MVMJitNode)); + entry_label->next = jg->first_node; + jg->first_node = entry_label; + jg->num_labels = 1; + + entry_label->type = MVM_JIT_NODE_LABEL; + entry_label->u.label.name = 0; + jitcode = MVM_jit_compile_graph(tc, jg); + } + else { + jitcode = NULL; + } + MVM_spesh_graph_destroy(tc, sg); + return jitcode; +} + +/* Builds up a native call site out of the supplied arguments. */ +MVMint8 MVM_nativecall_build(MVMThreadContext *tc, MVMObject *site, MVMString *lib, + MVMString *sym, MVMString *conv, MVMObject *arg_info, MVMObject *ret_info) { + char *lib_name = MVM_string_utf8_c8_encode_C_string(tc, lib); + char *sym_name = MVM_string_utf8_c8_encode_C_string(tc, sym); + MVMint8 keep_sym_name = 0; + MVMint16 i; + + unsigned int interval_id = MVM_telemetry_interval_start(tc, "building native call"); + + MVMObject *entry_point_o = (MVMObject *)MVM_repr_at_key_o(tc, ret_info, + tc->instance->str_consts.entry_point); + + /* Initialize the object; grab native call part of its body. */ + MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site); + + /* Try to load the library. */ + body->lib_name = lib_name; + body->lib_handle = MVM_nativecall_load_lib(lib_name[0] ? lib_name : NULL); + + if (!body->lib_handle) { + char *waste[] = { lib_name, NULL }; + MVM_free(sym_name); + MVM_telemetry_interval_stop(tc, interval_id, "error building native call"); + MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate native library '%s': %s", lib_name, dlerror()); + } + + /* Try to locate the symbol. */ + if (entry_point_o) { + body->entry_point = MVM_nativecall_unmarshal_cpointer(tc, entry_point_o); + body->sym_name = sym_name; + keep_sym_name = 1; + } + + if (!body->entry_point) { + body->entry_point = MVM_nativecall_find_sym(body->lib_handle, sym_name); + if (!body->entry_point) { + char *waste[] = { sym_name, lib_name, NULL }; + MVM_telemetry_interval_stop(tc, interval_id, "error building native call"); + MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate symbol '%s' in native library '%s'", + sym_name, lib_name); + } + body->sym_name = sym_name; + keep_sym_name = 1; + } + + MVM_telemetry_interval_annotate_dynamic((uintptr_t)body->entry_point, interval_id, body->sym_name); + + if (keep_sym_name == 0) { + MVM_free(sym_name); + } + + /* Set calling convention, if any. */ + body->convention = MVM_nativecall_get_calling_convention(tc, conv); + + /* Transform each of the args info structures into a flag. */ + body->num_args = MVM_repr_elems(tc, arg_info); + body->arg_types = MVM_malloc(sizeof(MVMint16) * (body->num_args ? body->num_args : 1)); + body->arg_info = MVM_malloc(sizeof(MVMObject *) * (body->num_args ? body->num_args : 1)); +#ifdef HAVE_LIBFFI + body->ffi_arg_types = MVM_malloc(sizeof(ffi_type *) * (body->num_args ? body->num_args : 1)); +#endif + for (i = 0; i < body->num_args; i++) { + MVMObject *info = MVM_repr_at_pos_o(tc, arg_info, i); + body->arg_types[i] = MVM_nativecall_get_arg_type(tc, info, 0); +#ifdef HAVE_LIBFFI + body->ffi_arg_types[i] = MVM_nativecall_get_ffi_type(tc, body->arg_types[i]); +#endif + if(body->arg_types[i] == MVM_NATIVECALL_ARG_CALLBACK) { + MVM_ASSIGN_REF(tc, &(site->header), body->arg_info[i], + MVM_repr_at_key_o(tc, info, tc->instance->str_consts.callback_args)); + } + else { + body->arg_info[i] = NULL; + } + } + + /* Transform return argument type info a flag. */ + body->ret_type = MVM_nativecall_get_arg_type(tc, ret_info, 1); +#ifdef HAVE_LIBFFI + body->ffi_ret_type = MVM_nativecall_get_ffi_type(tc, body->ret_type); +#endif + if (tc->instance->jit_enabled) { + body->jitcode = create_caller_code(tc, body); + } + else + body->jitcode = NULL; + + MVM_telemetry_interval_stop(tc, interval_id, "nativecall built"); + + return body->jitcode != NULL; +} + +static MVMObject * nativecall_cast(MVMThreadContext *tc, MVMObject *target_spec, MVMObject *target_type, void *cpointer_body) { + MVMObject *result = NULL; + + MVMROOT2(tc, target_spec, target_type, { + switch (REPR(target_type)->ID) { + case MVM_REPR_ID_P6opaque: { + const MVMStorageSpec *ss = REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec)); + if(ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) { + MVMint64 value = 0; + if (ss->is_unsigned) + switch(ss->bits) { + case 8: + value = *(MVMuint8 *)cpointer_body; + break; + case 16: + value = *(MVMuint16 *)cpointer_body; + break; + case 32: + value = *(MVMuint32 *)cpointer_body; + break; + case 64: + default: + value = *(MVMuint64 *)cpointer_body; + } + else + switch(ss->bits) { + case 8: + value = *(MVMint8 *)cpointer_body; + break; + case 16: + value = *(MVMint16 *)cpointer_body; + break; + case 32: + value = *(MVMint32 *)cpointer_body; + break; + case 64: + default: + value = *(MVMint64 *)cpointer_body; + } + + result = MVM_nativecall_make_int(tc, target_type, value); + } + else if(ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) { + MVMnum64 value; + + switch(ss->bits) { + case 32: + value = *(MVMnum32 *)cpointer_body; + break; + case 64: + default: + value = *(MVMnum64 *)cpointer_body; + } + + result = MVM_nativecall_make_num(tc, target_type, value); + } + else if(ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) { + result = MVM_nativecall_make_str(tc, target_type, MVM_NATIVECALL_ARG_UTF8STR, + (char *)cpointer_body); + } + else + MVM_exception_throw_adhoc(tc, "Internal error: unhandled target type"); + + break; + } + case MVM_REPR_ID_P6int: { + const MVMStorageSpec *ss = REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec)); + MVMint64 value; + if (ss->is_unsigned) + switch(ss->bits) { + case 8: + value = *(MVMuint8 *)cpointer_body; + break; + case 16: + value = *(MVMuint16 *)cpointer_body; + break; + case 32: + value = *(MVMuint32 *)cpointer_body; + break; + case 64: + default: + value = *(MVMuint64 *)cpointer_body; + } + else + switch(ss->bits) { + case 8: + value = *(MVMint8 *)cpointer_body; + break; + case 16: + value = *(MVMint16 *)cpointer_body; + break; + case 32: + value = *(MVMint32 *)cpointer_body; + break; + case 64: + default: + value = *(MVMint64 *)cpointer_body; + } + result = MVM_nativecall_make_int(tc, target_type, value); + break; + } + case MVM_REPR_ID_P6num: { + const MVMStorageSpec *ss = REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec)); + MVMnum64 value; + + switch(ss->bits) { + case 32: + value = *(MVMnum32 *)cpointer_body; + break; + case 64: + default: + value = *(MVMnum64 *)cpointer_body; + } + + result = MVM_nativecall_make_num(tc, target_type, value); + break; + } + case MVM_REPR_ID_MVMCStr: + case MVM_REPR_ID_P6str: + result = MVM_nativecall_make_str(tc, target_type, MVM_NATIVECALL_ARG_UTF8STR, + (char *)cpointer_body); + break; + case MVM_REPR_ID_MVMCStruct: + result = MVM_nativecall_make_cstruct(tc, target_type, (void *)cpointer_body); + break; + case MVM_REPR_ID_MVMCPPStruct: + result = MVM_nativecall_make_cppstruct(tc, target_type, (void *)cpointer_body); + break; + case MVM_REPR_ID_MVMCUnion: + result = MVM_nativecall_make_cunion(tc, target_type, (void *)cpointer_body); + break; + case MVM_REPR_ID_MVMCPointer: + result = MVM_nativecall_make_cpointer(tc, target_type, (void *)cpointer_body); + break; + case MVM_REPR_ID_MVMCArray: { + result = MVM_nativecall_make_carray(tc, target_type, (void *)cpointer_body); + break; + } + default: + MVM_exception_throw_adhoc(tc, "Internal error: unhandled target type"); + } + }); + + return result; +} + +MVMObject * MVM_nativecall_global(MVMThreadContext *tc, MVMString *lib, MVMString *sym, MVMObject *target_spec, MVMObject *target_type) { + char *lib_name = MVM_string_utf8_c8_encode_C_string(tc, lib); + char *sym_name = MVM_string_utf8_c8_encode_C_string(tc, sym); + DLLib *lib_handle; + void *entry_point; + MVMObject *ret = NULL; + + /* Try to load the library. */ + lib_handle = MVM_nativecall_load_lib(lib_name[0] ? lib_name : NULL); + if (!lib_handle) { + char *waste[] = { lib_name, NULL }; + MVM_free(sym_name); + MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate native library '%s': %s", lib_name, dlerror()); + } + + /* Try to locate the symbol. */ + entry_point = MVM_nativecall_find_sym(lib_handle, sym_name); + if (!entry_point) { + char *waste[] = { sym_name, lib_name, NULL }; + MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate symbol '%s' in native library '%s'", + sym_name, lib_name); + } + MVM_free(sym_name); + MVM_free(lib_name); + + if (REPR(target_type)->ID == MVM_REPR_ID_MVMCStr + || REPR(target_type)->ID == MVM_REPR_ID_P6str + || (REPR(target_type)->ID == MVM_REPR_ID_P6opaque + && REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec))->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR)) { + entry_point = *(void **)entry_point; + } + + ret = nativecall_cast(tc, target_spec, target_type, entry_point); + MVM_nativecall_free_lib(lib_handle); + return ret; +} + +MVMObject * MVM_nativecall_cast(MVMThreadContext *tc, MVMObject *target_spec, MVMObject *target_type, MVMObject *source) { + void *data_body; + + if (!source) + return target_type; + + if (REPR(source)->ID == MVM_REPR_ID_MVMCStruct) + data_body = MVM_nativecall_unmarshal_cstruct(tc, source); + else if (REPR(source)->ID == MVM_REPR_ID_MVMCPPStruct) + data_body = MVM_nativecall_unmarshal_cppstruct(tc, source); + else if (REPR(source)->ID == MVM_REPR_ID_MVMCUnion) + data_body = MVM_nativecall_unmarshal_cunion(tc, source); + else if (REPR(source)->ID == MVM_REPR_ID_MVMCPointer) + data_body = MVM_nativecall_unmarshal_cpointer(tc, source); + else if (REPR(source)->ID == MVM_REPR_ID_MVMCArray) + data_body = MVM_nativecall_unmarshal_carray(tc, source); + else if (REPR(source)->ID == MVM_REPR_ID_VMArray) + data_body = MVM_nativecall_unmarshal_vmarray(tc, source); + else + MVM_exception_throw_adhoc(tc, + "Native call expected return type with CPointer, CStruct, CArray, or VMArray representation, but got a %s (%s)", REPR(source)->name, MVM_6model_get_debug_name(tc, source)); + return nativecall_cast(tc, target_spec, target_type, data_body); +} + +MVMint64 MVM_nativecall_sizeof(MVMThreadContext *tc, MVMObject *obj) { + if (REPR(obj)->ID == MVM_REPR_ID_MVMCStruct) + return ((MVMCStructREPRData *)STABLE(obj)->REPR_data)->struct_size; + else if (REPR(obj)->ID == MVM_REPR_ID_MVMCPPStruct) + return ((MVMCPPStructREPRData *)STABLE(obj)->REPR_data)->struct_size; + else if (REPR(obj)->ID == MVM_REPR_ID_MVMCUnion) + return ((MVMCUnionREPRData *)STABLE(obj)->REPR_data)->struct_size; + else if (REPR(obj)->ID == MVM_REPR_ID_P6int) + return ((MVMP6intREPRData *)STABLE(obj)->REPR_data)->bits / 8; + else if (REPR(obj)->ID == MVM_REPR_ID_P6num) + return ((MVMP6numREPRData *)STABLE(obj)->REPR_data)->bits / 8; + else if (REPR(obj)->ID == MVM_REPR_ID_MVMCPointer + || REPR(obj)->ID == MVM_REPR_ID_MVMCArray + || REPR(obj)->ID == MVM_REPR_ID_MVMCStr + || REPR(obj)->ID == MVM_REPR_ID_P6str) + return sizeof(void *); + else + MVM_exception_throw_adhoc(tc, + "NativeCall op sizeof expected type with CPointer, CStruct, CArray, P6int or P6num representation, but got a %s (%s)", + REPR(obj)->name, MVM_6model_get_debug_name(tc, obj)); +} + +/* Write-barriers a dyncall object so that delayed changes to the C-side of + * objects are propagated to the HLL side. All CArray and CStruct arguments to + * C functions are write-barriered automatically, so this should be necessary + * only in the rarest of cases. */ +void MVM_nativecall_refresh(MVMThreadContext *tc, MVMObject *cthingy) { + if (!IS_CONCRETE(cthingy)) + return; + if (REPR(cthingy)->ID == MVM_REPR_ID_MVMCArray) { + MVMCArrayBody *body = (MVMCArrayBody *)OBJECT_BODY(cthingy); + MVMCArrayREPRData *repr_data = (MVMCArrayREPRData *)STABLE(cthingy)->REPR_data; + void **storage = (void **) body->storage; + MVMint64 i; + + /* No need to check for numbers. They're stored directly in the array. */ + if (repr_data->elem_kind == MVM_CARRAY_ELEM_KIND_NUMERIC) + return; + + for (i = 0; i < body->elems; i++) { + void *cptr; /* The pointer in the C storage. */ + void *objptr; /* The pointer in the object representing the C object. */ + + /* Ignore elements where we haven't generated an object. */ + if (!body->child_objs[i]) + continue; + + cptr = storage[i]; + if (IS_CONCRETE(body->child_objs[i])) { + switch (repr_data->elem_kind) { + case MVM_CARRAY_ELEM_KIND_CARRAY: + objptr = ((MVMCArrayBody *)OBJECT_BODY(body->child_objs[i]))->storage; + break; + case MVM_CARRAY_ELEM_KIND_CPOINTER: + objptr = ((MVMCPointerBody *)OBJECT_BODY(body->child_objs[i]))->ptr; + break; + case MVM_CARRAY_ELEM_KIND_CSTRUCT: + objptr = ((MVMCStructBody *)OBJECT_BODY(body->child_objs[i]))->cstruct; + break; + case MVM_CARRAY_ELEM_KIND_CPPSTRUCT: + objptr = ((MVMCPPStructBody *)OBJECT_BODY(body->child_objs[i]))->cppstruct; + break; + case MVM_CARRAY_ELEM_KIND_CUNION: + objptr = ((MVMCUnionBody *)OBJECT_BODY(body->child_objs[i]))->cunion; + break; + case MVM_CARRAY_ELEM_KIND_STRING: + objptr = NULL; /* TODO */ + break; + default: + MVM_exception_throw_adhoc(tc, + "Fatal error: bad elem_kind (%d) in CArray write barrier", + repr_data->elem_kind); + } + } + else { + objptr = NULL; + } + + if (objptr != cptr) + body->child_objs[i] = NULL; + else + MVM_nativecall_refresh(tc, body->child_objs[i]); + } + } + else if (REPR(cthingy)->ID == MVM_REPR_ID_MVMCStruct) { + MVMCStructBody *body = (MVMCStructBody *)OBJECT_BODY(cthingy); + MVMCStructREPRData *repr_data = (MVMCStructREPRData *)STABLE(cthingy)->REPR_data; + char *storage = (char *) body->cstruct; + MVMint64 i; + + for (i = 0; i < repr_data->num_attributes; i++) { + MVMint32 kind = repr_data->attribute_locations[i] & MVM_CSTRUCT_ATTR_MASK; + MVMint32 slot = repr_data->attribute_locations[i] >> MVM_CSTRUCT_ATTR_SHIFT; + void *cptr = NULL; /* The pointer in the C storage. */ + void *objptr = NULL; /* The pointer in the object representing the C object. */ + + if (kind == MVM_CSTRUCT_ATTR_IN_STRUCT || !body->child_objs[slot]) + continue; + + cptr = (void*)((uintptr_t)storage + (uintptr_t)repr_data->struct_offsets[i]); + if (IS_CONCRETE(body->child_objs[slot])) { + switch (kind) { + case MVM_CSTRUCT_ATTR_CARRAY: + objptr = ((MVMCArrayBody *)OBJECT_BODY(body->child_objs[slot]))->storage; + break; + case MVM_CSTRUCT_ATTR_CPTR: + objptr = ((MVMCPointerBody *)OBJECT_BODY(body->child_objs[slot]))->ptr; + break; + case MVM_CSTRUCT_ATTR_CSTRUCT: + objptr = (MVMCStructBody *)OBJECT_BODY(body->child_objs[slot]); + break; + case MVM_CSTRUCT_ATTR_CPPSTRUCT: + objptr = (MVMCPPStructBody *)OBJECT_BODY(body->child_objs[slot]); + break; + case MVM_CSTRUCT_ATTR_CUNION: + objptr = (MVMCUnionBody *)OBJECT_BODY(body->child_objs[slot]); + break; + case MVM_CSTRUCT_ATTR_STRING: + objptr = NULL; + break; + default: + MVM_exception_throw_adhoc(tc, + "Fatal error: bad kind (%d) in CStruct write barrier", + kind); + } + } + else { + objptr = NULL; + } + + if (objptr != cptr) + body->child_objs[slot] = NULL; + else + MVM_nativecall_refresh(tc, body->child_objs[slot]); + } + } + else if (REPR(cthingy)->ID == MVM_REPR_ID_MVMCPPStruct) { + MVMCPPStructBody *body = (MVMCPPStructBody *)OBJECT_BODY(cthingy); + MVMCPPStructREPRData *repr_data = (MVMCPPStructREPRData *)STABLE(cthingy)->REPR_data; + char *storage = (char *) body->cppstruct; + MVMint64 i; + + for (i = 0; i < repr_data->num_attributes; i++) { + MVMint32 kind = repr_data->attribute_locations[i] & MVM_CPPSTRUCT_ATTR_MASK; + MVMint32 slot = repr_data->attribute_locations[i] >> MVM_CPPSTRUCT_ATTR_SHIFT; + void *cptr = NULL; /* The pointer in the C storage. */ + void *objptr = NULL; /* The pointer in the object representing the C object. */ + + if (kind == MVM_CPPSTRUCT_ATTR_IN_STRUCT || !body->child_objs[slot]) + continue; + + cptr = (void*)((uintptr_t)storage + (uintptr_t)repr_data->struct_offsets[i]); + if (IS_CONCRETE(body->child_objs[slot])) { + switch (kind) { + case MVM_CPPSTRUCT_ATTR_CARRAY: + objptr = ((MVMCArrayBody *)OBJECT_BODY(body->child_objs[slot]))->storage; + break; + case MVM_CPPSTRUCT_ATTR_CPTR: + objptr = ((MVMCPointerBody *)OBJECT_BODY(body->child_objs[slot]))->ptr; + break; + case MVM_CPPSTRUCT_ATTR_CSTRUCT: + objptr = (MVMCStructBody *)OBJECT_BODY(body->child_objs[slot]); + break; + case MVM_CPPSTRUCT_ATTR_CPPSTRUCT: + objptr = (MVMCPPStructBody *)OBJECT_BODY(body->child_objs[slot]); + break; + case MVM_CPPSTRUCT_ATTR_CUNION: + objptr = (MVMCUnionBody *)OBJECT_BODY(body->child_objs[slot]); + break; + case MVM_CPPSTRUCT_ATTR_STRING: + objptr = NULL; + break; + default: + MVM_exception_throw_adhoc(tc, + "Fatal error: bad kind (%d) in CPPStruct write barrier", + kind); + } + } + else { + objptr = NULL; + } + + if (objptr != cptr) + body->child_objs[slot] = NULL; + else + MVM_nativecall_refresh(tc, body->child_objs[slot]); + } + } +} + +/* Locate the thread that a callback should be run on. */ +MVMThreadContext * MVM_nativecall_find_thread_context(MVMInstance *instance) { + MVMint64 wanted_thread_id = MVM_platform_thread_id(); + MVMThreadContext *tc = NULL; + while (1) { + uv_mutex_lock(&(instance->mutex_threads)); + if (instance->in_gc) { + /* VM is in GC; free lock since the GC will acquire it again to + * clear the in_gc flag, and sleep a bit until it's safe to read + * the threads list. */ + uv_mutex_unlock(&(instance->mutex_threads)); + MVM_platform_sleep(0.0001); + } + else { + /* Not in GC. If a GC starts while we are reading this, then we + * are holding mutex_threads, and the GC will block on it before + * it gets to a stage where it can move things. */ + MVMThread *thread = instance->threads; + while (thread) { + if (thread->body.native_thread_id == wanted_thread_id) { + tc = thread->body.tc; + if (tc) + break; + } + thread = thread->body.next; + } + if (!tc) + MVM_panic(1, "native callback ran on thread (%"PRId64") unknown to MoarVM", + wanted_thread_id); + uv_mutex_unlock(&(instance->mutex_threads)); + break; + } + } + return tc; +} + +void MVM_nativecall_invoke_jit(MVMThreadContext *tc, MVMObject *site) { + MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site); + MVMJitCode * const jitcode = body->jitcode; + + jitcode->func_ptr(tc, *tc->interp_cu, jitcode->labels[0]); +}