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 = ®s->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, ®s->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 = ®s->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, ®s->descs[0]))
{
use_cost += COST_THRASH;
}
else if(index != 1 && regs->descs[1].value
&& thrashes_value(gen, desc, reg, other_reg, ®s->descs[1]))
{
use_cost += COST_THRASH;
}
else if(index != 2 && regs->descs[2].value
&& thrashes_value(gen, desc, reg, other_reg, ®s->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 = ®s->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 )