void _jit_memory_unlock(jit_context_t context) { jit_mutex_unlock(&context->memory_lock); }
/*@ * @deftypefun jit_function_t jit_function_create (jit_context_t @var{context}, jit_type_t @var{signature}) * Create a new function block and associate it with a JIT context. * Returns NULL if out of memory. * * A function persists for the lifetime of its containing context. * It initially starts life in the "building" state, where the user * constructs instructions that represents the function body. * Once the build process is complete, the user calls * @code{jit_function_compile} to convert it into its executable form. * * It is recommended that you call @code{jit_context_build_start} before * calling @code{jit_function_create}, and then call * @code{jit_context_build_end} after you have called * @code{jit_function_compile}. This will protect the JIT's internal * data structures within a multi-threaded environment. * @end deftypefun @*/ jit_function_t jit_function_create(jit_context_t context, jit_type_t signature) { jit_function_t func; #if !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size)) jit_cache_t cache; #endif /* Allocate memory for the function and clear it */ func = jit_cnew(struct _jit_function); if(!func) { return 0; } #if !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size)) /* TODO: if the function is destroyed the redirector and indirector memory is leaked */ /* We need the cache lock while we are allocating redirector and indirector */ jit_mutex_lock(&(context->cache_lock)); /* Get the method cache */ cache = _jit_context_get_cache(context); if(!cache) { jit_mutex_unlock(&(context->cache_lock)); jit_free(func); return 0; } func->compilation_success_callback = 0; func->insn_compilation_callback = 0; func->current_code_location_start_column = 0; func->current_code_location_end_column = 0; func->current_code_location_start_line = 0; func->current_code_location_end_line = 0; # if defined(jit_redirector_size) /* Allocate redirector buffer */ func->redirector = _jit_cache_alloc_no_method(cache, jit_redirector_size, 1); if(!func->redirector) { jit_mutex_unlock(&(context->cache_lock)); jit_free(func); return 0; } # endif # if defined(jit_indirector_size) /* Allocate indirector buffer */ func->indirector = _jit_cache_alloc_no_method(cache, jit_indirector_size, 1); if(!func->indirector) { jit_mutex_unlock(&(context->cache_lock)); jit_free(func); return 0; } # endif jit_mutex_unlock(&(context->cache_lock)); #endif /* !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size)) */ /* Initialize the function block */ func->context = context; func->signature = jit_type_copy(signature); func->optimization_level = JIT_OPTLEVEL_NORMAL; #if !defined(JIT_BACKEND_INTERP) && defined(jit_redirector_size) /* If we aren't using interpretation, then point the function's initial entry point at the redirector, which in turn will invoke the on-demand compiler */ func->entry_point = _jit_create_redirector (func->redirector, (void *) context->on_demand_driver, func, jit_type_get_abi(signature)); jit_flush_exec(func->redirector, jit_redirector_size); #endif #if !defined(JIT_BACKEND_INTERP) && defined(jit_indirector_size) _jit_create_indirector(func->indirector, (void**) &(func->entry_point)); jit_flush_exec(func->indirector, jit_indirector_size); #endif /* Add the function to the context list */ func->next = 0; func->prev = context->last_function; if(context->last_function) { context->last_function->next = func; } else { context->functions = func; } context->last_function = func; /* Return the function to the caller */ return func; }