Alien-LibJIT

 view release on metacpan or  search on metacpan

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

 * is used in its place.  This is the usual case.  However, if the function
 * takes a variable number of arguments, then you may need to construct
 * an explicit signature for the non-fixed argument values.
 *
 * The @var{flags} parameter specifies additional information about the
 * type of call to perform:
 *
 * @table @code
 * @vindex JIT_CALL_NOTHROW
 * @item JIT_CALL_NOTHROW
 * The function never throws exceptions.
 *
 * @vindex JIT_CALL_NORETURN
 * @item JIT_CALL_NORETURN
 * The function will never return directly to its caller.  It may however
 * return to the caller indirectly by throwing an exception that the
 * caller catches.
 *
 * @vindex JIT_CALL_TAIL
 * @item JIT_CALL_TAIL
 * Apply tail call optimizations, as the result of this function
 * call will be immediately returned from the containing function.
 * Tail calls are only appropriate when the signature of the called
 * function matches the callee, and none of the parameters point
 * to local variables.
 * @end table
 *
 * If @var{jit_func} has already been compiled, then @code{jit_insn_call}
 * may be able to intuit some of the above flags for itself.  Otherwise
 * it is up to the caller to determine when the flags may be appropriate.
 * @end deftypefun
@*/
jit_value_t jit_insn_call
	(jit_function_t func, const char *name, jit_function_t jit_func,
	 jit_type_t signature, jit_value_t *args, unsigned int num_args, int flags)
{
	int is_nested;
	int nesting_level;
	jit_function_t temp_func;
	jit_value_t *new_args;
	jit_value_t return_value;
	jit_insn_t insn;
	jit_label_t entry_point;
	jit_label_t label_end;

	/* Bail out if there is something wrong with the parameters */
	if(!_jit_function_ensure_builder(func) || !jit_func)
	{
		return 0;
	}

	/* Get the default signature from "jit_func" */
	if(!signature)
	{
		signature = jit_func->signature;
	}

	/* Verify that tail calls are possible to the destination */
	if((flags & JIT_CALL_TAIL) != 0)
	{
		if(func->nested_parent || jit_func->nested_parent)
		{
			/* Cannot use tail calls with nested function calls */
			flags &= ~JIT_CALL_TAIL;
		}
		else if(!signature_identical(signature, func->signature))
		{
			/* The signatures are not the same, so tail calls not allowed */
			flags &= ~JIT_CALL_TAIL;
		}
	}

	/* Determine the nesting relationship with the current function */
	if(jit_func->nested_parent)
	{
		is_nested = 1;
		if(jit_func->nested_parent == func)
		{
			/* We are calling one of our children */
			nesting_level = -1;
		}
		else if(jit_func->nested_parent == func->nested_parent)
		{
			/* We are calling one of our direct siblings */
			nesting_level = 0;
		}
		else
		{
			/* Search up to find the actual nesting level */
			temp_func = func->nested_parent;
			nesting_level = 1;
			while(temp_func != 0 && temp_func != jit_func)
			{
				++nesting_level;
				temp_func = temp_func->nested_parent;
			}
		}
	}
	else
	{
		is_nested = 0;
		nesting_level = 0;
	}

	/* Convert the arguments to the actual parameter types */
	if(num_args > 0)
	{
		new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
		if(!convert_call_parameters(func, signature, args, num_args, new_args))
		{
			return 0;
		}
	}
	else
	{
		new_args = args;
	}

	/* Intuit additional flags from "jit_func" if it was already compiled */
	if(func->no_throw)
	{
		flags |= JIT_CALL_NOTHROW;
	}
	if(func->no_return)
	{
		flags |= JIT_CALL_NORETURN;
	}

	/* Set up exception frame information for the call */
	if(!setup_eh_frame_for_call(func, flags))
	{
		return 0;
	}

	/* Create the instructions to push the parameters onto the stack */
	if(!create_call_setup_insns
			(func, jit_func, signature, new_args, num_args,
			 is_nested, nesting_level, &return_value, flags))
	{
		return 0;
	}

	/* Output the "call" instruction */
	if((flags & JIT_CALL_TAIL) != 0 && func == jit_func)
	{
		/* We are performing a tail call to ourselves, which we can
		   turn into an unconditional branch back to our entry point */
		entry_point = jit_label_undefined;
		label_end = jit_label_undefined;
		if(!jit_insn_branch(func, &entry_point))
		{
			return 0;
		}
		if(!jit_insn_label(func, &entry_point))
		{

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

		if(!jit_insn_move_blocks_to_start(func, entry_point, label_end))
		{
			return 0;
		}
	}
	else
	{
		/* Functions that call out are not leaves */
		func->builder->non_leaf = 1;

		/* Performing a regular call, or a tail call to someone else */
		insn = _jit_block_add_insn(func->builder->current_block);
		if(!insn)
		{
			return 0;
		}
		if((flags & JIT_CALL_TAIL) != 0)
		{
			func->builder->has_tail_call = 1;
			insn->opcode = JIT_OP_CALL_TAIL;
		}
		else
		{
			insn->opcode = JIT_OP_CALL;
		}
		insn->flags = JIT_INSN_DEST_IS_FUNCTION | JIT_INSN_VALUE1_IS_NAME;
		insn->dest = (jit_value_t)jit_func;
		insn->value1 = (jit_value_t)name;
	}

	/* Handle return to the caller */
	return handle_return(func, signature, flags, is_nested,
			     new_args, num_args, return_value);
}

/*@
 * @deftypefun jit_value_t jit_insn_call_indirect (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{flags})
 * Call a function via an indirect pointer.
 * @end deftypefun
@*/
jit_value_t jit_insn_call_indirect
		(jit_function_t func, jit_value_t value, jit_type_t signature,
		 jit_value_t *args, unsigned int num_args, int flags)
{
	jit_value_t *new_args;
	jit_value_t return_value;
	jit_insn_t insn;

	/* Bail out if there is something wrong with the parameters */
	if(!_jit_function_ensure_builder(func) || !value || !signature)
	{
		return 0;
	}

	/* Verify that tail calls are possible to the destination */
#if defined(JIT_BACKEND_INTERP)
	flags &= ~JIT_CALL_TAIL;
#else
	if((flags & JIT_CALL_TAIL) != 0)
	{
		if(func->nested_parent)
		{
			flags &= ~JIT_CALL_TAIL;
		}
		else if(!signature_identical(signature, func->signature))
		{
			flags &= ~JIT_CALL_TAIL;
		}
	}
#endif

	/* We are making a native call */
	flags |= JIT_CALL_NATIVE;

	/* Convert the arguments to the actual parameter types */
	if(num_args > 0)
	{
		new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
		if(!convert_call_parameters(func, signature, args, num_args, new_args))
		{
			return 0;
		}
	}
	else
	{
		new_args = args;
	}

	/* Set up exception frame information for the call */
	if(!setup_eh_frame_for_call(func, flags))
	{
		return 0;
	}

	/* Create the instructions to push the parameters onto the stack */
	if(!create_call_setup_insns
		(func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
	{
		return 0;
	}

	/* Move the indirect pointer value into an appropriate register */
	if(!_jit_setup_indirect_pointer(func, value))
	{
		return 0;
	}

	/* Functions that call out are not leaves */
	func->builder->non_leaf = 1;

	/* Output the "call_indirect" instruction */
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, value);
	if((flags & JIT_CALL_TAIL) != 0)
	{
		func->builder->has_tail_call = 1;
		insn->opcode = JIT_OP_CALL_INDIRECT_TAIL;
	}
	else
	{
		insn->opcode = JIT_OP_CALL_INDIRECT;
	}
	insn->flags = JIT_INSN_VALUE2_IS_SIGNATURE;
	insn->value1 = value;
	insn->value2 = (jit_value_t)jit_type_copy(signature);

	/* Handle return to the caller */
	return handle_return(func, signature, flags, 0,
			     new_args, num_args, return_value);
}

/*@
 * @deftypefun jit_value_t jit_insn_call_indirect_vtable (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{flags})
 * Call a function via an indirect pointer.  This version differs from
 * @code{jit_insn_call_indirect} in that we assume that @var{value}
 * contains a pointer that resulted from calling
 * @code{jit_function_to_vtable_pointer}.  Indirect vtable pointer
 * calls may be more efficient on some platforms than regular indirect calls.
 * @end deftypefun
@*/
jit_value_t jit_insn_call_indirect_vtable
	(jit_function_t func, jit_value_t value, jit_type_t signature,
	 jit_value_t *args, unsigned int num_args, int flags)
{
	jit_value_t *new_args;
	jit_value_t return_value;
	jit_insn_t insn;

	/* Bail out if there is something wrong with the parameters */
	if(!_jit_function_ensure_builder(func) || !value || !signature)
	{
		return 0;
	}

	/* Verify that tail calls are possible to the destination */
	if((flags & JIT_CALL_TAIL) != 0)
	{
		if(func->nested_parent)
		{
			flags &= ~JIT_CALL_TAIL;
		}
		else if(!signature_identical(signature, func->signature))
		{
			flags &= ~JIT_CALL_TAIL;
		}
	}

	/* Convert the arguments to the actual parameter types */
	if(num_args > 0)
	{
		new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
		if(!convert_call_parameters(func, signature, args, num_args, new_args))
		{
			return 0;
		}
	}
	else
	{
		new_args = args;
	}

	/* Set up exception frame information for the call */
	if(!setup_eh_frame_for_call(func, flags))
	{
		return 0;
	}

	/* Create the instructions to push the parameters onto the stack */
	if(!create_call_setup_insns
		(func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
	{
		return 0;
	}

	/* Move the indirect pointer value into an appropriate register */
	if(!_jit_setup_indirect_pointer(func, value))
	{
		return 0;
	}

	/* Functions that call out are not leaves */
	func->builder->non_leaf = 1;

	/* Output the "call_vtable_ptr" instruction */
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, value);
	if((flags & JIT_CALL_TAIL) != 0)
	{
		func->builder->has_tail_call = 1;
		insn->opcode = JIT_OP_CALL_VTABLE_PTR_TAIL;
	}
	else
	{
		insn->opcode = JIT_OP_CALL_VTABLE_PTR;
	}
	insn->value1 = value;

	/* Handle return to the caller */
	return handle_return(func, signature, flags, 0,
			     new_args, num_args, return_value);
}

/*@
 * @deftypefun jit_value_t jit_insn_call_native (jit_function_t @var{func}, const char *@var{name}, void *@var{native_func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{exception_return}, int @var{flags}...
 * Output an instruction that calls an external native function.
 * The @var{name} is for diagnostic purposes only, and can be NULL.
 * @end deftypefun
@*/
jit_value_t jit_insn_call_native
	(jit_function_t func, const char *name, void *native_func,
	 jit_type_t signature, jit_value_t *args, unsigned int num_args, int flags)
{
	jit_value_t *new_args;
	jit_value_t return_value;
	jit_insn_t insn;
	jit_type_t return_type;

	/* Bail out if there is something wrong with the parameters */
	if(!_jit_function_ensure_builder(func) || !native_func || !signature)
	{
		return 0;
	}

	/* Verify that tail calls are possible to the destination */
#if defined(JIT_BACKEND_INTERP)
	flags &= ~JIT_CALL_TAIL;
#else
	if((flags & JIT_CALL_TAIL) != 0)
	{
		if(func->nested_parent)
		{
			flags &= ~JIT_CALL_TAIL;
		}
		else if(!signature_identical(signature, func->signature))
		{
			flags &= ~JIT_CALL_TAIL;
		}
	}
#endif

	/* We are making a native call */
	flags |= JIT_CALL_NATIVE;

	/* Convert the arguments to the actual parameter types */
	if(num_args > 0)
	{
		new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
		if(!convert_call_parameters(func, signature, args, num_args, new_args))
		{
			return 0;
		}
	}
	else
	{
		new_args = args;
	}

	/* Set up exception frame information for the call */
	if(!setup_eh_frame_for_call(func, flags))
	{
		return 0;
	}

	/* Create the instructions to push the parameters onto the stack */
	if(!create_call_setup_insns
		(func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
	{
		return 0;
	}

	/* Functions that call out are not leaves */
	func->builder->non_leaf = 1;

	/* Output the "call_external" instruction */
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	if((flags & JIT_CALL_TAIL) != 0)
	{
		func->builder->has_tail_call = 1;
		insn->opcode = JIT_OP_CALL_EXTERNAL_TAIL;
	}
	else
	{
		insn->opcode = JIT_OP_CALL_EXTERNAL;
	}
	insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME;
	insn->dest = (jit_value_t)native_func;

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

					   		(func, jit_type_int, frame_offset));
}

/*@
 * @deftypefun int jit_insn_outgoing_reg (jit_function_t @var{func}, jit_value_t @var{value}, int @var{reg})
 * Output an instruction that copies the contents of @var{value}
 * into the register @var{reg} at this point in the code.  This is
 * typically used just before making an outgoing subroutine call.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up the registers for a subroutine call.
 * @end deftypefun
@*/
int jit_insn_outgoing_reg(jit_function_t func, jit_value_t value, int reg)
{
	return create_note(func, JIT_OP_OUTGOING_REG, value,
					   jit_value_create_nint_constant
					   		(func, jit_type_int, (jit_nint)reg));
}

/*@
 * @deftypefun int jit_insn_outgoing_frame_posn (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{frame_offset})
 * Output an instruction that notes that the contents of @var{value}
 * should be stored in the stack frame at @var{frame_offset} at this point
 * in the code.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up an outgoing frame for tail calls.
 * @end deftypefun
@*/
int jit_insn_outgoing_frame_posn
		(jit_function_t func, jit_value_t value, jit_nint frame_offset)
{
	return create_note(func, JIT_OP_OUTGOING_FRAME_POSN, value,
					   jit_value_create_nint_constant
					   		(func, jit_type_int, frame_offset));
}

/*@
 * @deftypefun int jit_insn_return_reg (jit_function_t @var{func}, jit_value_t @var{value}, int @var{reg})
 * Output an instruction that notes that the contents of @var{value}
 * can be found in the register @var{reg} at this point in the code.
 * This is similar to @code{jit_insn_incoming_reg}, except that it
 * refers to return values, not parameter values.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to handle returns from subroutine calls.
 * @end deftypefun
@*/
int jit_insn_return_reg(jit_function_t func, jit_value_t value, int reg)
{
	return create_note(func, JIT_OP_RETURN_REG, value,
					   jit_value_create_nint_constant
					   		(func, jit_type_int, (jit_nint)reg));
}

/*@
 * @deftypefun int jit_insn_setup_for_nested (jit_function_t @var{func}, int @var{nested_level}, int @var{reg})
 * Output an instruction to set up for a nested function call.
 * The @var{nested_level} value will be -1 to call a child, zero to call a
 * sibling of @var{func}, 1 to call a sibling of the parent, 2 to call
 * a sibling of the grandparent, etc.  If @var{reg} is not -1, then
 * it indicates the register to receive the parent frame information.
 * If @var{reg} is -1, then the frame information will be pushed on the stack.
 *
 * You normally wouldn't call this yourself - it is used internally by the
 * CPU back ends to set up the parameters for a nested subroutine call.
 * @end deftypefun
@*/
int jit_insn_setup_for_nested(jit_function_t func, int nested_level, int reg)
{
	if(nested_level < 0)
	{
		return create_unary_note
			(func, JIT_OP_SETUP_FOR_NESTED,
			 jit_value_create_nint_constant
				 (func, jit_type_int, (jit_nint)reg));
	}
	else
	{
		return create_note
			(func, JIT_OP_SETUP_FOR_SIBLING,
			 jit_value_create_nint_constant
				 (func, jit_type_int, (jit_nint)nested_level),
			 jit_value_create_nint_constant
				 (func, jit_type_int, (jit_nint)reg));
	}
}

/*@
 * @deftypefun int jit_insn_flush_struct (jit_function_t @var{func}, jit_value_t @var{value})
 * Flush a small structure return value out of registers and back
 * into the local variable frame.  You normally wouldn't call this
 * yourself - it is used internally by the CPU back ends to handle
 * structure returns from functions.
 * @end deftypefun
@*/
int jit_insn_flush_struct(jit_function_t func, jit_value_t value)
{
	if(value)
	{
		jit_value_set_addressable(value);
	}
	return create_unary_note(func, JIT_OP_FLUSH_SMALL_STRUCT, value);
}

/*@
 * @deftypefun jit_value_t jit_insn_import (jit_function_t @var{func}, jit_value_t @var{value})
 * Import @var{value} from an outer nested scope into @var{func}.  Returns
 * the effective address of the value for local access via a pointer.
 * Returns NULL if out of memory or the value is not accessible via a
 * parent, grandparent, or other ancestor of @var{func}.
 * @end deftypefun
@*/
jit_value_t jit_insn_import(jit_function_t func, jit_value_t value)
{
	jit_function_t value_func;
	jit_function_t current_func;
	int level;

	/* Make sure that we have a builder before we start */
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}

	/* If the value is already local, then return the local address */
	value_func = jit_value_get_function(value);
	if(value_func == func)
	{
		return jit_insn_address_of(func, value);
	}

	/* Find the nesting level of the value, where 1 is our parent */
	level = 1;
	current_func = func->nested_parent;
	while(current_func != 0 && current_func != value_func)
	{
		++level;
		current_func = current_func->nested_parent;
	}
	if(!current_func)
	{
		/* The value is not accessible from this scope */
		return 0;
	}

	/* Output the relevant import instruction, which will also cause
	   it to be marked as a non-local addressable by "jit_value_ref" */
	return apply_binary
		(func, JIT_OP_IMPORT, value,
		 jit_value_create_nint_constant(func, jit_type_int, (jit_nint)level),
		 jit_type_void_ptr);
}

/*@
 * @deftypefun int jit_insn_push (jit_function_t @var{func}, jit_value_t @var{value})
 * Push a value onto the function call stack, in preparation for a call.
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up the stack for a subroutine call.
 * @end deftypefun
@*/
int jit_insn_push(jit_function_t func, jit_value_t value)
{
	jit_type_t type;
	if(!value)
	{
		return 0;
	}
	type = jit_type_promote_int(jit_type_normalize(jit_value_get_type(value)));
	switch(type->kind)
	{
		case JIT_TYPE_SBYTE:
		case JIT_TYPE_UBYTE:
		case JIT_TYPE_SHORT:
		case JIT_TYPE_USHORT:
		case JIT_TYPE_INT:
		case JIT_TYPE_UINT:
		{
			return create_unary_note(func, JIT_OP_PUSH_INT, value);
		}
		/* Not reached */

		case JIT_TYPE_LONG:
		case JIT_TYPE_ULONG:
		{
			return create_unary_note(func, JIT_OP_PUSH_LONG, value);
		}
		/* Not reached */

		case JIT_TYPE_FLOAT32:
		{
			return create_unary_note(func, JIT_OP_PUSH_FLOAT32, value);
		}
		/* Not reached */

		case JIT_TYPE_FLOAT64:
		{
			return create_unary_note(func, JIT_OP_PUSH_FLOAT64, value);
		}



( run in 0.413 second using v1.01-cache-2.11-cpan-1edf4fed603 )