/* * jit-apply.c - Dynamic invocation and closure support functions. * * Copyright (C) 2004 Southern Storm Software, Pty Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "jit-internal.h" #include "jit-memory.h" #include "jit-apply-rules.h" #include "jit-apply-func.h" #include "jit-cache.h" #if HAVE_ALLOCA_H #include #endif #ifdef JIT_WIN32_PLATFORM #include #ifndef alloca #define alloca _alloca #endif #endif /* If you need to tweak the way that this code behaves for a specific platform, then you would normally do it in "tools/gen-apply.c" or the CPU-specific "jit-apply-XXX.h" file, not here. */ /*@ @section Function application and closures @cindex Function application @cindex Closures @cindex jit-apply.h Sometimes all you have for a function is a pointer to it and a dynamic description of its arguments. Calling such a function can be extremely difficult in standard C. The routines in this section, particularly @code{jit_apply}, provide a convenient interface for doing this. At other times, you may wish to wrap up one of your own dynamic functions in such a way that it appears to be a regular C function. This is performed with @code{jit_closure_create}. @*/ /* * Flags that indicate which structure sizes are returned in registers. */ unsigned char const _jit_apply_return_in_reg[] = JIT_APPLY_STRUCT_RETURN_IN_REG_INIT; /* * Get the maximum argument stack size of a signature type. */ static unsigned int jit_type_get_max_arg_size(jit_type_t signature) { unsigned int size; unsigned int typeSize; unsigned int param; jit_type_t type; if(signature->size) { /* We have a cached argument size from last time */ return signature->size; } size = 0; param = jit_type_num_params(signature); while(param > 0) { --param; type = jit_type_normalize(jit_type_get_param(signature, param)); switch(type->kind) { case JIT_TYPE_SBYTE: case JIT_TYPE_UBYTE: case JIT_TYPE_SHORT: case JIT_TYPE_USHORT: case JIT_TYPE_INT: case JIT_TYPE_UINT: case JIT_TYPE_NINT: case JIT_TYPE_NUINT: case JIT_TYPE_PTR: case JIT_TYPE_SIGNATURE: { size += sizeof(jit_nint); } break; case JIT_TYPE_LONG: case JIT_TYPE_ULONG: { #ifdef JIT_NATIVE_INT32 /* Add one extra word for possible alignment padding */ size += sizeof(jit_long) + sizeof(jit_nint); #else size += sizeof(jit_nint); #endif } break; case JIT_TYPE_FLOAT32: case JIT_TYPE_FLOAT64: case JIT_TYPE_NFLOAT: { /* Allocate space for an "nfloat" and an alignment word */ size += (sizeof(jit_nfloat) + sizeof(jit_nint) * 2 - 1) & ~(sizeof(jit_nint) - 1); } break; case JIT_TYPE_STRUCT: case JIT_TYPE_UNION: { /* Allocate space for the structure and an alignment word */ typeSize = jit_type_get_size(type); size += (typeSize + sizeof(jit_nint) * 2 - 1) & ~(sizeof(jit_nint) - 1); } break; } } type = jit_type_get_return(signature); if(jit_type_is_struct(type) || jit_type_is_union(type)) { /* Add one extra word for the possibility of a structure pointer */ size += sizeof(jit_nint); } signature->size = size; return size; } /* * Copy apply arguments into position. */ static void jit_apply_builder_add_arguments (jit_apply_builder *builder, jit_type_t signature, void **args, unsigned int index, unsigned int num_args) { unsigned int param; jit_type_t type; for(param = 0; param < num_args; ++param) { type = jit_type_normalize (jit_type_get_param(signature, index + param)); switch(type->kind) { case JIT_TYPE_SBYTE: { jit_apply_builder_add_sbyte (builder, *((jit_sbyte *)(args[param]))); } break; case JIT_TYPE_UBYTE: { jit_apply_builder_add_ubyte (builder, *((jit_ubyte *)(args[param]))); } break; case JIT_TYPE_SHORT: { jit_apply_builder_add_short (builder, *((jit_short *)(args[param]))); } break; case JIT_TYPE_USHORT: { jit_apply_builder_add_ushort (builder, *((jit_ushort *)(args[param]))); } break; case JIT_TYPE_INT: { jit_apply_builder_add_int (builder, *((jit_int *)(args[param]))); } break; case JIT_TYPE_UINT: { jit_apply_builder_add_uint (builder, *((jit_uint *)(args[param]))); } break; case JIT_TYPE_NINT: case JIT_TYPE_PTR: case JIT_TYPE_SIGNATURE: { jit_apply_builder_add_nint (builder, *((jit_nint *)(args[param]))); } break; case JIT_TYPE_NUINT: { jit_apply_builder_add_nuint (builder, *((jit_nuint *)(args[param]))); } break; case JIT_TYPE_LONG: { jit_apply_builder_add_long (builder, *((jit_long *)(args[param]))); } break; case JIT_TYPE_ULONG: { jit_apply_builder_add_ulong (builder, *((jit_ulong *)(args[param]))); } break; case JIT_TYPE_FLOAT32: { jit_apply_builder_add_float32 (builder, *((jit_float32 *)(args[param]))); } break; case JIT_TYPE_FLOAT64: { jit_apply_builder_add_float64 (builder, *((jit_float64 *)(args[param]))); } break; case JIT_TYPE_NFLOAT: { jit_apply_builder_add_nfloat (builder, *((jit_nfloat *)(args[param]))); } break; case JIT_TYPE_STRUCT: case JIT_TYPE_UNION: { jit_apply_builder_add_struct (builder, args[param], jit_type_get_size(type), jit_type_get_alignment(type)); } break; } } } /* * Get the return value after calling a function using "__builtin_apply". */ static void jit_apply_builder_get_return (jit_apply_builder *builder, void *return_value, jit_type_t type, jit_apply_return *result) { switch(type->kind) { case JIT_TYPE_SBYTE: { *((jit_sbyte *)return_value) = jit_apply_return_get_sbyte(result); } break; case JIT_TYPE_UBYTE: { *((jit_ubyte *)return_value) = jit_apply_return_get_ubyte(result); } break; case JIT_TYPE_SHORT: { *((jit_short *)return_value) = jit_apply_return_get_short(result); } break; case JIT_TYPE_USHORT: { *((jit_ushort *)return_value) = jit_apply_return_get_ushort(result); } break; case JIT_TYPE_INT: { *((jit_int *)return_value) = jit_apply_return_get_int(result); } break; case JIT_TYPE_UINT: { *((jit_uint *)return_value) = jit_apply_return_get_uint(result); } break; case JIT_TYPE_NINT: case JIT_TYPE_PTR: case JIT_TYPE_SIGNATURE: { *((jit_nint *)return_value) = jit_apply_return_get_nint(result); } break; case JIT_TYPE_NUINT: { *((jit_nuint *)return_value) = jit_apply_return_get_nuint(result); } break; case JIT_TYPE_LONG: { *((jit_long *)return_value) = jit_apply_return_get_long(result); } break; case JIT_TYPE_ULONG: { *((jit_ulong *)return_value) = jit_apply_return_get_ulong(result); } break; case JIT_TYPE_FLOAT32: { *((jit_float32 *)return_value) = jit_apply_return_get_float32(result); } break; case JIT_TYPE_FLOAT64: { *((jit_float64 *)return_value) = jit_apply_return_get_float64(result); } break; case JIT_TYPE_NFLOAT: { *((jit_nfloat *)return_value) = jit_apply_return_get_nfloat(result); } break; case JIT_TYPE_STRUCT: case JIT_TYPE_UNION: { unsigned int size = jit_type_get_size(type); jit_apply_builder_get_struct_return (builder, size, return_value, result); } break; } } /*@ * @deftypefun void jit_apply (jit_type_t signature, {void *} func, {void **} args, {unsigned int} num_fixed_args, {void *} return_value) * Call a function that has a particular function signature. * If the signature has more than @code{num_fixed_args} arguments, * then it is assumed to be a vararg call, with the additional * arguments passed in the vararg argument area on the stack. * The @code{signature} must specify the type of all arguments, * including those in the vararg argument area. * @end deftypefun @*/ void jit_apply(jit_type_t signature, void *func, void **args, unsigned int num_fixed_args, void *return_value) { jit_apply_builder builder; unsigned int size; jit_apply_return *apply_return; jit_type_t type; /* Initialize the argument builder */ jit_apply_builder_init(&builder, signature); /* Handle the structure return argument */ type = jit_type_normalize(jit_type_get_return(signature)); if(jit_type_is_struct(type) || jit_type_is_union(type)) { size = jit_type_get_size(type); jit_apply_builder_add_struct_return(&builder, size, return_value); } /* Copy the arguments into position */ jit_apply_builder_add_arguments (&builder, signature, args, 0, num_fixed_args); jit_apply_builder_start_varargs(&builder); jit_apply_builder_add_arguments (&builder, signature, args + num_fixed_args, num_fixed_args, jit_type_num_params(signature) - num_fixed_args); /* Call the function using "__builtin_apply" or something similar */ if(type->kind < JIT_TYPE_FLOAT32 || type->kind > JIT_TYPE_NFLOAT) { jit_builtin_apply(func, builder.apply_args, builder.stack_used, 0, apply_return); } else { jit_builtin_apply(func, builder.apply_args, builder.stack_used, 1, apply_return); } /* Copy the return value into position */ if(return_value != 0 && type != jit_type_void) { jit_apply_builder_get_return (&builder, return_value, type, apply_return); } } /*@ * @deftypefun void jit_apply_raw (jit_type_t signature, {void *} func, {void *} args, {void *} return_value) * Call a function, passing a set of raw arguments. This can only * be used if @code{jit_raw_supported} returns non-zero for the signature. * The @code{args} value is assumed to be an array of @code{jit_nint} values * that correspond to each of the arguments. Raw function calls * are slightly faster than their non-raw counterparts, but can * only be used in certain circumstances. * @end deftypefun @*/ void jit_apply_raw(jit_type_t signature, void *func, void *args, void *return_value) { jit_apply_return *apply_return; unsigned int size; jit_type_t type; /* Call the function using "__builtin_apply" or something similar */ type = jit_type_normalize(jit_type_get_return(signature)); size = jit_type_num_params(signature) * sizeof(jit_nint); if(type->kind < JIT_TYPE_FLOAT32 || type->kind > JIT_TYPE_NFLOAT) { jit_builtin_apply(func, args, size, 0, apply_return); } else { jit_builtin_apply(func, args, size, 1, apply_return); } /* Copy the return value into position */ if(return_value != 0 && type != jit_type_void) { jit_apply_builder_get_return (0, return_value, type, apply_return); } } /*@ * @deftypefun int jit_raw_supported (jit_type_t signature) * Determine if @code{jit_apply_raw} can be used to call functions * with a particular signature. Returns zero if not. * @end deftypefun @*/ int jit_raw_supported(jit_type_t signature) { #if JIT_APPLY_NUM_WORD_REGS == 0 && JIT_APPLY_NUM_FLOAT_REGS == 0 && \ JIT_APPLY_STRUCT_RETURN_SPECIAL_REG == 0 unsigned int param; jit_type_t type; #if JIT_APPLY_X86_FASTCALL != 0 /* Cannot use raw calls with fastcall functions */ if(jit_type_get_abi(signature) == jit_abi_fastcall) { return 0; } #endif /* Check that all of the arguments are word-sized */ param = jit_type_num_params(signature); while(param > 0) { --param; type = jit_type_normalize(jit_type_get_param(signature, param)); if(type->kind < JIT_TYPE_SBYTE || type->kind > JIT_TYPE_NUINT) { return 0; } } /* Check that the return value does not involve structures */ type = jit_type_get_return(signature); if(jit_type_is_struct(type) || jit_type_is_union(type)) { return 0; } /* The signature is suitable for use with "jit_apply_raw" */ return 1; #else /* We cannot use raw calls if we need to use registers in applys */ return 0; #endif } /* * Define the structure of a vararg list for closures. */ struct jit_closure_va_list { jit_apply_builder builder; }; #ifdef jit_closure_size /* * Define the closure structure. */ typedef struct jit_closure *jit_closure_t; struct jit_closure { unsigned char buf[jit_closure_size]; jit_type_t signature; jit_closure_func func; void *user_data; }; /* * Handler that is called when a closure is invoked. */ static void closure_handler(jit_closure_t closure, void *apply_args) { jit_type_t signature = closure->signature; jit_type_t type; jit_apply_builder parser; void *return_buffer; void **args; void *temp_arg; unsigned int num_params; unsigned int param; jit_apply_return apply_return; int is_float_return; /* Initialize the argument parser */ jit_apply_parser_init(&parser, closure->signature, apply_args); /* Allocate space for the return value */ type = jit_type_normalize(jit_type_get_return(signature)); if(!type || type == jit_type_void) { return_buffer = 0; } else if(jit_type_return_via_pointer(type)) { jit_apply_parser_get_struct_return(&parser, return_buffer); } else { return_buffer = alloca(jit_type_get_size(type)); } /* Allocate space for the argument buffer. We allow for one extra argument to hold the "va" list */ num_params = jit_type_num_params(signature); args = (void **)alloca((num_params + 1) * sizeof(void *)); /* Extract the fixed arguments */ for(param = 0; param < num_params; ++param) { type = jit_type_normalize(jit_type_get_param(signature, param)); if(!type) { args[param] = 0; continue; } temp_arg = alloca(jit_type_get_size(type)); args[param] = temp_arg; switch(type->kind) { case JIT_TYPE_SBYTE: { jit_apply_parser_get_sbyte (&parser, *((jit_sbyte *)temp_arg)); } break; case JIT_TYPE_UBYTE: { jit_apply_parser_get_ubyte (&parser, *((jit_ubyte *)temp_arg)); } break; case JIT_TYPE_SHORT: { jit_apply_parser_get_short (&parser, *((jit_short *)temp_arg)); } break; case JIT_TYPE_USHORT: { jit_apply_parser_get_ushort (&parser, *((jit_ushort *)temp_arg)); } break; case JIT_TYPE_INT: { jit_apply_parser_get_int (&parser, *((jit_int *)temp_arg)); } break; case JIT_TYPE_UINT: { jit_apply_parser_get_uint (&parser, *((jit_uint *)temp_arg)); } break; case JIT_TYPE_LONG: { jit_apply_parser_get_long (&parser, *((jit_long *)temp_arg)); } break; case JIT_TYPE_ULONG: { jit_apply_parser_get_ulong (&parser, *((jit_ulong *)temp_arg)); } break; case JIT_TYPE_FLOAT32: { jit_apply_parser_get_float32 (&parser, *((jit_float32 *)temp_arg)); } break; case JIT_TYPE_FLOAT64: { jit_apply_parser_get_float64 (&parser, *((jit_float64 *)temp_arg)); } break; case JIT_TYPE_NFLOAT: { jit_apply_parser_get_nfloat (&parser, *((jit_nfloat *)temp_arg)); } break; case JIT_TYPE_STRUCT: case JIT_TYPE_UNION: { jit_apply_parser_get_struct (&parser, jit_type_get_size(type), jit_type_get_alignment(type), temp_arg); } break; } } /* Adjust the argument parser for the start of the va arguments */ jit_apply_parser_start_varargs(&parser); /* Record the address of the va handler in the last argument slot. Not all functions will need this, but it doesn't hurt to include it */ args[num_params] = &parser; /* Call the user's closure handling function */ (*(closure->func))(signature, return_buffer, args, closure->user_data); /* Set up the "apply return" buffer */ jit_memzero(&apply_return, sizeof(apply_return)); type = jit_type_normalize(jit_type_get_return(signature)); is_float_return = 0; if(type) { switch(type->kind) { case JIT_TYPE_SBYTE: { jit_apply_return_set_sbyte (&apply_return, *((jit_sbyte *)return_buffer)); } break; case JIT_TYPE_UBYTE: { jit_apply_return_set_ubyte (&apply_return, *((jit_ubyte *)return_buffer)); } break; case JIT_TYPE_SHORT: { jit_apply_return_set_short (&apply_return, *((jit_short *)return_buffer)); } break; case JIT_TYPE_USHORT: { jit_apply_return_set_ushort (&apply_return, *((jit_ushort *)return_buffer)); } break; case JIT_TYPE_INT: { jit_apply_return_set_int (&apply_return, *((jit_int *)return_buffer)); } break; case JIT_TYPE_UINT: { jit_apply_return_set_uint (&apply_return, *((jit_uint *)return_buffer)); } break; case JIT_TYPE_LONG: { jit_apply_return_set_long (&apply_return, *((jit_long *)return_buffer)); } break; case JIT_TYPE_ULONG: { jit_apply_return_set_ulong (&apply_return, *((jit_ulong *)return_buffer)); } break; case JIT_TYPE_FLOAT32: { jit_apply_return_set_float32 (&apply_return, *((jit_float32 *)return_buffer)); is_float_return = 1; } break; case JIT_TYPE_FLOAT64: { jit_apply_return_set_float64 (&apply_return, *((jit_float64 *)return_buffer)); is_float_return = 1; } break; case JIT_TYPE_NFLOAT: { jit_apply_return_set_nfloat (&apply_return, *((jit_nfloat *)return_buffer)); is_float_return = 1; } break; case JIT_TYPE_STRUCT: case JIT_TYPE_UNION: { if(!jit_type_return_via_pointer(type)) { jit_memcpy(&apply_return, return_buffer, jit_type_get_size(type)); } } break; } } /* Return the result to the caller */ if(!is_float_return) { jit_builtin_return_int(&apply_return); } else { jit_builtin_return_float(&apply_return); } } #endif /* jit_closure_size */ /*@ * @deftypefun {void *} jit_closure_create (jit_context_t context, jit_type_t signature, {jit_closure_func} func, {void *} user_data) * Create a closure from a function signature, a closure handling function, * and a user data value. Returns NULL if out of memory, or if closures are * not supported. The @code{func} argument should have the following * prototype: * * @example * void func (jit_type_t signature, void *result, * void **args, void *user_data); * @end example * * If the closure signature includes variable arguments, then @code{args} * will contain pointers to the fixed arguments, followed by a * @code{jit_closure_va_list_t} value for accessing the remainder of * the arguments. * * The memory for the closure will be reclaimed when the @code{context} * is destroyed. * @end deftypefun @*/ void *jit_closure_create(jit_context_t context, jit_type_t signature, jit_closure_func func, void *user_data) { #ifdef jit_closure_size jit_cache_t cache; jit_closure_t closure; /* Validate the parameters */ if(!context || !signature || !func) { return 0; } /* Acquire the cache lock while we do this */ jit_mutex_lock(&(context->cache_lock)); /* Allocate space for the closure within the context's function cache */ cache = _jit_context_get_cache(context); if(!cache) { jit_mutex_unlock(&(context->cache_lock)); return 0; } closure = (jit_closure_t)_jit_cache_alloc_no_method (cache, sizeof(struct jit_closure), jit_closure_align); if(!closure) { jit_mutex_unlock(&(context->cache_lock)); return 0; } /* Fill in the closure fields */ _jit_create_closure (closure->buf, (void *)closure_handler, closure, signature); closure->signature = signature; closure->func = func; closure->user_data = user_data; /* Perform a cache flush on the closure's code */ jit_flush_exec(closure->buf, sizeof(closure->buf)); /* Unlock the cache, as we are finished with it */ jit_mutex_unlock(&(context->cache_lock)); /* Return the completed closure to the caller */ return closure; #else /* Closures are not supported on this platform */ return 0; #endif } /*@ * @deftypefun int jit_closures_supported (void) * Determine if this platform has support for closures. * @end deftypefun @*/ int jit_closures_supported(void) { #ifdef jit_closure_size return 1; #else return 0; #endif } /*@ * @deftypefun jit_nint jit_closure_va_get_nint (jit_closure_va_list_t va) * @deftypefunx jit_nuint jit_closure_va_get_nuint (jit_closure_va_list_t va) * @deftypefunx jit_long jit_closure_va_get_long (jit_closure_va_list_t va) * @deftypefunx jit_ulong jit_closure_va_get_ulong (jit_closure_va_list_t va) * @deftypefunx jit_float32 jit_closure_va_get_float32 (jit_closure_va_list_t va) * @deftypefunx jit_float64 jit_closure_va_get_float64 (jit_closure_va_list_t va) * @deftypefunx jit_nfloat jit_closure_va_get_nfloat (jit_closure_va_list_t va) * @deftypefunx {void *} jit_closure_va_get_ptr (jit_closure_va_list_t va) * Get the next value of a specific type from a closure's variable arguments. * @end deftypefun @*/ jit_nint jit_closure_va_get_nint(jit_closure_va_list_t va) { jit_nint value; jit_apply_parser_get_nint(&(va->builder), value); return value; } jit_nuint jit_closure_va_get_nuint(jit_closure_va_list_t va) { jit_nuint value; jit_apply_parser_get_nuint(&(va->builder), value); return value; } jit_long jit_closure_va_get_long(jit_closure_va_list_t va) { jit_long value; jit_apply_parser_get_long(&(va->builder), value); return value; } jit_ulong jit_closure_va_get_ulong(jit_closure_va_list_t va) { jit_ulong value; jit_apply_parser_get_ulong(&(va->builder), value); return value; } jit_float32 jit_closure_va_get_float32(jit_closure_va_list_t va) { jit_float32 value; jit_apply_parser_get_float32(&(va->builder), value); return value; } jit_float64 jit_closure_va_get_float64(jit_closure_va_list_t va) { jit_float64 value; jit_apply_parser_get_float64(&(va->builder), value); return value; } jit_nfloat jit_closure_va_get_nfloat(jit_closure_va_list_t va) { jit_nfloat value; jit_apply_parser_get_nfloat(&(va->builder), value); return value; } void *jit_closure_va_get_ptr(jit_closure_va_list_t va) { jit_nint value; jit_apply_parser_get_nint(&(va->builder), value); return (void *)value; } /*@ * @deftypefun void jit_closure_va_get_struct (jit_closure_va_list_t va, void *buf, jit_type_t type) * Get a structure or union value of a specific @code{type} from a closure's * variable arguments, and copy it into @code{buf}. * @end deftypefun @*/ void jit_closure_va_get_struct (jit_closure_va_list_t va, void *buf, jit_type_t type) { jit_apply_parser_get_struct (&(va->builder), jit_type_get_size(type), jit_type_get_alignment(type), buf); }