Alien-LibJIT

 view release on metacpan or  search on metacpan

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

 */

#include "jit-internal.h"
#include "jit-reg-alloc.h"
#include <jit/jit-dump.h>
#include <stdio.h>
#include <string.h>

/*@

The @code{libjit} library provides a number of functions for
performing register allocation within basic blocks so that you
mostly don't have to worry about it:

@*/

/*
 * Dump debug information about the register allocation state.
 */
#undef JIT_REG_DEBUG

/*
 * Minimum number of times a candidate must be used before it
 * is considered worthy of putting in a global register.
 */
#define	JIT_MIN_USED		3

/*
 * Use is_register_occupied() function.
 */
#undef IS_REGISTER_OCCUPIED

/*
 * Check if the register is on the register stack.
 */
#ifdef JIT_REG_STACK
#define IS_STACK_REG(reg)	((jit_reg_flags(reg) & JIT_REG_IN_STACK) != 0)
#else
#define IS_STACK_REG(reg)	(0)
#endif

/* The cost value that precludes using the register in question. */
#define COST_TOO_MUCH		1000000

#define COST_COPY		4
#define COST_SPILL_DIRTY	16
#define COST_SPILL_DIRTY_GLOBAL	4
#define COST_SPILL_CLEAN	1
#define COST_SPILL_CLEAN_GLOBAL	1
#define COST_GLOBAL_BIAS	2
#define COST_THRASH		100
#define COST_CLOBBER_GLOBAL	1000

#ifdef JIT_BACKEND_X86
# define ALLOW_CLOBBER_GLOBAL	1
#else
# define ALLOW_CLOBBER_GLOBAL	0
#endif

/* Value usage flags. */
#define VALUE_INPUT		1
#define VALUE_USED		2
#define VALUE_LIVE		4
#define VALUE_DEAD		8

/* Clobber flags. */
#define CLOBBER_NONE		0
#define CLOBBER_INPUT_VALUE	1
#define CLOBBER_REG		2
#define CLOBBER_OTHER_REG	4

#ifdef JIT_REG_DEBUG
#include <stdlib.h>

static void dump_regs(jit_gencode_t gen, const char *name)
{
	int reg, index;
	jit_value_t value;

	printf("%s:\n", name);
	for(reg = 0; reg < JIT_NUM_REGS; ++reg)
	{
		if(gen->contents[reg].num_values == 0 && !(gen->contents[reg].used_for_temp))
		{
			continue;
		}
		printf("\t%s: ", jit_reg_name(reg));
		if(gen->contents[reg].num_values > 0)
		{
			for(index = 0; index < gen->contents[reg].num_values; ++index)
			{
				value = gen->contents[reg].values[index];
				if(index)
				{
					fputs(", ", stdout);
				}
				jit_dump_value(stdout, jit_value_get_function(value), value, 0);
			}
			if(gen->contents[reg].used_for_temp)
			{
				printf(", used_for_temp");
			}
		}
		else if(gen->contents[reg].used_for_temp)
		{
			printf("used_for_temp");
		}
		else
		{
			printf("free");
		}
		putc('\n', stdout);
	}
#ifdef JIT_REG_STACK
	printf("stack_top: %d\n", gen->reg_stack_top);
#endif
	fflush(stdout);
}
#endif

/*
 * Find the start register of a register pair given the end register.
 */
static int
get_long_pair_start(int other_reg)
{
	int reg;
	for(reg = 0; reg < JIT_NUM_REGS; reg++)
	{
		if(other_reg == jit_reg_other_reg(reg))
		{
			return reg;
		}
	}
	return -1;
}

/*
 * Check if two values are known to be equal.
 */
static int
are_values_equal(_jit_regdesc_t *desc1, _jit_regdesc_t *desc2)
{
	if(desc1 && desc2 && desc1->value && desc2->value)
	{
		if(desc1->value == desc2->value)
		{
			return 1;
		}
		if(desc1->value->in_register && desc2->value->in_register)
		{
			return desc1->value->reg == desc2->value->reg;
		}
	}
	return 0;
}

/*
 * Exchange the content of two value descriptors.
 */
static void
swap_values(_jit_regdesc_t *desc1, _jit_regdesc_t *desc2)
{
	_jit_regdesc_t tdesc;
	tdesc = *desc1;
	*desc1 = *desc2;
	*desc2 = tdesc;
}

/*
 * Get value usage and liveness information. The accurate liveness data is
 * only available for values used by the current instruction.
 *
 * VALUE_INPUT flag is set if the value is one of the instruction's inputs.
 *
 * VALUE_LIVE and VALUE_USED flags are set for input values only according
 * to the liveness flags provided along with the instruction.
 *
 * VALUE_DEAD flag is set in two cases. First, it is always set for output
 * values. Second, it is set for input values that are neither live nor used.
 *
 * These flags are used when spilling a register. In this case we generally
 * do not know if the values in the register are used by the instruction. If
 * the VALUE_INPUT flag is present then it is so and the value has to be held
 * in the register for the instruction to succeed. If the VALUE_DEAD flag is
 * present then there is no need to spill the value and it may be discarded.
 * Otherwise the value must be spilled.
 *
 * The VALUE_LIVE and VALUE_USED flags may only be set for input values of
 * the instruction. For other values these flags are not set even if they are
 * perfectly alive. These flags are used as a hint for spill cost calculation.
 *
 * NOTE: The output value is considered to be dead because the instruction is
 * just about to recompute it so there is no point to save it.
 *
 * Generally, a value becomes dead just after the instruction that used it
 * last time. The allocator frees dead values after each instruction so it
 * might seem that there is no chance to find any dead value on the current
 * instruction. However if the value is used by the current instruction both
 * as the input and output then it was alive after the last instruction and
 * hence was not freed. And just in case if some dead values may creep through
 * the allocator's checks...
 */
static int
value_usage(_jit_regs_t *regs, jit_value_t value)
{
	int flags;

	flags = 0;
	if(value->is_constant)
	{
		flags |= VALUE_DEAD;
	}
	if(!regs)
	{
		return flags;
	}
	if(value == regs->descs[0].value)
	{
		if(regs->ternary)
		{
			flags |= VALUE_INPUT;
			if(regs->descs[0].used)
			{
				flags |= VALUE_LIVE | VALUE_USED;
			}
			else if(regs->descs[0].live)
			{
				flags |= VALUE_LIVE;
			}
			else
			{
				flags |= VALUE_DEAD;
			}
		}
		else
		{
			flags |= VALUE_DEAD;
		}
	}
	if(value == regs->descs[1].value)
	{
		flags |= VALUE_INPUT;
		if(regs->descs[1].used)
		{
			flags |= VALUE_LIVE | VALUE_USED;
		}
		else if(regs->descs[1].live)
		{
			flags |= VALUE_LIVE;
		}
		else
		{
			flags |= VALUE_DEAD;
		}
	}
	if(value == regs->descs[2].value)
	{
		flags |= VALUE_INPUT;
		if(regs->descs[2].used)
		{
			flags |= VALUE_LIVE | VALUE_USED;
		}
		else if(regs->descs[2].live)
		{
			flags |= VALUE_LIVE;
		}
		else
		{
			flags |= VALUE_DEAD;
		}
	}
	return flags;
}

/*
 * Check if the register contains any live values.
 */
static int
is_register_alive(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
	int index, usage;

	if(reg < 0)
	{
		return 0;
	}

	/* Assume that a global register is always alive unless it is to be
	   computed right away. */
	if(jit_reg_is_used(gen->permanent, reg))
	{
		if(!regs->ternary
		   && regs->descs[0].value
		   && regs->descs[0].value->has_global_register
		   && regs->descs[0].value->global_reg == reg)
		{
			return 0;
		}
		return 1;
	}

	if(gen->contents[reg].is_long_end)
	{
		reg = get_long_pair_start(reg);
	}
	for(index = 0; index < gen->contents[reg].num_values; index++)
	{
		usage = value_usage(regs, gen->contents[reg].values[index]);
		if((usage & VALUE_DEAD) == 0)
		{
			return 1;
		}
	}

	return 0;
}

#ifdef IS_REGISTER_OCCUPIED
/*
 * Check if the register contains any values either dead or alive
 * that may need to be evicted from it.
 */
static int
is_register_occupied(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
	if(reg < 0)
	{
		return 0;
	}

	/* Assume that a global register is always occupied. */
	if(jit_reg_is_used(gen->permanent, reg))
	{
		return 1;
	}

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

	return 0;
}
#endif

/*
 * Determine the effect of using a register for a value. This includes the
 * following:
 *  - whether the value is clobbered by the instruction;
 *  - whether the previous contents of the register is clobbered.
 *
 * The value is clobbered by the instruction if it is used as input value
 * and the output value will go to the same register and these two values
 * are not equal. Or the instruction has a side effect that destroys the
 * input value regardless of the output. This is indicated with the
 * CLOBBER_INPUT_VALUE flag.
 *
 * The previous content is clobbered if the register contains any non-dead
 * values that are destroyed by loading the input value, by computing the
 * output value, or as a side effect of the instruction.
 *
 * The previous content is not clobbered if the register contains only dead
 * values or it is used for input value that is already in the register so
 * there is no need to load it and at the same time the instruction has no
 * side effects that destroy the input value or the register is used for
 * output value and the only value it contained before is the same value.
 *
 * The flag CLOBBER_REG indicates if the previous content of the register is
 * clobbered. The flag CLOBBER_OTHER_REG indicates that the other register
 * in a long pair is clobbered.
 *
 */
static int
clobbers_register(jit_gencode_t gen, _jit_regs_t *regs, int index, int reg, int other_reg)
{
	int flags;

	if(!regs->descs[index].value)
	{
		return CLOBBER_NONE;
	}

	if(regs->ternary || !regs->descs[0].value)
	{
		/* this is either a ternary or binary or unary note */
		if(regs->descs[index].clobber)
		{
			flags = CLOBBER_INPUT_VALUE;
		}
#ifdef JIT_REG_STACK
		else if(IS_STACK_REG(reg) && !regs->no_pop)
		{
			flags = CLOBBER_INPUT_VALUE;
		}
#endif
		else
		{
			flags = CLOBBER_NONE;
		}
	}
	else if(index == 0)
	{
		/* this is the output value of a binary or unary op */

		/* special case: a copy operation. Check if we could coalesce
		   the destination value with the source. */
		if(regs->copy
		   && regs->descs[1].value
		   && regs->descs[1].value->in_register
		   && regs->descs[1].value->reg == reg
		   && ((regs->descs[0].value->in_register && regs->descs[0].value->reg == reg)
		       || gen->contents[reg].num_values < JIT_MAX_REG_VALUES
		       || !(regs->descs[1].used || regs->descs[1].live)))
		{
			return CLOBBER_NONE;
		}

		flags = CLOBBER_NONE;
#ifdef IS_REGISTER_OCCUPIED
		if(is_register_occupied(gen, regs, reg))
		{
			flags |= CLOBBER_REG;
		}
		if(is_register_occupied(gen, regs, other_reg))
		{
			flags |= CLOBBER_OTHER_REG;
		}
#else
		if(is_register_alive(gen, regs, reg))
		{
			flags |= CLOBBER_REG;
		}
		if(is_register_alive(gen, regs, other_reg))
		{
			flags |= CLOBBER_OTHER_REG;
		}
#endif
		return flags;
	}
	else if(regs->copy)
	{
		flags = CLOBBER_NONE;
	}
#ifdef JIT_REG_STACK
	else if(IS_STACK_REG(reg) && !regs->no_pop)
	{
		/* this is a binary or unary stack op -- the input value
		   is either popped or overwritten by the output */
		flags = CLOBBER_INPUT_VALUE;
	}
#endif
	else if(reg == regs->descs[0].reg
		|| reg == regs->descs[0].other_reg
		|| other_reg == regs->descs[0].reg)
	{
		/* the input value of a binary or unary op is clobbered
		   by the output value */
		flags = CLOBBER_INPUT_VALUE;
	}
	else if(regs->descs[index].clobber)
	{
		flags = CLOBBER_INPUT_VALUE;
	}
	else
	{
		flags = CLOBBER_NONE;
	}

	if(flags == CLOBBER_NONE)
	{
		if(regs->descs[index].value->has_global_register
		   && regs->descs[index].value->global_reg == reg)
		{
			return CLOBBER_NONE;
		}
		if(regs->descs[index].value->in_register
		   && regs->descs[index].value->reg == reg)
		{
			return CLOBBER_NONE;
		}
	}

#ifdef IS_REGISTER_OCCUPIED
	if(is_register_occupied(gen, regs, reg))
	{
		flags |= CLOBBER_REG;
	}
	if(is_register_occupied(gen, regs, other_reg))
	{
		flags |= CLOBBER_OTHER_REG;
	}
#else
	if(is_register_alive(gen, regs, reg))
	{
		flags |= CLOBBER_REG;
	}
	if(is_register_alive(gen, regs, other_reg))
	{
		flags |= CLOBBER_OTHER_REG;
	}
#endif
	return flags;
}

/*
 * Assign scratch register.
 */
static void
set_scratch_register(jit_gencode_t gen, _jit_regs_t *regs, int index, int reg)
{
	if(reg >= 0)
	{
		regs->scratch[index].reg = reg;

		jit_reg_set_used(gen->touched, reg);
		jit_reg_set_used(regs->clobber, reg);
		jit_reg_set_used(regs->assigned, reg);
	}
}

/*
 * Set value information.

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

	desc->early_clobber = ((flags & _JIT_REGS_EARLY_CLOBBER) != 0);
	desc->regclass = regclass;
	desc->live = live;
	desc->used = used;
}

/*
 * Assign register to a value.
 */
static void
set_regdesc_register(jit_gencode_t gen, _jit_regs_t *regs, int index, int reg, int other_reg)
{
	int assign;
	if(reg >= 0)
	{
		assign = (index > 0 || regs->ternary || regs->descs[0].early_clobber);

		regs->descs[index].reg = reg;
		regs->descs[index].other_reg = other_reg;

		jit_reg_set_used(gen->touched, reg);
		if(assign)
		{
			jit_reg_set_used(regs->assigned, reg);
		}
		if(other_reg >= 0)
		{
			jit_reg_set_used(gen->touched, other_reg);
			if(assign)
			{
				jit_reg_set_used(regs->assigned, other_reg);
			}
		}
	}
}

/*
 * Determine value flags.
 */
static void
set_regdesc_flags(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
  	_jit_regdesc_t *desc;
	int reg, other_reg;
	int clobber, clobber_input;
	int is_input, is_live_input, is_used_input;

#ifdef JIT_REG_DEBUG
	printf("set_regdesc_flags(index = %d)\n", index);
#endif

	desc = &regs->descs[index];
	if(desc->reg < 0 || desc->duplicate)
	{
		return;
	}

	/* See if the value clobbers the register it is assigned to. */
	clobber = clobbers_register(gen, regs, index, desc->reg, desc->other_reg);
#ifdef JIT_REG_DEBUG
	if((clobber & CLOBBER_INPUT_VALUE) != 0)
	{
		printf("clobber input\n");
	}
	if((clobber & CLOBBER_REG) != 0)
	{
		printf("clobber reg\n");
	}
	if((clobber & CLOBBER_OTHER_REG) != 0)
	{
		printf("clobber other reg\n");
	}
#endif

	/* See if this is an input value and whether it is alive. */
	if(regs->ternary)
	{
		is_input = 1;
		is_live_input = desc->live;
		is_used_input = desc->used;
	}
	else if(index > 0)
	{
		is_input = 1;
		if(regs->descs[0].value == desc->value)
		{
			is_live_input = is_used_input = 0;
		}
		else
		{
			is_live_input = desc->live;
			is_used_input = desc->used;
		}
	}
	else
	{
		is_input = is_live_input = is_used_input = 0;
	}

	if(is_input)
	{
		/* Find the register the value is already in (if any). */
		if(desc->value->in_register)
		{
			reg = desc->value->reg;
			if(gen->contents[reg].is_long_start)
			{
				other_reg = jit_reg_other_reg(reg);
			}
			else
			{
				other_reg = -1;
			}
		}
		else
		{
			reg = -1;
			other_reg = -1;
		}

		/* See if the input value is thrashed by other inputs. The allocator

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

			}
			if(index != 2 && !are_values_equal(desc, &regs->descs[2]))
			{
				if(reg == regs->descs[2].reg
				   || reg == regs->descs[2].other_reg
				   || (other_reg >= 0
				       && (other_reg == regs->descs[2].reg
					   || other_reg == regs->descs[2].other_reg)))
				{
					desc->thrash = 1;
				}
			}

			if(desc->thrash)
			{
				reg = -1;
				other_reg = -1;
			}
		}

		/* See if the value needs to be loaded or copied or none. */
		if(reg != desc->reg)
		{
			if(desc->value->has_global_register)
			{
				desc->copy = (desc->value->global_reg != desc->reg);
			}
			else if(reg < 0)
			{
				desc->load = 1;
			}
			else
			{
				desc->copy = 1;
			}
		}

		/* See if the input value needs to be stored before the
		   instruction and if it stays in the register after it. */
		if(desc->value->is_constant)
		{
			desc->kill = 1;
		}
		else if(!is_used_input)
		{
			desc->store = is_live_input;
			desc->kill = 1;
		}
		else
		{
			/* See if the input value is destroyed by the instruction. */
			clobber_input = 0;
			if(!desc->copy)
			{
				if(jit_reg_is_used(regs->clobber, desc->reg)
				   || (desc->other_reg >= 0
				       && jit_reg_is_used(regs->clobber, desc->other_reg)))
				{
					clobber_input = 1;
				}
				else if ((clobber & CLOBBER_INPUT_VALUE) != 0)
				{
					clobber_input = 1;
				}
			}
			else if(reg >= 0)
			{
				if(jit_reg_is_used(regs->clobber, reg)
				   || (other_reg >= 0
				       && jit_reg_is_used(regs->clobber, other_reg)))
				{
					clobber_input = 1;
				}
				else if(!regs->ternary
					&& regs->descs[0].value
					&& (reg == regs->descs[0].reg
					    || reg == regs->descs[0].other_reg
					    || other_reg == regs->descs[0].reg))
				{
					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). */

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

	regclass = regs->descs[index].regclass;

	if(index == regs->dest_input_index)
	{
		desc2 = &regs->descs[0];
	}
	else
	{
		desc2 = desc;
	}

	suitable_reg = -1;
	suitable_other_reg = -1;
	suitable_cost = COST_TOO_MUCH;
	suitable_age = -1;
	for(reg_index = 0; reg_index < regclass->num_regs; reg_index++)
	{
		reg = regclass->regs[reg_index];
		if(jit_reg_is_used(regs->assigned, reg))
		{
			continue;
		}

		other_reg = jit_reg_get_pair(desc->value->type, reg);
		if(other_reg >= 0 && jit_reg_is_used(regs->assigned, other_reg))
		{
			continue;
		}

		if((desc->value->in_global_register && desc->value->global_reg == reg)
		   || (desc->value->in_register && desc->value->reg == reg))
		{
			use_cost = 0;
		}
		else
		{
			use_cost = COST_COPY;
		}
		if(desc2->value->has_global_register && desc2->value->global_reg != reg)
		{
			use_cost += COST_GLOBAL_BIAS;
		}

		if(index != 0 && regs->ternary && regs->descs[0].value
		   && thrashes_value(gen, desc, reg, other_reg, &regs->descs[0]))
		{
			use_cost += COST_THRASH;
		}
		else if(index != 1 && regs->descs[1].value
			&& thrashes_value(gen, desc, reg, other_reg, &regs->descs[1]))
		{
			use_cost += COST_THRASH;
		}
		else if(index != 2 && regs->descs[2].value
			&& thrashes_value(gen, desc, reg, other_reg, &regs->descs[2]))
		{
			use_cost += COST_THRASH;
		}

		clobber = clobbers_register(gen, regs, index, reg, other_reg);
		if((clobber & CLOBBER_INPUT_VALUE) != 0)
		{
			if(desc->used)
			{
				use_cost += COST_SPILL_CLEAN;
			}
		}
		if((clobber & (CLOBBER_REG | CLOBBER_OTHER_REG)) != 0)
		{
			if(jit_reg_is_used(gen->permanent, reg))
			{
				continue;
			}
			if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
			{
#if ALLOW_CLOBBER_GLOBAL
				use_cost += COST_CLOBBER_GLOBAL;
#else
				continue;
#endif
			}
			if(!jit_reg_is_used(regs->clobber, reg)
			   && !(other_reg >= 0 && jit_reg_is_used(regs->clobber, other_reg)))
			{
				use_cost += compute_spill_cost(gen, regs, reg, other_reg);
			}
		}

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

		if(use_cost < suitable_cost
		   || (use_cost == suitable_cost
		       && gen->contents[reg].num_values > 0
		       && gen->contents[reg].age < suitable_age))
		{
			/* This is the oldest suitable register of this type */
			suitable_reg = reg;
			suitable_other_reg = other_reg;
			suitable_cost = use_cost;
			suitable_age = gen->contents[reg].age;
		}
	}

	if(suitable_reg >= 0)
	{
		set_regdesc_register(gen, regs, index, suitable_reg, suitable_other_reg);
	}
	else
	{
		jit_exception_builtin(JIT_RESULT_COMPILE_ERROR);
	}
}

/*
 * Assign diplicate input value to the same register if possible.
 * The first value has to be already assigned. The second value
 * is assigned to the same register if it is equal to the first
 * and neither of them is clobbered.
 */

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

{
	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;
	if(gen->contents[reg].is_long_start)
	{
		other_reg = jit_reg_other_reg(reg);
	}
	else
	{
		other_reg = -1;
	}

	gen->contents[reg].age = gen->current_age;
	if(other_reg >= 0)
	{
		gen->contents[other_reg].age = gen->current_age;
	}
	++(gen->current_age);
}

static void
save_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
	_jit_regdesc_t *desc;
	int reg, other_reg;

#ifdef JIT_REG_DEBUG
	printf("save_input_value(%d)\n", index);
#endif

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

	reg = desc->value->reg;
	if(gen->contents[reg].is_long_start)
	{
		other_reg = jit_reg_other_reg(reg);
	}
	else
	{
		other_reg = -1;
	}

	if(desc->thrash)
	{
		save_value(gen, desc->value, reg, other_reg, 1);



( run in 0.327 second using v1.01-cache-2.11-cpan-8644d7adfcd )