Alien-LibJIT
view release on metacpan or search on metacpan
libjit/jit/jit-insn.c view on Meta::CPAN
return jit_insn_call_intrinsic
(func, descr->nfname, descr->nffunc, descr->nfdesc, value1, value2);
}
}
/*
* Apply a unary arithmetic operator, after coercing the
* argument to a suitable numeric type.
*/
static jit_value_t apply_unary_arith
(jit_function_t func, const jit_opcode_descr *descr,
jit_value_t value1, int int_only, int float_only,
int overflow_check)
{
int oper;
jit_type_t result_type;
const jit_intrinsic_descr_t *desc;
if(!value1)
{
return 0;
}
result_type = common_binary
(value1->type, value1->type, int_only, float_only);
if(result_type == jit_type_int)
{
oper = descr->ioper;
desc = descr->idesc;
}
else if(result_type == jit_type_uint)
{
oper = descr->iuoper;
desc = descr->iudesc;
}
else if(result_type == jit_type_long)
{
oper = descr->loper;
desc = descr->ldesc;
}
else if(result_type == jit_type_ulong)
{
oper = descr->luoper;
desc = descr->ludesc;
}
else if(result_type == jit_type_float32)
{
oper = descr->foper;
desc = descr->fdesc;
}
else if(result_type == jit_type_float64)
{
oper = descr->doper;
desc = descr->ddesc;
}
else
{
oper = descr->nfoper;
desc = descr->nfdesc;
}
if(desc && desc->ptr_result_type)
{
func->builder->may_throw = 1;
}
value1 = jit_insn_convert(func, value1, result_type, overflow_check);
if(_jit_opcode_is_supported(oper))
{
return apply_unary(func, oper, value1, result_type);
}
else
{
return apply_intrinsic(func, descr, value1, 0, result_type);
}
}
/*
* Apply a binary arithmetic operator, after coercing both
* arguments to a common type.
*/
static jit_value_t apply_arith
(jit_function_t func, const jit_opcode_descr *descr,
jit_value_t value1, jit_value_t value2,
int int_only, int float_only, int overflow_check)
{
int oper;
jit_type_t result_type;
const jit_intrinsic_descr_t *desc;
if(!value1 || !value2)
{
return 0;
}
result_type = common_binary
(value1->type, value2->type, int_only, float_only);
if(result_type == jit_type_int)
{
oper = descr->ioper;
desc = descr->idesc;
}
else if(result_type == jit_type_uint)
{
oper = descr->iuoper;
desc = descr->iudesc;
}
else if(result_type == jit_type_long)
{
oper = descr->loper;
desc = descr->ldesc;
}
else if(result_type == jit_type_ulong)
{
oper = descr->luoper;
desc = descr->ludesc;
}
else if(result_type == jit_type_float32)
{
oper = descr->foper;
desc = descr->fdesc;
}
else if(result_type == jit_type_float64)
{
oper = descr->doper;
desc = descr->ddesc;
}
else
{
oper = descr->nfoper;
desc = descr->nfdesc;
}
if(desc && desc->ptr_result_type)
{
func->builder->may_throw = 1;
}
value1 = jit_insn_convert(func, value1, result_type, overflow_check);
value2 = jit_insn_convert(func, value2, result_type, overflow_check);
if(_jit_opcode_is_supported(oper))
{
return apply_binary(func, oper, value1, value2, result_type);
}
else
{
return apply_intrinsic(func, descr, value1, value2, result_type);
}
}
/*
* Apply a binary shift operator, after coercing both
* arguments to suitable types.
*/
static jit_value_t apply_shift
(jit_function_t func, const jit_opcode_descr *descr,
jit_value_t value1, jit_value_t value2)
{
int oper;
jit_type_t result_type;
jit_type_t count_type;
if(!value1 || !value2)
{
return 0;
}
result_type = common_binary(value1->type, value1->type, 1, 0);
if(result_type == jit_type_int)
{
oper = descr->ioper;
}
else if(result_type == jit_type_uint)
{
oper = descr->iuoper;
}
else if(result_type == jit_type_long)
{
oper = descr->loper;
}
else if(result_type == jit_type_ulong)
{
oper = descr->luoper;
}
else
{
/* Shouldn't happen */
oper = descr->loper;
}
count_type = jit_type_promote_int(jit_type_normalize(value2->type));
if(count_type != jit_type_int)
{
count_type = jit_type_uint;
}
value1 = jit_insn_convert(func, value1, result_type, 0);
value2 = jit_insn_convert(func, value2, count_type, 0);
if(_jit_opcode_is_supported(oper))
{
return apply_binary(func, oper, value1, value2, result_type);
libjit/jit/jit-insn.c view on Meta::CPAN
jit_insn_mul(func, index,
jit_value_create_nint_constant
(func, jit_type_nint, size)));
}
/*@
* @deftypefun int jit_insn_store_elem (jit_function_t @var{func}, jit_value_t @var{base_addr}, jit_value_t @var{index}, jit_value_t @var{value})
* Store @var{value} at position @var{index} of the array starting at
* @var{base_addr}. The effective address of the storage location is
* @code{@var{base_addr} + @var{index} * sizeof(jit_value_get_type(@var{value}))}.
* @end deftypefun
@*/
int jit_insn_store_elem
(jit_function_t func, jit_value_t base_addr,
jit_value_t index, jit_value_t value)
{
jit_nint size;
int opcode;
jit_type_t elem_type;
/* Get the size of the element that we are fetching */
if(!value)
{
return 0;
}
elem_type = jit_value_get_type(value);
size = (jit_nint)(jit_type_get_size(elem_type));
/* Convert the index into a native integer */
index = jit_insn_convert(func, index, jit_type_nint, 0);
if(!index)
{
return 0;
}
/* If the index is constant, then turn this into a relative store */
if(jit_value_is_constant(index))
{
return jit_insn_store_relative
(func, base_addr,
jit_value_get_nint_constant(index) * size, value);
}
/* See if we can use a special-case instruction */
opcode = _jit_store_opcode(JIT_OP_STORE_ELEMENT_BYTE, 0, elem_type);
if(opcode != 0 && opcode != (JIT_OP_STORE_ELEMENT_BYTE + 7))
{
return apply_ternary(func, opcode, base_addr, index, value);
}
/* Calculate the effective address and then use a relative store */
base_addr = jit_insn_add(func, base_addr,
jit_insn_mul(func, index,
jit_value_create_nint_constant
(func, jit_type_nint, size)));
return jit_insn_store_relative(func, base_addr, 0, value);
}
/*@
* @deftypefun int jit_insn_check_null (jit_function_t @var{func}, jit_value_t @var{value})
* Check @var{value} to see if it is NULL. If it is, then throw the
* built-in @code{JIT_RESULT_NULL_REFERENCE} exception.
* @end deftypefun
@*/
int jit_insn_check_null(jit_function_t func, jit_value_t value)
{
if(!_jit_function_ensure_builder(func))
{
return 0;
}
/* Do the check only if the value is no not Null constant */
if(value->is_nint_constant && (value->address != 0))
{
return 1;
}
func->builder->may_throw = 1;
return create_unary_note(func, JIT_OP_CHECK_NULL, value);
}
int _jit_insn_check_is_redundant(const jit_insn_iter_t *iter)
{
jit_insn_iter_t new_iter = *iter;
jit_insn_t insn;
jit_value_t value;
/* Back up to find the "check_null" instruction of interest */
insn = jit_insn_iter_previous(&new_iter);
value = insn->value1;
/* The value must be temporary or local, and not volatile or addressable.
Otherwise the value could be vulnerable to aliasing side-effects that
could make it NULL again even after we have checked it */
if(!(value->is_temporary) || !(value->is_local))
{
return 0;
}
if(value->is_volatile || value->is_addressable)
{
return 0;
}
/* Search back for a previous "check_null" instruction */
while((insn = jit_insn_iter_previous(&new_iter)) != 0)
{
if(insn->opcode == JIT_OP_CHECK_NULL && insn->value1 == value)
{
/* This is the previous "check_null" that we were looking for */
return 1;
}
if(insn->opcode >= JIT_OP_STORE_RELATIVE_BYTE &&
insn->opcode <= JIT_OP_STORE_RELATIVE_STRUCT)
{
/* This stores to the memory referenced by the destination,
not to the destination itself, so it cannot affect "value" */
continue;
}
if(insn->dest == value)
{
/* The value was used as a destination, so we must check */
return 0;
}
}
/* There was no previous "check_null" instruction for this value */
return 0;
}
/*@
* @deftypefun jit_value_t jit_insn_add (jit_function_t @var{func}, jit_value_t @var{value1}, jit_value_t @var{value2})
* Add two values together and return the result in a new temporary value.
* @end deftypefun
@*/
jit_value_t jit_insn_add
(jit_function_t func, jit_value_t value1, jit_value_t value2)
{
static jit_opcode_descr const add_descr = {
libjit/jit/jit-insn.c view on Meta::CPAN
}
value_labels->free_address = 1;
value_num_labels = jit_value_create_nint_constant(func, jit_type_uint, num_labels);
if(!value_num_labels)
{
_jit_value_free(value_labels);
return 0;
}
/* Add a new branch instruction */
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
jit_value_ref(func, value);
insn->opcode = JIT_OP_JUMP_TABLE;
insn->flags = JIT_INSN_DEST_IS_VALUE;
insn->dest = value;
insn->value1 = value_labels;
insn->value2 = value_num_labels;
/* Add a new block for the fall-through case */
return jit_insn_new_block(func);
}
/*@
* @deftypefun jit_value_t jit_insn_address_of (jit_function_t @var{func}, jit_value_t @var{value1})
* Get the address of a value into a new temporary.
* @end deftypefun
@*/
jit_value_t jit_insn_address_of(jit_function_t func, jit_value_t value1)
{
jit_type_t type;
jit_value_t result;
if(!value1)
{
return 0;
}
if(jit_value_is_constant(value1))
{
return 0;
}
type = jit_type_create_pointer(jit_value_get_type(value1), 1);
if(!type)
{
return 0;
}
jit_value_set_addressable(value1);
result = apply_unary(func, JIT_OP_ADDRESS_OF, value1, type);
jit_type_free(type);
return result;
}
/*@
* @deftypefun jit_value_t jit_insn_address_of_label (jit_function_t @var{func}, jit_label_t *@var{label})
* Get the address of @var{label} into a new temporary. This is typically
* used for exception handling, to track where in a function an exception
* was actually thrown.
* @end deftypefun
@*/
jit_value_t jit_insn_address_of_label(jit_function_t func, jit_label_t *label)
{
jit_value_t dest;
jit_insn_t insn;
if(!_jit_function_ensure_builder(func) || !label)
{
return 0;
}
if(*label == jit_label_undefined)
{
*label = (func->builder->next_label)++;
}
if(!_jit_block_record_label_flags(func, *label, JIT_LABEL_ADDRESS_OF))
{
return 0;
}
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return 0;
}
dest = jit_value_create(func, jit_type_void_ptr);
if(!dest)
{
return 0;
}
insn->opcode = (short)JIT_OP_ADDRESS_OF_LABEL;
insn->flags = JIT_INSN_VALUE1_IS_LABEL;
insn->dest = dest;
insn->value1 = (jit_value_t)(*label);
return dest;
}
/*
* Information about the opcodes for a particular conversion.
*/
typedef struct jit_convert_info
{
int cvt1;
jit_type_t type1;
int cvt2;
jit_type_t type2;
int cvt3;
jit_type_t type3;
} jit_convert_info_t;
#define CVT(opcode,name) opcode, (jit_type_t)&_jit_type_##name##_def
#define CVT_NONE 0, 0
/*
* Intrinsic equivalents for the conversion opcodes.
*/
typedef struct jit_convert_intrinsic
{
const char *name;
void *func;
jit_intrinsic_descr_t descr;
libjit/jit/jit-insn.c view on Meta::CPAN
CVT_INTRINSIC_CHECK(jit_long_to_uint_ovf, long, uint),
#endif
CVT_INTRINSIC(jit_long_to_uint, long, uint),
CVT_INTRINSIC(jit_int_to_long, int, long),
CVT_INTRINSIC(jit_uint_to_long, uint, long),
CVT_INTRINSIC_CHECK(jit_long_to_uint_ovf, long, uint),
CVT_INTRINSIC_CHECK(jit_long_to_int_ovf, long, int),
CVT_INTRINSIC_CHECK(jit_ulong_to_long_ovf, ulong, long),
CVT_INTRINSIC_CHECK(jit_long_to_ulong_ovf, long, ulong),
CVT_INTRINSIC(jit_float32_to_int, float32, int),
CVT_INTRINSIC(jit_float32_to_uint, float32, uint),
CVT_INTRINSIC(jit_float32_to_long, float32, long),
CVT_INTRINSIC(jit_float32_to_ulong, float32, ulong),
CVT_INTRINSIC_CHECK(jit_float32_to_int_ovf, float32, int),
CVT_INTRINSIC_CHECK(jit_float32_to_uint_ovf, float32, uint),
CVT_INTRINSIC_CHECK(jit_float32_to_long_ovf, float32, long),
CVT_INTRINSIC_CHECK(jit_float32_to_ulong_ovf, float32, ulong),
CVT_INTRINSIC(jit_int_to_float32, int, float32),
CVT_INTRINSIC(jit_uint_to_float32, uint, float32),
CVT_INTRINSIC(jit_long_to_float32, long, float32),
CVT_INTRINSIC(jit_ulong_to_float32, ulong, float32),
CVT_INTRINSIC(jit_float32_to_float64, float32, float64),
CVT_INTRINSIC(jit_float64_to_int, float64, int),
CVT_INTRINSIC(jit_float64_to_uint, float64, uint),
CVT_INTRINSIC(jit_float64_to_long, float64, long),
CVT_INTRINSIC(jit_float64_to_ulong, float64, ulong),
CVT_INTRINSIC_CHECK(jit_float64_to_int_ovf, float64, int),
CVT_INTRINSIC_CHECK(jit_float64_to_uint_ovf, float64, uint),
CVT_INTRINSIC_CHECK(jit_float64_to_long_ovf, float64, long),
CVT_INTRINSIC_CHECK(jit_float64_to_ulong_ovf, float64, ulong),
CVT_INTRINSIC(jit_int_to_float64, int, float64),
CVT_INTRINSIC(jit_uint_to_float64, uint, float64),
CVT_INTRINSIC(jit_long_to_float64, long, float64),
CVT_INTRINSIC(jit_ulong_to_float64, ulong, float64),
CVT_INTRINSIC(jit_float64_to_float32, float64, float32),
CVT_INTRINSIC(jit_nfloat_to_int, nfloat, int),
CVT_INTRINSIC(jit_nfloat_to_uint, nfloat, uint),
CVT_INTRINSIC(jit_nfloat_to_long, nfloat, long),
CVT_INTRINSIC(jit_nfloat_to_ulong, nfloat, ulong),
CVT_INTRINSIC_CHECK(jit_nfloat_to_int_ovf, nfloat, int),
CVT_INTRINSIC_CHECK(jit_nfloat_to_uint_ovf, nfloat, uint),
CVT_INTRINSIC_CHECK(jit_nfloat_to_long_ovf, nfloat, long),
CVT_INTRINSIC_CHECK(jit_nfloat_to_ulong_ovf, nfloat, ulong),
CVT_INTRINSIC(jit_int_to_nfloat, int, nfloat),
CVT_INTRINSIC(jit_uint_to_nfloat, uint, nfloat),
CVT_INTRINSIC(jit_long_to_nfloat, long, nfloat),
CVT_INTRINSIC(jit_ulong_to_nfloat, ulong, nfloat),
CVT_INTRINSIC(jit_nfloat_to_float32, nfloat, float32),
CVT_INTRINSIC(jit_nfloat_to_float64, nfloat, float64),
CVT_INTRINSIC(jit_float32_to_nfloat, float32, nfloat),
CVT_INTRINSIC(jit_float64_to_nfloat, float64, nfloat)
};
/*
* Apply a unary conversion operator.
*/
static jit_value_t apply_unary_conversion
(jit_function_t func, int oper, jit_value_t value1,
jit_type_t result_type)
{
/* Set the "may_throw" flag if the conversion may throw an exception */
if(convert_intrinsics[oper - 1].descr.ptr_result_type)
{
func->builder->may_throw = 1;
}
/* Bail out early if the conversion opcode is supported by the back end */
if(_jit_opcode_is_supported(oper))
{
return apply_unary(func, oper, value1, result_type);
}
/* Call the appropriate intrinsic method */
return jit_insn_call_intrinsic
(func, convert_intrinsics[oper - 1].name,
convert_intrinsics[oper - 1].func,
&(convert_intrinsics[oper - 1].descr), value1, 0);
}
/*@
* @deftypefun jit_value_t jit_insn_convert (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{type}, int @var{overflow_check})
* Convert the contents of a value into a new type, with optional
* overflow checking.
* @end deftypefun
@*/
jit_value_t jit_insn_convert(jit_function_t func, jit_value_t value,
jit_type_t type, int overflow_check)
{
jit_type_t vtype;
const jit_convert_info_t *opcode_map;
/* Bail out if we previously ran out of memory on this function */
if(!value)
{
return 0;
}
/* Normalize the source and destination types */
type = jit_type_normalize(type);
vtype = jit_type_normalize(value->type);
/* If the types are identical, then return the source value as-is */
if(type == vtype)
{
return value;
}
/* If the source is a constant, then perform a constant conversion.
If an overflow might result, we perform the computation at runtime */
if(jit_value_is_constant(value))
{
jit_constant_t const_value;
const_value = jit_value_get_constant(value);
if(jit_constant_convert(&const_value, &const_value,
type, overflow_check))
{
return jit_value_create_constant(func, &const_value);
}
}
/* Promote the source type, to reduce the number of cases in
the switch statement below */
vtype = jit_type_promote_int(vtype);
libjit/jit/jit-insn.c view on Meta::CPAN
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)
{
libjit/jit/jit-insn.c view on Meta::CPAN
unsigned int arg_num;
/* If we are performing a tail call, then duplicate the argument
values so that we don't accidentally destroy parameters in
situations like func(x, y) -> func(y, x) */
if((flags & JIT_CALL_TAIL) != 0 && num_args > 0)
{
new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
for(arg_num = 0; arg_num < num_args; ++arg_num)
{
value = args[arg_num];
if(value && value->is_parameter)
{
value = jit_insn_dup(func, value);
if(!value)
{
return 0;
}
}
new_args[arg_num] = value;
}
args = new_args;
}
/* If we are performing a tail call, then store back to our own parameters */
if((flags & JIT_CALL_TAIL) != 0)
{
for(arg_num = 0; arg_num < num_args; ++arg_num)
{
if(!jit_insn_store(func, jit_value_get_param(func, arg_num),
args[arg_num]))
{
return 0;
}
}
*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 */
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))
{
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;
libjit/jit/jit-insn.c view on Meta::CPAN
if(!create_note
(func, JIT_OP_RETURN_SMALL_STRUCT, value,
jit_value_create_nint_constant
(func, jit_type_nint,
(jit_nint)(jit_type_get_size(type)))))
{
break;
}
}
}
break;
default:
{
/* Everything else uses the normal return logic */
return jit_insn_return
(func, jit_insn_load_relative(func, value, 0, type));
}
/* Not reached */
}
/* Mark the current block as "ends in dead" */
func->builder->current_block->ends_in_dead = 1;
/* Start a new block just after the "return" instruction */
return jit_insn_new_block(func);
}
/*@
* @deftypefun int jit_insn_default_return (jit_function_t @var{func})
* Add an instruction to return a default value if control reaches this point.
* This is typically used at the end of a function to ensure that all paths
* return to the caller. Returns zero if out of memory, 1 if a default
* return was added, and 2 if a default return was not needed.
*
* Note: if this returns 1, but the function signature does not return
* @code{void}, then it indicates that a higher-level language error
* has occurred and the function should be abandoned.
* @end deftypefun
@*/
int jit_insn_default_return(jit_function_t func)
{
/* Ensure that we have a builder for this function */
if(!_jit_function_ensure_builder(func))
{
return 0;
}
/* If the last block ends in an unconditional branch, or is dead,
then we don't need to add a default return */
if(jit_block_current_is_dead(func))
{
return 2;
}
/* Add a simple "void" return to terminate the function */
return jit_insn_return(func, 0);
}
/*@
* @deftypefun int jit_insn_throw (jit_function_t @var{func}, jit_value_t @var{value})
* Throw a pointer @var{value} as an exception object. This can also
* be used to "rethrow" an object from a catch handler that is not
* interested in handling the exception.
* @end deftypefun
@*/
int jit_insn_throw(jit_function_t func, jit_value_t value)
{
if(!_jit_function_ensure_builder(func))
{
return 0;
}
func->builder->may_throw = 1;
func->builder->non_leaf = 1; /* May have to call out to throw */
if(!create_unary_note(func, JIT_OP_THROW, value))
{
return 0;
}
func->builder->current_block->ends_in_dead = 1;
return jit_insn_new_block(func);
}
/*@
* @deftypefun jit_value_t jit_insn_get_call_stack (jit_function_t @var{func})
* Get an object that represents the current position in the code,
* and all of the functions that are currently on the call stack.
* This is equivalent to calling @code{jit_exception_get_stack_trace},
* and is normally used just prior to @code{jit_insn_throw} to record
* the location of the exception that is being thrown.
* @end deftypefun
@*/
jit_value_t jit_insn_get_call_stack(jit_function_t func)
{
jit_type_t type;
jit_value_t value;
/* Create a signature prototype for "void *()" */
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void_ptr, 0, 0, 1);
if(!type)
{
return 0;
}
/* Call "jit_exception_get_stack_trace" to obtain the stack trace */
value = jit_insn_call_native
(func, "jit_exception_get_stack_trace",
(void *)jit_exception_get_stack_trace, type, 0, 0, 0);
/* Clean up and exit */
jit_type_free(type);
return value;
}
/*@
* @deftypefun jit_value_t jit_insn_thrown_exception (jit_function_t @var{func})
* Get the value that holds the most recent thrown exception. This is
* typically used in @code{catch} clauses.
* @end deftypefun
@*/
jit_value_t jit_insn_thrown_exception(jit_function_t func)
{
if(!_jit_function_ensure_builder(func))
{
return 0;
}
if(!(func->builder->thrown_exception))
{
func->builder->thrown_exception =
jit_value_create(func, jit_type_void_ptr);
}
return func->builder->thrown_exception;
}
/*
* Initialize the "setjmp" setup block that is needed to catch exceptions
* thrown back to this level of execution. The block looks like this:
*
* jit_jmp_buf jbuf;
* void *catcher;
*
* _jit_unwind_push_setjmp(&jbuf);
* if(setjmp(&jbuf.buf))
* {
* catch_pc = jbuf.catch_pc;
* if(catch_pc)
* {
* jbuf.catch_pc = 0;
* goto *catcher;
* }
* else
* {
* _jit_unwind_pop_and_rethrow();
* }
* }
*
* The field "jbuf.catch_pc" will be set to the address of the relevant
* "catch" block just before a subroutine call that may involve exceptions.
* It will be reset to NULL after such subroutine calls.
*
* Native back ends are responsible for outputting a call to the function
* "_jit_unwind_pop_setjmp()" just before "return" instructions if the
* "has_try" flag is set on the function.
*/
static int initialize_setjmp_block(jit_function_t func)
{
#if !defined(JIT_BACKEND_INTERP)
jit_label_t start_label = jit_label_undefined;
jit_label_t end_label = jit_label_undefined;
jit_label_t code_label = jit_label_undefined;
jit_label_t rethrow_label = jit_label_undefined;
jit_type_t type;
jit_value_t args[2];
jit_value_t value;
/* Bail out if we have already done this before */
if(func->builder->setjmp_value)
{
return 1;
}
func->builder->catcher_label = jit_label_undefined;
/* Force the start of a new block to mark the start of the init code */
if(!jit_insn_label(func, &start_label))
{
return 0;
}
/* Create a value to hold an item of type "jit_jmp_buf" */
type = jit_type_create_struct(0, 0, 1);
if(!type)
{
return 0;
}
jit_type_set_size_and_alignment
(type, sizeof(jit_jmp_buf), JIT_BEST_ALIGNMENT);
if((func->builder->setjmp_value = jit_value_create(func, type)) == 0)
{
jit_type_free(type);
return 0;
}
jit_type_free(type);
/* Call "_jit_unwind_push_setjmp" with "&setjmp_value" as its argument */
type = jit_type_void_ptr;
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, &type, 1, 1);
if(!type)
{
return 0;
}
args[0] = jit_insn_address_of(func, func->builder->setjmp_value);
jit_insn_call_native
(func, "_jit_unwind_push_setjmp",
(void *)_jit_unwind_push_setjmp, type, args, 1, JIT_CALL_NOTHROW);
jit_type_free(type);
/* Call "__sigsetjmp" or "setjmp" with "&setjmp_value" as its argument.
We prefer "__sigsetjmp" because it is least likely to be a macro */
#if defined(HAVE___SIGSETJMP) || defined(HAVE_SIGSETJMP)
{
jit_type_t params[2];
params[0] = jit_type_void_ptr;
params[1] = jit_type_sys_int;
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_int, params, 2, 1);
}
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))
{
return 0;
}
/* Move the initialization code to the head of the function so that
it is performed once upon entry to the function */
return jit_insn_move_blocks_to_start(func, start_label, end_label);
#else
/* The interpreter doesn't need the "setjmp" setup block */
func->builder->catcher_label = jit_label_undefined;
return 1;
#endif
}
/*@
* @deftypefun int jit_insn_uses_catcher (jit_function_t @var{func})
* Notify the function building process that @var{func} contains
* some form of @code{catch} clause for catching exceptions. This must
* be called before any instruction that is covered by a @code{try},
* ideally at the start of the function output process.
* @end deftypefun
@*/
int jit_insn_uses_catcher(jit_function_t func)
{
if(!_jit_function_ensure_builder(func))
{
return 0;
}
if(func->has_try)
{
return 1;
}
func->has_try = 1;
func->builder->may_throw = 1;
func->builder->non_leaf = 1;
return initialize_setjmp_block(func);
}
/*@
* @deftypefun jit_value_t jit_insn_start_catcher (jit_function_t @var{func})
* Start the catcher block for @var{func}. There should be exactly one
* catcher block for any function that involves a @code{try}. All
* exceptions that are thrown within the function will cause control
* to jump to this point. Returns a value that holds the exception
* that was thrown.
* @end deftypefun
@*/
jit_value_t jit_insn_start_catcher(jit_function_t func)
{
jit_value_t value;
#if !defined(JIT_BACKEND_INTERP)
jit_value_t last_exception;
jit_type_t type;
#endif
if(!_jit_function_ensure_builder(func))
{
return 0;
}
if(!jit_insn_label(func, &(func->builder->catcher_label)))
{
return 0;
}
value = jit_insn_thrown_exception(func);
if(!value)
{
return 0;
}
#if defined(JIT_BACKEND_INTERP)
/* In the interpreter, the exception object will be on the top of
the operand stack when control reaches the catcher */
if(!jit_insn_incoming_reg(func, value, 0))
{
return 0;
}
#else
type = jit_type_create_signature(jit_abi_cdecl, jit_type_void_ptr, 0, 0, 1);
if(!type)
{
return 0;
}
last_exception = jit_insn_call_native(
func, "jit_exception_get_last",
(void *)jit_exception_get_last, type, 0, 0, JIT_CALL_NOTHROW);
jit_insn_store(func, value, last_exception);
jit_type_free(type);
#endif
return value;
}
/*@
* @deftypefun int jit_insn_branch_if_pc_not_in_range (jit_function_t @var{func}, jit_label_t @var{start_label}, jit_label_t @var{end_label}, jit_label_t *@var{label})
* Branch to @var{label} if the program counter where an exception occurred
* does not fall between @var{start_label} and @var{end_label}.
* @end deftypefun
@*/
int jit_insn_branch_if_pc_not_in_range
(jit_function_t func, jit_label_t start_label,
jit_label_t end_label, jit_label_t *label)
{
jit_value_t value1;
jit_value_t value2;
/* Ensure that we have a function builder and a try block */
if(!_jit_function_ensure_builder(func))
{
return 0;
}
if(!(func->has_try))
{
return 0;
}
/* Flush any stack pops that were deferred previously */
if(!jit_insn_flush_defer_pop(func, 0))
{
return 0;
}
/* Get the location where the exception occurred in this function */
#if defined(JIT_BACKEND_INTERP)
value1 = create_dest_note
(func, JIT_OP_LOAD_EXCEPTION_PC, jit_type_void_ptr);
#else
value1 = func->builder->thrown_pc;
#endif
if(!value1)
{
return 0;
}
/* Compare the location against the start and end labels */
value2 = jit_insn_address_of_label(func, &start_label);
if(!value2)
{
return 0;
}
if(!jit_insn_branch_if(func, jit_insn_lt(func, value1, value2), label))
{
return 0;
}
value2 = jit_insn_address_of_label(func, &end_label);
if(!value2)
{
return 0;
}
if(!jit_insn_branch_if(func, jit_insn_ge(func, value1, value2), label))
{
return 0;
}
/* If control gets here, then we have a location match */
return 1;
}
/*@
* @deftypefun int jit_insn_rethrow_unhandled (jit_function_t @var{func})
* Rethrow the current exception because it cannot be handled by
* any of the @code{catch} blocks in the current function.
*
* Note: this is intended for use within catcher blocks. It should not
* be used to rethrow exceptions in response to programmer requests
* (e.g. @code{throw;} in C#). The @code{jit_insn_throw} function
* should be used for that purpose.
* @end deftypefun
@*/
int jit_insn_rethrow_unhandled(jit_function_t func)
{
jit_value_t value;
#if !defined(JIT_BACKEND_INTERP)
jit_type_t type;
#endif
/* Ensure that we have a function builder */
if(!_jit_function_ensure_builder(func))
{
return 0;
}
/* Get the current exception value to be thrown */
value = jit_insn_thrown_exception(func);
if(!value)
{
return 0;
}
#if defined(JIT_BACKEND_INTERP)
/* Rethrow the current exception (interpreter version) */
if(!create_unary_note(func, JIT_OP_RETHROW, value))
{
return 0;
}
#else /* !JIT_BACKEND_INTERP */
/* Call "_jit_unwind_pop_setjmp" to remove the current exception catcher */
if(!_jit_function_ensure_builder(func))
{
return 0;
}
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);
/* Call the "jit_exception_throw" function to effect the rethrow */
type = jit_type_void_ptr;
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, &type, 1, 1);
if(!type)
{
return 0;
}
jit_insn_call_native
(func, "jit_exception_throw",
(void *)jit_exception_throw, type, &value, 1,
JIT_CALL_NOTHROW | JIT_CALL_NORETURN);
jit_type_free(type);
#endif /* !JIT_BACKEND_INTERP */
/* The current block ends in dead and we need to start a new block */
func->builder->current_block->ends_in_dead = 1;
return jit_insn_new_block(func);
}
/*@
* @deftypefun int jit_insn_start_finally (jit_function_t @var{func}, jit_label_t *@var{finally_label})
* Start a @code{finally} clause.
* @end deftypefun
@*/
int jit_insn_start_finally(jit_function_t func, jit_label_t *finally_label)
{
if(!jit_insn_label(func, finally_label))
{
return 0;
}
return create_noarg_note(func, JIT_OP_ENTER_FINALLY);
}
/*@
* @deftypefun int jit_insn_return_from_finally (jit_function_t @var{func})
* Return from the @code{finally} clause to where it was called from.
* This is usually the last instruction in a @code{finally} clause.
* @end deftypefun
@*/
int jit_insn_return_from_finally(jit_function_t func)
{
/* Flush any deferred stack pops before we return */
if(!jit_insn_flush_defer_pop(func, 0))
{
return 0;
}
/* Mark the end of the "finally" clause */
if(!create_noarg_note(func, JIT_OP_LEAVE_FINALLY))
{
return 0;
}
/* The current block ends in a dead instruction */
func->builder->current_block->ends_in_dead = 1;
/* Create a new block for the following code */
return jit_insn_new_block(func);
}
/*@
* @deftypefun int jit_insn_call_finally (jit_function_t @var{func}, jit_label_t *@var{finally_label})
* Call a @code{finally} clause.
* @end deftypefun
@*/
int jit_insn_call_finally(jit_function_t func, jit_label_t *finally_label)
{
jit_insn_t insn;
( run in 0.783 second using v1.01-cache-2.11-cpan-d7f47b0818f )