/* * Allocate a new value from a function's memory pool. */ static jit_value_t alloc_value(jit_function_t func, jit_type_t type) { /* Ensure that we have a builder for this function */ if(!_jit_function_ensure_builder(func)) { return 0; } jit_value_t value = jit_memory_pool_alloc(&func->builder->value_pool, struct _jit_value); if(!value) { return 0; } value->block = func->builder->current_block; value->type = jit_type_copy(type); value->reg = -1; value->frame_offset = JIT_INVALID_FRAME_OFFSET; value->index = -1; return value; }
PHP_METHOD(Type, __construct) { zval *ztype = NULL; zend_bool zpointer = 0; php_jit_type_t *ptype; if (php_jit_parameters("z|b", &ztype, &zpointer) != SUCCESS) { php_jit_exception("unexpected parameters, expected (int|Type of [, bool pointer = false])"); return; } ptype = PHP_JIT_FETCH_TYPE(getThis()); switch (Z_TYPE_P(ztype)) { case IS_LONG: if (zpointer) { ptype->type = jit_type_create_pointer( php_jit_type(Z_LVAL_P(ztype)), 1); ptype->pt = 1; } else ptype->type = php_jit_type(Z_LVAL_P(ztype)); ptype->id = Z_LVAL_P(ztype); break; case IS_OBJECT: { php_jit_type_t *patype = PHP_JIT_FETCH_TYPE(ztype); if (zpointer) { ptype->type = jit_type_create_pointer(patype->type, 1); ptype->pt = (patype->pt + 1); } else ptype->type = jit_type_copy(patype->type); ptype->id = patype->id; ptype->copied = 1; } break; default: php_jit_exception("unexpected parameters, expected (int|Type of [, bool pointer = false])"); } }
PHP_METHOD(Struct, __construct) { HashTable *zfields; HashPosition zposition; php_jit_struct_t *pstruct; zval *zmember; zend_ulong nfield = 0; jit_type_t *jfields; char **jnames = NULL; if (php_jit_parameters("H", &zfields) != SUCCESS) { php_jit_exception("unexpected parameters, expected (Type[] fields)"); return; } pstruct = PHP_JIT_FETCH_STRUCT(getThis()); pstruct->nfields = zend_hash_num_elements(zfields); pstruct->zfields = (zval*) ecalloc(pstruct->nfields, sizeof(zval)); jfields = (jit_type_t*) ecalloc(pstruct->nfields, sizeof(jit_type_t)); for (zend_hash_internal_pointer_reset_ex(zfields, &zposition); (zmember = zend_hash_get_current_data_ex(zfields, &zposition)); zend_hash_move_forward_ex(zfields, &zposition)) { zend_ulong znidx = 0L; zend_string *zname = NULL; php_jit_type_t *ptype; if (!zmember || Z_TYPE_P(zmember) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(zmember), jit_type_ce)) { php_jit_exception("non type found in fields list at %d", nfield); return; } ZVAL_COPY(&pstruct->zfields[nfield], zmember); ptype = PHP_JIT_FETCH_TYPE(&pstruct->zfields[nfield]); jfields[nfield] = jit_type_copy(ptype->type); if (zend_hash_get_current_key_ex(zfields, &zname, &znidx, &zposition) == HASH_KEY_IS_STRING) { if (!zname || !ZSTR_LEN(zname)) { php_jit_exception("invalid name found in fields list at %d", nfield); efree(jfields); return; } if (!pstruct->names) { pstruct->names = ecalloc(pstruct->nfields, sizeof(zend_string*)); } pstruct->names[nfield] = zend_string_copy(zname); } else { if (pstruct->names) { php_jit_exception("un-named type found in fields list at %d", nfield); efree(jfields); return; } } nfield++; } pstruct->type = jit_type_create_struct(jfields, pstruct->nfields, 0); if (pstruct->names) { jit_type_set_names(pstruct->type, (char**) pstruct->names, pstruct->nfields); } efree(jfields); }
/*@ * @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)) unsigned char *trampoline; #endif /* Acquire the memory context */ _jit_memory_lock(context); if(!_jit_memory_ensure(context)) { _jit_memory_unlock(context); return 0; } /* Allocate memory for the function and clear it */ func = _jit_memory_alloc_function(context); if(!func) { _jit_memory_unlock(context); return 0; } #if !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size)) trampoline = (unsigned char *) _jit_memory_alloc_trampoline(context); if(!trampoline) { _jit_memory_free_function(context, func); _jit_memory_unlock(context); return 0; } # if defined(jit_redirector_size) func->redirector = trampoline; trampoline += jit_redirector_size; # endif # if defined(jit_indirector_size) func->indirector = trampoline; # endif #endif /* !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size)) */ /* Release the memory context */ _jit_memory_unlock(context); /* 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; }
/*@ * @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; }