Alien-LibJIT
view release on metacpan or search on metacpan
libjit/jit/jit-insn.c view on Meta::CPAN
* @end deftypefun
@*/
int jit_insn_store(jit_function_t func, jit_value_t dest, jit_value_t value)
{
jit_insn_t insn;
if(!dest || !value)
{
return 0;
}
if(!_jit_function_ensure_builder(func))
{
return 0;
}
value = jit_insn_convert(func, value, dest->type, 0);
if(!value)
{
return 0;
}
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
jit_value_ref(func, dest);
jit_value_ref(func, value);
insn->opcode = (short)_jit_store_opcode
(JIT_OP_COPY_INT, JIT_OP_COPY_STORE_BYTE, dest->type);
insn->dest = dest;
insn->value1 = value;
return 1;
}
/*
* Scan back through the current block, looking for an address instruction that
* involves "value" as its destination. Returns NULL if no such instruction was
* found, or it is blocked by a later use of "value".
*
* The instruction found may then be combined into a new single instruction with
* the following "load_relative", "store_relative", or another "relative_add".
*
* For instance, consider the code like this:
*
* i) y = address_of(x)
* ...
* j) z = add_relative(y, a)
*
* Let's suppose that we need to add a "store_realtive(z, b, v)" instruction.
* The "find_base_insn()" call will return the instruction "j" and we will be
* able to emit the instruction "store_relative(y, a + b, v)" instead. If "z"
* is not used elsewhere then "j" will be optimized away by the dead code
* elimination pass.
*
* Repetitive use of this procedure for a chain of "add_relative" instructions
* converts it into a series of indpenedent instructions each using the very
* first address in the chain as its base. Therefore regardless of the initial
* chain length it is always enough to make single "find_base_insn()" call to
* get the base address of the entire chain (think induction).
*
* Note that in this situation the second "find_base_insn()" call will return
* the instruction "i" that obtains the base address as the address of a local
* frame variable. This instruction is a candidate for being moved down to
* where the "load_relative" or "store_relative" occurs. This might make it
* easier for the code generator to handle field accesses whitin local
* variables.
*
* The "plast" argument indicates if the found instruction is already the last
* one, so there is no need to move it down.
*/
static jit_insn_t
find_base_insn(
jit_function_t func,
jit_insn_iter_t iter,
jit_value_t value,
int *plast)
{
int last;
jit_insn_t insn;
jit_insn_iter_t iter2;
jit_insn_t insn2;
/* The "value" could be vulnerable to aliasing effects so we cannot
optimize it */
if(value->is_addressable || value->is_volatile)
{
return 0;
}
/* We are about to check the last instruction before the current one */
last = 1;
/* Iterate back through the block looking for a suitable instruction */
while((insn = jit_insn_iter_previous(&iter)) != 0)
{
/* This instruction uses "value" in some way */
if(insn->dest == value)
{
/* This is the instruction we were looking for */
if(insn->opcode == JIT_OP_ADDRESS_OF)
{
*plast = last;
return insn;
}
if(insn->opcode == JIT_OP_ADD_RELATIVE)
{
value = insn->value1;
if(value->is_addressable || value->is_volatile)
{
return 0;
}
/* Scan forwards to ensure that "insn->value1"
is not modified anywhere in the instructions
that follow */
iter2 = iter;
jit_insn_iter_next(&iter2);
while((insn2 = jit_insn_iter_next(&iter2)) != 0)
{
if(insn2->dest == value
&& (insn2->flags & JIT_INSN_DEST_IS_VALUE) == 0)
{
return 0;
libjit/jit/jit-insn.c view on Meta::CPAN
CVT_NONE,
CVT_NONE}
};
opcode_map = to_nfloat;
}
break;
}
if(opcode_map)
{
switch(vtype->kind)
{
case JIT_TYPE_UINT: opcode_map += 2; break;
case JIT_TYPE_LONG: opcode_map += 4; break;
case JIT_TYPE_ULONG: opcode_map += 6; break;
case JIT_TYPE_FLOAT32: opcode_map += 8; break;
case JIT_TYPE_FLOAT64: opcode_map += 10; break;
case JIT_TYPE_NFLOAT: opcode_map += 12; break;
}
if(overflow_check)
{
opcode_map += 1;
}
if(opcode_map->cvt1)
{
value = apply_unary_conversion
(func, opcode_map->cvt1, value, opcode_map->type1);
}
if(opcode_map->cvt2)
{
value = apply_unary_conversion
(func, opcode_map->cvt2, value, opcode_map->type2);
}
if(opcode_map->cvt3)
{
value = apply_unary_conversion
(func, opcode_map->cvt3, value, opcode_map->type3);
}
}
return value;
}
/*
* Convert the parameters for a function call into their final types.
*/
static int convert_call_parameters
(jit_function_t func, jit_type_t signature,
jit_value_t *args, unsigned int num_args,
jit_value_t *new_args)
{
unsigned int param;
for(param = 0; param < num_args; ++param)
{
new_args[param] = jit_insn_convert
(func, args[param],
jit_type_get_param(signature, param), 0);
}
return 1;
}
/*
* Set up the exception frame information before a function call out.
*/
static int setup_eh_frame_for_call(jit_function_t func, int flags)
{
#if !defined(JIT_BACKEND_INTERP)
jit_type_t type;
jit_value_t args[2];
jit_insn_t insn;
/* If "tail" is set, then we need to pop the "setjmp" context */
if((flags & JIT_CALL_TAIL) != 0 && func->has_try)
{
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, 0, 0, 1);
if(!type)
{
return 0;
}
jit_insn_call_native
(func, "_jit_unwind_pop_setjmp",
(void *)_jit_unwind_pop_setjmp, type, 0, 0, JIT_CALL_NOTHROW);
jit_type_free(type);
}
/* If "nothrow" or "tail" is set, then there is no more to do */
if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) != 0)
{
return 1;
}
/* This function may throw an exception */
func->builder->may_throw = 1;
#if JIT_APPLY_BROKEN_FRAME_BUILTINS != 0
{
jit_value_t eh_frame_info;
jit_type_t params[2];
/* Get the value that holds the exception frame information */
if((eh_frame_info = func->builder->eh_frame_info) == 0)
{
type = jit_type_create_struct(0, 0, 0);
if(!type)
{
return 0;
}
jit_type_set_size_and_alignment
(type, sizeof(struct jit_backtrace), sizeof(void *));
eh_frame_info = jit_value_create(func, type);
jit_type_free(type);
if(!eh_frame_info)
{
return 0;
}
func->builder->eh_frame_info = eh_frame_info;
}
/* Output an instruction to load the "pc" into a value */
args[1] = jit_value_create(func, jit_type_void_ptr);
if(!(args[1]))
{
return 0;
}
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
jit_value_ref(func, args[1]);
insn->opcode = JIT_OP_LOAD_PC;
insn->dest = args[1];
/* Load the address of "eh_frame_info" into another value */
args[0] = jit_insn_address_of(func, eh_frame_info);
if(!(args[0]))
{
return 0;
}
/* Create a signature for the prototype "void (void *, void *)" */
params[0] = jit_type_void_ptr;
params[1] = jit_type_void_ptr;
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, params, 2, 1);
if(!type)
{
return 0;
}
/* Call the "_jit_backtrace_push" function */
jit_insn_call_native
(func, "_jit_backtrace_push",
(void *)_jit_backtrace_push, type, args, 2, JIT_CALL_NOTHROW);
jit_type_free(type);
}
#endif
/* Update the "catch_pc" value to reflect the current context */
if(func->builder->setjmp_value != 0)
{
args[0] = jit_value_create(func, jit_type_void_ptr);
if(!(args[0]))
{
return 0;
}
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
jit_value_ref(func, args[0]);
insn->opcode = JIT_OP_LOAD_PC;
insn->dest = args[0];
if(!jit_insn_store_relative
(func, jit_insn_address_of(func, func->builder->setjmp_value),
jit_jmp_catch_pc_offset, args[0]))
{
return 0;
}
}
/* We are now ready to make the actual function call */
return 1;
#else /* JIT_BACKEND_INTERP */
/* The interpreter handles exception frames for us */
if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) == 0)
{
func->builder->may_throw = 1;
}
return 1;
#endif
}
/*
* Restore the exception handling frame after a function call.
*/
static int restore_eh_frame_after_call(jit_function_t func, int flags)
{
#if !defined(JIT_BACKEND_INTERP)
jit_value_t value;
/* If the "nothrow", "noreturn", or "tail" flags are set, then we
don't need to worry about this */
if((flags & (JIT_CALL_NOTHROW | JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
{
return 1;
}
#if JIT_APPLY_BROKEN_FRAME_BUILTINS != 0
{
jit_type_t type;
/* Create the signature prototype "void (void)" */
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, 0, 0, 0);
if(!type)
{
return 0;
}
/* Call the "_jit_backtrace_pop" function */
jit_insn_call_native
(func, "_jit_backtrace_pop",
(void *)_jit_backtrace_pop, type, 0, 0, JIT_CALL_NOTHROW);
jit_type_free(type);
}
#endif
/* Clear the "catch_pc" value for the current context */
if(func->builder->setjmp_value != 0)
{
value = jit_value_create_nint_constant(func, jit_type_void_ptr, 0);
if(!value)
{
return 0;
}
if(!jit_insn_store_relative
(func, jit_insn_address_of(func, func->builder->setjmp_value),
jit_jmp_catch_pc_offset, value))
{
return 0;
}
}
/* Everything is back to where it should be */
return 1;
#else /* JIT_BACKEND_INTERP */
/* The interpreter handles exception frames for us */
return 1;
#endif
}
/*
* Determine if two signatures are identical for the purpose of tail calls.
*/
static int signature_identical(jit_type_t type1, jit_type_t type2)
{
unsigned int param;
/* Handle the easy case first */
if(type1 == type2)
{
return 1;
}
/* Remove the tags and then bail out if either type is invalid */
type1 = jit_type_remove_tags(type1);
type2 = jit_type_remove_tags(type2);
if(!type1 || !type2)
{
return 0;
}
/* Normalize pointer types, but leave signature types as-is */
if(type1->kind == JIT_TYPE_PTR)
{
type1 = jit_type_normalize(type1);
}
if(type2->kind == JIT_TYPE_PTR)
{
type2 = jit_type_normalize(type2);
}
#ifdef JIT_NFLOAT_IS_DOUBLE
/* "double" and "nfloat" are identical on this platform */
if((type1->kind == JIT_TYPE_FLOAT64 || type1->kind == JIT_TYPE_NFLOAT) &&
(type2->kind == JIT_TYPE_FLOAT64 || type2->kind == JIT_TYPE_NFLOAT))
{
return 1;
}
#endif
/* If the kinds are not the same now, then we don't have a match */
if(type1->kind != type2->kind)
{
return 0;
}
/* Structure and union types must have the same size and alignment */
if(type1->kind == JIT_TYPE_STRUCT || type1->kind == JIT_TYPE_UNION)
{
return (jit_type_get_size(type1) == jit_type_get_size(type2) &&
jit_type_get_alignment(type1) == jit_type_get_alignment(type2));
}
/* Signature types must be compared component-by-component */
if(type1->kind == JIT_TYPE_SIGNATURE)
{
libjit/jit/jit-insn.c view on Meta::CPAN
}
}
*struct_return = 0;
return 1;
}
/* Let the back end do the work */
return _jit_create_call_setup_insns
(func, signature, args, num_args,
is_nested, nesting_level, struct_return, flags);
}
static jit_value_t
handle_return(jit_function_t func,
jit_type_t signature,
int flags, int is_nested,
jit_value_t *args, unsigned int num_args,
jit_value_t return_value)
{
/* If the function does not return, then end the current block.
The next block does not have "entered_via_top" set so that
it will be eliminated during later code generation */
if((flags & (JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
{
func->builder->current_block->ends_in_dead = 1;
}
/* If the function may throw an exceptions then end the current
basic block to account for exceptional control flow */
if((flags & JIT_CALL_NOTHROW) == 0)
{
if(!jit_insn_new_block(func))
{
return 0;
}
}
/* Create space for the return value, if we don't already have one */
if(!return_value)
{
return_value = jit_value_create(func, jit_type_get_return(signature));
if(!return_value)
{
return 0;
}
}
/* Create the instructions necessary to move the return value into place */
if((flags & JIT_CALL_TAIL) == 0)
{
if(!_jit_create_call_return_insns(func,
signature,
args, num_args,
return_value,
is_nested))
{
return 0;
}
}
/* Restore exception frame information after the call */
if(!restore_eh_frame_after_call(func, flags))
{
return 0;
}
/* Return the value containing the result to the caller */
return return_value;
}
/*@
* @deftypefun jit_value_t jit_insn_call (jit_function_t @var{func}, const char *@var{name}, jit_function_t @var{jit_func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{flags})
* Call the function @var{jit_func}, which may or may not be translated yet.
* The @var{name} is for diagnostic purposes only, and can be NULL.
*
* If @var{signature} is NULL, then the actual signature of @var{jit_func}
* is used in its place. This is the usual case. However, if the function
* takes a variable number of arguments, then you may need to construct
* an explicit signature for the non-fixed argument values.
*
* The @var{flags} parameter specifies additional information about the
* type of call to perform:
*
* @table @code
* @vindex JIT_CALL_NOTHROW
* @item JIT_CALL_NOTHROW
* The function never throws exceptions.
*
* @vindex JIT_CALL_NORETURN
* @item JIT_CALL_NORETURN
* The function will never return directly to its caller. It may however
* return to the caller indirectly by throwing an exception that the
* caller catches.
*
* @vindex JIT_CALL_TAIL
* @item JIT_CALL_TAIL
* Apply tail call optimizations, as the result of this function
* call will be immediately returned from the containing function.
* Tail calls are only appropriate when the signature of the called
* function matches the callee, and none of the parameters point
* to local variables.
* @end table
*
* If @var{jit_func} has already been compiled, then @code{jit_insn_call}
* may be able to intuit some of the above flags for itself. Otherwise
* it is up to the caller to determine when the flags may be appropriate.
* @end deftypefun
@*/
jit_value_t jit_insn_call
(jit_function_t func, const char *name, jit_function_t jit_func,
jit_type_t signature, jit_value_t *args, unsigned int num_args, int flags)
{
int is_nested;
int nesting_level;
jit_function_t temp_func;
jit_value_t *new_args;
jit_value_t return_value;
jit_insn_t insn;
jit_label_t entry_point;
jit_label_t label_end;
/* Bail out if there is something wrong with the parameters */
libjit/jit/jit-insn.c view on Meta::CPAN
flags &= ~JIT_CALL_TAIL;
}
}
/* Determine the nesting relationship with the current function */
if(jit_func->nested_parent)
{
is_nested = 1;
if(jit_func->nested_parent == func)
{
/* We are calling one of our children */
nesting_level = -1;
}
else if(jit_func->nested_parent == func->nested_parent)
{
/* We are calling one of our direct siblings */
nesting_level = 0;
}
else
{
/* Search up to find the actual nesting level */
temp_func = func->nested_parent;
nesting_level = 1;
while(temp_func != 0 && temp_func != jit_func)
{
++nesting_level;
temp_func = temp_func->nested_parent;
}
}
}
else
{
is_nested = 0;
nesting_level = 0;
}
/* Convert the arguments to the actual parameter types */
if(num_args > 0)
{
new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
if(!convert_call_parameters(func, signature, args, num_args, new_args))
{
return 0;
}
}
else
{
new_args = args;
}
/* Intuit additional flags from "jit_func" if it was already compiled */
if(func->no_throw)
{
flags |= JIT_CALL_NOTHROW;
}
if(func->no_return)
{
flags |= JIT_CALL_NORETURN;
}
/* Set up exception frame information for the call */
if(!setup_eh_frame_for_call(func, flags))
{
return 0;
}
/* Create the instructions to push the parameters onto the stack */
if(!create_call_setup_insns
(func, jit_func, signature, new_args, num_args,
is_nested, nesting_level, &return_value, flags))
{
return 0;
}
/* Output the "call" instruction */
if((flags & JIT_CALL_TAIL) != 0 && func == jit_func)
{
/* We are performing a tail call to ourselves, which we can
turn into an unconditional branch back to our entry point */
entry_point = jit_label_undefined;
label_end = jit_label_undefined;
if(!jit_insn_branch(func, &entry_point))
{
return 0;
}
if(!jit_insn_label(func, &entry_point))
{
return 0;
}
if(!jit_insn_label(func, &label_end))
{
return 0;
}
if(!jit_insn_move_blocks_to_start(func, entry_point, label_end))
{
return 0;
}
}
else
{
/* Functions that call out are not leaves */
func->builder->non_leaf = 1;
/* Performing a regular call, or a tail call to someone else */
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
if((flags & JIT_CALL_TAIL) != 0)
{
func->builder->has_tail_call = 1;
insn->opcode = JIT_OP_CALL_TAIL;
}
else
{
insn->opcode = JIT_OP_CALL;
}
insn->flags = JIT_INSN_DEST_IS_FUNCTION | JIT_INSN_VALUE1_IS_NAME;
insn->dest = (jit_value_t)jit_func;
insn->value1 = (jit_value_t)name;
}
/* Handle return to the caller */
return handle_return(func, signature, flags, is_nested,
new_args, num_args, return_value);
}
/*@
* @deftypefun jit_value_t jit_insn_call_indirect (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{flags})
* Call a function via an indirect pointer.
* @end deftypefun
@*/
jit_value_t jit_insn_call_indirect
(jit_function_t func, jit_value_t value, jit_type_t signature,
jit_value_t *args, unsigned int num_args, int flags)
{
jit_value_t *new_args;
jit_value_t return_value;
jit_insn_t insn;
/* Bail out if there is something wrong with the parameters */
if(!_jit_function_ensure_builder(func) || !value || !signature)
{
return 0;
}
/* Verify that tail calls are possible to the destination */
#if defined(JIT_BACKEND_INTERP)
flags &= ~JIT_CALL_TAIL;
#else
if((flags & JIT_CALL_TAIL) != 0)
{
if(func->nested_parent)
{
flags &= ~JIT_CALL_TAIL;
}
else if(!signature_identical(signature, func->signature))
{
flags &= ~JIT_CALL_TAIL;
}
}
#endif
/* We are making a native call */
flags |= JIT_CALL_NATIVE;
/* Convert the arguments to the actual parameter types */
if(num_args > 0)
{
new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
if(!convert_call_parameters(func, signature, args, num_args, new_args))
{
return 0;
}
}
else
{
new_args = args;
}
/* Set up exception frame information for the call */
if(!setup_eh_frame_for_call(func, flags))
{
return 0;
}
/* Create the instructions to push the parameters onto the stack */
if(!create_call_setup_insns
(func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
{
return 0;
}
/* Move the indirect pointer value into an appropriate register */
if(!_jit_setup_indirect_pointer(func, value))
{
return 0;
}
/* Functions that call out are not leaves */
func->builder->non_leaf = 1;
/* Output the "call_indirect" instruction */
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
jit_value_ref(func, value);
if((flags & JIT_CALL_TAIL) != 0)
{
func->builder->has_tail_call = 1;
insn->opcode = JIT_OP_CALL_INDIRECT_TAIL;
}
else
{
insn->opcode = JIT_OP_CALL_INDIRECT;
}
insn->flags = JIT_INSN_VALUE2_IS_SIGNATURE;
insn->value1 = value;
insn->value2 = (jit_value_t)jit_type_copy(signature);
/* Handle return to the caller */
return handle_return(func, signature, flags, 0,
new_args, num_args, return_value);
}
/*@
* @deftypefun jit_value_t jit_insn_call_indirect_vtable (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{flags})
* Call a function via an indirect pointer. This version differs from
* @code{jit_insn_call_indirect} in that we assume that @var{value}
* contains a pointer that resulted from calling
* @code{jit_function_to_vtable_pointer}. Indirect vtable pointer
* calls may be more efficient on some platforms than regular indirect calls.
* @end deftypefun
@*/
jit_value_t jit_insn_call_indirect_vtable
(jit_function_t func, jit_value_t value, jit_type_t signature,
jit_value_t *args, unsigned int num_args, int flags)
{
jit_value_t *new_args;
jit_value_t return_value;
jit_insn_t insn;
/* Bail out if there is something wrong with the parameters */
if(!_jit_function_ensure_builder(func) || !value || !signature)
{
return 0;
}
/* Verify that tail calls are possible to the destination */
if((flags & JIT_CALL_TAIL) != 0)
{
if(func->nested_parent)
{
flags &= ~JIT_CALL_TAIL;
}
else if(!signature_identical(signature, func->signature))
{
flags &= ~JIT_CALL_TAIL;
}
}
/* Convert the arguments to the actual parameter types */
if(num_args > 0)
{
new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
if(!convert_call_parameters(func, signature, args, num_args, new_args))
{
return 0;
}
}
else
{
new_args = args;
}
/* Set up exception frame information for the call */
if(!setup_eh_frame_for_call(func, flags))
{
return 0;
}
/* Create the instructions to push the parameters onto the stack */
if(!create_call_setup_insns
(func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
{
return 0;
}
/* Move the indirect pointer value into an appropriate register */
if(!_jit_setup_indirect_pointer(func, value))
{
return 0;
}
/* Functions that call out are not leaves */
func->builder->non_leaf = 1;
/* Output the "call_vtable_ptr" instruction */
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
jit_value_ref(func, value);
if((flags & JIT_CALL_TAIL) != 0)
{
func->builder->has_tail_call = 1;
insn->opcode = JIT_OP_CALL_VTABLE_PTR_TAIL;
}
else
{
insn->opcode = JIT_OP_CALL_VTABLE_PTR;
}
insn->value1 = value;
/* Handle return to the caller */
return handle_return(func, signature, flags, 0,
new_args, num_args, return_value);
}
/*@
* @deftypefun jit_value_t jit_insn_call_native (jit_function_t @var{func}, const char *@var{name}, void *@var{native_func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{exception_return}, int @var{flags}...
* Output an instruction that calls an external native function.
* The @var{name} is for diagnostic purposes only, and can be NULL.
* @end deftypefun
@*/
jit_value_t jit_insn_call_native
(jit_function_t func, const char *name, void *native_func,
jit_type_t signature, jit_value_t *args, unsigned int num_args, int flags)
{
jit_value_t *new_args;
jit_value_t return_value;
jit_insn_t insn;
jit_type_t return_type;
/* Bail out if there is something wrong with the parameters */
if(!_jit_function_ensure_builder(func) || !native_func || !signature)
{
return 0;
}
/* Verify that tail calls are possible to the destination */
#if defined(JIT_BACKEND_INTERP)
flags &= ~JIT_CALL_TAIL;
#else
if((flags & JIT_CALL_TAIL) != 0)
{
if(func->nested_parent)
{
flags &= ~JIT_CALL_TAIL;
}
else if(!signature_identical(signature, func->signature))
{
flags &= ~JIT_CALL_TAIL;
}
}
#endif
/* We are making a native call */
flags |= JIT_CALL_NATIVE;
/* Convert the arguments to the actual parameter types */
if(num_args > 0)
{
new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
if(!convert_call_parameters(func, signature, args, num_args, new_args))
{
return 0;
}
}
else
{
new_args = args;
}
/* Set up exception frame information for the call */
if(!setup_eh_frame_for_call(func, flags))
{
return 0;
}
/* Create the instructions to push the parameters onto the stack */
if(!create_call_setup_insns
(func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
{
return 0;
}
/* Functions that call out are not leaves */
func->builder->non_leaf = 1;
/* Output the "call_external" instruction */
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
if((flags & JIT_CALL_TAIL) != 0)
{
func->builder->has_tail_call = 1;
insn->opcode = JIT_OP_CALL_EXTERNAL_TAIL;
}
else
{
insn->opcode = JIT_OP_CALL_EXTERNAL;
}
insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME;
insn->dest = (jit_value_t)native_func;
insn->value1 = (jit_value_t)name;
#ifdef JIT_BACKEND_INTERP
insn->flags |= JIT_INSN_VALUE2_IS_SIGNATURE;
insn->value2 = (jit_value_t)jit_type_copy(signature);
#endif
/* Handle return to the caller */
return_value = handle_return(func, signature, flags, 0,
new_args, num_args, return_value);
/* Make sure that returned byte / short values get zero / sign extended */
return_type = jit_type_normalize(return_value->type);
switch(return_type->kind)
{
case JIT_TYPE_SBYTE:
/* Force sbyte sign extension to int */
return_value = apply_unary_conversion(func, JIT_OP_TRUNC_SBYTE,
return_value, return_type);
break;
case JIT_TYPE_UBYTE:
/* Force ubyte zero extension to uint */
return_value = apply_unary_conversion(func, JIT_OP_TRUNC_UBYTE,
return_value, return_type);
break;
case JIT_TYPE_SHORT:
/* Force short sign extension to int */
return_value = apply_unary_conversion(func, JIT_OP_TRUNC_SHORT,
return_value, return_type);
break;
libjit/jit/jit-insn.c view on Meta::CPAN
temp_value = jit_insn_call_native
(func, name, intrinsic_func, signature, param_values,
num_params, JIT_CALL_NOTHROW);
if(!temp_value)
{
jit_type_free(signature);
return 0;
}
jit_type_free(signature);
/* If no exceptions to report, then return "temp_value" as the result */
if(!return_value)
{
return temp_value;
}
/* Determine if an exception was reported */
cond_value = jit_insn_ge(func, temp_value,
jit_value_create_nint_constant(func, jit_type_int, 1));
if(!cond_value)
{
return 0;
}
label = jit_label_undefined;
if(!jit_insn_branch_if(func, cond_value, &label))
{
return 0;
}
/* Call the "jit_exception_builtin" function to report the exception */
param_types[0] = jit_type_int;
signature = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, param_types, 1, 1);
if(!signature)
{
return 0;
}
param_values[0] = temp_value;
jit_insn_call_native
(func, "jit_exception_builtin",
(void *)jit_exception_builtin, signature, param_values, 1,
JIT_CALL_NORETURN);
jit_type_free(signature);
/* Execution continues here if there was no exception */
if(!jit_insn_label(func, &label))
{
return 0;
}
/* Return the temporary that contains the result value */
return return_value;
}
/*@
* @deftypefun int jit_insn_incoming_reg (jit_function_t @var{func}, jit_value_t @var{value}, int @var{reg})
* Output an instruction that notes that the contents of @var{value}
* can be found in the register @var{reg} at this point in the code.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to set up the function's entry frame and the
* values of registers on return from a subroutine call.
* @end deftypefun
@*/
int jit_insn_incoming_reg(jit_function_t func, jit_value_t value, int reg)
{
if(value && value->is_parameter)
{
value->is_reg_parameter = 1;
}
return create_note(func, JIT_OP_INCOMING_REG, value,
jit_value_create_nint_constant
(func, jit_type_int, (jit_nint)reg));
}
/*@
* @deftypefun int jit_insn_incoming_frame_posn (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{frame_offset})
* Output an instruction that notes that the contents of @var{value}
* can be found in the stack frame at @var{frame_offset} at this point
* in the code.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to set up the function's entry frame.
* @end deftypefun
@*/
int jit_insn_incoming_frame_posn
(jit_function_t func, jit_value_t value, jit_nint frame_offset)
{
return create_note(func, JIT_OP_INCOMING_FRAME_POSN, value,
jit_value_create_nint_constant
(func, jit_type_int, frame_offset));
}
/*@
* @deftypefun int jit_insn_outgoing_reg (jit_function_t @var{func}, jit_value_t @var{value}, int @var{reg})
* Output an instruction that copies the contents of @var{value}
* into the register @var{reg} at this point in the code. This is
* typically used just before making an outgoing subroutine call.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to set up the registers for a subroutine call.
* @end deftypefun
@*/
int jit_insn_outgoing_reg(jit_function_t func, jit_value_t value, int reg)
{
return create_note(func, JIT_OP_OUTGOING_REG, value,
jit_value_create_nint_constant
(func, jit_type_int, (jit_nint)reg));
}
/*@
* @deftypefun int jit_insn_outgoing_frame_posn (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{frame_offset})
* Output an instruction that notes that the contents of @var{value}
* should be stored in the stack frame at @var{frame_offset} at this point
* in the code.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to set up an outgoing frame for tail calls.
* @end deftypefun
@*/
int jit_insn_outgoing_frame_posn
(jit_function_t func, jit_value_t value, jit_nint frame_offset)
{
return create_note(func, JIT_OP_OUTGOING_FRAME_POSN, value,
jit_value_create_nint_constant
(func, jit_type_int, frame_offset));
}
/*@
* @deftypefun int jit_insn_return_reg (jit_function_t @var{func}, jit_value_t @var{value}, int @var{reg})
* Output an instruction that notes that the contents of @var{value}
* can be found in the register @var{reg} at this point in the code.
* This is similar to @code{jit_insn_incoming_reg}, except that it
* refers to return values, not parameter values.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to handle returns from subroutine calls.
* @end deftypefun
@*/
int jit_insn_return_reg(jit_function_t func, jit_value_t value, int reg)
{
return create_note(func, JIT_OP_RETURN_REG, value,
jit_value_create_nint_constant
(func, jit_type_int, (jit_nint)reg));
}
/*@
* @deftypefun int jit_insn_setup_for_nested (jit_function_t @var{func}, int @var{nested_level}, int @var{reg})
* Output an instruction to set up for a nested function call.
* 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. If @var{reg} is not -1, then
* it indicates the register to receive the parent frame information.
* If @var{reg} is -1, then the frame information will be pushed on the stack.
*
* You normally wouldn't call this yourself - it is used internally by the
* CPU back ends to set up the parameters for a nested subroutine call.
* @end deftypefun
@*/
int jit_insn_setup_for_nested(jit_function_t func, int nested_level, int reg)
{
if(nested_level < 0)
{
return create_unary_note
(func, JIT_OP_SETUP_FOR_NESTED,
jit_value_create_nint_constant
(func, jit_type_int, (jit_nint)reg));
}
else
{
return create_note
(func, JIT_OP_SETUP_FOR_SIBLING,
jit_value_create_nint_constant
(func, jit_type_int, (jit_nint)nested_level),
jit_value_create_nint_constant
(func, jit_type_int, (jit_nint)reg));
}
}
/*@
* @deftypefun int jit_insn_flush_struct (jit_function_t @var{func}, jit_value_t @var{value})
* Flush a small structure return value out of registers and back
* into the local variable frame. You normally wouldn't call this
* yourself - it is used internally by the CPU back ends to handle
* structure returns from functions.
* @end deftypefun
@*/
int jit_insn_flush_struct(jit_function_t func, jit_value_t value)
{
if(value)
{
jit_value_set_addressable(value);
}
return create_unary_note(func, JIT_OP_FLUSH_SMALL_STRUCT, value);
}
/*@
* @deftypefun jit_value_t jit_insn_import (jit_function_t @var{func}, jit_value_t @var{value})
* Import @var{value} from an outer nested scope into @var{func}. Returns
* the effective address of the value for local access via a pointer.
* Returns NULL if out of memory or the value is not accessible via a
* parent, grandparent, or other ancestor of @var{func}.
* @end deftypefun
@*/
jit_value_t jit_insn_import(jit_function_t func, jit_value_t value)
{
jit_function_t value_func;
jit_function_t current_func;
int level;
/* Make sure that we have a builder before we start */
if(!_jit_function_ensure_builder(func))
{
return 0;
}
/* If the value is already local, then return the local address */
value_func = jit_value_get_function(value);
if(value_func == func)
{
return jit_insn_address_of(func, value);
}
/* Find the nesting level of the value, where 1 is our parent */
level = 1;
current_func = func->nested_parent;
while(current_func != 0 && current_func != value_func)
{
++level;
current_func = current_func->nested_parent;
}
if(!current_func)
{
/* The value is not accessible from this scope */
return 0;
}
/* Output the relevant import instruction, which will also cause
it to be marked as a non-local addressable by "jit_value_ref" */
return apply_binary
(func, JIT_OP_IMPORT, value,
jit_value_create_nint_constant(func, jit_type_int, (jit_nint)level),
jit_type_void_ptr);
libjit/jit/jit-insn.c view on Meta::CPAN
value = jit_insn_address_of(func, value);
if(!value)
{
return 0;
}
return create_note
(func, JIT_OP_PUSH_STRUCT, value,
jit_value_create_nint_constant
(func, jit_type_nint, (jit_nint)jit_type_get_size(type)));
}
/* Not reached */
}
return 1;
}
/*@
* @deftypefun int jit_insn_push_ptr (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{type})
* Push @code{*@var{value}} onto the function call stack, in preparation for a call.
* This is normally used for returning @code{struct} and @code{union}
* values where you have the effective address of the structure, rather
* than the structure's contents, in @var{value}.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to set up the stack for a subroutine call.
* @end deftypefun
@*/
int jit_insn_push_ptr(jit_function_t func, jit_value_t value, jit_type_t type)
{
if(!value || !type)
{
return 0;
}
switch(jit_type_normalize(type)->kind)
{
case JIT_TYPE_STRUCT:
case JIT_TYPE_UNION:
{
/* Push the structure onto the stack by address */
return create_note
(func, JIT_OP_PUSH_STRUCT, value,
jit_value_create_nint_constant
(func, jit_type_nint, (jit_nint)jit_type_get_size(type)));
}
/* Not reached */
default:
{
/* Load the value from the address and push it normally */
return jit_insn_push
(func, jit_insn_load_relative(func, value, 0, type));
}
/* Not reached */
}
}
/*@
* @deftypefun int jit_insn_set_param (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{offset})
* Set the parameter slot at @var{offset} in the outgoing parameter area
* to @var{value}. This may be used instead of @code{jit_insn_push}
* if it is more efficient to store directly to the stack than to push.
* The outgoing parameter area is allocated within the frame when
* the function is first entered.
*
* You normally wouldn't call this yourself - it is used internally
* by the CPU back ends to set up the stack for a subroutine call.
* @end deftypefun
@*/
int jit_insn_set_param(jit_function_t func, jit_value_t value, jit_nint offset)
{
jit_type_t type;
if(!value)
{
return 0;
}
type = jit_type_promote_int(jit_type_normalize(jit_value_get_type(value)));
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:
{
return create_note(func, JIT_OP_SET_PARAM_INT, value,
jit_value_create_nint_constant
(func, jit_type_nint, offset));
}
/* Not reached */
case JIT_TYPE_LONG:
case JIT_TYPE_ULONG:
{
return create_note(func, JIT_OP_SET_PARAM_LONG, value,
jit_value_create_nint_constant
(func, jit_type_nint, offset));
}
/* Not reached */
case JIT_TYPE_FLOAT32:
{
return create_note(func, JIT_OP_SET_PARAM_FLOAT32, value,
jit_value_create_nint_constant
(func, jit_type_nint, offset));
}
/* Not reached */
case JIT_TYPE_FLOAT64:
{
return create_note(func, JIT_OP_SET_PARAM_FLOAT64, value,
jit_value_create_nint_constant
(func, jit_type_nint, offset));
}
/* Not reached */
case JIT_TYPE_NFLOAT:
{
return create_note(func, JIT_OP_SET_PARAM_NFLOAT, value,
jit_value_create_nint_constant
(func, jit_type_nint, offset));
}
libjit/jit/jit-insn.c view on Meta::CPAN
if(!type)
{
return 0;
}
args[0] = jit_insn_address_of(func, func->builder->setjmp_value);
args[1] = jit_value_create_nint_constant(func, jit_type_sys_int, 1);
#if defined(HAVE___SIGSETJMP)
value = jit_insn_call_native
(func, "__sigsetjmp", (void *)__sigsetjmp,
type, args, 2, JIT_CALL_NOTHROW);
#else
value = jit_insn_call_native
(func, "sigsetjmp", (void *)sigsetjmp,
type, args, 2, JIT_CALL_NOTHROW);
#endif
jit_type_free(type);
if(!value)
{
return 0;
}
#else /* !HAVE_SIGSETJMP */
type = jit_type_void_ptr;
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_int, &type, 1, 1);
if(!type)
{
return 0;
}
args[0] = jit_insn_address_of(func, func->builder->setjmp_value);
#if defined(HAVE__SETJMP)
value = jit_insn_call_native
(func, "_setjmp", (void *)_setjmp, type, args, 1, JIT_CALL_NOTHROW);
#else
value = jit_insn_call_native
(func, "setjmp", (void *)setjmp, type, args, 1, JIT_CALL_NOTHROW);
#endif
jit_type_free(type);
if(!value)
{
return 0;
}
#endif /* !HAVE_SIGSETJMP */
/* Branch to the end of the init code if "setjmp" returned zero */
if(!jit_insn_branch_if_not(func, value, &code_label))
{
return 0;
}
/* We need a value to hold the location of the thrown exception */
if((func->builder->thrown_pc =
jit_value_create(func, jit_type_void_ptr)) == 0)
{
return 0;
}
/* Get the value of "catch_pc" from within "setjmp_value" and store it
into the current frame. This indicates where the exception occurred */
value = jit_insn_load_relative
(func, jit_insn_address_of(func, func->builder->setjmp_value),
jit_jmp_catch_pc_offset, jit_type_void_ptr);
if(!value)
{
return 0;
}
if(!jit_insn_store(func, func->builder->thrown_pc, value))
{
return 0;
}
if(!jit_insn_branch_if_not(func, value, &rethrow_label))
{
return 0;
}
/* Clear the original "catch_pc" value within "setjmp_value" */
if(!jit_insn_store_relative
(func, jit_insn_address_of(func, func->builder->setjmp_value),
jit_jmp_catch_pc_offset, jit_value_create_nint_constant
(func, jit_type_void_ptr, 0)))
{
return 0;
}
/* Jump to this function's exception catcher */
if(!jit_insn_branch(func, &(func->builder->catcher_label)))
{
return 0;
}
/* Mark the position of the rethrow label */
if(!jit_insn_label(func, &rethrow_label))
{
return 0;
}
/* Call "_jit_unwind_pop_and_rethrow" to pop the current
"setjmp" context and then rethrow the current exception */
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, 0, 0, 1);
if(!type)
{
return 0;
}
jit_insn_call_native
(func, "_jit_unwind_pop_and_rethrow",
(void *)_jit_unwind_pop_and_rethrow, type, 0, 0,
JIT_CALL_NOTHROW | JIT_CALL_NORETURN);
jit_type_free(type);
/* Insert the target to jump to the normal code. */
if(!jit_insn_label(func, &code_label))
{
return 0;
}
/* Force the start of a new block to mark the end of the init code */
if(!jit_insn_label(func, &end_label))
{
( run in 0.939 second using v1.01-cache-2.11-cpan-df04353d9ac )