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_XMM4		20
#define	X86_64_REG_XMM5		21
#define	X86_64_REG_XMM6		22
#define	X86_64_REG_XMM7		23
#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 */

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

		return inst;
	}
	if(block->address)
	{
		jit_nint offset;

		/* We already know the address of the block */
		offset = ((unsigned char *)(block->address)) - (inst + 2);
		if(x86_is_imm8(offset))
		{
			/* We can output a short-form backwards branch */
			*inst++ = (unsigned char)opcode;
			*inst++ = (unsigned char)offset;
		}
		else
		{
			/* We need to output a long-form backwards branch */
			offset -= 3;
			opcode = long_form_branch(opcode);
			if(opcode < 256)
			{
				*inst++ = (unsigned char)opcode;
			}
			else
			{
				*inst++ = (unsigned char)(opcode >> 8);
				*inst++ = (unsigned char)opcode;
				--offset;
			}
			x86_imm_emit32(inst, offset);
		}
	}
	else
	{
		jit_int fixup;

		/* Output a placeholder and record on the block's fixup list */
		opcode = long_form_branch(opcode);
		if(opcode < 256)
		{
			*inst++ = (unsigned char)opcode;
		}
		else
		{
			*inst++ = (unsigned char)(opcode >> 8);
			*inst++ = (unsigned char)opcode;
		}
		if(block->fixup_list)
		{
			fixup = _JIT_CALC_FIXUP(block->fixup_list, inst);
		}
		else
		{
			fixup = 0;
		}
		block->fixup_list = (void *)inst;
		x86_imm_emit32(inst, fixup);

		if(DEBUG_FIXUPS)
		{
			fprintf(stderr,
					"Block: %lx, Current Fixup: %lx, Next fixup: %lx\n",
					(jit_nint)block, (jit_nint)(block->fixup_list),
					(jit_nint)fixup);
		}
	}
	return inst;
}

/*
 * Jump to the current function's epilog.
 */
static unsigned char *
jump_to_epilog(jit_gencode_t gen, unsigned char *inst, jit_block_t block)
{
	jit_int fixup;

	/* If the epilog is the next thing that we will output,
	   then fall through to the epilog directly */
	if(_jit_block_is_final(block))
	{
		return inst;
	}

	/* Output a placeholder for the jump and add it to the fixup list */
	*inst++ = (unsigned char)0xE9;
	if(gen->epilog_fixup)
	{
		fixup = _JIT_CALC_FIXUP(gen->epilog_fixup, inst);
	}
	else
	{
		fixup = 0;
	}
	gen->epilog_fixup = (void *)inst;
	x86_imm_emit32(inst, fixup);
	return inst;
}

/*
 * fixup a register being alloca'd to by accounting for the param area
 */
static unsigned char *
fixup_alloca(jit_gencode_t gen, unsigned char *inst, int reg)
{
#ifdef JIT_USE_PARAM_AREA
	jit_int fixup;
	jit_int temp;

	/*
	 * emit the instruction and then replace the imm section of op with
	 * the fixup.
	 * NOTE: We are using the temp variable here to avoid a compiler
	 * warning and the temp value to make sure that an instruction with
	 * a 32 bit immediate is emitted. The temp value in the instruction
	 * will be replaced by the fixup
	 */
	temp = 1234567;
	x86_64_add_reg_imm_size(inst, reg, temp, 8);

	/* Make inst pointing to the 32bit immediate in the instruction */

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

					}
					else if(IS_XMM_REG(src_reg))
					{
						x86_64_movss_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
											 _jit_reg_info[src_reg].cpu_reg);
					}
				}
			}
			break;

			case JIT_TYPE_FLOAT64:
			{
				if(IS_FPU_REG(reg))
				{
					if(IS_FPU_REG(src_reg))
					{
						x86_fld_reg(inst, fp_stack_index(gen, src_reg));
					}
					else if(IS_XMM_REG(src_reg))
					{
						/* Fix the position of the value in the stack frame */
						_jit_gen_fix_value(value);
						offset = (int)(value->frame_offset);

						x86_64_movsd_membase_reg(inst, X86_64_RBP, offset,
												 _jit_reg_info[src_reg].cpu_reg);
						x86_64_fld_membase_size(inst, X86_64_RBP, offset, 8);
					}
				}
				else if(IS_XMM_REG(reg))
				{
					if(IS_FPU_REG(src_reg))
					{
						/* Fix the position of the value in the stack frame */
						_jit_gen_fix_value(value);
						offset = (int)(value->frame_offset);

						x86_64_fst_membase_size(inst, X86_64_RBP, offset, 8);
						x86_64_movsd_reg_membase(inst, _jit_reg_info[reg].cpu_reg,
												 X86_64_RBP, offset);
					}
					else if(IS_XMM_REG(src_reg))
					{
						x86_64_movsd_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
											 _jit_reg_info[src_reg].cpu_reg);
					}
				}
			}
			break;

			case JIT_TYPE_NFLOAT:
			{
				if(IS_FPU_REG(reg))
				{
					if(IS_FPU_REG(src_reg))
					{
						x86_fld_reg(inst, fp_stack_index(gen, src_reg));
					}
					else
					{
						fputs("Unsupported native float reg - reg move\n", stderr);
					}
				}
			}
			break;

			case JIT_TYPE_STRUCT:
			case JIT_TYPE_UNION:
			{
				if(IS_GENERAL_REG(reg))
				{
					if(IS_GENERAL_REG(src_reg))
					{
						x86_64_mov_reg_reg_size(inst, _jit_reg_info[reg].cpu_reg,
												_jit_reg_info[src_reg].cpu_reg, 8);
					}
					else if(IS_XMM_REG(src_reg))
					{
						x86_64_movq_reg_xreg(inst, _jit_reg_info[reg].cpu_reg,
											 _jit_reg_info[src_reg].cpu_reg);
					}
					else
					{
						fputs("Unsupported struct/union reg - reg move\n", stderr);
					}
				}
				else if(IS_XMM_REG(reg))
				{
					if(IS_GENERAL_REG(src_reg))
					{
						x86_64_movq_xreg_reg(inst, _jit_reg_info[reg].cpu_reg,
											 _jit_reg_info[src_reg].cpu_reg);
					}
					else if(IS_XMM_REG(src_reg))
					{
						x86_64_movaps_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
											  _jit_reg_info[src_reg].cpu_reg);
					}
					else
					{
						fputs("Unsupported struct/union reg - reg move\n", stderr);
					}
				}
				else
				{
					fputs("Unsupported struct/union reg - reg move\n", stderr);
				}
			}
		}
	}
	else
	{
		/* Fix the position of the value in the stack frame */
		_jit_gen_fix_value(value);
		offset = (int)(value->frame_offset);

		/* Load the value into the specified register */
		switch(type->kind)
		{
			case JIT_TYPE_SBYTE:
			{
				x86_64_movsx8_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
											   X86_64_RBP, offset, 4);
			}
			break;

			case JIT_TYPE_UBYTE:
			{
				x86_64_movzx8_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
											   X86_64_RBP, offset, 4);
			}
			break;

			case JIT_TYPE_SHORT:
			{
				x86_64_movsx16_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
												X86_64_RBP, offset, 4);
			}
			break;

			case JIT_TYPE_USHORT:
			{
				x86_64_movzx16_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
												X86_64_RBP, offset, 4);
			}
			break;

			case JIT_TYPE_INT:
			case JIT_TYPE_UINT:
			{
				x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
											X86_64_RBP, offset, 4);
			}
			break;

			case JIT_TYPE_LONG:
			case JIT_TYPE_ULONG:
			{
				x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
											X86_64_RBP, offset, 8);
			}
			break;

			case JIT_TYPE_FLOAT32:
			{
				if(IS_GENERAL_REG(reg))

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

	if(regs_to_save > 0)
	{
		int current_offset;
#ifdef JIT_USE_PARAM_AREA
		if(func->builder->param_area_size > 0 &&
		   func->builder->param_area_size <= 0x50)
		{
			current_offset = func->builder->param_area_size;
		}
		else
#endif /* JIT_USE_PARAM_AREA */
		{
			current_offset = 0;
		}

		/* Save registers that 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)
			{
				x86_64_mov_membase_reg_size(inst, X86_64_RSP, current_offset,
											_jit_reg_info[reg].cpu_reg, 8);
				current_offset += 8;
			}
		}
	}
#ifdef JIT_USE_PARAM_AREA
	if(func->builder->param_area_size > 0x50 && regs_to_save > 0)
	{
		x86_64_sub_reg_imm_size(inst, X86_64_RSP, func->builder->param_area_size, 8);
	}
#endif /* JIT_USE_PARAM_AREA */

	/* Copy the prolog into place and return the adjusted entry position */
	reg = (int)(inst - prolog);
	jit_memcpy(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg, prolog, reg);
	return (void *)(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg);
}

void
_jit_gen_epilog(jit_gencode_t gen, jit_function_t func)
{
	unsigned char *inst;
	int reg;
	int current_offset;
	jit_int *fixup;
	jit_int *next;

	/* Bail out if there is insufficient space for the epilog */
	_jit_gen_check_space(gen, 48);

	inst = gen->ptr;

	/* Perform fixups on any blocks that jump to the epilog */
	fixup = (jit_int *)(gen->epilog_fixup);
	while(fixup != 0)
	{
		if(DEBUG_FIXUPS)
		{
			fprintf(stderr, "Fixup Address: %lx, Value: %x\n",
					(jit_nint)fixup, fixup[0]);
		}
		next = (jit_int *)_JIT_CALC_NEXT_FIXUP(fixup, fixup[0]);
		fixup[0] = (jit_int)(((jit_nint)inst) - ((jit_nint)fixup) - 4);
		fixup = next;
	}
	gen->epilog_fixup = 0;

	/* Perform fixups on any alloca calls */
	fixup = (jit_int *)(gen->alloca_fixup);
	while (fixup != 0)
	{
		next = (jit_int *)_JIT_CALC_NEXT_FIXUP(fixup, fixup[0]);
		fixup[0] = func->builder->param_area_size;
		if(DEBUG_FIXUPS)
		{
			fprintf(stderr, "Fixup Param Area Size: %lx, Value: %x\n",
					(jit_nint)fixup, fixup[0]);
		}
		fixup = next;
	}
	gen->alloca_fixup = 0;

	/* Restore the used callee saved registers */
	if(gen->stack_changed)
	{
		int frame_size = func->builder->frame_size;
		int regs_saved = 0;

		/* Get the number of registers we preserves */
		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_saved;
			}
		}

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

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

		current_offset = -frame_size;

		for(reg = 0; reg <= 14; ++reg)
		{
			if(jit_reg_is_used(gen->touched, reg) &&
			   (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)
			{
				x86_64_mov_reg_membase_size(inst, _jit_reg_info[reg].cpu_reg,
							    X86_64_RBP, current_offset, 8);
				current_offset += 8;
			}
		}
	}
	else
	{
#ifdef JIT_USE_PARAM_AREA
		if(func->builder->param_area_size > 0)
		{
			current_offset = func->builder->param_area_size;
		}
		else
		{
			current_offset = 0;
		}
#else /* !JIT_USE_PARAM_AREA */
		current_offset = 0;
#endif /* !JIT_USE_PARAM_AREA */
		for(reg = 0; reg <= 14; ++reg)
		{
			if(jit_reg_is_used(gen->touched, reg) &&
			   (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0)

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

	}
	else if(dreg == X86_64_RSI)
	{
		if(sreg == X86_64_RDI)
		{
			/* The registers are swapped so we need a temporary register */
			x86_64_mov_reg_reg_size(inst, X86_64_RCX, X86_64_RSI, 8);
			x86_64_mov_reg_reg_size(inst, X86_64_RSI, X86_64_RDI, 8);
			x86_64_mov_reg_reg_size(inst, X86_64_RDI, X86_64_RCX, 8);
		}
		else
		{
			x86_64_mov_reg_reg_size(inst, X86_64_RDI, X86_64_RSI, 8);
			if(sreg != X86_64_RSI)
			{
				x86_64_mov_reg_reg_size(inst, X86_64_RSI, sreg, 8);
			}
		}
	}
	else
	{
		x86_64_mov_reg_reg_size(inst, X86_64_RSI, sreg, 8);
		x86_64_mov_reg_reg_size(inst, X86_64_RDI, dreg, 8);
	}
	/* Move the size to argument register 3 now */
	if((size > 0) && (size <= jit_max_uint))
	{
		x86_64_mov_reg_imm_size(inst, X86_64_RDX, size, 4);
	}
	else
	{
		x86_64_mov_reg_imm_size(inst, X86_64_RDX, size, 8);
	}
	if(soffset != 0)
	{
		x86_64_add_reg_imm_size(inst, X86_64_RSI, soffset, 8);
	}
	if(doffset != 0)
	{
		x86_64_add_reg_imm_size(inst, X86_64_RDI, doffset, 8);
	}
	inst = x86_64_call_code(inst, (jit_nint)jit_memcpy);
	return inst;
}

void
_jit_gen_start_block(jit_gencode_t gen, jit_block_t block)
{
	jit_int *fixup;
	jit_int *next;
	void **absolute_fixup;
	void **absolute_next;

	/* Set the address of this block */
	block->address = (void *)(gen->ptr);

	/* If this block has pending fixups, then apply them now */
	fixup = (jit_int *)(block->fixup_list);
	if(DEBUG_FIXUPS && fixup)
	{
		fprintf(stderr, "Block: %lx\n", (jit_nint)block);
	}
	while(fixup != 0)
	{
		if(DEBUG_FIXUPS)
		{
			fprintf(stderr, "Fixup Address: %lx, Value: %x\n",
					(jit_nint)fixup, fixup[0]);
		}
		next = (jit_int *)_JIT_CALC_NEXT_FIXUP(fixup, fixup[0]);
		fixup[0] = (jit_int)
			(((jit_nint)(block->address)) - ((jit_nint)fixup) - 4);
		fixup = next;
	}
	block->fixup_list = 0;

	/* Absolute fixups contain complete pointers */
	absolute_fixup = (void**)(block->fixup_absolute_list);
	while(absolute_fixup != 0)
	{
		absolute_next = (void **)(absolute_fixup[0]);
		absolute_fixup[0] = (void *)((jit_nint)(block->address));
		absolute_fixup = absolute_next;
	}
	block->fixup_absolute_list = 0;
}

void
_jit_gen_end_block(jit_gencode_t gen, jit_block_t block)
{
	/* Nothing to do here for x86 */
}

int
_jit_gen_is_global_candidate(jit_type_t type)
{
	switch(jit_type_remove_tags(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:
		case JIT_TYPE_LONG:
		case JIT_TYPE_ULONG:
		case JIT_TYPE_NINT:
		case JIT_TYPE_NUINT:
		case JIT_TYPE_PTR:
		case JIT_TYPE_SIGNATURE:
		{
			return 1;
		}
	}
	return 0;
}

/*
 * Do the stuff usually handled in jit-rules.c for native implementations
 * here too because the common implementation is not enough for x86_64.
 */

/*
 * Determine if a type corresponds to a structure or union.
 */
static int
is_struct_or_union(jit_type_t type)

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

			}
			else
			{
				int reg = _jit_reg_info[return_param.un.reg_info[0].reg].cpu_reg;

				if(IS_GENERAL_REG(return_param.un.reg_info[0].reg))
				{
					x86_64_mov_membase_reg_size(inst, X86_64_RBP, offset,
												reg, 8);
				}
				else
				{
					x86_64_movsd_membase_reg(inst, X86_64_RBP, offset, reg);
				}
				size -= 8;
				reg = _jit_reg_info[return_param.un.reg_info[1].reg].cpu_reg;
				if(IS_GENERAL_REG(return_param.un.reg_info[1].reg))
				{
					if(size <= 4)
					{
						x86_64_mov_membase_reg_size(inst, X86_64_RBP,
													offset + 8, reg, 4);
					}
					else
					{
						x86_64_mov_membase_reg_size(inst, X86_64_RBP,
													offset + 8, reg, 8);
					}
				}
				else
				{
					if(size <= 4)
					{
						x86_64_movss_membase_reg(inst, X86_64_RBP,
												 offset + 8, reg);
					}
					else
					{
						x86_64_movsd_membase_reg(inst, X86_64_RBP,
												 offset + 8, reg);
					}
				}
			}
		}
	}
	return inst;
}

void
_jit_gen_insn(jit_gencode_t gen, jit_function_t func,
			  jit_block_t block, jit_insn_t insn)
{
	switch(insn->opcode)
	{
	#define JIT_INCLUDE_RULES
	#include "jit-rules-x86-64.inc"
	#undef JIT_INCLUDE_RULES

	default:
		{
			fprintf(stderr, "TODO(%x) at %s, %d\n",
				(int)(insn->opcode), __FILE__, (int)__LINE__);
		}
		break;
	}
}

/*
 * Fixup the passing area after all parameters have been allocated either
 * in registers or on the stack.
 * This is typically used for adding pad words for keeping the stack aligned.
 */
void
_jit_fix_call_stack(jit_param_passing_t *passing)
{
	if((passing->stack_size & 0x0f) != 0)
	{
		passing->stack_size = (passing->stack_size + 0x0f) & ~((jit_nint)0x0f);
		passing->stack_pad = 1;
	}
}

#ifndef JIT_USE_PARAM_AREA
/*
 * Setup the call stack before pushing any parameters.
 * This is used usually for pushing pad words for alignment.
 * The function is needed only if the backend doesn't work with the
 * parameter area.
 */
int
_jit_setup_call_stack(jit_function_t func, jit_param_passing_t *passing)
{
	if(passing->stack_pad)
	{
		int current;
		jit_value_t pad_value;

		pad_value = jit_value_create_nint_constant(func, jit_type_nint, 0);
		if(!pad_value)
		{
			return 0;
		}
		for(current = 0; current < passing->stack_pad; ++current)
		{
			if(!jit_insn_push(func, pad_value))
			{
				return 0;
			}
		}
	}
	return 1;
}
#endif /* !JIT_USE_PARAM_AREA */

/*
 * Push a parameter onto the stack.
 */
static int
push_param(jit_function_t func, _jit_param_t *param, jit_type_t type)
{
	if(is_struct_or_union(type) && !is_struct_or_union(param->value->type))



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