Alien-LibJIT
view release on metacpan or search on metacpan
libjit/jit/jit-insn.c view on Meta::CPAN
* 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 */
if(!_jit_function_ensure_builder(func) || !jit_func)
{
return 0;
}
/* Get the default signature from "jit_func" */
if(!signature)
{
signature = jit_func->signature;
}
/* Verify that tail calls are possible to the destination */
if((flags & JIT_CALL_TAIL) != 0)
{
if(func->nested_parent || jit_func->nested_parent)
{
/* Cannot use tail calls with nested function calls */
flags &= ~JIT_CALL_TAIL;
}
else if(!signature_identical(signature, func->signature))
{
/* The signatures are not the same, so tail calls not allowed */
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))
{
libjit/jit/jit-insn.c view on Meta::CPAN
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;
libjit/jit/jit-insn.c view on Meta::CPAN
(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);
}
/*@
* @deftypefun int jit_insn_push (jit_function_t @var{func}, jit_value_t @var{value})
* Push a value onto the function call stack, in preparation for a call.
* 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(jit_function_t func, jit_value_t value)
{
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_unary_note(func, JIT_OP_PUSH_INT, value);
}
/* Not reached */
case JIT_TYPE_LONG:
case JIT_TYPE_ULONG:
{
return create_unary_note(func, JIT_OP_PUSH_LONG, value);
}
/* Not reached */
case JIT_TYPE_FLOAT32:
{
return create_unary_note(func, JIT_OP_PUSH_FLOAT32, value);
}
/* Not reached */
case JIT_TYPE_FLOAT64:
{
return create_unary_note(func, JIT_OP_PUSH_FLOAT64, value);
}
( run in 0.413 second using v1.01-cache-2.11-cpan-1edf4fed603 )