Alien-LibJIT

 view release on metacpan or  search on metacpan

libjit/jit/jit-rules-x86-64.c  view on Meta::CPAN

#define	X86_64_REG_XMM8		24
#define	X86_64_REG_XMM9		25
#define	X86_64_REG_XMM10	26
#define	X86_64_REG_XMM11	27
#define	X86_64_REG_XMM12	28
#define	X86_64_REG_XMM13	29
#define	X86_64_REG_XMM14	30
#define	X86_64_REG_XMM15	31
#define	X86_64_REG_ST0		32
#define	X86_64_REG_ST1		33
#define	X86_64_REG_ST2		34
#define	X86_64_REG_ST3		35
#define	X86_64_REG_ST4		36
#define	X86_64_REG_ST5		37
#define	X86_64_REG_ST6		38
#define	X86_64_REG_ST7		39

/*
 * Determine if a pseudo register number is general, xmm or fpu.
 */
#define	IS_GENERAL_REG(reg)	(((reg) & ~0x0f) == 0)
#define	IS_XMM_REG(reg)		(((reg) & ~0x0f) == 0x10)
#define	IS_FPU_REG(reg)		(((reg) & ~0x0f) == 0x20)

/*
 * Scratch register, that is used for calls via register and
 * for loading the exception pc to the setjmp buffer.
 * This register *MUST* not be used for parameter passing and
 * *MUST* not be a callee saved register.
 * For SysV abi R11 is perfect.
 */
#define X86_64_SCRATCH X86_64_R11

/*
 * Set this definition to 1 if the OS supports the SysV red zone.
 * This is a 128 byte area below the stack pointer that is guaranteed
 * to be not modified by interrupts or signal handlers.
 * This allows us to use a temporary area on the stack without
 * having to modify the stack pointer saving us two instructions.
 * TODO: Make this a configure switch.
 */
#define HAVE_RED_ZONE 1

/*
 * Some declarations that should be replaced by querying the cpuinfo
 * if generating code for the current cpu.
 */
/*
#define HAVE_X86_SSE_4_1 0
#define HAVE_X86_SSE_4 0
#define HAVE_X86_SSE_3 0
#define HAVE_X86_FISTTP 0
*/

#define	TODO() \
do { \
	fprintf(stderr, "TODO at %s, %d\n", __FILE__, (int)__LINE__); \
} while(0)

/*
 * Setup or teardown the x86 code output process.
 */
#define	jit_cache_setup_output(needed)		\
	unsigned char *inst = gen->ptr;		\
	_jit_gen_check_space(gen, (needed))

#define	jit_cache_end_output()	\
	gen->ptr = inst

/*
 * Set this to 1 for debugging fixups
 */
#define DEBUG_FIXUPS 0

/*
 * The maximum block size copied inline
 */
#define _JIT_MAX_MEMCPY_INLINE	0x40

/*
 * va_list type as specified in x86_64 sysv abi version 0.99
 * Figure 3.34
 */
typedef struct
{
	unsigned int gp_offset;
	unsigned int fp_offset;
	void *overflow_arg_area;
	void *reg_save_area;
} _jit_va_list;

/* Registers used for INTEGER arguments */
static int _jit_word_arg_regs[] = {X86_64_REG_RDI, X86_64_REG_RSI,
								   X86_64_REG_RDX, X86_64_REG_RCX,
								   X86_64_REG_R8, X86_64_REG_R9};
#define _jit_num_word_regs	6

/* Registers used for float arguments */
static int _jit_float_arg_regs[] = {X86_64_REG_XMM0, X86_64_REG_XMM1,
									X86_64_REG_XMM2, X86_64_REG_XMM3,
									X86_64_REG_XMM4, X86_64_REG_XMM5,
									X86_64_REG_XMM6, X86_64_REG_XMM7};
#define _jit_num_float_regs	8

/* Registers used for returning INTEGER values */
static int _jit_word_return_regs[] = {X86_64_REG_RAX, X86_64_REG_RDX};
#define _jit_num_word_return_regs	2

/* Registers used for returning sse values */
static int _jit_sse_return_regs[] = {X86_64_REG_XMM0, X86_64_REG_XMM1};
#define _jit_num_sse_return_regs	2

/*
 * X86_64 register classes
 */
static _jit_regclass_t *x86_64_reg;		/* X86_64 general purpose registers */
static _jit_regclass_t *x86_64_creg;	/* X86_64 call clobbered general */
										/* purpose registers */
static _jit_regclass_t *x86_64_dreg;	/* general purpose registers that */
										/* can be used as divisor */
										/* (all but %rax and %rdx) */

libjit/jit/jit-rules-x86-64.c  view on Meta::CPAN

	if(value)
	{
		jit_type_t type = jit_type_normalize(value->type);

		_jit_gen_fix_value(value);

		_spill_reg(&inst, type, value->global_reg, value->frame_offset);
	}
	else
	{
		x86_64_push_reg_size(inst, _jit_reg_info[reg].cpu_reg, 8);
	}
	jit_cache_end_output();
}

void
_jit_gen_load_global(jit_gencode_t gen, int reg, jit_value_t value)
{
	jit_cache_setup_output(16);
	if(value)
	{
		x86_64_mov_reg_membase_size(inst,
			_jit_reg_info[value->global_reg].cpu_reg,
			X86_64_RBP, value->frame_offset, 8);
	}
	else
	{
		x86_64_pop_reg_size(inst, _jit_reg_info[reg].cpu_reg, 8);
	}
	jit_cache_end_output();
}

void
_jit_gen_spill_reg(jit_gencode_t gen, int reg,
				   int other_reg, jit_value_t value)
{
	jit_type_t type;

	/* Make sure that we have sufficient space */
	jit_cache_setup_output(16);

	/* If the value is associated with a global register, then copy to that */
	if(value->has_global_register)
	{
		reg = _jit_reg_info[reg].cpu_reg;
		other_reg = _jit_reg_info[value->global_reg].cpu_reg;
		x86_64_mov_reg_reg_size(inst, other_reg, reg, sizeof(void *));
		jit_cache_end_output();
		return;
	}

	/* Fix the value in place within the local variable frame */
	_jit_gen_fix_value(value);

	/* Get the normalized type */
	type = jit_type_normalize(value->type);

	/* and spill the register */
	_spill_reg(&inst, type, reg, value->frame_offset);

	/* End the code output process */
	jit_cache_end_output();
}

void
_jit_gen_free_reg(jit_gencode_t gen, int reg,
		  int other_reg, int value_used)
{
	/* We only need to take explicit action if we are freeing a
	   floating-point register whose value hasn't been used yet */
	if(!value_used && IS_FPU_REG(reg))
	{
		_jit_gen_check_space(gen, 2);
		x86_fstp(gen->ptr, reg - X86_64_REG_ST0);
	}
}

/*
 * Set a register value based on a condition code.
 */
static unsigned char *
setcc_reg(unsigned char *inst, int reg, int cond, int is_signed)
{
	/* Use a SETcc instruction if we have a basic register */
	x86_64_set_reg(inst, cond, reg, is_signed);
	x86_64_movzx8_reg_reg_size(inst, reg, reg, 4);
	return inst;
}

/*
 * Helper macros for fixup handling.
 *
 * We have only 4 bytes for the jump offsets.
 * Therefore we have do something tricky here.
 * The fixup pointer in the block/gen points to the last fixup.
 * The fixup itself contains the offset to the previous fixup or
 * null if it's the last fixup in the list.
 */

/*
 * Calculate the fixup value
 * This is the value stored as placeholder in the instruction.
 */
#define _JIT_CALC_FIXUP(fixup_list, inst) \
	((jit_int)((jit_nint)(inst) - (jit_nint)(fixup_list)))

/*
 * Calculate the pointer to the fixup value.
 */
#define _JIT_CALC_NEXT_FIXUP(fixup_list, fixup) \
	((fixup) ? ((jit_nint)(fixup_list) - (jit_nint)(fixup)) : (jit_nint)0)

/*
 * Get the long form of a branch opcode.
 */
static int
long_form_branch(int opcode)
{
	if(opcode == 0xEB)
	{
		return 0xE9;

libjit/jit/jit-rules-x86-64.c  view on Meta::CPAN

		/* Fix the value in place within the local variable frame */
		_jit_gen_fix_value(value);

		/* Output an appropriate instruction to spill the value */
		offset = (int)(value->frame_offset);

		/* Spill the top of the floating-point register stack */
		switch(jit_type_normalize(value->type)->kind)
		{
			case JIT_TYPE_FLOAT32:
			{
				if(pop)
				{
					x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 4);
				}
				else
				{
					x86_64_fst_membase_size(inst, X86_64_RBP, offset, 4);
				}
			}
			break;

			case JIT_TYPE_FLOAT64:
			{
				if(pop)
				{
					x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 8);
				}
				else
				{
					x86_64_fst_membase_size(inst, X86_64_RBP, offset, 8);
				}
			}
			break;

			case JIT_TYPE_NFLOAT:
			{
				if(sizeof(jit_nfloat) == sizeof(jit_float64))
				{
					if(pop)
					{
						x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 8);
					}
					else
					{
						x86_64_fst_membase_size(inst, X86_64_RBP, offset, 8);
					}
				}
				else
				{
					x86_64_fstp_membase_size(inst, X86_64_RBP, offset, 10);
					if(!pop)
					{
						x86_64_fld_membase_size(inst, X86_64_RBP, offset, 10);
					}
				}
			}
			break;
		}

		/* End the code output process */
		jit_cache_end_output();
	}
}

void
_jit_gen_load_value(jit_gencode_t gen, int reg, int other_reg, jit_value_t value)
{
	jit_type_t type;
	int src_reg, other_src_reg;
	void *ptr;
	int offset;

	/* Make sure that we have sufficient space */
	jit_cache_setup_output(16);

	type = jit_type_normalize(value->type);

	/* Load zero */
	if(value->is_constant)
	{
		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:
			{
				if((jit_nint)(value->address) == 0)
				{
					x86_64_clear_reg(inst, _jit_reg_info[reg].cpu_reg);
				}
				else
				{
					x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
							(jit_nint)(value->address), 4);
				}
			}
			break;

			case JIT_TYPE_LONG:
			case JIT_TYPE_ULONG:
			{
				if((jit_nint)(value->address) == 0)
				{
					x86_64_clear_reg(inst, _jit_reg_info[reg].cpu_reg);
				}
				else
				{
					if((jit_nint)(value->address) > 0 && (jit_nint)(value->address) <= (jit_nint)jit_max_uint)
					{
						x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
								(jit_nint)(value->address), 4);

					}
					else
					{
						x86_64_mov_reg_imm_size(inst, _jit_reg_info[reg].cpu_reg,
								(jit_nint)(value->address), 8);

libjit/jit/jit-rules-x86-64.c  view on Meta::CPAN

			case JIT_TYPE_STRUCT:
			case JIT_TYPE_UNION:
			{
				jit_nuint size = jit_type_get_size(type);

				if(IS_GENERAL_REG(reg))
				{
					if(size == 1)
					{
						x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
													X86_64_RBP, offset, 1);
					}
					else if(size == 2)
					{
						x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
													X86_64_RBP, offset, 2);
					}
					else if(size <= 4)
					{
						x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
													X86_64_RBP, offset, 4);
					}
					else if(size <= 8)
					{
						x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
													X86_64_RBP, offset, 8);
					}
				}
				else if(IS_XMM_REG(reg))
				{
					if(size <= 4)
					{
						x86_64_movss_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
												 X86_64_RBP, offset);
					}
					else if(size <= 8)
					{
						x86_64_movsd_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
												 X86_64_RBP, offset);
					}
					else
					{
						int alignment = jit_type_get_alignment(type);

						if((alignment & 0xf) == 0)
						{
							x86_64_movaps_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
													  X86_64_RBP, offset);
						}
						else
						{
							x86_64_movups_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
													  X86_64_RBP, offset);
						}
					}
				}
			}
		}
	}

	/* End the code output process */
	jit_cache_end_output();
}

void
_jit_gen_get_elf_info(jit_elf_info_t *info)
{
	info->machine = 62;	/* EM_X86_64 */
	info->abi = 0;		/* ELFOSABI_SYSV */
	info->abi_version = 0;
}

void *
_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf)
{
	unsigned char prolog[JIT_PROLOG_SIZE];
	unsigned char *inst = prolog;
	int reg;
	int frame_size = 0;
	int regs_to_save = 0;

	/* Push ebp onto the stack */
	x86_64_push_reg_size(inst, X86_64_RBP, 8);

	/* Initialize EBP for the current frame */
	x86_64_mov_reg_reg_size(inst, X86_64_RBP, X86_64_RSP, 8);

	/* Allocate space for the local variable frame */
	if(func->builder->frame_size > 0)
	{
		/* Make sure that the framesize is a multiple of 8 bytes */
		frame_size = (func->builder->frame_size + 0x7) & ~0x7;
	}

	/* Get the number of registers we need to preserve */
	for(reg = 0; reg < 14; ++reg)
	{
		if(jit_reg_is_used(gen->touched, reg) &&
		   (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
		{
			++regs_to_save;
		}
	}

	/* add the register save area to the initial frame size */
	frame_size += (regs_to_save << 3);

#ifdef JIT_USE_PARAM_AREA
	/* Add the param area to the frame_size if the additional offset
	   doesnt cause the offsets in the register saves become 4 bytes */
	if(func->builder->param_area_size > 0 &&
	   (func->builder->param_area_size <= 0x50 || regs_to_save == 0))
	{
		frame_size += func->builder->param_area_size;
	}
#endif /* JIT_USE_PARAM_AREA */

	/* Make sure that the framesize is a multiple of 16 bytes */
	/* so that the final RSP will be alligned on a 16byte boundary. */
	frame_size = (frame_size + 0xf) & ~0xf;

libjit/jit/jit-rules-x86-64.c  view on Meta::CPAN

		*struct_return = value;
		return_ptr = jit_insn_address_of(func, value);
		if(!return_ptr)
		{
			return 0;
		}
		jit_memset(&struct_return_param, 0, sizeof(_jit_param_t));
		struct_return_param.value = return_ptr;
		if(!(_jit_classify_param(&passing, &struct_return_param,
								 jit_type_void_ptr)))
		{
			return 0;
		}
	}
	else
	{
		*struct_return = 0;
		return_ptr = 0;
	}

	/* Let the backend classify the parameters */
	for(current_param = 0; current_param < num_args; current_param++)
	{
		jit_type_t param_type;

		param_type = jit_type_get_param(signature, current_param);
		param_type = jit_type_normalize(param_type);
		
		if(!(_jit_classify_param(&passing, &(passing.params[current_param]),
								 param_type)))
		{
			return 0;
		}
		/* Set the argument value */
		passing.params[current_param].value = args[current_param];
	}

	/* Let the backend do final adjustments to the passing area */
	_jit_fix_call_stack(&passing);

#ifdef JIT_USE_PARAM_AREA
	if(passing.stack_size > func->builder->param_area_size)
	{
		func->builder->param_area_size = passing.stack_size;
	}
#else
	/* Flush deferred stack pops from previous calls if too many
	   parameters have collected up on the stack since last time */
	if(!jit_insn_flush_defer_pop(func, 32 - passing.stack_size))
	{
		return 0;
	}

	if(!_jit_setup_call_stack(func, &passing))
	{
		return 0;
	}
#endif

	/* Now setup the arguments on the stack or in the registers in reverse order */
	/* First process the params passed on the stack */
	current_param = num_args;
	while(current_param > 0)
	{
		--current_param;
		if(param[current_param].arg_class == JIT_ARG_CLASS_STACK)
		{
			jit_type_t param_type;

			param_type = jit_type_get_param(signature, current_param);
			if(!_jit_setup_outgoing_param(func, &(param[current_param]), param_type))
			{
				return 0;
			}
		}
	}

	/* Handle the structure return pointer if it's passed on the stack */
	if(return_ptr)
	{
		if(struct_return_param.arg_class == JIT_ARG_CLASS_STACK)
		{
			if(!_jit_setup_outgoing_param(func, &struct_return_param,
										  jit_type_void_ptr))
			{
				return 0;
			}
		}
	}

	/* Now setup the values passed in registers */
	current_param = num_args;
	while(current_param > 0)
	{
		--current_param;

		if(param[current_param].arg_class != JIT_ARG_CLASS_STACK)
		{
			jit_type_t param_type;

			param_type = jit_type_get_param(signature, current_param);
			if(!_jit_setup_reg_param(func, &(param[current_param]), param_type))
			{
				return 0;
			}
		}
	}

	/* Handle the structure return pointer if required */
	if(return_ptr)
	{
		if(struct_return_param.arg_class != JIT_ARG_CLASS_STACK)
		{
			if(!_jit_setup_reg_param(func, &struct_return_param,
									 jit_type_void_ptr))
			{
				return 0;
			}
		}
	}



( run in 0.813 second using v1.01-cache-2.11-cpan-5b529ec07f3 )