/*
 * Store a value known to be not null in a local variable.
 */
static void _ILJitLocalStoreNotNullValue(ILJITCoder *coder,
										 ILUInt32 paramNum,
										 ILJitValue value)
{
	ILJitLocalSlot *slot = _ILJitLocalGet(coder, paramNum);

	jit_insn_store(coder->jitFunction, slot->value,
				   _ILJitValueConvertImplicit(coder->jitFunction,
											  value,
											  jit_value_get_type(slot->value)));

	slot->flags |= (_IL_JIT_VALUE_INITIALIZED | _IL_JIT_VALUE_NULLCHECKED);
}
/*
 * Initialize the local value to 0.
 */
static int _ILJitLocalInit(ILJITCoder *coder, ILJitLocalSlot *slot)
{
	if((slot->flags & _IL_JIT_VALUE_INITIALIZED) == 0)
	{
		ILJitType type = jit_value_get_type(slot->value);

		if(!jit_type_is_struct(type))
		{
			int typeKind = jit_type_get_kind(type);
			ILJitValue constant = 0;

			if(_JIT_TYPEKIND_IS_FLOAT(typeKind))
			{
				if(!(constant = jit_value_create_nfloat_constant(coder->jitFunction,
															type,
															(jit_nfloat)0)))
				{
					return 0;
				}
				jit_insn_store(coder->jitFunction, slot->value, constant);
			}
			else
			{
				if(_JIT_TYPEKIND_IS_LONG(typeKind))
				{
					if(!(constant = jit_value_create_long_constant(coder->jitFunction,
															  type, (jit_long)0)))
					{
						return 0;
					}
					jit_insn_store(coder->jitFunction, slot->value, constant);
				}
				else
				{
					if(!(constant = jit_value_create_nint_constant(coder->jitFunction,
															  type, (jit_nint)0)))
					{
						return 0;
					}
					jit_insn_store(coder->jitFunction, slot->value, constant);
				}
			}
		}
		slot->flags |= _IL_JIT_VALUE_INITIALIZED;
	}
	return 1;
}
/*
 * Create a new jit_value_t with the type of the existing jit_value_t in the
 * local slot and replace the existing one with the new one.
 * Clear the protect flag afterwards.
 */
static int _ILJitLocalSlotNewValue(ILJITCoder *jitCoder,
                                   ILJitLocalSlot *localSlot)
{
	ILJitType type;
	ILJitValue value;

	if(!(type = jit_value_get_type(localSlot->value)))
	{
		return 0;
	}
	if(!(value = jit_value_create(jitCoder->jitFunction, type)))
	{
		return 0;
	}
	localSlot->value = value;
	localSlot->refValue = 0;
	localSlot->flags &= ~_IL_JIT_VALUE_PROTECT;
	return 1;
}
/*
 * Store a value in a parameter.
 */
static void _ILJitParamStoreValue(ILJITCoder *coder, ILUInt32 paramNum,
													 ILJitValue value)
{
	ILJitLocalSlot *slot = _ILJitParamGet(coder, paramNum);

#ifdef	_IL_JIT_ENABLE_INLINE
	if(slot->flags & _IL_JIT_VALUE_PROTECT)
	{
		if(!(_ILJitLocalSlotNewValue(coder, slot)))
		{
			return;
		}
	}
#endif
	jit_insn_store(coder->jitFunction, slot->value,
				   _ILJitValueConvertImplicit(coder->jitFunction,
											  value,
											  jit_value_get_type(slot->value)));

	slot->flags &= ~_IL_JIT_VALUE_NULLCHECKED;
}
Example #5
0
File: jit-dump.c Project: 8l/lllm
/*@
 * @deftypefun void jit_dump_value (FILE *@var{stream}, jit_function_t @var{func}, jit_value_t @var{value}, const char *@var{prefix})
 * Dump the name of a value to a stdio stream.  If @var{prefix} is not
 * NULL, then it indicates a type prefix to add to the value name.
 * If @var{prefix} is NULL, then this function intuits the type prefix.
 * @end deftypefun
@*/
void jit_dump_value(FILE *stream, jit_function_t func, jit_value_t value, const char *prefix)
{
	jit_pool_block_t block;
	unsigned int block_size;
	unsigned int posn;

	/* Bail out if we have insufficient informaition for the dump */
	if(!stream || !func || !(func->builder) || !value)
	{
		return;
	}

	/* Handle constants and non-local variables */
	if(value->is_constant)
	{
		jit_constant_t const_value;
		char buf[64];
		char *name;
		const_value = jit_value_get_constant(value);
		switch((jit_type_promote_int
					(jit_type_normalize(const_value.type)))->kind)
		{
			case JIT_TYPE_INT:
			{
				if(const_value.un.int_value < 0)
				{
					name = format_integer
						(buf, 1, (jit_ulong)(jit_uint)
							(-(const_value.un.int_value)));
				}
				else
				{
					name = format_integer
						(buf, 0, (jit_ulong)(jit_uint)
							(const_value.un.int_value));
				}
			}
			break;

			case JIT_TYPE_UINT:
			{
				name = format_integer
					(buf, 0, (jit_ulong)(const_value.un.uint_value));
			}
			break;

			case JIT_TYPE_LONG:
			{
				if(const_value.un.long_value < 0)
				{
					name = format_integer
						(buf, 1, (jit_ulong)(-(const_value.un.long_value)));
				}
				else
				{
					name = format_integer
						(buf, 0, (jit_ulong)(const_value.un.long_value));
				}
			}
			break;

			case JIT_TYPE_ULONG:
			{
				name = format_integer(buf, 0, const_value.un.ulong_value);
			}
			break;

			case JIT_TYPE_FLOAT32:
			{
				jit_snprintf(buf, sizeof(buf), "%f",
							 (double)(const_value.un.float32_value));
				name = buf;
			}
			break;

			case JIT_TYPE_FLOAT64:
			{
				jit_snprintf(buf, sizeof(buf), "%f",
							 (double)(const_value.un.float64_value));
				name = buf;
			}
			break;

			case JIT_TYPE_NFLOAT:
			{
				jit_snprintf(buf, sizeof(buf), "%f",
							 (double)(const_value.un.nfloat_value));
				name = buf;
			}
			break;

			default:
			{
				name = "<unknown-constant>";
			}
			break;
		}
		fputs(name, stream);
		return;
	}
	else if(value->is_local && value->block->func != func)
	{
		/* Accessing a local variable in an outer function frame */
		int scope = 0;
		while(func && func->builder && func != value->block->func)
		{
			++scope;
			func = func->nested_parent;
		}
		fprintf(stream, "{%d}", scope);
		if(!func || !(func->builder))
		{
			return;
		}
	}

	/* Intuit the prefix if one was not supplied */
	if(!prefix)
	{
		switch(jit_type_normalize(jit_value_get_type(value))->kind)
		{
			case JIT_TYPE_VOID:			prefix = "v"; break;
			case JIT_TYPE_SBYTE:		prefix = "i"; break;
			case JIT_TYPE_UBYTE:		prefix = "i"; break;
			case JIT_TYPE_SHORT:		prefix = "i"; break;
			case JIT_TYPE_USHORT:		prefix = "i"; break;
			case JIT_TYPE_INT:			prefix = "i"; break;
			case JIT_TYPE_UINT:			prefix = "i"; break;
			case JIT_TYPE_LONG:			prefix = "l"; break;
			case JIT_TYPE_ULONG:		prefix = "l"; break;
			case JIT_TYPE_FLOAT32:		prefix = "f"; break;
			case JIT_TYPE_FLOAT64:		prefix = "d"; break;
			case JIT_TYPE_NFLOAT:		prefix = "D"; break;
			case JIT_TYPE_STRUCT:		prefix = "s"; break;
			case JIT_TYPE_UNION:		prefix = "u"; break;
			default:					prefix = "?"; break;
		}
	}

	/* Get the position of the value within the function's value pool */
	block = func->builder->value_pool.blocks;
	block_size = func->builder->value_pool.elem_size *
				 func->builder->value_pool.elems_per_block;
	posn = 1;
	while(block != 0)
	{
		if(((char *)value) >= block->data &&
		   ((char *)value) < (block->data + block_size))
		{
			posn += (((char *)value) - block->data) /
					func->builder->value_pool.elem_size;
			break;
		}
		posn += func->builder->value_pool.elems_per_block;
		block = block->next;
	}

	/* Dump the prefix and the position, as the value's final name */
	fprintf(stream, "%s%u", prefix, posn);
}
/*
 * Save the current jitStack status to the label.
 * This is done when the label is referenced the first time.
 */
static int _ILJitLabelSaveStack(ILJITCoder *coder, ILJITLabel *label)
{
	int coderStackHeight = _ILJitStackHeight(coder);
#ifdef	_IL_JIT_ENABLE_INLINE
	int coderStackBase;

	if(coder->currentInlineContext)
	{
		coderStackBase = coder->currentInlineContext->stackBase;
		coderStackHeight -= coderStackBase;
	}
	else
	{
		coderStackBase = 0;
	}
#else	/* !_IL_JIT_ENABLE_INLINE */
	int coderStackBase = 0;
#endif	/* !_IL_JIT_ENABLE_INLINE */

	if(((label->labelType & (_IL_JIT_LABEL_NORMAL |
							 _IL_JIT_LABEL_STARTCATCH)) != 0) &&
		(coderStackHeight > 0))
	{
		int current = 0;
		ILJitValue *stack = ILMemStackAllocItem(&(coder->stackStates),
									coderStackHeight * sizeof(ILJitValue));
		if(!stack)
		{
			return 0;
		}
		/* Now save the current stack state. */
		for(current = 0; current < coderStackHeight; current++)
		{
			ILJitStackItem *stackItem = _ILJitStackItemGet(coder, coderStackBase + current);

			stack[current] = _ILJitStackItemValue(*stackItem);
			if(jit_value_is_constant(_ILJitStackItemValue(*stackItem)))
			{
				/* We have to handle this case different. */
				/* Create a local value of the type of the constant. */
				ILJitValue temp = jit_value_create(coder->jitFunction,
												   jit_value_get_type(_ILJitStackItemValue(*stackItem)));
				/* and store the value of the constant in the new temporary. */
				jit_insn_store(coder->jitFunction, temp, _ILJitStackItemValue(*stackItem));
				/* Now replace the constant with the new temporary. */
				stack[current] = temp;
				_ILJitStackItemSetValue(*stackItem, temp);
			}
			else if(_ILJitStackItemNeedsDupOnLabel(*stackItem))
			{
				ILJitValue temp = jit_insn_dup(coder->jitFunction,
											   _ILJitStackItemValue(*stackItem));
				stack[current] = temp;
				_ILJitStackItemSetValue(*stackItem, temp);
			}
		}
		label->jitStack = stack;
		label->stackSize = coderStackHeight;
	}
	return 1;
}
Example #7
0
/*@
 * @deftypefun int _jit_create_call_return_insns (jit_function_t @var{func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, jit_value_t @var{return_value}, int @var{is_nested})
 * Create instructions within @var{func} to clean up after a function call
 * and to place the function's result into @var{return_value}.
 * This should use @code{jit_insn_pop_stack} to pop values off the system
 * stack and @code{jit_insn_return_reg} to tell @code{libjit} which
 * register contains the return value.  In the case of a @code{void}
 * function, @var{return_value} will be NULL.
 *
 * Note: the argument values are passed again because it may not be possible
 * to determine how many bytes to pop from the stack from the @var{signature}
 * alone; especially if the called function is vararg.
 * @end deftypefun
@*/
int _jit_create_call_return_insns
	(jit_function_t func, jit_type_t signature,
	 jit_value_t *args, unsigned int num_args,
	 jit_value_t return_value, int is_nested)
{
	jit_nint pop_items;
	unsigned int size;
	jit_type_t return_type;
	int ptr_return;

	/* Calculate the number of items that we need to pop */
	pop_items = 0;
	while(num_args > 0)
	{
		--num_args;
		size = jit_type_get_size(jit_value_get_type(args[num_args]));
		pop_items += JIT_NUM_ITEMS_IN_STRUCT(size);
	}
	return_type = jit_type_get_return(signature);
	return_type = jit_type_remove_tags(return_type);
	ptr_return = jit_type_return_via_pointer(return_type);
	if(ptr_return)
	{
		++pop_items;
	}
	if(is_nested)
	{
		/* The interpreter needs two arguments for the parent frame info */
		pop_items += 2;
	}

	/* Pop the items from the system stack */
	if(pop_items > 0)
	{
		if(!jit_insn_pop_stack(func, pop_items))
		{
			return 0;
		}
	}

	/* Bail out now if we don't need to worry about return values */
	if(!return_value || ptr_return)
	{
		return 1;
	}

	/* Structure values must be flushed into the frame, and
	   everything else ends up in the top-most stack register */
	if(jit_type_is_struct(return_type) || jit_type_is_union(return_type))
	{
		if(!jit_insn_flush_struct(func, return_value))
		{
			return 0;
		}
	}
	else if(return_type->kind != JIT_TYPE_VOID)
	{
		if(!jit_insn_return_reg(func, return_value, 0))
		{
			return 0;
		}
	}

	/* Everything is back where it needs to be */
	return 1;
}
Example #8
0
/*@
 * @deftypefun int _jit_create_call_setup_insns (jit_function_t @var{func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{is_nested}, int @var{nested_level}, jit_value_t *@var{struct_return}, int @var{flags})
 * Create instructions within @var{func} necessary to set up for a
 * function call to a function with the specified @var{signature}.
 * Use @code{jit_insn_push} to push values onto the system stack,
 * or @code{jit_insn_outgoing_reg} to copy values into call registers.
 *
 * If @var{is_nested} is non-zero, then it indicates that we are calling a
 * nested function within the current function's nested relationship tree.
 * 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.  The @code{jit_insn_setup_for_nested}
 * instruction should be used to create the nested function setup code.
 *
 * If the function returns a structure by pointer, then @var{struct_return}
 * must be set to a new local variable that will contain the returned
 * structure.  Otherwise it should be set to NULL.
 * @end deftypefun
@*/
int _jit_create_call_setup_insns
	(jit_function_t func, jit_type_t signature,
	 jit_value_t *args, unsigned int num_args,
	 int is_nested, int nested_level, jit_value_t *struct_return, int flags)
{
	jit_type_t type;
	jit_type_t vtype;
	jit_value_t value;
	unsigned int arg_num;
	jit_nint offset;
	jit_nuint size;

	/* Regular or tail call? */
	if((flags & JIT_CALL_TAIL) == 0)
	{
		/* Push all of the arguments in reverse order */
		while(num_args > 0)
		{
			--num_args;
			type = jit_type_get_param(signature, num_args);
			type = jit_type_remove_tags(type);
			if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
			{
				/* If the value is a pointer, then we are pushing a structure
				   argument by pointer rather than by local variable */
				vtype = jit_type_normalize(jit_value_get_type(args[num_args]));
				if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE)
				{
					if(!jit_insn_push_ptr(func, args[num_args], type))
					{
						return 0;
					}
					continue;
				}
			}
			if(!jit_insn_push(func, args[num_args]))
			{
				return 0;
			}
		}

		/* Do we need to add a structure return pointer argument? */
		type = jit_type_get_return(signature);
		if(jit_type_return_via_pointer(type))
		{
			value = jit_value_create(func, type);
			if(!value)
			{
				return 0;
			}
			*struct_return = value;
			value = jit_insn_address_of(func, value);
			if(!value)
			{
				return 0;
			}
			if(!jit_insn_push(func, value))
			{
				return 0;
			}
		}
		else if((flags & JIT_CALL_NATIVE) != 0)
		{
			/* Native calls always return a return area pointer */
			if(!jit_insn_push_return_area_ptr(func))
			{
				return 0;
			}
			*struct_return = 0;
		}
		else
		{
			*struct_return = 0;
		}

		/* Do we need to add nested function scope information? */
		if(is_nested)
		{
			if(!jit_insn_setup_for_nested(func, nested_level, -1))
			{
				return 0;
			}
		}
	}
	else
	{
		/* Copy the arguments into our own parameter slots */
		offset = -1;
		if(func->nested_parent)
		{
			offset -= 2;
		}
		type = jit_type_get_return(signature);
		if(jit_type_return_via_pointer(type))
		{
			--offset;
		}
		for(arg_num = 0; arg_num < num_args; ++arg_num)
		{
			type = jit_type_get_param(signature, arg_num);
			value = jit_value_create(func, type);
			if(!value)
			{
				return 0;
			}
			if(!jit_insn_outgoing_frame_posn(func, value, offset))
			{
				return 0;
			}
			type = jit_type_remove_tags(type);
			size = jit_type_get_size(type);
			offset -= (jit_nint)(JIT_NUM_ITEMS_IN_STRUCT(size));
			if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
			{
				/* If the value is a pointer, then we are pushing a structure
				   argument by pointer rather than by local variable */
				vtype = jit_type_normalize(jit_value_get_type(args[arg_num]));
				if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE)
				{
					value = jit_insn_address_of(func, value);
					if(!value)
					{
						return 0;
					}
					if(!jit_insn_memcpy
							(func, value, args[arg_num],
							 jit_value_create_nint_constant
								(func, jit_type_nint, (jit_nint)size)))
					{
						return 0;
					}
					continue;
				}
			}
			if(!jit_insn_store(func, value, args[arg_num]))
			{
				return 0;
			}
		}
		*struct_return = 0;
	}

	/* The call is ready to proceed */
	return 1;
}
Example #9
0
/*@
 * @deftypefun int _jit_create_entry_insns (jit_function_t @var{func})
 * Create instructions in the entry block to initialize the
 * registers and frame offsets that contain the parameters.
 * Returns zero if out of memory.
 *
 * This function is called when a builder is initialized.  It should
 * scan the signature and decide which register or frame position
 * contains each of the parameters and then call either
 * @code{jit_insn_incoming_reg} or @code{jit_insn_incoming_frame_posn}
 * to notify @code{libjit} of the location.
 * @end deftypefun
@*/
int _jit_create_entry_insns(jit_function_t func)
{
	jit_type_t signature = func->signature;
	jit_type_t type;
	jit_nint offset;
	jit_value_t value;
	unsigned int num_params;
	unsigned int param;

	/* Reset the frame size for this function */
	func->builder->frame_size = 0;

	/* The starting parameter offset.  We use negative offsets to indicate
	   an offset into the "args" block, and positive offsets to indicate
	   an offset into the "frame" block.  The negative values will be
	   flipped when we output the argument opcodes for interpretation */
	offset = -1;

	/* If the function is nested, then we need two extra parameters
	   to pass the pointer to the parent's local variables and arguments */
	if(func->nested_parent)
	{
		offset -= 2;
	}

	/* Allocate the structure return pointer */
	value = jit_value_get_struct_pointer(func);
	if(value)
	{
		if(!jit_insn_incoming_frame_posn(func, value, offset))
		{
			return 0;
		}
		--offset;
	}

	/* Allocate the parameter offsets */
	num_params = jit_type_num_params(signature);
	for(param = 0; param < num_params; ++param)
	{
		value = jit_value_get_param(func, param);
		if(!value)
		{
			continue;
		}

		type = jit_type_remove_tags(jit_value_get_type(value));
		switch(type->kind)
		{
		case JIT_TYPE_SBYTE:
		case JIT_TYPE_UBYTE:
			if(!jit_insn_incoming_frame_posn(func, value,
							 offset - _jit_int_lowest_byte()))
			{
				return 0;
			}
			--offset;
			break;

		case JIT_TYPE_SHORT:
		case JIT_TYPE_USHORT:
			if(!jit_insn_incoming_frame_posn(func, value,
							 offset - _jit_int_lowest_short()))
			{
				return 0;
			}
			--offset;
			break;

		case JIT_TYPE_INT:
		case JIT_TYPE_UINT:
		case JIT_TYPE_NINT:
		case JIT_TYPE_NUINT:
		case JIT_TYPE_SIGNATURE:
		case JIT_TYPE_PTR:
		case JIT_TYPE_LONG:
		case JIT_TYPE_ULONG:
		case JIT_TYPE_FLOAT32:
		case JIT_TYPE_FLOAT64:
		case JIT_TYPE_NFLOAT:
			if(!jit_insn_incoming_frame_posn(func, value, offset))
			{
				return 0;
			}
			--offset;
			break;

		case JIT_TYPE_STRUCT:
		case JIT_TYPE_UNION:
			if(!jit_insn_incoming_frame_posn(func, value, offset))
			{
				return 0;
			}
			offset -= JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type));
			break;
		}
	}
	return 1;
}
Example #10
0
/*@
 * @deftypefun void _jit_gen_insn (jit_gencode_t @var{gen}, jit_function_t @var{func}, jit_block_t @var{block}, jit_insn_t @var{insn})
 * Generate native code for the specified @var{insn}.  This function should
 * call the appropriate register allocation routines, output the instruction,
 * and then arrange for the result to be placed in an appropriate register
 * or memory destination.
 * @end deftypefun
@*/
void _jit_gen_insn(jit_gencode_t gen, jit_function_t func,
				   jit_block_t block, jit_insn_t insn)
{
	jit_label_t label;
	void **pc;
	jit_nint offset;
	jit_nint size;

	switch(insn->opcode)
	{
	case JIT_OP_BR_IEQ:
	case JIT_OP_BR_INE:
	case JIT_OP_BR_ILT:
	case JIT_OP_BR_ILT_UN:
	case JIT_OP_BR_ILE:
	case JIT_OP_BR_ILE_UN:
	case JIT_OP_BR_IGT:
	case JIT_OP_BR_IGT_UN:
	case JIT_OP_BR_IGE:
	case JIT_OP_BR_IGE_UN:
	case JIT_OP_BR_LEQ:
	case JIT_OP_BR_LNE:
	case JIT_OP_BR_LLT:
	case JIT_OP_BR_LLT_UN:
	case JIT_OP_BR_LLE:
	case JIT_OP_BR_LLE_UN:
	case JIT_OP_BR_LGT:
	case JIT_OP_BR_LGT_UN:
	case JIT_OP_BR_LGE:
	case JIT_OP_BR_LGE_UN:
	case JIT_OP_BR_FEQ:
	case JIT_OP_BR_FNE:
	case JIT_OP_BR_FLT:
	case JIT_OP_BR_FLE:
	case JIT_OP_BR_FGT:
	case JIT_OP_BR_FGE:
	case JIT_OP_BR_FLT_INV:
	case JIT_OP_BR_FLE_INV:
	case JIT_OP_BR_FGT_INV:
	case JIT_OP_BR_FGE_INV:
	case JIT_OP_BR_DEQ:
	case JIT_OP_BR_DNE:
	case JIT_OP_BR_DLT:
	case JIT_OP_BR_DLE:
	case JIT_OP_BR_DGT:
	case JIT_OP_BR_DGE:
	case JIT_OP_BR_DLT_INV:
	case JIT_OP_BR_DLE_INV:
	case JIT_OP_BR_DGT_INV:
	case JIT_OP_BR_DGE_INV:
	case JIT_OP_BR_NFEQ:
	case JIT_OP_BR_NFNE:
	case JIT_OP_BR_NFLT:
	case JIT_OP_BR_NFLE:
	case JIT_OP_BR_NFGT:
	case JIT_OP_BR_NFGE:
	case JIT_OP_BR_NFLT_INV:
	case JIT_OP_BR_NFLE_INV:
	case JIT_OP_BR_NFGT_INV:
	case JIT_OP_BR_NFGE_INV:
		/* Binary branch */
		load_value(gen, insn->value2, 2);
		/* Fall through */

	case JIT_OP_BR_IFALSE:
	case JIT_OP_BR_ITRUE:
	case JIT_OP_BR_LFALSE:
	case JIT_OP_BR_LTRUE:
		/* Unary branch */
		load_value(gen, insn->value1, 1);
		/* Fall through */

	case JIT_OP_BR:
	case JIT_OP_CALL_FINALLY:
		/* Unconditional branch */
	branch:
		label = (jit_label_t)(insn->dest);
		pc = (void **)(gen->ptr);
		jit_cache_opcode(gen, insn->opcode);
		block = jit_block_from_label(func, label);
		if(!block)
		{
			break;
		}
		if(block->address)
		{
			/* We already know the address of the block */
			jit_cache_native(gen, ((void **)(block->address)) - pc);
		}
		else
		{
			/* Record this position on the block's fixup list */
			jit_cache_native(gen, block->fixup_list);
			block->fixup_list = (void *)pc;
		}
		break;

	case JIT_OP_CALL_FILTER:
		/* Branch to a filter subroutine, load the filter
		   parameter to the r0 register */
		load_value(gen, insn->value1, 0);
		goto branch;

	case JIT_OP_JUMP_TABLE:
	{
		jit_label_t *labels;
		jit_nint num_labels;
		jit_nint index;

		load_value(gen, insn->dest, 0);

		labels = (jit_label_t *) insn->value1->address;
		num_labels = insn->value2->address;

		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, num_labels);
		for(index = 0; index < num_labels; index++)
		{
			block = jit_block_from_label(func, labels[index]);
			if(!block)
			{
				return;
			}
			if(block->address)
			{
				/* We already know the address of the block */
				jit_cache_native(gen, block->address);
			}
			else
			{
				/* Record this position on the block's fixup list */
				pc = (void **)(gen->ptr);
				jit_cache_native(gen, block->fixup_absolute_list);
				block->fixup_absolute_list = pc;
			}
		}
		break;
	}

	case JIT_OP_ADDRESS_OF_LABEL:
		/* Get the address of a particular label */
		label = (jit_label_t)(insn->value1);
		block = jit_block_from_label(func, label);
		if(!block)
		{
			break;
		}
		pc = (void **)(gen->ptr);
		jit_cache_opcode(gen, insn->opcode);
		if(block->address)
		{
			/* We already know the address of the block */
			jit_cache_native(gen, ((void **)(block->address)) - pc);
		}
		else
		{
			/* Record this position on the block's fixup list */
			jit_cache_native(gen, block->fixup_list);
			block->fixup_list = (void *)pc;
		}
		store_value(gen, insn->dest);
		break;

	case JIT_OP_CALL:
	case JIT_OP_CALL_TAIL:
		/* Call a function, whose pointer is supplied explicitly */
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, (jit_nint)(insn->dest));
		break;

	case JIT_OP_CALL_INDIRECT:
	case JIT_OP_CALL_INDIRECT_TAIL:
		/* Call a function, whose pointer is supplied in the register */
		load_value(gen, insn->value1, 1);
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, (jit_nint)(insn->value2));
		jit_cache_native(gen, (jit_nint)
				 (jit_type_num_params((jit_type_t)(insn->value2))));
		break;

	case JIT_OP_CALL_VTABLE_PTR:
	case JIT_OP_CALL_VTABLE_PTR_TAIL:
		/* Call a function, whose vtable pointer is supplied in the register */
		load_value(gen, insn->value1, 1);
		jit_cache_opcode(gen, insn->opcode);
		break;

	case JIT_OP_CALL_EXTERNAL:
	case JIT_OP_CALL_EXTERNAL_TAIL:
		/* Call a native function, whose pointer is supplied explicitly */
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, (jit_nint)(insn->value2));
		jit_cache_native(gen, (jit_nint)(insn->dest));
		jit_cache_native(gen, (jit_nint)
				 (jit_type_num_params((jit_type_t)(insn->value2))));
		break;

	case JIT_OP_RETURN:
		/* Return from the current function with no result */
		jit_cache_opcode(gen, JIT_OP_RETURN);
		break;

	case JIT_OP_RETURN_INT:
	case JIT_OP_RETURN_LONG:
	case JIT_OP_RETURN_FLOAT32:
	case JIT_OP_RETURN_FLOAT64:
	case JIT_OP_RETURN_NFLOAT:
		/* Return from the current function with a specific result */
		load_value(gen, insn->value1, 1);
		jit_cache_opcode(gen, insn->opcode);
		break;

	case JIT_OP_RETURN_SMALL_STRUCT:
		/* Return from current function with a small structure result */
		load_value(gen, insn->value1, 1);
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, jit_value_get_nint_constant(insn->value2));
		break;

	case JIT_OP_SETUP_FOR_NESTED:
		/* TODO!!! */
		/* Set up to call a nested child */
		jit_cache_opcode(gen, insn->opcode);
		adjust_working(gen, 2);
		break;

	case JIT_OP_SETUP_FOR_SIBLING:
		/* TODO!!! */
		/* Set up to call a nested sibling */
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, jit_value_get_nint_constant(insn->value1));
		adjust_working(gen, 2);
		break;

	case JIT_OP_IMPORT:
		/* Import a local variable from an outer nested scope */
		_jit_gen_fix_value(insn->value1);
		if(insn->value1->frame_offset >= 0)
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_IMPORT_LOCAL);
			jit_cache_native(gen, insn->value1->frame_offset);
			jit_cache_native(gen, jit_value_get_nint_constant(insn->value2));
		}
		else
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_IMPORT_ARG);
			jit_cache_native(gen, -(insn->value1->frame_offset + 1));
			jit_cache_native(gen, jit_value_get_nint_constant(insn->value2));
		}
		store_value(gen, insn->dest);
		break;

	case JIT_OP_THROW:
		/* Throw an exception */
		load_value(gen, insn->value1, 1);
		jit_cache_opcode(gen, insn->opcode);
		break;

	case JIT_OP_LOAD_PC:
	case JIT_OP_LOAD_EXCEPTION_PC:
		/* Load the current program counter onto the stack */
		jit_cache_opcode(gen, insn->opcode);
		store_value(gen, insn->dest);
		break;

	case JIT_OP_CALL_FILTER_RETURN:
		/* The r0 register currently contains "dest" */
		store_value(gen, insn->dest);
		break;

	case JIT_OP_ENTER_FINALLY:
		/* Record that the finally return address is on the stack */
		++(gen->extra_working_space);
		break;

	case JIT_OP_LEAVE_FINALLY:
		/* Leave a finally clause */
		jit_cache_opcode(gen, insn->opcode);
		break;

	case JIT_OP_ENTER_FILTER:
		/* The top of the stack contains the return address,
		   the r0 register contains the "dest" (filter parameter). */
		++(gen->extra_working_space);
		store_value(gen, insn->dest);
		break;

	case JIT_OP_LEAVE_FILTER:
		/* Leave a filter clause, returning a particular value */
		load_value(gen, insn->value1, 0);
		jit_cache_opcode(gen, insn->opcode);
		break;

	case JIT_OP_INCOMING_REG:
		/* Store incoming value (in interpreter this is used to
		   pass an exception object to the catcher) */
		store_value(gen, insn->value1);
		break;

	case JIT_OP_RETURN_REG:
		/* Push a function return value back onto the stack */
		switch(jit_type_normalize(insn->value1->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:
			jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_INT);
			store_value(gen, insn->value1);
			break;

		case JIT_TYPE_LONG:
		case JIT_TYPE_ULONG:
			jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_LONG);
			store_value(gen, insn->value1);
			break;

		case JIT_TYPE_FLOAT32:
			jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_FLOAT32);
			store_value(gen, insn->value1);
			break;

		case JIT_TYPE_FLOAT64:
			jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_FLOAT64);
			store_value(gen, insn->value1);
			break;

		case JIT_TYPE_NFLOAT:
			jit_cache_opcode(gen, JIT_INTERP_OP_LDR_0_NFLOAT);
			store_value(gen, insn->value1);
			break;
		}
		break;

	case JIT_OP_COPY_LOAD_SBYTE:
	case JIT_OP_COPY_LOAD_UBYTE:
	case JIT_OP_COPY_LOAD_SHORT:
	case JIT_OP_COPY_LOAD_USHORT:
	case JIT_OP_COPY_INT:
	case JIT_OP_COPY_LONG:
	case JIT_OP_COPY_FLOAT32:
	case JIT_OP_COPY_FLOAT64:
	case JIT_OP_COPY_NFLOAT:
	case JIT_OP_COPY_STORE_BYTE:
	case JIT_OP_COPY_STORE_SHORT:
		/* Copy a value from one temporary variable to another */
		load_value(gen, insn->value1, 0);
		store_value(gen, insn->dest);
		break;

	case JIT_OP_COPY_STRUCT:
		/* Copy a struct from one address to another */
		load_value(gen, insn->dest, 0);
		load_value(gen, insn->value1, 1);
		size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->dest));
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, size);
		break;

	case JIT_OP_ADDRESS_OF:
		/* Get the address of a local variable */
		_jit_gen_fix_value(insn->value1);
		if(insn->value1->frame_offset >= 0)
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_LDLA_0);
			jit_cache_native(gen, insn->value1->frame_offset);
		}
		else
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_LDAA_0);
			jit_cache_native(gen, -(insn->value1->frame_offset + 1));
		}
		store_value(gen, insn->dest);
		break;

	case JIT_OP_PUSH_INT:
	case JIT_OP_PUSH_LONG:
	case JIT_OP_PUSH_FLOAT32:
	case JIT_OP_PUSH_FLOAT64:
	case JIT_OP_PUSH_NFLOAT:
		/* Push an item onto the stack, ready for a function call */
		load_value(gen, insn->value1, 1);
		jit_cache_opcode(gen, insn->opcode);
		adjust_working(gen, 1);
		break;

	case JIT_OP_PUSH_STRUCT:
		/* Load the pointer value */
		load_value(gen, insn->value1, 1);
		/* Push the structure at the designated pointer */
		size = jit_value_get_nint_constant(insn->value2);
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, size);
		adjust_working(gen, JIT_NUM_ITEMS_IN_STRUCT(size));
		break;

	case JIT_OP_PUSH_RETURN_AREA_PTR:
		/* Push the address of the interpreter's return area */
		jit_cache_opcode(gen, insn->opcode);
		adjust_working(gen, 1);
		break;

	case JIT_OP_POP_STACK:
		/* Pop parameter values from the stack after a function returns */
		size = jit_value_get_nint_constant(insn->value1);
		if(size == 1)
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_POP);
		}
		else if(size == 2)
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_POP_2);
		}
		else if(size == 3)
		{
			jit_cache_opcode(gen, JIT_INTERP_OP_POP_3);
		}
		else if(size != 0)
		{
			jit_cache_opcode(gen, JIT_OP_POP_STACK);
			jit_cache_native(gen, size);
		}
		break;

	case JIT_OP_FLUSH_SMALL_STRUCT:
		/* Flush a small structure return value back into the frame */
		load_value(gen, insn->value1, 0);
		size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->value1));
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, size);
		break;

	case JIT_OP_LOAD_RELATIVE_SBYTE:
	case JIT_OP_LOAD_RELATIVE_UBYTE:
	case JIT_OP_LOAD_RELATIVE_SHORT:
	case JIT_OP_LOAD_RELATIVE_USHORT:
	case JIT_OP_LOAD_RELATIVE_INT:
	case JIT_OP_LOAD_RELATIVE_LONG:
	case JIT_OP_LOAD_RELATIVE_FLOAT32:
	case JIT_OP_LOAD_RELATIVE_FLOAT64:
	case JIT_OP_LOAD_RELATIVE_NFLOAT:
		/* Load a value from a relative pointer */
		load_value(gen, insn->value1, 1);
		offset = jit_value_get_nint_constant(insn->value2);
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, offset);
		store_value(gen, insn->dest);
		break;

	case JIT_OP_LOAD_RELATIVE_STRUCT:
		/* Load a structured value from a relative pointer */
		load_value(gen, insn->dest, 0);
		load_value(gen, insn->value1, 1);
		offset = jit_value_get_nint_constant(insn->value2);
		size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->dest));
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, offset);
		jit_cache_native(gen, size);
		break;

	case JIT_OP_STORE_RELATIVE_BYTE:
	case JIT_OP_STORE_RELATIVE_SHORT:
	case JIT_OP_STORE_RELATIVE_INT:
	case JIT_OP_STORE_RELATIVE_LONG:
	case JIT_OP_STORE_RELATIVE_FLOAT32:
	case JIT_OP_STORE_RELATIVE_FLOAT64:
	case JIT_OP_STORE_RELATIVE_NFLOAT:
		/* Store a value to a relative pointer */
		load_value(gen, insn->dest, 0);
		load_value(gen, insn->value1, 1);
		offset = jit_value_get_nint_constant(insn->value2);
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, offset);
		break;

	case JIT_OP_STORE_RELATIVE_STRUCT:
		/* Store a structured value to a relative pointer */
		load_value(gen, insn->dest, 0);
		load_value(gen, insn->value1, 1);
		offset = jit_value_get_nint_constant(insn->value2);
		size = (jit_nint)jit_type_get_size(jit_value_get_type(insn->value1));
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, offset);
		jit_cache_native(gen, size);
		break;

	case JIT_OP_ADD_RELATIVE:
		/* Add a relative offset to a pointer */
		offset = jit_value_get_nint_constant(insn->value2);
		if(offset != 0)
		{
			load_value(gen, insn->value1, 1);
			jit_cache_opcode(gen, insn->opcode);
			jit_cache_native(gen, offset);
			store_value(gen, insn->dest);
		}
		else
		{
			load_value(gen, insn->value1, 0);
			store_value(gen, insn->dest);
		}
		break;

	case JIT_OP_MARK_BREAKPOINT:
		/* Mark the current location as a potential breakpoint */
		jit_cache_opcode(gen, insn->opcode);
		jit_cache_native(gen, insn->value1->address);
		jit_cache_native(gen, insn->value2->address);
		break;

	default:
		if(insn->dest && (insn->flags & JIT_INSN_DEST_IS_VALUE) != 0)
		{
			load_value(gen, insn->dest, 0);
		}
		if(insn->value1)
		{
			load_value(gen, insn->value1, 1);
		}
		if(insn->value2)
		{
			load_value(gen, insn->value2, 2);
		}
		jit_cache_opcode(gen, insn->opcode);
		if(insn->dest && (insn->flags & JIT_INSN_DEST_IS_VALUE) == 0)
		{
			store_value(gen, insn->dest);
		}
		break;
	}
}
Example #11
0
int _jit_create_call_return_insns
	(jit_function_t func, jit_type_t signature,
	 jit_value_t *args, unsigned int num_args,
	 jit_value_t return_value, int is_nested)
{
	jit_nint pop_bytes;
	unsigned int size;
	jit_type_t return_type;
	int ptr_return;

	/* Calculate the number of bytes that we need to pop */
	return_type = jit_type_normalize(jit_type_get_return(signature));
	ptr_return = jit_type_return_via_pointer(return_type);
#if JIT_APPLY_X86_FASTCALL == 1
	if(jit_type_get_abi(signature) == jit_abi_stdcall ||
        jit_type_get_abi(signature) == jit_abi_thiscall||
        jit_type_get_abi(signature) == jit_abi_fastcall)
	{
		/* STDCALL, THISCALL and FASTCALL functions pop their own arguments */
		pop_bytes = 0;
	}
	else
#endif
	{
		pop_bytes = 0;
		while(num_args > 0)
		{
			--num_args;
			size = jit_type_get_size(jit_value_get_type(args[num_args]));
			pop_bytes += ROUND_STACK(size);
		}
#if JIT_APPLY_X86_POP_STRUCT_RETURN == 1
		if(ptr_return && is_nested)
		{
			/* Note: we only need this for nested functions, because
			   regular functions will pop the structure return for us */
			pop_bytes += sizeof(void *);
		}
#else
		if(ptr_return)
		{
			pop_bytes += sizeof(void *);
		}
#endif
		if(is_nested)
		{
			pop_bytes += sizeof(void *);
		}
	}

	/* Pop the bytes from the system stack */
	if(pop_bytes > 0)
	{
		if(!jit_insn_defer_pop_stack(func, pop_bytes))
		{
			return 0;
		}
	}

	/* Bail out now if we don't need to worry about return values */
	if(!return_value || ptr_return)
	{
		return 1;
	}

	/* Structure values must be flushed into the frame, and
	   everything else ends up in a register */
	if(jit_type_is_struct(return_type) || jit_type_is_union(return_type))
	{
		if(!jit_insn_flush_struct(func, return_value))
		{
			return 0;
		}
	}
	else if(return_type == jit_type_float32 ||
			return_type == jit_type_float64 ||
            return_type == jit_type_nfloat  ||
            jit_type_get_kind(return_type) == JIT_TYPE_FLOAT32 ||
            jit_type_get_kind(return_type) == JIT_TYPE_FLOAT64 ||
            jit_type_get_kind(return_type) == JIT_TYPE_NFLOAT )
	{
		if(!jit_insn_return_reg(func, return_value, X86_REG_ST0))
		{
			return 0;
		}
	}
	else if(return_type->kind != JIT_TYPE_VOID)
	{
		if(!jit_insn_return_reg(func, return_value, X86_REG_EAX))
		{
			return 0;
		}
	}

	/* Everything is back where it needs to be */
	return 1;
}