Alien-LibJIT

 view release on metacpan or  search on metacpan

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

 * @end deftypefun
@*/
int jit_insn_store(jit_function_t func, jit_value_t dest, jit_value_t value)
{
	jit_insn_t insn;
	if(!dest || !value)
	{
		return 0;
	}
	if(!_jit_function_ensure_builder(func))
	{
		return 0;
	}
	value = jit_insn_convert(func, value, dest->type, 0);
	if(!value)
	{
		return 0;
	}
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	jit_value_ref(func, dest);
	jit_value_ref(func, value);
	insn->opcode = (short)_jit_store_opcode
		(JIT_OP_COPY_INT, JIT_OP_COPY_STORE_BYTE, dest->type);
	insn->dest = dest;
	insn->value1 = value;
	return 1;
}

/*
 * Scan back through the current block, looking for an address instruction that
 * involves "value" as its destination. Returns NULL if no such instruction was
 * found, or it is blocked by a later use of "value".
 *
 * The instruction found may then be combined into a new single instruction with
 * the following "load_relative", "store_relative", or another "relative_add".
 *
 * For instance, consider the code like this:
 *
 * i) y = address_of(x)
 * ...
 * j) z = add_relative(y, a)
 *
 * Let's suppose that we need to add a "store_realtive(z, b, v)" instruction.
 * The "find_base_insn()" call will return the instruction "j" and we will be
 * able to emit the instruction "store_relative(y, a + b, v)" instead. If "z"
 * is not used elsewhere then "j" will be optimized away by the dead code
 * elimination pass.
 *
 * Repetitive use of this procedure for a chain of "add_relative" instructions
 * converts it into a series of indpenedent instructions each using the very
 * first address in the chain as its base. Therefore regardless of the initial
 * chain length it is always enough to make single "find_base_insn()" call to
 * get the base address of the entire chain (think induction).
 *
 * Note that in this situation the second "find_base_insn()" call will return
 * the instruction "i" that obtains the base address as the address of a local
 * frame variable. This instruction is a candidate for being moved down to
 * where the "load_relative" or "store_relative" occurs. This might make it
 * easier for the code generator to handle field accesses whitin local
 * variables.
 *
 * The "plast" argument indicates if the found instruction is already the last
 * one, so there is no need to move it down.
 */
static jit_insn_t
find_base_insn(
	jit_function_t func,
	jit_insn_iter_t iter,
	jit_value_t value,
	int *plast)
{
	int last;
	jit_insn_t insn;
	jit_insn_iter_t iter2;
	jit_insn_t insn2;

	/* The "value" could be vulnerable to aliasing effects so we cannot
	   optimize it */
	if(value->is_addressable || value->is_volatile)
	{
		return 0;
	}

	/* We are about to check the last instruction before the current one */
	last = 1;

	/* Iterate back through the block looking for a suitable instruction */
	while((insn = jit_insn_iter_previous(&iter)) != 0)
	{
		/* This instruction uses "value" in some way */
		if(insn->dest == value)
		{
			/* This is the instruction we were looking for */
			if(insn->opcode == JIT_OP_ADDRESS_OF)
			{
				*plast = last;
				return insn;
			}
			if(insn->opcode == JIT_OP_ADD_RELATIVE)
			{
				value = insn->value1;
				if(value->is_addressable || value->is_volatile)
				{
					return 0;
				}

				/* Scan forwards to ensure that "insn->value1"
				   is not modified anywhere in the instructions
				   that follow */
				iter2 = iter;
				jit_insn_iter_next(&iter2);
				while((insn2 = jit_insn_iter_next(&iter2)) != 0)
				{
					if(insn2->dest == value
					   && (insn2->flags & JIT_INSN_DEST_IS_VALUE) == 0)
					{
						return 0;

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

					CVT_NONE,
					CVT_NONE}
			};
			opcode_map = to_nfloat;
		}
		break;
	}
	if(opcode_map)
	{
		switch(vtype->kind)
		{
			case JIT_TYPE_UINT:		opcode_map += 2; break;
			case JIT_TYPE_LONG:		opcode_map += 4; break;
			case JIT_TYPE_ULONG:	opcode_map += 6; break;
			case JIT_TYPE_FLOAT32:	opcode_map += 8; break;
			case JIT_TYPE_FLOAT64:	opcode_map += 10; break;
			case JIT_TYPE_NFLOAT:	opcode_map += 12; break;
		}
		if(overflow_check)
		{
			opcode_map += 1;
		}
		if(opcode_map->cvt1)
		{
			value = apply_unary_conversion
				(func, opcode_map->cvt1, value, opcode_map->type1);
		}
		if(opcode_map->cvt2)
		{
			value = apply_unary_conversion
				(func, opcode_map->cvt2, value, opcode_map->type2);
		}
		if(opcode_map->cvt3)
		{
			value = apply_unary_conversion
				(func, opcode_map->cvt3, value, opcode_map->type3);
		}
	}
	return value;
}

/*
 * Convert the parameters for a function call into their final types.
 */
static int convert_call_parameters
	(jit_function_t func, jit_type_t signature,
	 jit_value_t *args, unsigned int num_args,
	 jit_value_t *new_args)
{
	unsigned int param;
	for(param = 0; param < num_args; ++param)
	{
		new_args[param] = jit_insn_convert
			(func, args[param],
			 jit_type_get_param(signature, param), 0);
	}
	return 1;
}

/*
 * Set up the exception frame information before a function call out.
 */
static int setup_eh_frame_for_call(jit_function_t func, int flags)
{
#if !defined(JIT_BACKEND_INTERP)
	jit_type_t type;
	jit_value_t args[2];
	jit_insn_t insn;

	/* If "tail" is set, then we need to pop the "setjmp" context */
	if((flags & JIT_CALL_TAIL) != 0 && func->has_try)
	{
		type = jit_type_create_signature
			(jit_abi_cdecl, jit_type_void, 0, 0, 1);
		if(!type)
		{
			return 0;
		}
		jit_insn_call_native
			(func, "_jit_unwind_pop_setjmp",
			 (void *)_jit_unwind_pop_setjmp, type, 0, 0, JIT_CALL_NOTHROW);
		jit_type_free(type);
	}

	/* If "nothrow" or "tail" is set, then there is no more to do */
	if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) != 0)
	{
		return 1;
	}

	/* This function may throw an exception */
	func->builder->may_throw = 1;

#if JIT_APPLY_BROKEN_FRAME_BUILTINS != 0
	{
		jit_value_t eh_frame_info;
		jit_type_t params[2];

		/* Get the value that holds the exception frame information */
		if((eh_frame_info = func->builder->eh_frame_info) == 0)
		{
			type = jit_type_create_struct(0, 0, 0);
			if(!type)
			{
				return 0;
			}
			jit_type_set_size_and_alignment
				(type, sizeof(struct jit_backtrace), sizeof(void *));
			eh_frame_info = jit_value_create(func, type);
			jit_type_free(type);
			if(!eh_frame_info)
			{
				return 0;
			}
			func->builder->eh_frame_info = eh_frame_info;
		}

		/* Output an instruction to load the "pc" into a value */
		args[1] = jit_value_create(func, jit_type_void_ptr);
		if(!(args[1]))
		{
			return 0;
		}
		insn = _jit_block_add_insn(func->builder->current_block);
		if(!insn)
		{
			return 0;
		}
		jit_value_ref(func, args[1]);
		insn->opcode = JIT_OP_LOAD_PC;
		insn->dest = args[1];

		/* Load the address of "eh_frame_info" into another value */
		args[0] = jit_insn_address_of(func, eh_frame_info);
		if(!(args[0]))
		{
			return 0;
		}

		/* Create a signature for the prototype "void (void *, void *)" */
		params[0] = jit_type_void_ptr;
		params[1] = jit_type_void_ptr;
		type = jit_type_create_signature
			(jit_abi_cdecl, jit_type_void, params, 2, 1);
		if(!type)
		{
			return 0;
		}

		/* Call the "_jit_backtrace_push" function */
		jit_insn_call_native
			(func, "_jit_backtrace_push",
			 (void *)_jit_backtrace_push, type, args, 2, JIT_CALL_NOTHROW);
		jit_type_free(type);
	}
#endif

	/* Update the "catch_pc" value to reflect the current context */
	if(func->builder->setjmp_value != 0)
	{
		args[0] = jit_value_create(func, jit_type_void_ptr);
		if(!(args[0]))
		{
			return 0;
		}
		insn = _jit_block_add_insn(func->builder->current_block);
		if(!insn)
		{
			return 0;
		}
		jit_value_ref(func, args[0]);
		insn->opcode = JIT_OP_LOAD_PC;
		insn->dest = args[0];
		if(!jit_insn_store_relative
			(func, jit_insn_address_of(func, func->builder->setjmp_value),
			 jit_jmp_catch_pc_offset, args[0]))
		{
			return 0;
		}
	}

	/* We are now ready to make the actual function call */
	return 1;
#else /* JIT_BACKEND_INTERP */
	/* The interpreter handles exception frames for us */
	if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) == 0)
	{
		func->builder->may_throw = 1;
	}
	return 1;
#endif
}

/*
 * Restore the exception handling frame after a function call.
 */
static int restore_eh_frame_after_call(jit_function_t func, int flags)
{
#if !defined(JIT_BACKEND_INTERP)
	jit_value_t value;

	/* If the "nothrow", "noreturn", or "tail" flags are set, then we
	   don't need to worry about this */
	if((flags & (JIT_CALL_NOTHROW | JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
	{
		return 1;
	}

#if JIT_APPLY_BROKEN_FRAME_BUILTINS != 0
	{
		jit_type_t type;

		/* Create the signature prototype "void (void)" */
		type = jit_type_create_signature
			(jit_abi_cdecl, jit_type_void, 0, 0, 0);
		if(!type)
		{
			return 0;
		}

		/* Call the "_jit_backtrace_pop" function */
		jit_insn_call_native
			(func, "_jit_backtrace_pop",
			 (void *)_jit_backtrace_pop, type, 0, 0, JIT_CALL_NOTHROW);
		jit_type_free(type);
	}
#endif

	/* Clear the "catch_pc" value for the current context */
	if(func->builder->setjmp_value != 0)
	{
		value = jit_value_create_nint_constant(func, jit_type_void_ptr, 0);
		if(!value)
		{
			return 0;
		}
		if(!jit_insn_store_relative
			(func, jit_insn_address_of(func, func->builder->setjmp_value),
			 jit_jmp_catch_pc_offset, value))
		{
			return 0;
		}
	}

	/* Everything is back to where it should be */
	return 1;
#else /* JIT_BACKEND_INTERP */
	/* The interpreter handles exception frames for us */
	return 1;
#endif
}

/*
 * Determine if two signatures are identical for the purpose of tail calls.
 */
static int signature_identical(jit_type_t type1, jit_type_t type2)
{
	unsigned int param;

	/* Handle the easy case first */
	if(type1 == type2)
	{
		return 1;
	}

	/* Remove the tags and then bail out if either type is invalid */
	type1 = jit_type_remove_tags(type1);
	type2 = jit_type_remove_tags(type2);
	if(!type1 || !type2)
	{
		return 0;
	}

	/* Normalize pointer types, but leave signature types as-is */
	if(type1->kind == JIT_TYPE_PTR)
	{
		type1 = jit_type_normalize(type1);
	}
	if(type2->kind == JIT_TYPE_PTR)
	{
		type2 = jit_type_normalize(type2);
	}

#ifdef JIT_NFLOAT_IS_DOUBLE
	/* "double" and "nfloat" are identical on this platform */
	if((type1->kind == JIT_TYPE_FLOAT64 || type1->kind == JIT_TYPE_NFLOAT) &&
	   (type2->kind == JIT_TYPE_FLOAT64 || type2->kind == JIT_TYPE_NFLOAT))
	{
		return 1;
	}
#endif

	/* If the kinds are not the same now, then we don't have a match */
	if(type1->kind != type2->kind)
	{
		return 0;
	}

	/* Structure and union types must have the same size and alignment */
	if(type1->kind == JIT_TYPE_STRUCT || type1->kind == JIT_TYPE_UNION)
	{
		return (jit_type_get_size(type1) == jit_type_get_size(type2) &&
				jit_type_get_alignment(type1) == jit_type_get_alignment(type2));
	}

	/* Signature types must be compared component-by-component */
	if(type1->kind == JIT_TYPE_SIGNATURE)
	{

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

			}
		}
		*struct_return = 0;
		return 1;
	}

	/* Let the back end do the work */
	return _jit_create_call_setup_insns
		(func, signature, args, num_args,
		 is_nested, nesting_level, struct_return, flags);
}

static jit_value_t
handle_return(jit_function_t func,
	      jit_type_t signature,
	      int flags, int is_nested,
	      jit_value_t *args, unsigned int num_args,
	      jit_value_t return_value)
{
	/* If the function does not return, then end the current block.
	   The next block does not have "entered_via_top" set so that
	   it will be eliminated during later code generation */
	if((flags & (JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
	{
		func->builder->current_block->ends_in_dead = 1;
	}

	/* If the function may throw an exceptions then end the current
	   basic block to account for exceptional control flow */
	if((flags & JIT_CALL_NOTHROW) == 0)
	{
		if(!jit_insn_new_block(func))
		{
			return 0;
		}
	}

	/* Create space for the return value, if we don't already have one */
	if(!return_value)
	{
		return_value = jit_value_create(func, jit_type_get_return(signature));
		if(!return_value)
		{
			return 0;
		}
	}

	/* Create the instructions necessary to move the return value into place */
	if((flags & JIT_CALL_TAIL) == 0)
	{
		if(!_jit_create_call_return_insns(func,
						  signature,
						  args, num_args,
						  return_value,
						  is_nested))
		{
			return 0;
		}
	}

	/* Restore exception frame information after the call */
	if(!restore_eh_frame_after_call(func, flags))
	{
		return 0;
	}

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

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

	/* Bail out if there is something wrong with the parameters */

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

			flags &= ~JIT_CALL_TAIL;
		}
	}

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

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

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

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

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

	/* Output the "call" instruction */
	if((flags & JIT_CALL_TAIL) != 0 && func == jit_func)
	{
		/* We are performing a tail call to ourselves, which we can
		   turn into an unconditional branch back to our entry point */
		entry_point = jit_label_undefined;
		label_end = jit_label_undefined;
		if(!jit_insn_branch(func, &entry_point))
		{
			return 0;
		}
		if(!jit_insn_label(func, &entry_point))
		{
			return 0;
		}
		if(!jit_insn_label(func, &label_end))
		{
			return 0;
		}
		if(!jit_insn_move_blocks_to_start(func, entry_point, label_end))
		{
			return 0;
		}
	}
	else
	{
		/* Functions that call out are not leaves */
		func->builder->non_leaf = 1;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* Output the "call_external" instruction */
	insn = _jit_block_add_insn(func->builder->current_block);
	if(!insn)
	{
		return 0;
	}
	if((flags & JIT_CALL_TAIL) != 0)
	{
		func->builder->has_tail_call = 1;
		insn->opcode = JIT_OP_CALL_EXTERNAL_TAIL;
	}
	else
	{
		insn->opcode = JIT_OP_CALL_EXTERNAL;
	}
	insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME;
	insn->dest = (jit_value_t)native_func;
	insn->value1 = (jit_value_t)name;
#ifdef JIT_BACKEND_INTERP
	insn->flags |= JIT_INSN_VALUE2_IS_SIGNATURE;
	insn->value2 = (jit_value_t)jit_type_copy(signature);
#endif

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

	/* Make sure that returned byte / short values get zero / sign extended */
	return_type = jit_type_normalize(return_value->type);
	switch(return_type->kind)
	{
	case JIT_TYPE_SBYTE:
		/* Force sbyte sign extension to int */
		return_value = apply_unary_conversion(func, JIT_OP_TRUNC_SBYTE,
						      return_value, return_type);
		break;
	case JIT_TYPE_UBYTE:
		/* Force ubyte zero extension to uint */
		return_value = apply_unary_conversion(func, JIT_OP_TRUNC_UBYTE,
						      return_value, return_type);
		break;
	case JIT_TYPE_SHORT:
		/* Force short sign extension to int */
		return_value = apply_unary_conversion(func, JIT_OP_TRUNC_SHORT,
						      return_value, return_type);
		break;

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

	temp_value = jit_insn_call_native
		(func, name, intrinsic_func, signature, param_values,
		 num_params, JIT_CALL_NOTHROW);
	if(!temp_value)
	{
		jit_type_free(signature);
		return 0;
	}
	jit_type_free(signature);

	/* If no exceptions to report, then return "temp_value" as the result */
	if(!return_value)
	{
		return temp_value;
	}

	/* Determine if an exception was reported */
	cond_value = jit_insn_ge(func, temp_value,
		jit_value_create_nint_constant(func, jit_type_int, 1));
	if(!cond_value)
	{
		return 0;
	}
	label = jit_label_undefined;
	if(!jit_insn_branch_if(func, cond_value, &label))
	{
		return 0;
	}

	/* Call the "jit_exception_builtin" function to report the exception */
	param_types[0] = jit_type_int;
	signature = jit_type_create_signature
		(jit_abi_cdecl, jit_type_void, param_types, 1, 1);
	if(!signature)
	{
		return 0;
	}
	param_values[0] = temp_value;
	jit_insn_call_native
		(func, "jit_exception_builtin",
		 (void *)jit_exception_builtin, signature, param_values, 1,
		 JIT_CALL_NORETURN);
	jit_type_free(signature);

	/* Execution continues here if there was no exception */
	if(!jit_insn_label(func, &label))
	{
		return 0;
	}

	/* Return the temporary that contains the result value */
	return return_value;
}

/*@
 * @deftypefun int jit_insn_incoming_reg (jit_function_t @var{func}, jit_value_t @var{value}, int @var{reg})
 * Output an instruction that notes that the contents of @var{value}
 * can be found in the register @var{reg} at this point in the code.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up the function's entry frame and the
 * values of registers on return from a subroutine call.
 * @end deftypefun
@*/
int jit_insn_incoming_reg(jit_function_t func, jit_value_t value, int reg)
{
	if(value && value->is_parameter)
	{
		value->is_reg_parameter = 1;
	}
	return create_note(func, JIT_OP_INCOMING_REG, value,
					   jit_value_create_nint_constant
					   		(func, jit_type_int, (jit_nint)reg));
}

/*@
 * @deftypefun int jit_insn_incoming_frame_posn (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{frame_offset})
 * Output an instruction that notes that the contents of @var{value}
 * can be found in the stack frame at @var{frame_offset} at this point
 * in the code.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up the function's entry frame.
 * @end deftypefun
@*/
int jit_insn_incoming_frame_posn
		(jit_function_t func, jit_value_t value, jit_nint frame_offset)
{
	return create_note(func, JIT_OP_INCOMING_FRAME_POSN, value,
					   jit_value_create_nint_constant
					   		(func, jit_type_int, frame_offset));
}

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

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

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

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

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

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

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

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

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

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

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

			value = jit_insn_address_of(func, value);
			if(!value)
			{
				return 0;
			}
			return create_note
				(func, JIT_OP_PUSH_STRUCT, value,
				 jit_value_create_nint_constant
				 	(func, jit_type_nint, (jit_nint)jit_type_get_size(type)));
		}
		/* Not reached */
	}
	return 1;
}

/*@
 * @deftypefun int jit_insn_push_ptr (jit_function_t @var{func}, jit_value_t @var{value}, jit_type_t @var{type})
 * Push @code{*@var{value}} onto the function call stack, in preparation for a call.
 * This is normally used for returning @code{struct} and @code{union}
 * values where you have the effective address of the structure, rather
 * than the structure's contents, in @var{value}.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up the stack for a subroutine call.
 * @end deftypefun
@*/
int jit_insn_push_ptr(jit_function_t func, jit_value_t value, jit_type_t type)
{
	if(!value || !type)
	{
		return 0;
	}
	switch(jit_type_normalize(type)->kind)
	{
		case JIT_TYPE_STRUCT:
		case JIT_TYPE_UNION:
		{
			/* Push the structure onto the stack by address */
			return create_note
				(func, JIT_OP_PUSH_STRUCT, value,
				 jit_value_create_nint_constant
				 	(func, jit_type_nint, (jit_nint)jit_type_get_size(type)));
		}
		/* Not reached */

		default:
		{
			/* Load the value from the address and push it normally */
			return jit_insn_push
				(func, jit_insn_load_relative(func, value, 0, type));
		}
		/* Not reached */
	}
}

/*@
 * @deftypefun int jit_insn_set_param (jit_function_t @var{func}, jit_value_t @var{value}, jit_nint @var{offset})
 * Set the parameter slot at @var{offset} in the outgoing parameter area
 * to @var{value}.  This may be used instead of @code{jit_insn_push}
 * if it is more efficient to store directly to the stack than to push.
 * The outgoing parameter area is allocated within the frame when
 * the function is first entered.
 *
 * You normally wouldn't call this yourself - it is used internally
 * by the CPU back ends to set up the stack for a subroutine call.
 * @end deftypefun
@*/
int jit_insn_set_param(jit_function_t func, jit_value_t value, jit_nint offset)
{
	jit_type_t type;
	if(!value)
	{
		return 0;
	}
	type = jit_type_promote_int(jit_type_normalize(jit_value_get_type(value)));
	switch(type->kind)
	{
		case JIT_TYPE_SBYTE:
		case JIT_TYPE_UBYTE:
		case JIT_TYPE_SHORT:
		case JIT_TYPE_USHORT:
		case JIT_TYPE_INT:
		case JIT_TYPE_UINT:
		{
			return create_note(func, JIT_OP_SET_PARAM_INT, value,
							   jit_value_create_nint_constant
							   		(func, jit_type_nint, offset));
		}
		/* Not reached */

		case JIT_TYPE_LONG:
		case JIT_TYPE_ULONG:
		{
			return create_note(func, JIT_OP_SET_PARAM_LONG, value,
							   jit_value_create_nint_constant
							   		(func, jit_type_nint, offset));
		}
		/* Not reached */

		case JIT_TYPE_FLOAT32:
		{
			return create_note(func, JIT_OP_SET_PARAM_FLOAT32, value,
							   jit_value_create_nint_constant
							   		(func, jit_type_nint, offset));
		}
		/* Not reached */

		case JIT_TYPE_FLOAT64:
		{
			return create_note(func, JIT_OP_SET_PARAM_FLOAT64, value,
							   jit_value_create_nint_constant
							   		(func, jit_type_nint, offset));
		}
		/* Not reached */

		case JIT_TYPE_NFLOAT:
		{
			return create_note(func, JIT_OP_SET_PARAM_NFLOAT, value,
							   jit_value_create_nint_constant
							   		(func, jit_type_nint, offset));
		}

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

	if(!type)
	{
		return 0;
	}
	args[0] = jit_insn_address_of(func, func->builder->setjmp_value);
	args[1] = jit_value_create_nint_constant(func, jit_type_sys_int, 1);
#if defined(HAVE___SIGSETJMP)
	value = jit_insn_call_native
		(func, "__sigsetjmp", (void *)__sigsetjmp,
		 type, args, 2, JIT_CALL_NOTHROW);
#else
	value = jit_insn_call_native
		(func, "sigsetjmp", (void *)sigsetjmp,
		 type, args, 2, JIT_CALL_NOTHROW);
#endif
	jit_type_free(type);
	if(!value)
	{
		return 0;
	}
#else	/* !HAVE_SIGSETJMP */
	type = jit_type_void_ptr;
	type = jit_type_create_signature
		(jit_abi_cdecl, jit_type_int, &type, 1, 1);
	if(!type)
	{
		return 0;
	}
	args[0] = jit_insn_address_of(func, func->builder->setjmp_value);

#if defined(HAVE__SETJMP)
	value = jit_insn_call_native
		(func, "_setjmp", (void *)_setjmp, type, args, 1, JIT_CALL_NOTHROW);

#else
	value = jit_insn_call_native
		(func, "setjmp", (void *)setjmp, type, args, 1, JIT_CALL_NOTHROW);
#endif

	jit_type_free(type);
	if(!value)
	{
		return 0;
	}
#endif	/* !HAVE_SIGSETJMP */

	/* Branch to the end of the init code if "setjmp" returned zero */
	if(!jit_insn_branch_if_not(func, value, &code_label))
	{
		return 0;
	}

	/* We need a value to hold the location of the thrown exception */
	if((func->builder->thrown_pc =
			jit_value_create(func, jit_type_void_ptr)) == 0)
	{
		return 0;
	}

	/* Get the value of "catch_pc" from within "setjmp_value" and store it
	   into the current frame.  This indicates where the exception occurred */
	value = jit_insn_load_relative
		(func, jit_insn_address_of(func, func->builder->setjmp_value),
		 jit_jmp_catch_pc_offset, jit_type_void_ptr);
	if(!value)
	{
		return 0;
	}
	if(!jit_insn_store(func, func->builder->thrown_pc, value))
	{
		return 0;
	}
	if(!jit_insn_branch_if_not(func, value, &rethrow_label))
	{
		return 0;
	}

	/* Clear the original "catch_pc" value within "setjmp_value" */
	if(!jit_insn_store_relative
		(func, jit_insn_address_of(func, func->builder->setjmp_value),
		 jit_jmp_catch_pc_offset, jit_value_create_nint_constant
		 	(func, jit_type_void_ptr, 0)))
	{
		return 0;
	}

	/* Jump to this function's exception catcher */
	if(!jit_insn_branch(func, &(func->builder->catcher_label)))
	{
		return 0;
	}

	/* Mark the position of the rethrow label */
	if(!jit_insn_label(func, &rethrow_label))
	{
		return 0;
	}

	/* Call "_jit_unwind_pop_and_rethrow" to pop the current
	   "setjmp" context and then rethrow the current exception */
	type = jit_type_create_signature
		(jit_abi_cdecl, jit_type_void, 0, 0, 1);
	if(!type)
	{
		return 0;
	}
	jit_insn_call_native
		(func, "_jit_unwind_pop_and_rethrow",
		 (void *)_jit_unwind_pop_and_rethrow, type, 0, 0,
		 JIT_CALL_NOTHROW | JIT_CALL_NORETURN);
	jit_type_free(type);

	/* Insert the target to jump to the normal code. */
	if(!jit_insn_label(func, &code_label))
	{
		return 0;
	}

	/* Force the start of a new block to mark the end of the init code */
	if(!jit_insn_label(func, &end_label))
	{



( run in 0.939 second using v1.01-cache-2.11-cpan-df04353d9ac )