Alien-LibJIT

 view release on metacpan or  search on metacpan

libjit/jit/jit-reg-alloc.c  view on Meta::CPAN

					clobber_input = 1;
				}
			}

			if(clobber_input)
			{
				desc->store = 1;
				desc->kill = 1;
			}
		}

		/* Store the value if it is going to be thrashed by another one. */
		if(desc->thrash)
		{
			desc->store = 1;
		}

#ifdef JIT_REG_STACK
		/* Count stack registers. */
		if(IS_STACK_REG(desc->reg))
		{
			++(regs->wanted_stack_count);
			if(!desc->load && !desc->copy)
			{
				++(regs->loaded_stack_count);
			}
		}
#endif
	}

	/* See if the value clobbers a global register. In this case the global
	   register is pushed onto stack before the instruction and popped back
	   after it. */
	if(!desc->copy
	   && (!desc->value->has_global_register || desc->value->global_reg != desc->reg)
	   && (jit_reg_is_used(gen->permanent, desc->reg)
	       || (desc->other_reg >= 0 && jit_reg_is_used(gen->permanent, desc->other_reg))))
	{
		desc->kill = 1;
	}

	/* Set clobber flags (this indicates registers to be spilled). */
	if((clobber & CLOBBER_REG) != 0)
	{
		jit_reg_set_used(regs->clobber, desc->reg);
	}
	if((clobber & CLOBBER_OTHER_REG) != 0)
	{
		jit_reg_set_used(regs->clobber, desc->other_reg);
	}

#ifdef JIT_REG_DEBUG
	printf("value = ");
	jit_dump_value(stdout, jit_value_get_function(desc->value), desc->value, 0);
	printf("\n");
	printf("value->in_register = %d\n", desc->value->in_register);
	printf("value->reg = %d\n", desc->value->reg);
	printf("value->has_global_register = %d\n", desc->value->has_global_register);
	printf("value->in_global_register = %d\n", desc->value->in_global_register);
	printf("value->global_reg = %d\n", desc->value->global_reg);
	printf("value->in_frame = %d\n", desc->value->in_frame);
	printf("reg = %d\n", desc->reg);
	printf("other_reg = %d\n", desc->other_reg);
	printf("live = %d\n", desc->live);
	printf("used = %d\n", desc->used);
	printf("clobber = %d\n", desc->clobber);
	printf("early_clobber = %d\n", desc->early_clobber);
	printf("duplicate = %d\n", desc->duplicate);
	printf("thrash = %d\n", desc->thrash);
	printf("store = %d\n", desc->store);
	printf("load = %d\n", desc->load);
	printf("copy = %d\n", desc->copy);
	printf("kill = %d\n", desc->kill);
#endif
}

/*
 * Compute the register spill cost. The register spill cost is computed as
 * the sum of spill costs of individual values the register contains. The
 * spill cost of a value depends on the following factors:
 *
 * 1. Values that are not used after the current instruction may be safely
 *    discareded so their spill cost is taken to be zero.
 * 2. Values that are spilled to global registers are cheaper than values
 *    that are spilled into stack frame.
 * 3. Clean values are cheaper than dirty values.
 *
 * NOTE: A value is clean if it was loaded from the stack frame or from a
 * global register and has not changed since then. Otherwise it is dirty.
 * There is no need to spill clean values. However their spill cost is
 * considered to be non-zero so that the register allocator will choose
 * those registers that do not contain live values over those that contain
 * live albeit clean values.
 *
 * For global registers this function returns the cost of zero. So global
 * registers have to be handled separately.
 */
static int
compute_spill_cost(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg)
{
	int cost, index, usage;
	jit_value_t value;

	if(gen->contents[reg].is_long_end)
	{
		reg = get_long_pair_start(reg);
	}

	cost = 0;
	for(index = 0; index < gen->contents[reg].num_values; index++)
	{
		value = gen->contents[reg].values[index];
		usage = value_usage(regs, value);
		if((usage & VALUE_DEAD) != 0)
		{
			/* the value is not spilled */
			continue;
		}
		if((usage & VALUE_LIVE) != 0 && (usage & VALUE_USED) == 0)
		{
			/* the value has to be spilled anyway */
			/* NOTE: This is true for local register allocation,
			   review for future global allocator. */
			continue;
		}
		if(value->has_global_register)
		{
			if(value->in_global_register)
			{
				cost += COST_SPILL_CLEAN_GLOBAL;
			}
			else
			{
				cost += COST_SPILL_DIRTY_GLOBAL;
			}
		}
		else
		{
			if(value->in_frame)
			{
				cost += COST_SPILL_CLEAN;
			}
			else
			{
				cost += COST_SPILL_DIRTY;
			}
		}
	}

	if(gen->contents[reg].is_long_start)
	{
		return cost * 2;
	}

	if(other_reg >= 0)
	{
		for(index = 0; index < gen->contents[other_reg].num_values; index++)
		{
			value = gen->contents[other_reg].values[index];
			usage = value_usage(regs, value);
			if((usage & VALUE_DEAD) != 0)
			{
				/* the value is not spilled */
				continue;
			}
			if((usage & VALUE_LIVE) != 0 && (usage & VALUE_USED) == 0)
			{
				/* the value has to be spilled anyway */
				/* NOTE: This is true for local register allocation,
				   review for future global allocator. */
				continue;
			}
			if(value->has_global_register)
			{
				if(value->in_global_register)
				{
					cost += COST_SPILL_CLEAN_GLOBAL;
				}
				else
				{
					cost += COST_SPILL_DIRTY_GLOBAL;
				}
			}
			else
			{
				if(value->in_frame)
				{
					cost += COST_SPILL_CLEAN;
				}
				else
				{
					cost += COST_SPILL_DIRTY;
				}
			}
		}
	}

	return cost;
}

static int
thrashes_value(jit_gencode_t gen,
	       _jit_regdesc_t *desc, int reg, int other_reg,
	       _jit_regdesc_t *desc2)
{
	int reg2, other_reg2;

#if ALLOW_CLOBBER_GLOBAL
	if(desc2->value->has_global_register)
	{
		if(desc2->value->global_reg == reg)
		{
			if(desc && desc2->value == desc->value)
			{
				return 0;
			}
			return 1;
		}
		if(desc2->value->global_reg == other_reg)
		{
			return 1;
		}
	}
#endif

	if(desc2->value->in_register)
	{
		reg2 = desc2->value->reg;
		if(reg2 == reg)
		{
			if(are_values_equal(desc2, desc))
			{
				return 0;
			}
			return 1;
		}
		if(reg2 == other_reg)
		{
			return 1;
		}
		if(gen->contents[reg2].is_long_start)
		{
			other_reg2 = jit_reg_other_reg(reg2);
			if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
			{
				return 1;

libjit/jit/jit-reg-alloc.c  view on Meta::CPAN

			}
		}
		else
		{
			if(regs->x87_arith && index == 2
			   && desc2->value->in_register && !desc2->copy)
			{
				desc->reg = desc2->value->reg;
			}
			else
			{
				desc->reg = (gen->reg_stack_top
					     - regs->loaded_stack_count
					     + regs->wanted_stack_count
					     - index);
			}
		}
	}

#ifdef JIT_REG_DEBUG
	printf("reg = %d\n", desc->reg);
	if(desc->reg < JIT_REG_STACK_START || desc->reg > JIT_REG_STACK_END)
	{
		printf("*** Invalid stack register! ***\n");
		abort();
	}
#endif
}

#endif

/*
 * Associate a temporary with register.
 */
static void
bind_temporary(jit_gencode_t gen, int reg, int other_reg)
{
#ifdef JIT_REG_DEBUG
	printf("bind_temporary(reg = %d, other_reg = %d)\n", reg, other_reg);
#endif

	gen->contents[reg].num_values = 0;
	gen->contents[reg].age = 0;
	gen->contents[reg].used_for_temp = 1;
	gen->contents[reg].is_long_end = 0;
	gen->contents[reg].is_long_start = 0;
	if(other_reg >= 0)
	{
		gen->contents[other_reg].num_values = 0;
		gen->contents[other_reg].age = 0;
		gen->contents[other_reg].used_for_temp = 1;
		gen->contents[other_reg].is_long_end = 0;
		gen->contents[other_reg].is_long_start = 0;
	}
}

/*
 * Associate value with register.
 */
static void
bind_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg, int still_in_frame)
{
#ifdef JIT_REG_DEBUG
	printf("bind_value(value = ");
	jit_dump_value(stdout, jit_value_get_function(value), value, 0);
	printf(", reg = %d, other_reg = %d, still_in_frame = %d)\n",
	       reg, other_reg, still_in_frame);
#endif

	if(value->has_global_register && value->global_reg == reg)
	{
		value->in_register = 0;
		value->in_global_register = 1;
		return;
	}

	if(value->is_constant)
	{
		still_in_frame = 0;
	}

#ifdef JIT_REG_DEBUG
	if(gen->contents[reg].num_values == JIT_MAX_REG_VALUES)
	{
		printf("*** Too many values for one register! ***\n");
		abort();
	}
#endif

	gen->contents[reg].values[gen->contents[reg].num_values] = value;
	++(gen->contents[reg].num_values);
	gen->contents[reg].age = gen->current_age;
	gen->contents[reg].used_for_temp = 0;
	gen->contents[reg].is_long_end = 0;
	if(other_reg == -1)
	{
		gen->contents[reg].is_long_start = 0;
	}
	else
	{
		gen->contents[reg].is_long_start = 1;
		gen->contents[other_reg].num_values = 0;
		gen->contents[other_reg].age = gen->current_age;
		gen->contents[other_reg].used_for_temp = 0;
		gen->contents[other_reg].is_long_start = 0;
		gen->contents[other_reg].is_long_end = 1;
	}
	++(gen->current_age);

	/* Adjust the value to reflect that it is in "reg", and maybe the frame */
	value->in_register = 1;
	if(value->has_global_register)
	{
		value->in_global_register = still_in_frame;
	}
	else
	{
		value->in_frame = still_in_frame;
	}
	value->reg = reg;
}

/*
 * Disassociate value with register.
 */
static void
unbind_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg)
{
	int index;

#ifdef JIT_REG_DEBUG
	printf("unbind_value(value = ");
	jit_dump_value(stdout, jit_value_get_function(value), value, 0);
	printf(", reg = %d, other_reg = %d)\n", reg, other_reg);
#endif

	if(!value->in_register || value->reg != reg)
	{
		return;
	}

	value->in_register = 0;
	value->reg = -1;

	for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
	{
		if(gen->contents[reg].values[index] == value)
		{
			--(gen->contents[reg].num_values);
			for(; index < gen->contents[reg].num_values; index++)
			{
				gen->contents[reg].values[index] = gen->contents[reg].values[index + 1];
			}
			break;
		}
	}

	if(gen->contents[reg].num_values == 0 && other_reg >= 0)
	{
		gen->contents[reg].is_long_start = 0;
		gen->contents[other_reg].is_long_end = 0;
	}
}

/*
 * Swap the contents of a register and the top of the register stack. If
 * the register is not a stack register then the function has no effect.
 */
#ifdef JIT_REG_STACK
static void
exch_stack_top(jit_gencode_t gen, int reg, int pop)
{
	int top, index;
	int num_values, used_for_temp, age;
	jit_value_t value1, value2;

#ifdef JIT_REG_DEBUG
	printf("exch_stack_top(reg = %d, pop = %d)\n", reg, pop);

libjit/jit/jit-reg-alloc.c  view on Meta::CPAN

	{
		num_values = 0;
		used_for_temp = 0;
		age = 0;
	}
	else
 	{
		num_values = gen->contents[reg].num_values;
		used_for_temp = gen->contents[reg].used_for_temp;
		age = gen->contents[reg].age;
	}
	gen->contents[reg].num_values = gen->contents[top].num_values;
	gen->contents[reg].used_for_temp = gen->contents[top].used_for_temp;
	gen->contents[reg].age = gen->contents[top].age;
	gen->contents[top].num_values = num_values;
	gen->contents[top].used_for_temp = used_for_temp;
	gen->contents[top].age = age;
}
#endif

/*
 * Drop value from the register and optionally bind a temporary value in place of it.
 */
static void
free_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg, int temp)
{
#ifdef JIT_REG_DEBUG
	printf("free_value(value = ");
	jit_dump_value(stdout, jit_value_get_function(value), value, 0);
	printf(", reg = %d, other_reg = %d, temp = %d)\n", reg, other_reg, temp);
#endif

	/* Never free global registers. */
	if(value->has_global_register && value->global_reg == reg)
	{
		return;
	}

	if(gen->contents[reg].num_values == 1)
	{
		if(temp)
		{
			unbind_value(gen, value, reg, other_reg);
			bind_temporary(gen, reg, other_reg);
			return;
		}
#ifdef JIT_REG_STACK
		if(IS_STACK_REG(reg))
		{
			/* Free stack register. */
			exch_stack_top(gen, reg, 1);
			return;
		}
#endif
	}

	unbind_value(gen, value, reg, other_reg);
}

/*
 * Save the value from the register into its frame position and optionally free it.
 * If the value is already in the frame or is a constant then it is not saved but
 * the free option still applies to them.
 */
static void
save_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg, int free)
{
#ifdef JIT_REG_DEBUG
	printf("save_value(value = ");
	jit_dump_value(stdout, jit_value_get_function(value), value, 0);
	printf(", reg = %d, other_reg = %d, free=%d)\n", reg, other_reg, free);
#endif
	/* First take care of values that reside in global registers. */
	if(value->has_global_register)
	{
		/* Never free global registers. */
		if(value->global_reg == reg)
		{
			return;
		}

		if(!value->in_global_register)
		{
			_jit_gen_spill_reg(gen, reg, other_reg, value);
			value->in_global_register = 1;
		}
		if(free)
		{
			unbind_value(gen, value, reg, other_reg);
		}
		return;
	}

	/* Take care of constants and values that are already in frame. */
	if(value->is_constant || value->in_frame)
	{
		if(free)
		{
			free_value(gen, value, reg, other_reg, 0);
		}
		return;
	}

	/* Now really save the value into the frame. */
#ifdef JIT_REG_STACK
	if(IS_STACK_REG(reg))
	{
		int top;

		/* Find the top of the stack. */
		top = gen->reg_stack_top - 1;

		/* Move the value on the stack top if it is not already there. */
		if(top != reg)
		{
			exch_stack_top(gen, reg, 0);
		}

		if(free)
		{
			if(gen->contents[top].num_values == 1)
			{
				_jit_gen_spill_top(gen, top, value, 1);
				--(gen->reg_stack_top);
			}
			else
			{
				_jit_gen_spill_top(gen, top, value, 0);
			}
			unbind_value(gen, value, top, 0);
		}
		else
		{
			_jit_gen_spill_top(gen, top, value, 0);
		}
	}
	else
#endif
	{
		_jit_gen_spill_reg(gen, reg, other_reg, value);
		if(free)
		{
			unbind_value(gen, value, reg, other_reg);
		}
	}

	value->in_frame = 1;
}

/*
 * Spill a specific register.
 */
static void
spill_register(jit_gencode_t gen, int reg)
{
	int other_reg, index;
	jit_value_t value;

#ifdef JIT_REG_DEBUG
	printf("spill_register(reg = %d)\n", reg);
#endif

	/* Find the other register in a long pair */
	if(gen->contents[reg].is_long_start)
	{
		other_reg = jit_reg_other_reg(reg);
	}
	else if(gen->contents[reg].is_long_end)
	{
		other_reg = reg;
		reg = get_long_pair_start(reg);
	}
	else
	{
		other_reg = -1;
	}

	for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
	{
		value = gen->contents[reg].values[index];
		save_value(gen, value, reg, other_reg, 1);
	}
}

/*
 * Spill a register clobbered by the instruction.
 */
static void
spill_clobbered_register(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
	int other_reg, index, usage;
	jit_value_t value;

#ifdef JIT_REG_DEBUG
	printf("spill_clobbered_register(reg = %d)\n", reg);
#endif

#ifdef JIT_REG_STACK
	/* For a stack register spill it in two passes. First drop values that
	   reqiure neither spilling nor a generation of the free instruction.
	   Then lazily exchange the register with the top and spill or free it
	   as necessary. This approach might save a exch/free instructions in
	   certain cases. */
	if(IS_STACK_REG(reg))
	{
		for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
		{
			if(gen->contents[reg].num_values == 1)
			{
				break;
			}

			value = gen->contents[reg].values[index];
			usage = value_usage(regs, value);
			if((usage & VALUE_INPUT) != 0)
			{
				continue;
			}
			if((usage & VALUE_DEAD) != 0 || value->in_frame)
			{
				unbind_value(gen, value, reg, -1);
			}
		}
		for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
		{
			int top;

			value = gen->contents[reg].values[index];
			usage = value_usage(regs, value);
			if((usage & VALUE_INPUT) != 0)
			{
				if((usage & VALUE_DEAD) != 0 || value->in_frame)
				{
					continue;
				}

				top = gen->reg_stack_top - 1;
				if(reg != top)
				{
					exch_stack_top(gen, reg, 0);
					reg = top;
				}

				save_value(gen, value, reg, -1, 0);
			}
			else
			{
				top = gen->reg_stack_top - 1;
				if(reg != top)
				{
					exch_stack_top(gen, reg, 0);
					reg = top;
				}

				if((usage & VALUE_DEAD) != 0 || value->in_frame)
				{
					free_value(gen, value, reg, -1, 0);
				}
				else
				{
					save_value(gen, value, reg, -1, 1);
				}
			}
		}
	}
	else
#endif
	{
		/* Find the other register in a long pair */
		if(gen->contents[reg].is_long_start)
		{
			other_reg = jit_reg_other_reg(reg);
		}
		else if(gen->contents[reg].is_long_end)
		{
			other_reg = reg;
			reg = get_long_pair_start(reg);
		}
		else
		{
			other_reg = -1;
		}

		for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
		{
			value = gen->contents[reg].values[index];
			usage = value_usage(regs, value);
			if((usage & VALUE_DEAD) == 0)
			{
				if((usage & VALUE_INPUT) == 0)
				{
					save_value(gen, value, reg, other_reg, 1);
				}
				else
				{
					save_value(gen, value, reg, other_reg, 0);
				}
			}
			else
			{
				if((usage & VALUE_INPUT) == 0)
				{
					free_value(gen, value, reg, other_reg, 0);
				}
			}
		}
	}
}

static void
update_age(jit_gencode_t gen, _jit_regdesc_t *desc)
{
	int reg, other_reg;

	reg = desc->value->reg;

libjit/jit/jit-reg-alloc.c  view on Meta::CPAN

	printf("commit_input_value(%d)\n", index);
#endif

	desc = &regs->descs[index];
	if(!desc->value || desc->duplicate)
	{
		return;
	}

#ifdef JIT_REG_STACK
	if(!IS_STACK_REG(desc->reg))
	{
		killed = 0;
	}
#endif

	if(desc->copy)
	{
#ifdef JIT_REG_STACK
		if(killed)
		{
			killed = 0;
		}
		else
#endif
		{
			gen->contents[desc->reg].used_for_temp = 0;
			if(desc->other_reg >= 0)
			{
				gen->contents[desc->other_reg].used_for_temp = 0;
			}
		}
	}

#ifdef JIT_REG_STACK
	if(!killed && desc->kill && desc->value->in_register)
#else
	if(desc->kill && desc->value->in_register)
#endif
	{
		reg = desc->value->reg;
		if(gen->contents[reg].is_long_start)
		{
			other_reg = jit_reg_other_reg(reg);
		}
		else
		{
			other_reg = -1;
		}
		free_value(gen, desc->value, reg, other_reg, 0);
	}

#ifdef JIT_REG_DEBUG
	printf("value = ");
	jit_dump_value(stdout, jit_value_get_function(desc->value), desc->value, 0);
	printf("\n");
	printf("value->in_register = %d\n", desc->value->in_register);
	printf("value->reg = %d\n", desc->value->reg);
	printf("value->in_global_register = %d\n", desc->value->in_global_register);
	printf("value->global_reg = %d\n", desc->value->global_reg);
	printf("value->in_frame = %d\n", desc->value->in_frame);
#endif
}

static void
commit_output_value(jit_gencode_t gen, _jit_regs_t *regs, int push_stack_top)
{
	_jit_regdesc_t *desc;

#ifdef JIT_REG_DEBUG
	printf("commit_output_value()\n");
#endif

	desc = &regs->descs[0];
	if(!desc->value)
	{
		return;
	}

#ifdef JIT_REG_STACK
	if(IS_STACK_REG(desc->reg) && push_stack_top)
	{
		++(gen->reg_stack_top);
	}
#endif
	bind_value(gen, desc->value, desc->reg, desc->other_reg, 0);

	if(!desc->used)
	{
		if(desc->live)
		{
			save_value(gen, desc->value, desc->reg, desc->other_reg, 1);
		}
		else
		{
			free_value(gen, desc->value, desc->reg, desc->other_reg, 0);
		}
	}
	else if(desc->kill)
	{
		save_value(gen, desc->value, desc->reg, desc->other_reg, 1);
	}

#ifdef JIT_REG_DEBUG
	printf("value = ");
	jit_dump_value(stdout, jit_value_get_function(desc->value), desc->value, 0);
	printf("\n");
	printf("value->in_register = %d\n", desc->value->in_register);
	printf("value->reg = %d\n", desc->value->reg);
	printf("value->in_global_register = %d\n", desc->value->in_global_register);
	printf("value->global_reg = %d\n", desc->value->global_reg);
	printf("value->in_frame = %d\n", desc->value->in_frame);
#endif
}

/*@
 * @deftypefun void _jit_regs_lookup (char *name)
 * Get the pseudo register by its name.
 * @end deftypefun
@*/
int
_jit_regs_lookup(char *name)
{
	int reg;
	if(name)
	{
		for(reg = 0; reg < JIT_NUM_REGS; reg++)
		{
			if(strcmp(jit_reg_name(reg), name) == 0)
			{
				return reg;
			}
		}
	}
	return -1;
}

/*@
 * @deftypefun void _jit_regs_alloc_global (jit_gencode_t gen, jit_function_t func)
 * Perform global register allocation on the values in @code{func}.
 * This is called during function compilation just after variable
 * liveness has been computed.
 * @end deftypefun
@*/
void _jit_regs_alloc_global(jit_gencode_t gen, jit_function_t func)
{
#if JIT_NUM_GLOBAL_REGS != 0
	jit_value_t candidates[JIT_NUM_GLOBAL_REGS];
	int num_candidates = 0;
	int index, reg, posn, num;
	jit_pool_block_t block;
	jit_value_t value, temp;

	/* If the function has a "try" block, then don't do global allocation
	   as the "longjmp" for exception throws will wipe out global registers */
	if(func->has_try)
	{
		return;
	}

	/* If the current function involves a tail call, then we don't do
	   global register allocation and we also prevent the code generator
	   from using any of the callee-saved registers.  This simplifies
	   tail calls, which don't have to worry about restoring such registers */
	if(func->builder->has_tail_call)
	{
		for(reg = 0; reg < JIT_NUM_REGS; ++reg)
		{
			if((jit_reg_flags(reg) & (JIT_REG_FIXED|JIT_REG_CALL_USED)) == 0)
			{
				jit_reg_set_used(gen->permanent, reg);
			}

libjit/jit/jit-reg-alloc.c  view on Meta::CPAN

	{
		/* Skip this register if it is permanent or fixed */
		if(jit_reg_is_used(gen->permanent, reg)
		   || (jit_reg_flags(reg) & JIT_REG_FIXED) != 0)
		{
			continue;
		}

		/* If this is a stack register, then we need to find the
		   register that contains the top-most stack position,
		   because we must spill stack registers from top down.
		   As we spill each one, something else will become the top */
#ifdef JIT_REG_STACK
		if(IS_STACK_REG(reg))
		{
			if(gen->reg_stack_top > JIT_REG_STACK_START)
			{
				spill_register(gen, gen->reg_stack_top - 1);
			}
		}
		else
#endif
		{
			spill_register(gen, reg);
		}
	}

#ifdef JIT_REG_DEBUG
	printf("leave _jit_regs_spill_all\n\n");
#endif
}

/*@
 * @deftypefun void _jit_regs_set_incoming (jit_gencode_t gen, int reg, jit_value_t value)
 * Set pseudo register @code{reg} to record that it currently holds the
 * contents of @code{value}.  The register must not contain any other
 * live value at this point.
 * @end deftypefun
@*/
void
_jit_regs_set_incoming(jit_gencode_t gen, int reg, jit_value_t value)
{
	int other_reg;

	/* Find the other register in a register pair */
	other_reg = jit_reg_get_pair(value->type, reg);

	/* avd: It's too late to spill here, if there was any
	   value it is already cloberred by the incoming value.
	   So for correct code generation the register must be
	   free by now (spilled at some earlier point). */
#if 0
	/* Eject any values that are currently in the register */
	spill_register(gen, reg);
	if(other_reg >= 0)
	{
		spill_register(gen, other_reg);
	}
#endif

	/* Record that the value is in "reg", but not in the frame */
#ifdef JIT_REG_STACK
	if(IS_STACK_REG(reg))
	{
		++(gen->reg_stack_top);
	}
#endif
	bind_value(gen, value, reg, other_reg, 0);
}

/*@
 * @deftypefun void _jit_regs_set_outgoing (jit_gencode_t gen, int reg, jit_value_t value)
 * Load the contents of @code{value} into pseudo register @code{reg},
 * spilling out the current contents.  This is used to set up outgoing
 * parameters for a function call.
 * @end deftypefun
@*/
void
_jit_regs_set_outgoing(jit_gencode_t gen, int reg, jit_value_t value)
{
	int other_reg;

#ifdef JIT_BACKEND_X86
	jit_type_t type;

	other_reg = -1;

	type = jit_type_normalize(value->type);
	if(type)
	{
		/* We might need to put float values in register pairs under x86 */
		if(type->kind == JIT_TYPE_LONG || type->kind == JIT_TYPE_ULONG ||
		   type->kind == JIT_TYPE_FLOAT64 || type->kind == JIT_TYPE_NFLOAT)
		{
			/* Long values in outgoing registers must be in ECX:EDX,
			   not in the ordinary register pairing of ECX:EBX */
			other_reg = 2;

			/* Force the value out of whatever register it is already in */
			_jit_regs_force_out(gen, value, 0);
		}
	}
#else
	other_reg = jit_reg_get_pair(value->type, reg);
#endif

	if(value->in_register && value->reg == reg)
	{
		/* The value is already in the register, but we may need to spill
		   if the frame copy is not up to date with the register */
		if(!(value->in_global_register || value->in_frame))
		{
			save_value(gen, value, reg, other_reg, 0);
		}

		/* The value is no longer "really" in the register.  A copy is
		   left behind, but the value itself reverts to the frame copy
		   as we are about to kill the registers in a function call */
		free_value(gen, value, reg, other_reg, 1);
	}
	else
	{
		/* Reload the value into the specified register */
		spill_register(gen, reg);
		if(other_reg >= 0)
		{
			spill_register(gen, other_reg);
		}

		_jit_gen_load_value(gen, reg, other_reg, value);
	}

	jit_reg_set_used(gen->inhibit, reg);
	if(other_reg >= 0)
	{
		jit_reg_set_used(gen->inhibit, other_reg);
	}
}

/*@
 * @deftypefun void _jit_regs_clear_all_outgoing (jit_gencode_t gen)
 * Free registers used fot outgoing parameters.  This is used to
 * clean up after a function call.
 * @end deftypefun
@*/
void
_jit_regs_clear_all_outgoing(jit_gencode_t gen)
{
	gen->inhibit = jit_regused_init;
}

/* TODO: remove this function */
/*@
 * @deftypefun void _jit_regs_force_out (jit_gencode_t gen, jit_value_t value, int is_dest)
 * If @code{value} is currently in a register, then force its value out
 * into the stack frame.  The @code{is_dest} flag indicates that the value
 * will be a destination, so we don't care about the original value.
 *
 * This function is deprecated and going to be removed soon.
 *
 * @end deftypefun
@*/
void _jit_regs_force_out(jit_gencode_t gen, jit_value_t value, int is_dest)
{
	int reg, other_reg;
	if(value->in_register)
	{
		reg = value->reg;
		other_reg = jit_reg_get_pair(value->type, reg);

		if(is_dest)
		{
			free_value(gen, value, reg, other_reg, 0);
		}
		else
		{
			save_value(gen, value, reg, other_reg, 1);
		}
	}
}

/* TODO: remove this function */
/*@
 * @deftypefun int _jit_regs_load_value (jit_gencode_t gen, jit_value_t value, int destroy, int used_again)
 * Load a value into any register that is suitable and return that register.
 * If the value needs a long pair, then this will return the first register
 * in the pair.  Returns -1 if the value will not fit into any register.
 *
 * If @code{destroy} is non-zero, then we are about to destroy the register,
 * so the system must make sure that such destruction will not side-effect
 * @code{value} or any of the other values currently in that register.
 *
 * If @code{used_again} is non-zero, then it indicates that the value is
 * used again further down the block.
 *
 * This function is deprecated and going to be removed soon.
 *
 * @end deftypefun
@*/
int
_jit_regs_load_value(jit_gencode_t gen, jit_value_t value, int destroy, int used_again)
{
	int type;
	int reg, other_reg;
	int spill_cost;
	int suitable_reg, suitable_other_reg;
	int suitable_cost;
	int suitable_age;

	/* If the value is in a global register, and we are not going
	   to destroy the value, then use the global register itself.
	   This will avoid a redundant register copy operation */
	if(value->in_global_register && !destroy)
	{
		return value->global_reg;
	}



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