Alien-LibJIT

 view release on metacpan or  search on metacpan

libjit/jit/jit-insn.c  view on Meta::CPAN

 * Apply a binary operator.
 */
static jit_value_t apply_binary
		(jit_function_t func, int oper, jit_value_t value1,
		 jit_value_t value2, jit_type_t result_type)
{
	jit_value_t dest;
	jit_insn_t insn;
	if(!value1 || !value2)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	dest = jit_value_create(func, result_type);
	if(!dest)
	{
		return 0;
	}
	jit_value_ref(func, value1);
	jit_value_ref(func, value2);
	insn->opcode = (short)oper;
	insn->dest = dest;
	insn->value1 = value1;
	insn->value2 = value2;
	return dest;
}

/*
 * Apply a ternary operator.
 */
static int apply_ternary
		(jit_function_t func, int oper, jit_value_t value1,
		 jit_value_t value2, jit_value_t value3)
{
	jit_insn_t insn;
	if(!value1 || !value2 || !value3)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, value1);
	jit_value_ref(func, value2);
	jit_value_ref(func, value3);
	insn->opcode = (short)oper;
	insn->flags = JIT_INSN_DEST_IS_VALUE;
	insn->dest = value1;
	insn->value1 = value2;
	insn->value2 = value3;
	return 1;
}

/*
 * Create a note instruction, which doesn't have a result.
 */
static int create_note
		(jit_function_t func, int oper, jit_value_t value1,
		 jit_value_t value2)
{
	jit_insn_t insn;
	if(!value1 || !value2)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, value1);
	jit_value_ref(func, value2);
	insn->opcode = (short)oper;
	insn->value1 = value1;
	insn->value2 = value2;
	return 1;
}

/*
 * Create a unary note instruction, which doesn't have a result.
 */
static int create_unary_note
		(jit_function_t func, int oper, jit_value_t value1)
{
	jit_insn_t insn;
	if(!value1)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, value1);
	insn->opcode = (short)oper;
	insn->value1 = value1;
	return 1;
}

libjit/jit/jit-insn.c  view on Meta::CPAN

	/* Bail out if the parameters are invalid */
	if(!value1)
	{
		return 0;
	}

	type = jit_type_normalize(value1->type);
	if(type == jit_type_float32)
	{
		oper = descr->foper;
	}
	else if(type == jit_type_float64)
	{
		oper = descr->doper;
	}
	else if(type == jit_type_nfloat)
	{
		oper = descr->nfoper;
	}
	else
	{
		/* if the value is not a float then the result is false */
		return jit_value_create_nint_constant(func, jit_type_int, 0);
	}

	if(_jit_opcode_is_supported(oper))
	{
		return apply_unary(func, oper, value1, jit_type_int);
	}
	else
	{
		return apply_intrinsic(func, descr, value1, 0, type);
	}
}

/*@
 * @deftypefun int jit_insn_get_opcode (jit_insn_t @var{insn})
 * Get the opcode that is associated with an instruction.
 * @end deftypefun
@*/
int jit_insn_get_opcode(jit_insn_t insn)
{
	if(insn)
	{
		return insn->opcode;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_value_t jit_insn_get_dest (jit_insn_t @var{insn})
 * Get the destination value that is associated with an instruction.
 * Returns NULL if the instruction does not have a destination.
 * @end deftypefun
@*/
jit_value_t jit_insn_get_dest(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
	{
		return insn->dest;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_value_t jit_insn_get_value1 (jit_insn_t @var{insn})
 * Get the first argument value that is associated with an instruction.
 * Returns NULL if the instruction does not have a first argument value.
 * @end deftypefun
@*/
jit_value_t jit_insn_get_value1(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
	{
		return insn->value1;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_value_t jit_insn_get_value2 (jit_insn_t @var{insn})
 * Get the second argument value that is associated with an instruction.
 * Returns NULL if the instruction does not have a second argument value.
 * @end deftypefun
@*/
jit_value_t jit_insn_get_value2(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
	{
		return insn->value2;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_label_t jit_insn_get_label (jit_insn_t @var{insn})
 * Get the label for a branch target from an instruction.
 * Returns NULL if the instruction does not have a branch target.
 * @end deftypefun
@*/
jit_label_t jit_insn_get_label(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_DEST_IS_LABEL) != 0)
	{
		return (jit_label_t)(insn->dest);
	}
	else if(insn && (insn->flags & JIT_INSN_VALUE1_IS_LABEL) != 0)
	{
		/* "address_of_label" instruction */
		return (jit_label_t)(insn->value1);
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_function_t jit_insn_get_function (jit_insn_t @var{insn})
 * Get the function for a call instruction.  Returns NULL if the
 * instruction does not refer to a called function.
 * @end deftypefun
@*/
jit_function_t jit_insn_get_function(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_DEST_IS_FUNCTION) != 0)
	{
		return (jit_function_t)(insn->dest);
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun {void *} jit_insn_get_native (jit_insn_t @var{insn})
 * Get the function pointer for a native call instruction.
 * Returns NULL if the instruction does not refer to a native
 * function call.
 * @end deftypefun
@*/
void *jit_insn_get_native(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_DEST_IS_NATIVE) != 0)
	{
		return (void *)(insn->dest);
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun {const char *} jit_insn_get_name (jit_insn_t @var{insn})
 * Get the diagnostic name for a function call.  Returns NULL
 * if the instruction does not have a diagnostic name.
 * @end deftypefun
@*/
const char *jit_insn_get_name(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_VALUE1_IS_NAME) != 0)
	{
		return (const char *)(insn->value1);
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun jit_type_t jit_insn_get_signature (jit_insn_t @var{insn})
 * Get the signature for a function call instruction.  Returns NULL
 * if the instruction is not a function call.
 * @end deftypefun
@*/
jit_type_t jit_insn_get_signature(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_VALUE2_IS_SIGNATURE) != 0)
	{
		return (jit_type_t)(insn->value2);
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun int jit_insn_dest_is_value (jit_insn_t @var{insn})
 * Returns a non-zero value if the destination for @var{insn} is
 * actually a source value.  This can happen with instructions
 * such as @code{jit_insn_store_relative} where the instruction
 * needs three source operands, and the real destination is a
 * side-effect on one of the sources.
 * @end deftypefun
@*/
int jit_insn_dest_is_value(jit_insn_t insn)
{
	if(insn && (insn->flags & JIT_INSN_DEST_IS_VALUE) != 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*@
 * @deftypefun void jit_insn_label (jit_function_t @var{func}, jit_label_t *@var{label})
 * Start a new basic block within the function @var{func} and give it the
 * specified @var{label}.  If the call is made when a new block was just
 * created by any previous call then that block is reused, no new block
 * is created.  Returns zero if out of memory.
 *
 * If the contents of @var{label} are @code{jit_label_undefined}, then this
 * function will allocate a new label for this block.  Otherwise it will
 * reuse the specified label from a previous branch instruction.
 * @end deftypefun
@*/
int
jit_insn_label(jit_function_t func, jit_label_t *label)
{
	jit_block_t block;

	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Create a new block if the current one is not empty */
	block = func->builder->current_block;
	if(_jit_block_get_last(block))
	{
		block = _jit_block_create(func);
		if(!block)
		{
			return 0;
		}
	}

	/* Record the label */
	if(*label == jit_label_undefined)
	{
		*label = (func->builder->next_label)++;
	}
	if(!_jit_block_record_label(block, *label))
	{
		_jit_block_destroy(block);
		return 0;
	}

	/* If the block is newly created then insert it to the end of
	   the function's block list */
	if(block != func->builder->current_block)

libjit/jit/jit-insn.c  view on Meta::CPAN

 * 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;
					}
				}

				*plast = last;
				return insn;
			}

			/* Oops. This instruction modifies "value" and blocks
			   any previous address_of or add_relative instructions */
			if((insn->flags & JIT_INSN_DEST_IS_VALUE) == 0)
			{
				break;
			}
		}

		/* We are to check instructions that preceed the last one */
		last = 0;
	}
	return 0;
}

/*@
 * @deftypefun jit_value_t jit_insn_load_relative (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{offset}, jit_type_t @var{type})
 * Load a value of the specified @var{type} from the effective address
 * @code{(@var{value} + @var{offset})}, where @var{value} is a pointer.
 * @end deftypefun
@*/
jit_value_t jit_insn_load_relative
		(jit_function_t func, jit_value_t value,
		 jit_nint offset, jit_type_t type)
{
	jit_insn_iter_t iter;
	jit_insn_t insn;
	int last;

	if(!value)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	jit_insn_iter_init_last(&iter, func->builder->current_block);
	insn = find_base_insn(func, iter, value, &last);
	if(insn && insn->opcode == JIT_OP_ADD_RELATIVE)
	{
		/* We have a previous "add_relative" instruction for this
		   pointer. Adjust the current offset accordingly */
		offset += jit_value_get_nint_constant(insn->value2);
		value = insn->value1;
		insn = find_base_insn(func, iter, value, &last);
		last = 0;
	}
	if(insn && insn->opcode == JIT_OP_ADDRESS_OF && !last)
	{
		/* Shift the "address_of" instruction down, to make
		   it easier for the code generator to handle field
		   accesses within local and global variables */
		value = jit_insn_address_of(func, insn->value1);
		if(!value)
		{
			return 0;
		}
	}
	return apply_binary
		(func, _jit_load_opcode(JIT_OP_LOAD_RELATIVE_SBYTE, type, 0, 0), value,
		 jit_value_create_nint_constant(func, jit_type_nint, offset), type);
}

libjit/jit/jit-insn.c  view on Meta::CPAN

/*@
 * @deftypefun int jit_insn_store_relative (jit_function_t @var{func}, jit_value_t @var{dest}, jit_nint @var{offset}, jit_value_t @var{value})
 * Store @var{value} at the effective address @code{(@var{dest} + @var{offset})},
 * where @var{dest} is a pointer.
 * @end deftypefun
@*/
int jit_insn_store_relative
		(jit_function_t func, jit_value_t dest,
		 jit_nint offset, jit_value_t value)
{
	jit_insn_iter_t iter;
	jit_insn_t insn;
	int last;
	jit_value_t offset_value;

	if(!dest || !value)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	jit_insn_iter_init_last(&iter, func->builder->current_block);
	insn = find_base_insn(func, iter, dest, &last);
	if(insn && insn->opcode == JIT_OP_ADD_RELATIVE)
	{
		/* We have a previous "add_relative" instruction for this
		   pointer. Adjust the current offset accordingly */
		offset += jit_value_get_nint_constant(insn->value2);
		dest = insn->value1;
		insn = find_base_insn(func, iter, value, &last);
		last = 0;
	}
	if(insn && insn->opcode == JIT_OP_ADDRESS_OF && !last)
	{
		/* Shift the "address_of" instruction down, to make
		   it easier for the code generator to handle field
		   accesses within local and global variables */
		dest = jit_insn_address_of(func, insn->value1);
		if(!dest)
		{
			return 0;
		}
	}

	offset_value = jit_value_create_nint_constant(func, jit_type_nint, offset);
	if(!offset_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_STORE_RELATIVE_BYTE, 0, value->type);
	insn->flags = JIT_INSN_DEST_IS_VALUE;
	insn->dest = dest;
	insn->value1 = value;
	insn->value2 = offset_value;
	return 1;
}

/*@
 * @deftypefun jit_value_t jit_insn_add_relative (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{offset})
 * Add the constant @var{offset} to the specified pointer @var{value}.
 * This is functionally identical to calling @code{jit_insn_add}, but
 * the JIT can optimize the code better if it knows that the addition
 * is being used to perform a relative adjustment on a pointer.
 * In particular, multiple relative adjustments on the same pointer
 * can be collapsed into a single adjustment.
 * @end deftypefun
@*/
jit_value_t jit_insn_add_relative
		(jit_function_t func, jit_value_t value, jit_nint offset)
{
	jit_insn_iter_t iter;
	jit_insn_t insn;
	int last;

	if(!value)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	jit_insn_iter_init_last(&iter, func->builder->current_block);
	insn = find_base_insn(func, iter, value, &last);
	if(insn && insn->opcode == JIT_OP_ADD_RELATIVE)
	{
		/* We have a previous "add_relative" instruction for this
		   pointer. Adjust the current offset accordingly */
		offset += jit_value_get_nint_constant(insn->value2);
		value = insn->value1;
	}

	return apply_binary(func, JIT_OP_ADD_RELATIVE, value,
			    jit_value_create_nint_constant(func, jit_type_nint, offset),
			    jit_type_void_ptr);
}

/*@
 * @deftypefun jit_value_t jit_insn_load_elem (jit_function_t @var{func}, jit_value_t @var{base_addr}, jit_value_t @var{index}, jit_type_t @var{elem_type})
 * Load an element of type @var{elem_type} from position @var{index} within
 * the array starting at @var{base_addr}.  The effective address of the
 * array element is @code{@var{base_addr} + @var{index} * sizeof(@var{elem_type})}.
 * @end deftypefun
@*/
jit_value_t jit_insn_load_elem
	(jit_function_t func, jit_value_t base_addr,
	 jit_value_t index, jit_type_t elem_type)
{
	jit_nint size;
	int opcode;

libjit/jit/jit-insn.c  view on Meta::CPAN

		descr = &descr_i_f;
	}
	else if(result_type == jit_type_float64)
	{
		oper = JIT_OP_DSIGN;
		intrinsic = (void *)jit_float64_sign;
		name = "jit_float64_sign";
		descr = &descr_i_d;
	}
	else
	{
		oper = JIT_OP_NFSIGN;
		intrinsic = (void *)jit_nfloat_sign;
		name = "jit_nfloat_sign";
		descr = &descr_i_D;
	}

	value1 = jit_insn_convert(func, value1, result_type, 0);
	if(_jit_opcode_is_supported(oper))
	{
		return apply_unary(func, oper, value1, jit_type_int);
	}
	else
	{
		return jit_insn_call_intrinsic
			(func, name, intrinsic, descr, value1, 0);
	}
}

/*@
 * @deftypefun int jit_insn_branch (jit_function_t @var{func}, jit_label_t *@var{label})
 * Terminate the current block by branching unconditionally
 * to a specific label.  Returns zero if out of memory.
 * @end deftypefun
@*/
int jit_insn_branch(jit_function_t func, jit_label_t *label)
{
	jit_insn_t insn;
	if(!label)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	if(*label == jit_label_undefined)
	{
		*label = (func->builder->next_label)++;
	}
	insn->opcode = (short)JIT_OP_BR;
	insn->flags = JIT_INSN_DEST_IS_LABEL;
	insn->dest = (jit_value_t)(*label);
	func->builder->current_block->ends_in_dead = 1;
	return jit_insn_new_block(func);
}

/*@
 * @deftypefun int jit_insn_branch_if (jit_function_t @var{func}, jit_value_t @var{value}, jit_label_t *@var{label})
 * Terminate the current block by branching to a specific label if
 * the specified value is non-zero.  Returns zero if out of memory.
 *
 * If @var{value} refers to a conditional expression that was created
 * by @code{jit_insn_eq}, @code{jit_insn_ne}, etc, then the conditional
 * expression will be replaced by an appropriate conditional branch
 * instruction.
 * @end deftypefun
@*/
int jit_insn_branch_if
		(jit_function_t func, jit_value_t value, jit_label_t *label)
{
	jit_insn_t insn;
	jit_insn_t prev;
	jit_block_t block;
	jit_type_t type;
	int opcode;
	jit_value_t value1;
	jit_value_t value2;

	/* Bail out if the parameters are invalid */
	if(!value || !label)
	{
		return 0;
	}

	/* Ensure that we have a function builder */
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	/* Flush any stack pops that were deferred previously */
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Allocate a new label identifier, if necessary */
	if(*label == jit_label_undefined)
	{
		*label = (func->builder->next_label)++;
	}

	/* If the condition is constant, then convert it into either
	   an unconditional branch or a fall-through, as appropriate */
	if(jit_value_is_constant(value))
	{
		if(jit_value_is_true(value))
		{
			return jit_insn_branch(func, label);
		}
		else

libjit/jit/jit-insn.c  view on Meta::CPAN

				case JIT_OP_IGT:		opcode = JIT_OP_BR_IGT;      break;
				case JIT_OP_IGT_UN:		opcode = JIT_OP_BR_IGT_UN;   break;
				case JIT_OP_IGE:		opcode = JIT_OP_BR_IGE;      break;
				case JIT_OP_IGE_UN:		opcode = JIT_OP_BR_IGE_UN;   break;
				case JIT_OP_LEQ:		opcode = JIT_OP_BR_LEQ;      break;
				case JIT_OP_LNE:		opcode = JIT_OP_BR_LNE;      break;
				case JIT_OP_LLT:		opcode = JIT_OP_BR_LLT;      break;
				case JIT_OP_LLT_UN:		opcode = JIT_OP_BR_LLT_UN;   break;
				case JIT_OP_LLE:		opcode = JIT_OP_BR_LLE;      break;
				case JIT_OP_LLE_UN:		opcode = JIT_OP_BR_LLE_UN;   break;
				case JIT_OP_LGT:		opcode = JIT_OP_BR_LGT;      break;
				case JIT_OP_LGT_UN:		opcode = JIT_OP_BR_LGT_UN;   break;
				case JIT_OP_LGE:		opcode = JIT_OP_BR_LGE;      break;
				case JIT_OP_LGE_UN:		opcode = JIT_OP_BR_LGE_UN;   break;
				case JIT_OP_FEQ:		opcode = JIT_OP_BR_FEQ;      break;
				case JIT_OP_FNE:		opcode = JIT_OP_BR_FNE;      break;
				case JIT_OP_FLT:		opcode = JIT_OP_BR_FLT;      break;
				case JIT_OP_FLE:		opcode = JIT_OP_BR_FLE;      break;
				case JIT_OP_FGT:		opcode = JIT_OP_BR_FGT;      break;
				case JIT_OP_FGE:		opcode = JIT_OP_BR_FGE;      break;
				case JIT_OP_FLT_INV:	opcode = JIT_OP_BR_FLT_INV;  break;
				case JIT_OP_FLE_INV:	opcode = JIT_OP_BR_FLE_INV;  break;
				case JIT_OP_FGT_INV:	opcode = JIT_OP_BR_FGT_INV;  break;
				case JIT_OP_FGE_INV:	opcode = JIT_OP_BR_FGE_INV;  break;
				case JIT_OP_DEQ:		opcode = JIT_OP_BR_DEQ;      break;
				case JIT_OP_DNE:		opcode = JIT_OP_BR_DNE;      break;
				case JIT_OP_DLT:		opcode = JIT_OP_BR_DLT;      break;
				case JIT_OP_DLE:		opcode = JIT_OP_BR_DLE;      break;
				case JIT_OP_DGT:		opcode = JIT_OP_BR_DGT;      break;
				case JIT_OP_DGE:		opcode = JIT_OP_BR_DGE;      break;
				case JIT_OP_DLT_INV:	opcode = JIT_OP_BR_DLT_INV;  break;
				case JIT_OP_DLE_INV:	opcode = JIT_OP_BR_DLE_INV;  break;
				case JIT_OP_DGT_INV:	opcode = JIT_OP_BR_DGT_INV;  break;
				case JIT_OP_DGE_INV:	opcode = JIT_OP_BR_DGE_INV;  break;
				case JIT_OP_NFEQ:		opcode = JIT_OP_BR_NFEQ;     break;
				case JIT_OP_NFNE:		opcode = JIT_OP_BR_NFNE;     break;
				case JIT_OP_NFLT:		opcode = JIT_OP_BR_NFLT;     break;
				case JIT_OP_NFLE:		opcode = JIT_OP_BR_NFLE;     break;
				case JIT_OP_NFGT:		opcode = JIT_OP_BR_NFGT;     break;
				case JIT_OP_NFGE:		opcode = JIT_OP_BR_NFGE;     break;
				case JIT_OP_NFLT_INV:	opcode = JIT_OP_BR_NFLT_INV; break;
				case JIT_OP_NFLE_INV:	opcode = JIT_OP_BR_NFLE_INV; break;
				case JIT_OP_NFGT_INV:	opcode = JIT_OP_BR_NFGT_INV; break;
				case JIT_OP_NFGE_INV:	opcode = JIT_OP_BR_NFGE_INV; break;
			}
			/* Add a new branch instruction */
			/* Save the values from the previous insn because *prev might
			   become invalid if the call to _jit_block_add_insn triggers
			   a reallocation of the insns array. */
			value1 = prev->value1;
			value2 = prev->value2;
			insn = _jit_block_add_insn(func->builder->current_block);
			if(!insn)
			{
				return 0;
			}

			jit_value_ref(func, value1);
			jit_value_ref(func, value2);
			insn->opcode = (short)opcode;
			insn->flags = JIT_INSN_DEST_IS_LABEL;
			insn->dest = (jit_value_t)(*label);
			insn->value1 = value1;
			insn->value2 = value2;
			goto add_block;
		}
	}

	/* Coerce the result to something comparable and determine the opcode */
	type = jit_type_promote_int(jit_type_normalize(value->type));
	if(type == jit_type_int || type == jit_type_uint)
	{
		opcode = JIT_OP_BR_ITRUE;
		value2 = 0;
	}
	else if(type == jit_type_long || type == jit_type_ulong)
	{
		opcode = JIT_OP_BR_LTRUE;
		value2 = 0;
	}
	else if(type == jit_type_float32)
	{
		opcode = JIT_OP_BR_FNE;
		value2 = jit_value_create_float32_constant
			(func, jit_type_float32, (jit_float32)0.0);
		if(!value2)
		{
			return 0;
		}
	}
	else if(type == jit_type_float64)
	{
		opcode = JIT_OP_BR_DNE;
		value2 = jit_value_create_float64_constant
			(func, jit_type_float64, (jit_float64)0.0);
		if(!value2)
		{
			return 0;
		}
	}
	else
	{
		type = jit_type_nfloat;
		opcode = JIT_OP_BR_NFNE;
		value2 = jit_value_create_nfloat_constant
			(func, jit_type_nfloat, (jit_nfloat)0.0);
		if(!value2)
		{
			return 0;
		}
	}
	value = jit_insn_convert(func, value, type, 0);
	if(!value)
	{
		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);
	jit_value_ref(func, value2);
	insn->opcode = (short)opcode;
	insn->flags = JIT_INSN_DEST_IS_LABEL;
	insn->dest = (jit_value_t)(*label);
	insn->value1 = value;
	insn->value2 = value2;

add_block:
	/* Add a new block for the fall-through case */
	return jit_insn_new_block(func);
}

/*@
 * @deftypefun int jit_insn_branch_if_not (jit_function_t @var{func}, jit_value_t @var{value}, jit_label_t *@var{label})
 * Terminate the current block by branching to a specific label if
 * the specified value is zero.  Returns zero if out of memory.
 *
 * If @var{value} refers to a conditional expression that was created
 * by @code{jit_insn_eq}, @code{jit_insn_ne}, etc, then the conditional
 * expression will be followed by an appropriate conditional branch
 * instruction, instead of a value load.
 * @end deftypefun
@*/
int jit_insn_branch_if_not
		(jit_function_t func, jit_value_t value, jit_label_t *label)
{
	jit_insn_t insn;
	jit_insn_t prev;
	jit_block_t block;
	jit_type_t type;
	int opcode;
	jit_value_t value1;
	jit_value_t value2;

	/* Bail out if the parameters are invalid */
	if(!value || !label)
	{
		return 0;
	}

	/* Ensure that we have a function builder */
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	/* Flush any stack pops that were deferred previously */
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Allocate a new label identifier, if necessary */
	if(*label == jit_label_undefined)
	{
		*label = (func->builder->next_label)++;
	}

	/* If the condition is constant, then convert it into either
	   an unconditional branch or a fall-through, as appropriate */
	if(jit_value_is_constant(value))
	{
		if(!jit_value_is_true(value))

libjit/jit/jit-insn.c  view on Meta::CPAN

				case JIT_OP_IGT_UN:		opcode = JIT_OP_BR_ILE_UN;   break;
				case JIT_OP_IGE:		opcode = JIT_OP_BR_ILT;      break;
				case JIT_OP_IGE_UN:		opcode = JIT_OP_BR_ILT_UN;   break;
				case JIT_OP_LEQ:		opcode = JIT_OP_BR_LNE;      break;
				case JIT_OP_LNE:		opcode = JIT_OP_BR_LEQ;      break;
				case JIT_OP_LLT:		opcode = JIT_OP_BR_LGE;      break;
				case JIT_OP_LLT_UN:		opcode = JIT_OP_BR_LGE_UN;   break;
				case JIT_OP_LLE:		opcode = JIT_OP_BR_LGT;      break;
				case JIT_OP_LLE_UN:		opcode = JIT_OP_BR_LGT_UN;   break;
				case JIT_OP_LGT:		opcode = JIT_OP_BR_LLE;      break;
				case JIT_OP_LGT_UN:		opcode = JIT_OP_BR_LLE_UN;   break;
				case JIT_OP_LGE:		opcode = JIT_OP_BR_LLT;      break;
				case JIT_OP_LGE_UN:		opcode = JIT_OP_BR_LLT_UN;   break;
				case JIT_OP_FEQ:		opcode = JIT_OP_BR_FNE;      break;
				case JIT_OP_FNE:		opcode = JIT_OP_BR_FEQ;      break;
				case JIT_OP_FLT:		opcode = JIT_OP_BR_FGE_INV;  break;
				case JIT_OP_FLE:		opcode = JIT_OP_BR_FGT_INV;  break;
				case JIT_OP_FGT:		opcode = JIT_OP_BR_FLE_INV;  break;
				case JIT_OP_FGE:		opcode = JIT_OP_BR_FLT_INV;  break;
				case JIT_OP_FLT_INV:	opcode = JIT_OP_BR_FGE;      break;
				case JIT_OP_FLE_INV:	opcode = JIT_OP_BR_FGT;      break;
				case JIT_OP_FGT_INV:	opcode = JIT_OP_BR_FLE;      break;
				case JIT_OP_FGE_INV:	opcode = JIT_OP_BR_FLT;      break;
				case JIT_OP_DEQ:		opcode = JIT_OP_BR_DNE;      break;
				case JIT_OP_DNE:		opcode = JIT_OP_BR_DEQ;      break;
				case JIT_OP_DLT:		opcode = JIT_OP_BR_DGE_INV;  break;
				case JIT_OP_DLE:		opcode = JIT_OP_BR_DGT_INV;  break;
				case JIT_OP_DGT:		opcode = JIT_OP_BR_DLE_INV;  break;
				case JIT_OP_DGE:		opcode = JIT_OP_BR_DLT_INV;  break;
				case JIT_OP_DLT_INV:	opcode = JIT_OP_BR_DGE;      break;
				case JIT_OP_DLE_INV:	opcode = JIT_OP_BR_DGT;      break;
				case JIT_OP_DGT_INV:	opcode = JIT_OP_BR_DLE;      break;
				case JIT_OP_DGE_INV:	opcode = JIT_OP_BR_DLT;      break;
				case JIT_OP_NFEQ:		opcode = JIT_OP_BR_NFNE;     break;
				case JIT_OP_NFNE:		opcode = JIT_OP_BR_NFEQ;     break;
				case JIT_OP_NFLT:		opcode = JIT_OP_BR_NFGE_INV; break;
				case JIT_OP_NFLE:		opcode = JIT_OP_BR_NFGT_INV; break;
				case JIT_OP_NFGT:		opcode = JIT_OP_BR_NFLE_INV; break;
				case JIT_OP_NFGE:		opcode = JIT_OP_BR_NFLT_INV; break;
				case JIT_OP_NFLT_INV:	opcode = JIT_OP_BR_NFGE;     break;
				case JIT_OP_NFLE_INV:	opcode = JIT_OP_BR_NFGT;     break;
				case JIT_OP_NFGT_INV:	opcode = JIT_OP_BR_NFLE;     break;
				case JIT_OP_NFGE_INV:	opcode = JIT_OP_BR_NFLT;     break;
			}

			/* Add a new branch instruction */
			/* Save the values from the previous insn because *prev might
			   become invalid if the call to _jit_block_add_insn triggers
			   a reallocation of the insns array. */
			value1 = prev->value1;
			value2 = prev->value2;
			insn = _jit_block_add_insn(func->builder->current_block);
			if(!insn)
			{
				return 0;
			}

			jit_value_ref(func, value1);
			jit_value_ref(func, value2);
			insn->opcode = (short)opcode;
			insn->flags = JIT_INSN_DEST_IS_LABEL;
			insn->dest = (jit_value_t)(*label);
			insn->value1 = value1;
			insn->value2 = value2;
			goto add_block;
		}
	}

	/* Coerce the result to something comparable and determine the opcode */
	type = jit_type_promote_int(jit_type_normalize(value->type));
	if(type == jit_type_int || type == jit_type_uint)
	{
		opcode = JIT_OP_BR_IFALSE;
		value2 = 0;
	}
	else if(type == jit_type_long || type == jit_type_ulong)
	{
		opcode = JIT_OP_BR_LFALSE;
		value2 = 0;
	}
	else if(type == jit_type_float32)
	{
		opcode = JIT_OP_BR_FEQ;
		value2 = jit_value_create_float32_constant
			(func, jit_type_float32, (jit_float32)0.0);
		if(!value2)
		{
			return 0;
		}
	}
	else if(type == jit_type_float64)
	{
		opcode = JIT_OP_BR_DEQ;
		value2 = jit_value_create_float64_constant
			(func, jit_type_float64, (jit_float64)0.0);
		if(!value2)
		{
			return 0;
		}
	}
	else
	{
		type = jit_type_nfloat;
		opcode = JIT_OP_BR_NFEQ;
		value2 = jit_value_create_nfloat_constant
			(func, jit_type_nfloat, (jit_nfloat)0.0);
		if(!value2)
		{
			return 0;
		}
	}
	value = jit_insn_convert(func, value, type, 0);
	if(!value)
	{
		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);
	jit_value_ref(func, value2);
	insn->opcode = (short)opcode;
	insn->flags = JIT_INSN_DEST_IS_LABEL;
	insn->dest = (jit_value_t)(*label);
	insn->value1 = value;
	insn->value2 = value2;

add_block:
	/* Add a new block for the fall-through case */
	return jit_insn_new_block(func);
}

/*@
 * @deftypefun int jit_insn_jump_table (jit_function_t @var{func}, jit_value_t @var{value}, jit_label_t *@var{labels}, unsigned int @var{num_labels})
 * Branch to a label from the @var{labels} table. The @var{value} is the
 * index of the label. It is allowed to have identical labels in the table.
 * If an entry in the table has @code{jit_label_undefined} value then it is
 * replaced with a newly allocated label.
 * @end deftypefun
@*/
int jit_insn_jump_table
	(jit_function_t func, jit_value_t value,
	 jit_label_t *labels, unsigned int num_labels)
{
	jit_insn_t insn;
	unsigned int index;
	jit_label_t *new_labels;
	jit_value_t value_labels;
	jit_value_t value_num_labels;

	/* Bail out if the parameters are invalid */
	if(!value || !labels || !num_labels)
	{
		return 0;
	}

	/* Ensure that we have a function builder */
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	/* Flush any stack pops that were deferred previously */
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Allocate new label identifiers, if necessary */
	for(index = 0; index < num_labels; index++)
	{
		if(labels[index] == jit_label_undefined)
		{
			labels[index] = (func->builder->next_label)++;
		}
	}

	/* If the condition is constant, then convert it into either
	   an unconditional branch or a fall-through, as appropriate */
	if(jit_value_is_constant(value))
	{
		index = jit_value_get_nint_constant(value);
		if(index < num_labels && index >= 0)
		{
			return jit_insn_branch(func, &labels[index]);
		}
		else
		{
			return 1;
		}
	}

	new_labels = jit_malloc(num_labels * sizeof(jit_label_t));
	if(!new_labels)
	{
		return 0;
	}
	for(index = 0; index < num_labels; index++)
	{
		new_labels[index] = labels[index];
	}

	value_labels = jit_value_create_nint_constant(func, jit_type_void_ptr, (jit_nint) new_labels);
	if(!value_labels)
	{
		jit_free(new_labels);
		return 0;
	}
	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;

} jit_convert_intrinsic_t;
#define	CVT_INTRINSIC_NULL	\
	{0, 0, {0, 0, 0, 0}}
#define	CVT_INTRINSIC(name,intype,outtype)	\
	{#name, (void *)name, \
	 {(jit_type_t)&_jit_type_##outtype##_def, 0, \
	  (jit_type_t)&_jit_type_##intype##_def, 0}}
#define	CVT_INTRINSIC_CHECK(name,intype,outtype)	\
	{#name, (void *)name, \
	 {(jit_type_t)&_jit_type_int_def, \
	  (jit_type_t)&_jit_type_##outtype##_def, \
	  (jit_type_t)&_jit_type_##intype##_def, 0}}
static jit_convert_intrinsic_t const convert_intrinsics[] = {
	CVT_INTRINSIC(jit_int_to_sbyte, int, int),
	CVT_INTRINSIC(jit_int_to_ubyte, int, int),
	CVT_INTRINSIC(jit_int_to_short, int, int),
	CVT_INTRINSIC(jit_int_to_ushort, int, int),
#ifdef JIT_NATIVE_INT32
	CVT_INTRINSIC(jit_int_to_int, int, int),
	CVT_INTRINSIC(jit_uint_to_uint, uint, uint),
#else
	CVT_INTRINSIC(jit_long_to_int, long, int),
	CVT_INTRINSIC(jit_long_to_uint, long, uint),
#endif
	CVT_INTRINSIC_CHECK(jit_int_to_sbyte_ovf, int, int),
	CVT_INTRINSIC_CHECK(jit_int_to_ubyte_ovf, int, int),
	CVT_INTRINSIC_CHECK(jit_int_to_short_ovf, int, int),
	CVT_INTRINSIC_CHECK(jit_int_to_ushort_ovf, int, int),
#ifdef JIT_NATIVE_INT32
	CVT_INTRINSIC_CHECK(jit_int_to_int_ovf, int, int),

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

libjit/jit/jit-insn.c  view on Meta::CPAN

			};
			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;
	}

libjit/jit/jit-insn.c  view on Meta::CPAN

		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)
	{
		if(type1->abi != type2->abi)
		{
			return 0;
		}
		if(!signature_identical(type1->sub_type, type2->sub_type))
		{
			return 0;
		}
		if(type1->num_components != type2->num_components)
		{
			return 0;
		}
		for(param = 0; param < type1->num_components; ++param)
		{
			if(!signature_identical(type1->components[param].type,
									type2->components[param].type))
			{
				return 0;
			}
		}
	}

	/* If we get here, then the types are compatible */
	return 1;
}

/*
 * Create call setup instructions, taking tail calls into effect.
 */
static int create_call_setup_insns
	(jit_function_t func, jit_function_t callee, jit_type_t signature,
	 jit_value_t *args, unsigned int num_args,
	 int is_nested, int nesting_level, jit_value_t *struct_return, int flags)
{
	jit_value_t *new_args;
	jit_value_t value;
	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;
			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;
	case JIT_TYPE_USHORT:
		/* Force ushort zero extension to uint */
		return_value = apply_unary_conversion(func, JIT_OP_TRUNC_USHORT,
						      return_value, return_type);
		break;
	}

	/* Return the value containing the result to the caller */
	return return_value;
}

/*@
 * @deftypefun jit_value_t jit_insn_call_intrinsic (jit_function_t @var{func}, const char *@var{name}, void *@var{intrinsic_func}, const jit_intrinsic_descr_t *@var{descriptor}, jit_value_t @var{arg1}, jit_value_t @var{arg2})
 * Output an instruction that calls an intrinsic function.  The descriptor
 * contains the following fields:
 *
 * @table @code
 * @item return_type
 * The type of value that is returned from the intrinsic.
 *
 * @item ptr_result_type
 * This should be NULL for an ordinary intrinsic, or the result type
 * if the intrinsic reports exceptions.
 *
 * @item arg1_type
 * The type of the first argument.
 *
 * @item arg2_type
 * The type of the second argument, or NULL for a unary intrinsic.
 * @end table
 *
 * If all of the arguments are constant, then @code{jit_insn_call_intrinsic}
 * will call the intrinsic directly to calculate the constant result.
 * If the constant computation will result in an exception, then
 * code is output to cause the exception at runtime.
 *
 * The @var{name} is for diagnostic purposes only, and can be NULL.
 * @end deftypefun
@*/

libjit/jit/jit-insn.c  view on Meta::CPAN

	}

	/* 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;

libjit/jit/jit-insn.c  view on Meta::CPAN

@*/
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;

	/* Ensure that we have a function builder */
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	/* Flush any stack pops that were deferred previously */
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Allocate the label number if necessary */
	if(*finally_label == jit_label_undefined)
	{
		*finally_label = (func->builder->next_label)++;
	}

	/* Calling a finally handler makes the function not a leaf because
	   we may need to do a native "call" to invoke the handler */
	func->builder->non_leaf = 1;

	/* Add a new branch instruction to branch to the finally handler */
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	insn->opcode = (short)JIT_OP_CALL_FINALLY;
	insn->flags = JIT_INSN_DEST_IS_LABEL;
	insn->dest = (jit_value_t)(*finally_label);

	/* Create a new block for the following code */
	return jit_insn_new_block(func);
}

/*@
 * @deftypefun jit_value_t jit_insn_start_filter (jit_function_t @var{func}, jit_label_t *@var{label}, jit_type_t @var{type})
 * Define the start of a filter.  Filters are embedded subroutines within
 * functions that are used to filter exceptions in @code{catch} blocks.
 *
 * A filter subroutine takes a single argument (usually a pointer) and
 * returns a single result (usually a boolean).  The filter has complete
 * access to the local variables of the function, and can use any of
 * them in the filtering process.
 *
 * This function returns a temporary value of the specified @var{type},
 * indicating the parameter that is supplied to the filter.
 * @end deftypefun
@*/
jit_value_t jit_insn_start_filter
	(jit_function_t func, jit_label_t *label, jit_type_t type)
{
	/* Set a label at this point to start a new block */
	if(!jit_insn_label(func, label))
	{
		return 0;
	}

	/* Create a note to load the filter's parameter at runtime */
	return create_dest_note(func, JIT_OP_ENTER_FILTER, type);
}

/*@
 * @deftypefun int jit_insn_return_from_filter (jit_function_t @var{func}, jit_value_t @var{value})
 * Return from a filter subroutine with the specified @code{value} as
 * its result.
 * @end deftypefun
@*/
int jit_insn_return_from_filter(jit_function_t func, jit_value_t value)
{
	/* Flush any deferred stack pops before we return */
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Mark the end of the "filter" clause */
	if(!create_unary_note(func, JIT_OP_LEAVE_FILTER, value))
	{
		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 jit_value_t jit_insn_call_filter (jit_function_t @var{func}, jit_label_t *@var{label}, jit_value_t @var{value}, jit_type_t @var{type})
 * Call the filter subroutine at @var{label}, passing it @var{value} as
 * its argument.  This function returns a value of the specified
 * @var{type}, indicating the filter's result.
 * @end deftypefun
@*/
jit_value_t jit_insn_call_filter
	(jit_function_t func, jit_label_t *label,
	 jit_value_t value, jit_type_t type)
{
	jit_insn_t insn;

	/* Ensure that we have a function builder */
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	/* Flush any stack pops that were deferred previously */
	if(!jit_insn_flush_defer_pop(func, 0))
	{
		return 0;
	}

	/* Allocate the label number if necessary */
	if(*label == jit_label_undefined)
	{
		*label = (func->builder->next_label)++;
	}

	/* Calling a filter makes the function not a leaf because we may
	   need to do a native "call" to invoke the handler */
	func->builder->non_leaf = 1;

	/* Add a new branch instruction to branch to the filter */
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, value);
	insn->opcode = (short)JIT_OP_CALL_FILTER;
	insn->flags = JIT_INSN_DEST_IS_LABEL;
	insn->dest = (jit_value_t)(*label);
	insn->value1 = value;

	/* Create a new block, and add the filter return logic to it */
	if(!jit_insn_new_block(func))
	{
		return 0;
	}
	return create_dest_note(func, JIT_OP_CALL_FILTER_RETURN, type);
}

/*@
 * @deftypefun int jit_insn_memcpy (jit_function_t @var{func}, jit_value_t @var{dest}, jit_value_t @var{src}, jit_value_t @var{size})
 * Copy the @var{size} bytes of memory at @var{src} to @var{dest}.
 * It is assumed that the source and destination do not overlap.
 * @end deftypefun
@*/
int jit_insn_memcpy
	(jit_function_t func, jit_value_t dest,
	 jit_value_t src, jit_value_t size)
{
	size = jit_insn_convert(func, size, jit_type_nint, 0);
	return apply_ternary(func, JIT_OP_MEMCPY, dest, src, size);
}

/*@
 * @deftypefun int jit_insn_memmove (jit_function_t @var{func}, jit_value_t @var{dest}, jit_value_t @var{src}, jit_value_t @var{size})
 * Copy the @var{size} bytes of memory at @var{src} to @var{dest}.
 * This is save to use if the source and destination overlap.
 * @end deftypefun
@*/
int jit_insn_memmove
	(jit_function_t func, jit_value_t dest,
	 jit_value_t src, jit_value_t size)
{
	size = jit_insn_convert(func, size, jit_type_nint, 0);
	return apply_ternary(func, JIT_OP_MEMMOVE, dest, src, size);
}

/*@
 * @deftypefun int jit_insn_memset (jit_function_t @var{func}, jit_value_t @var{dest}, jit_value_t @var{value}, jit_value_t @var{size})
 * Set the @var{size} bytes at @var{dest} to @var{value}.
 * @end deftypefun
@*/
int jit_insn_memset
	(jit_function_t func, jit_value_t dest,
	 jit_value_t value, jit_value_t size)
{
	value = jit_insn_convert(func, value, jit_type_int, 0);
	size = jit_insn_convert(func, size, jit_type_nint, 0);
	return apply_ternary(func, JIT_OP_MEMSET, dest, value, size);
}

/*@
 * @deftypefun jit_value_t jit_insn_alloca (jit_function_t @var{func}, jit_value_t @var{size})
 * Allocate @var{size} bytes of memory from the stack.
 * @end deftypefun
@*/
jit_value_t jit_insn_alloca(jit_function_t func, jit_value_t size)
{



( run in 0.679 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )