Alien-LibJIT
view release on metacpan or search on metacpan
libjit/jit/jit-rules-interp.c view on Meta::CPAN
* some ARM cores have floating-point registers.
* @end deftypefun
@*/
void _jit_init_backend(void)
{
/* Nothing to do here for the interpreter */
}
/*@
* @deftypefun void _jit_gen_get_elf_info (jit_elf_info_t *@var{info})
* Get the ELF machine and ABI type information for this platform.
* The @code{machine} field should be set to one of the @code{EM_*}
* values in @code{jit-elf-defs.h}. The @code{abi} field should
* be set to one of the @code{ELFOSABI_*} values in @code{jit-elf-defs.h}
* (@code{ELFOSABI_SYSV} will normally suffice if you are unsure).
* The @code{abi_version} field should be set to the ABI version,
* which is usually zero.
* @end deftypefun
@*/
void _jit_gen_get_elf_info(jit_elf_info_t *info)
{
/* The interpreter's ELF machine type is defined to be "Lj",
which hopefully won't clash with any standard types */
info->machine = 0x4C6A;
info->abi = 0;
info->abi_version = JIT_OPCODE_VERSION;
}
/*@
* @deftypefun int _jit_create_entry_insns (jit_function_t @var{func})
* Create instructions in the entry block to initialize the
* registers and frame offsets that contain the parameters.
* Returns zero if out of memory.
*
* This function is called when a builder is initialized. It should
* scan the signature and decide which register or frame position
* contains each of the parameters and then call either
* @code{jit_insn_incoming_reg} or @code{jit_insn_incoming_frame_posn}
* to notify @code{libjit} of the location.
* @end deftypefun
@*/
int _jit_create_entry_insns(jit_function_t func)
{
jit_type_t signature = func->signature;
jit_type_t type;
jit_nint offset;
jit_value_t value;
unsigned int num_params;
unsigned int param;
/* Reset the frame size for this function */
func->builder->frame_size = 0;
/* The starting parameter offset. We use negative offsets to indicate
an offset into the "args" block, and positive offsets to indicate
an offset into the "frame" block. The negative values will be
flipped when we output the argument opcodes for interpretation */
offset = -1;
/* If the function is nested, then we need two extra parameters
to pass the pointer to the parent's local variables and arguments */
if(func->nested_parent)
{
offset -= 2;
}
/* Allocate the structure return pointer */
value = jit_value_get_struct_pointer(func);
if(value)
{
if(!jit_insn_incoming_frame_posn(func, value, offset))
{
return 0;
}
--offset;
}
/* Allocate the parameter offsets */
num_params = jit_type_num_params(signature);
for(param = 0; param < num_params; ++param)
{
value = jit_value_get_param(func, param);
if(!value)
{
continue;
}
type = jit_type_normalize(jit_value_get_type(value));
switch(type->kind)
{
case JIT_TYPE_SBYTE:
case JIT_TYPE_UBYTE:
{
if(!jit_insn_incoming_frame_posn
(func, value, offset - _jit_int_lowest_byte()))
{
return 0;
}
--offset;
}
break;
case JIT_TYPE_SHORT:
case JIT_TYPE_USHORT:
{
if(!jit_insn_incoming_frame_posn
(func, value, offset - _jit_int_lowest_short()))
{
return 0;
}
--offset;
}
break;
case JIT_TYPE_INT:
case JIT_TYPE_UINT:
case JIT_TYPE_NINT:
case JIT_TYPE_NUINT:
case JIT_TYPE_SIGNATURE:
case JIT_TYPE_PTR:
case JIT_TYPE_LONG:
case JIT_TYPE_ULONG:
case JIT_TYPE_FLOAT32:
case JIT_TYPE_FLOAT64:
case JIT_TYPE_NFLOAT:
{
if(!jit_insn_incoming_frame_posn(func, value, offset))
{
return 0;
}
--offset;
}
break;
case JIT_TYPE_STRUCT:
case JIT_TYPE_UNION:
{
if(!jit_insn_incoming_frame_posn(func, value, offset))
{
return 0;
}
offset -= JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type));
}
break;
}
}
return 1;
}
/*@
* @deftypefun int _jit_create_call_setup_insns (jit_function_t @var{func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{is_nested}, int @var{nested_level}, jit_value_t *@var{struct_return}, int @var{flag...
* Create instructions within @var{func} necessary to set up for a
* function call to a function with the specified @var{signature}.
* Use @code{jit_insn_push} to push values onto the system stack,
* or @code{jit_insn_outgoing_reg} to copy values into call registers.
*
* If @var{is_nested} is non-zero, then it indicates that we are calling a
* nested function within the current function's nested relationship tree.
* The @var{nested_level} value will be -1 to call a child, zero to call a
* sibling of @var{func}, 1 to call a sibling of the parent, 2 to call
* a sibling of the grandparent, etc. The @code{jit_insn_setup_for_nested}
* instruction should be used to create the nested function setup code.
*
* If the function returns a structure by pointer, then @var{struct_return}
* must be set to a new local variable that will contain the returned
* structure. Otherwise it should be set to NULL.
* @end deftypefun
@*/
int _jit_create_call_setup_insns
(jit_function_t func, jit_type_t signature,
jit_value_t *args, unsigned int num_args,
int is_nested, int nested_level, jit_value_t *struct_return, int flags)
{
jit_type_t type;
jit_type_t vtype;
jit_value_t value;
unsigned int arg_num;
jit_nint offset;
jit_nuint size;
/* Regular or tail call? */
if((flags & JIT_CALL_TAIL) == 0)
{
/* Push all of the arguments in reverse order */
while(num_args > 0)
{
--num_args;
type = jit_type_normalize(jit_type_get_param(signature, num_args));
if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
{
/* If the value is a pointer, then we are pushing a structure
argument by pointer rather than by local variable */
vtype = jit_type_normalize(jit_value_get_type(args[num_args]));
if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE)
{
if(!jit_insn_push_ptr(func, args[num_args], type))
{
return 0;
}
continue;
}
}
if(!jit_insn_push(func, args[num_args]))
{
return 0;
}
}
/* Do we need to add a structure return pointer argument? */
type = jit_type_get_return(signature);
if(jit_type_return_via_pointer(type))
{
value = jit_value_create(func, type);
if(!value)
{
return 0;
}
*struct_return = value;
value = jit_insn_address_of(func, value);
if(!value)
{
return 0;
}
if(!jit_insn_push(func, value))
{
return 0;
}
}
else if((flags & JIT_CALL_NATIVE) != 0)
{
/* Native calls always return a return area pointer */
if(!jit_insn_push_return_area_ptr(func))
{
return 0;
}
*struct_return = 0;
}
else
{
*struct_return = 0;
}
/* Do we need to add nested function scope information? */
if(is_nested)
{
if(!jit_insn_setup_for_nested(func, nested_level, -1))
{
return 0;
}
}
}
else
{
/* Copy the arguments into our own parameter slots */
offset = -1;
if(func->nested_parent)
{
offset -= 2;
}
type = jit_type_get_return(signature);
if(jit_type_return_via_pointer(type))
{
--offset;
}
for(arg_num = 0; arg_num < num_args; ++arg_num)
{
type = jit_type_get_param(signature, arg_num);
value = jit_value_create(func, type);
if(!value)
{
return 0;
}
if(!jit_insn_outgoing_frame_posn(func, value, offset))
{
return 0;
}
type = jit_type_normalize(type);
size = jit_type_get_size(type);
offset -= (jit_nint)(JIT_NUM_ITEMS_IN_STRUCT(size));
if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
{
/* If the value is a pointer, then we are pushing a structure
argument by pointer rather than by local variable */
vtype = jit_type_normalize(jit_value_get_type(args[arg_num]));
if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE)
{
value = jit_insn_address_of(func, value);
if(!value)
{
return 0;
}
if(!jit_insn_memcpy
(func, value, args[arg_num],
jit_value_create_nint_constant
(func, jit_type_nint, (jit_nint)size)))
{
return 0;
}
continue;
}
}
if(!jit_insn_store(func, value, args[arg_num]))
{
return 0;
}
}
*struct_return = 0;
}
/* The call is ready to proceed */
return 1;
}
/*@
* @deftypefun int _jit_setup_indirect_pointer (jit_function_t @var{func}, jit_value_t @var{value})
* Place the indirect function pointer @var{value} into a suitable register
* or stack location for a subsequent indirect call.
* @end deftypefun
@*/
int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value)
{
//return jit_insn_outgoing_reg(func, value, 1);
return 1;
}
/*@
* @deftypefun int _jit_create_call_return_insns (jit_function_t @var{func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, jit_value_t @var{return_value}, int @var{is_nested})
* Create instructions within @var{func} to clean up after a function call
* and to place the function's result into @var{return_value}.
* This should use @code{jit_insn_pop_stack} to pop values off the system
* stack and @code{jit_insn_return_reg} to tell @code{libjit} which
* register contains the return value. In the case of a @code{void}
* function, @var{return_value} will be NULL.
*
* Note: the argument values are passed again because it may not be possible
* to determine how many bytes to pop from the stack from the @var{signature}
* alone; especially if the called function is vararg.
* @end deftypefun
@*/
int _jit_create_call_return_insns
(jit_function_t func, jit_type_t signature,
jit_value_t *args, unsigned int num_args,
jit_value_t return_value, int is_nested)
{
jit_nint pop_items;
unsigned int size;
jit_type_t return_type;
int ptr_return;
/* Calculate the number of items that we need to pop */
pop_items = 0;
while(num_args > 0)
{
--num_args;
size = jit_type_get_size(jit_value_get_type(args[num_args]));
pop_items += JIT_NUM_ITEMS_IN_STRUCT(size);
}
return_type = jit_type_normalize(jit_type_get_return(signature));
ptr_return = jit_type_return_via_pointer(return_type);
if(ptr_return)
{
++pop_items;
}
if(is_nested)
{
/* The interpreter needs two arguments for the parent frame info */
pop_items += 2;
}
/* Pop the items from the system stack */
if(pop_items > 0)
{
if(!jit_insn_pop_stack(func, pop_items))
{
return 0;
}
}
/* Bail out now if we don't need to worry about return values */
if(!return_value || ptr_return)
{
return 1;
}
/* Structure values must be flushed into the frame, and
everything else ends up in the top-most stack register */
if(jit_type_is_struct(return_type) || jit_type_is_union(return_type))
{
if(!jit_insn_flush_struct(func, return_value))
{
return 0;
}
}
else if(return_type->kind != JIT_TYPE_VOID)
{
if(!jit_insn_return_reg(func, return_value, 0))
{
return 0;
}
}
/* Everything is back where it needs to be */
return 1;
}
/*@
* @deftypefun int _jit_opcode_is_supported (int @var{opcode})
* Not all CPU's support all arithmetic, conversion, bitwise, or
* comparison operators natively. For example, most ARM platforms
* need to call out to helper functions to perform floating-point.
*
* If this function returns zero, then @code{jit-insn.c} will output a
* call to an intrinsic function that is equivalent to the desired opcode.
* This is how you tell @code{libjit} that you cannot handle the
* opcode natively.
*
* This function can also help you develop your back end incrementally.
* Initially, you can report that only integer operations are supported,
* and then once you have them working you can move on to the floating point
* operations.
* @end deftypefun
@*/
int _jit_opcode_is_supported(int opcode)
{
/* We support all opcodes in the interpreter */
return 1;
}
/*
* Calculate the size of the argument area for an interpreted function.
*/
unsigned int _jit_interp_calculate_arg_size
(jit_function_t func, jit_type_t signature)
{
unsigned int size = 0;
jit_type_t type;
unsigned int num_params;
unsigned int param;
/* Determine if we need nested parameter information */
if(func->nested_parent)
{
size += 2 * sizeof(jit_item);
}
/* Determine if we need a structure pointer argument */
type = jit_type_get_return(signature);
if(jit_type_return_via_pointer(type))
{
size += sizeof(jit_item);
}
/* Calculate the total size of the regular arguments */
num_params = jit_type_num_params(signature);
for(param = 0; param < num_params; ++param)
{
type = jit_type_normalize(jit_type_get_param(signature, param));
if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
{
size += JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type)) *
sizeof(jit_item);
}
else
{
size += sizeof(jit_item);
}
}
/* Return the final size to the caller */
return size;
}
/*@
* @deftypefun {void *} _jit_gen_prolog (jit_gencode_t @var{gen}, jit_function_t @var{func}, void *@var{buf})
* Generate the prolog for a function into a previously-prepared
* buffer area of @code{JIT_PROLOG_SIZE} bytes in size. Returns
* the start of the prolog, which may be different than @var{buf}.
*
* This function is called at the end of the code generation process,
* not the beginning. At this point, it is known which callee save
* registers must be preserved, allowing the back end to output the
* most compact prolog possible.
* @end deftypefun
@*/
void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf)
{
/* Output the jit_function_interp structure at the beginning */
jit_function_interp_t interp = (jit_function_interp_t)buf;
unsigned int max_working_area =
gen->max_working_area + gen->extra_working_space;
interp->func = func;
interp->args_size = _jit_interp_calculate_arg_size(func, func->signature);
interp->frame_size =
(func->builder->frame_size + max_working_area) * sizeof(jit_item);
interp->working_area = max_working_area;
return buf;
}
/*@
* @deftypefun void _jit_gen_epilog (jit_gencode_t @var{gen}, jit_function_t @var{func})
* Generate a function epilog, restoring the registers that
( run in 0.359 second using v1.01-cache-2.11-cpan-3d66aa2751a )