@*/
-/*@
- * @deftypefun void _jit_regs_init_for_block (jit_gencode_t gen)
- * Initialize the register allocation state for a new block.
- * @end deftypefun
-@*/
-void _jit_regs_init_for_block(jit_gencode_t gen)
-{
- int reg;
- gen->current_age = 1;
- for(reg = 0; reg < JIT_NUM_REGS; ++reg)
- {
- /* Clear everything except permanent and fixed registers */
- if(!jit_reg_is_used(gen->permanent, reg) &&
- (_jit_reg_info[reg].flags & JIT_REG_FIXED) == 0)
- {
- gen->contents[reg].num_values = 0;
- gen->contents[reg].is_long_start = 0;
- gen->contents[reg].is_long_end = 0;
- gen->contents[reg].age = 0;
- gen->contents[reg].remap = -1;
- gen->contents[reg].used_for_temp = 0;
- }
- gen->stack_map[reg] = -1;
- }
- gen->inhibit = jit_regused_init;
-}
+/*
+ * Dump debug information about the register allocation state.
+ */
+#undef JIT_REG_DEBUG
-/*@
- * @deftypefun int _jit_regs_needs_long_pair (jit_type_t type)
- * Determine if a type requires a long register pair.
- * @end deftypefun
-@*/
-int _jit_regs_needs_long_pair(jit_type_t type)
-{
-#if defined(JIT_NATIVE_INT32) && !defined(JIT_BACKEND_INTERP)
- type = jit_type_normalize(type);
- if(type)
- {
- if(type->kind == JIT_TYPE_LONG || type->kind == JIT_TYPE_ULONG)
- {
- return 1;
- }
- }
- return 0;
+/*
+ * 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
+
+/*
+ * Check if the register is on the register stack.
+ */
+#ifdef JIT_REG_STACK
+#define IS_STACK_REG(reg) ((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0)
#else
- /* We don't register pairs on 64-bit platforms or the interpreter */
- return 0;
+#define IS_STACK_REG(reg) (0)
#endif
-}
-
-/*@
- * @deftypefun int _jit_regs_get_cpu (jit_gencode_t gen, int reg, int *other_reg)
- * Get the CPU register that corresponds to a pseudo register.
- * "other_reg" will be set to the other register in a pair,
- * or -1 if the register is not part of a pair.
- * @end deftypefun
-@*/
-int _jit_regs_get_cpu(jit_gencode_t gen, int reg, int *other_reg)
-{
- int cpu_reg, other;
- cpu_reg = gen->contents[reg].remap;
- if(cpu_reg == -1)
- {
- cpu_reg = _jit_reg_info[reg].cpu_reg;
- }
- else
- {
- cpu_reg = _jit_reg_info[cpu_reg].cpu_reg;
- }
- if(gen->contents[reg].is_long_start)
- {
- other = _jit_reg_info[reg].other_reg;
- if(gen->contents[other].remap == -1)
- {
- other = _jit_reg_info[other].cpu_reg;
- }
- else
- {
- other = _jit_reg_info[gen->contents[other].remap].cpu_reg;
- }
- }
- else
- {
- other = -1;
- }
- if(other_reg)
- {
- *other_reg = other;
- }
- return cpu_reg;
-}
/*
- * Dump debug information about the register allocation state.
+ * Get the other register of a long pair.
*/
-/*#define JIT_REG_DEBUG 1*/
+#define OTHER_REG(reg) (_jit_reg_info[reg].other_reg)
+
+/* 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 2
+#define COST_SPILL_CLEAN 1
+#define COST_SPILL_CLEAN_GLOBAL 1
+#define COST_GLOBAL_BIAS 1
+#define COST_THRASH 32
+#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
static void dump_regs(jit_gencode_t gen, const char *name)
{
- int reg;
- unsigned int index;
+ 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) &&
- gen->contents[reg].remap == -1)
+ if(gen->contents[reg].num_values == 0 && !(gen->contents[reg].used_for_temp))
{
continue;
}
{
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
- (gen->contents[reg].values[index]),
- gen->contents[reg].values[index], 0);
+ }
+ jit_dump_value(stdout, jit_value_get_function(value), value, 0);
}
if(gen->contents[reg].used_for_temp)
{
{
printf("free");
}
- if(gen->contents[reg].remap != -1)
- {
- printf(", remap=%d", (int)(gen->contents[reg].remap));
- }
- for(index = 0; index < JIT_NUM_REGS; ++index)
- {
- if(gen->stack_map[index] == reg)
- {
- printf(", reverse_remap=%d", (int)index);
- }
- }
putc('\n', stdout);
}
}
#endif
/*
- * Spill all registers between two end points.
+ * Find the start register of a long pair given the end register.
*/
-static void spill_all_between(jit_gencode_t gen, int first, int last)
+static int
+get_long_pair_start(int other_reg)
{
- int reg, posn, other_reg, real_reg;
- int first_stack_reg = 0;
- jit_value_t value;
- int value_used;
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "enter spill_all_between");
-#endif
-
- /* Handle the non-stack registers first, as they are easy to spill */
- for(reg = first; reg <= last; ++reg)
+ int reg;
+ for(reg = 0; reg < JIT_NUM_REGS; reg++)
{
- /* Skip this register if it is permanent or fixed */
- if(jit_reg_is_used(gen->permanent, reg) ||
- (_jit_reg_info[reg].flags & JIT_REG_FIXED) != 0)
- {
- continue;
- }
-
- /* Remember this register if it is the start of a stack */
- if((_jit_reg_info[reg].flags & JIT_REG_START_STACK) != 0)
+ if(other_reg == OTHER_REG(reg))
{
- first_stack_reg = reg;
+ return reg;
}
+ }
+ return -1;
+}
- /* 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 */
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0)
- {
- real_reg = gen->stack_map[first_stack_reg];
- if(real_reg == -1)
- {
- continue;
- }
- }
- else
- {
- real_reg = reg;
- }
+/*
+ * Determine the type of register that we need.
+ */
+static int
+get_register_type(jit_value_t value, int need_pair)
+{
+ switch(jit_type_normalize(value->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_NINT:
+ case JIT_TYPE_NUINT:
+ case JIT_TYPE_SIGNATURE:
+ case JIT_TYPE_PTR:
+ return JIT_REG_WORD;
- /* Skip this register if there is nothing in it */
- if(gen->contents[real_reg].num_values == 0 &&
- !(gen->contents[real_reg].used_for_temp))
- {
- continue;
- }
+ case JIT_TYPE_LONG:
+ case JIT_TYPE_ULONG:
+ return need_pair ? JIT_REG_LONG : JIT_REG_WORD;
- /* Get the other register in a long pair, if there is one */
- if(gen->contents[real_reg].is_long_start)
- {
- other_reg = _jit_reg_info[real_reg].other_reg;
- }
- else
- {
- other_reg = -1;
- }
+ case JIT_TYPE_FLOAT32:
+ return JIT_REG_FLOAT32;
- /* Spill all values that are associated with the register */
- value_used = 0;
- if(gen->contents[real_reg].num_values > 0)
- {
- for(posn = gen->contents[real_reg].num_values - 1;
- posn >= 0; --posn)
- {
- value = gen->contents[real_reg].values[posn];
- if(value->has_global_register)
- {
- if(!(value->in_global_register))
- {
- _jit_gen_spill_reg(gen, real_reg, other_reg, value);
- value->in_global_register = 1;
- value_used = 1;
- }
- }
- else if(!(value->in_frame))
- {
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
- {
- _jit_gen_spill_reg(gen, real_reg, other_reg, value);
- }
- else
- {
- /* The back end needs to think that we are spilling
- the first register in the stack, regardless of
- what "real_reg" might happen to be */
- _jit_gen_spill_reg(gen, first_stack_reg, -1, value);
- }
- value->in_frame = 1;
- value_used = 1;
- }
- value->in_register = 0;
- value->reg = -1;
- }
- }
+ case JIT_TYPE_FLOAT64:
+ return JIT_REG_FLOAT64;
- /* Free the register */
- _jit_regs_free_reg(gen, real_reg, value_used);
+ case JIT_TYPE_NFLOAT:
+ return JIT_REG_NFLOAT;
}
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "leave spill_all_between");
-#endif
+ return 0;
}
/*
- * Spill a specific register. If it is in a stack, then all registers
- * above the specific register must also be spilled.
+ * Check if two values are known to be equal.
*/
-static void spill_register(jit_gencode_t gen, int reg)
+static int
+are_values_equal(_jit_regdesc_t *desc1, _jit_regdesc_t *desc2)
{
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
- {
- spill_all_between(gen, reg, reg);
- }
- else
+ if(desc1 && desc2 && desc1->value && desc2->value)
{
- int first_reg;
- reg = gen->contents[reg].remap;
- first_reg = reg;
- while((_jit_reg_info[first_reg].flags & JIT_REG_START_STACK) == 0)
+ if(desc1->value == desc2->value)
{
- --first_reg;
+ return 1;
+ }
+ if(desc1->value->in_register && desc2->value->in_register)
+ {
+ return desc1->value->reg == desc2->value->reg;
}
- spill_all_between(gen, first_reg, reg);
- }
-}
-
-/*
- * Spill all stack registers of a specific type.
- */
-static void spill_all_stack(jit_gencode_t gen, int reg)
-{
- int first_reg;
- while((_jit_reg_info[reg].flags & JIT_REG_START_STACK) == 0)
- {
- --reg;
- }
- first_reg = reg;
- while((_jit_reg_info[reg].flags & JIT_REG_END_STACK) == 0)
- {
- ++reg;
}
- spill_all_between(gen, first_reg, reg);
+ return 0;
}
-/*@
- * @deftypefun void _jit_regs_spill_all (jit_gencode_t gen)
- * Spill all of the temporary registers to memory locations.
- * Normally used at the end of a block, but may also be used in
- * situations where a value must be in a certain register and
- * it is too hard to swap things around to put it there.
- * @end deftypefun
-@*/
-void _jit_regs_spill_all(jit_gencode_t gen)
-{
-#ifdef JIT_REG_DEBUG
- printf("enter _jit_regs_spill_all\n");
-#endif
-
- spill_all_between(gen, 0, JIT_NUM_REGS - 1);
-
-#ifdef JIT_REG_DEBUG
- printf("leave _jit_regs_spill_all\n");
-#endif
-}
/*
- * Free a register within a stack, and renumber the other stack registers
- * to compensate for the change.
+ * 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. Also the old allocator might sometimes leave a dead
+ * value in the register and as of this writing the old allocator is still
+ * used by some rules. And just in case if some dead value may creep through
+ * the new allocator...
*/
-static void free_stack_reg(jit_gencode_t gen, int reg)
+static int
+value_usage(_jit_regs_t *regs, jit_value_t value)
{
- int remap;
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "enter free_stack_reg");
-#endif
+ int flags;
- /* Shift everything after this register up by one position */
- remap = gen->contents[reg].remap;
- if((_jit_reg_info[remap].flags & JIT_REG_END_STACK) == 0)
+ flags = 0;
+ if(value->is_constant)
+ {
+ flags |= VALUE_DEAD;
+ }
+ if(!regs)
+ {
+ return flags;
+ }
+ if(value == regs->descs[0].value)
{
- ++remap;
- for(;;)
+ if(regs->ternary)
{
- if(gen->stack_map[remap] == -1)
+ flags |= VALUE_INPUT;
+ if(regs->descs[0].used)
{
- /* There are no more active values in this stack */
- gen->stack_map[remap - 1] = -1;
- break;
+ flags |= VALUE_LIVE | VALUE_USED;
}
- else if((_jit_reg_info[remap].flags & JIT_REG_END_STACK) != 0)
+ else if(regs->descs[0].live)
{
- /* This is the last register in the stack */
- --(gen->contents[gen->stack_map[remap]].remap);
- gen->stack_map[remap - 1] = gen->stack_map[remap];
- gen->stack_map[remap] = -1;
- break;
+ flags |= VALUE_LIVE;
}
else
{
- /* Shift this stack entry up by one */
- --(gen->contents[gen->stack_map[remap]].remap);
- gen->stack_map[remap - 1] = gen->stack_map[remap];
- ++remap;
+ flags |= VALUE_DEAD;
}
}
+ else
+ {
+ flags |= VALUE_DEAD;
+ }
}
-
- /* Clear the remapping for the register */
- gen->contents[reg].remap = -1;
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "leave free_stack_reg");
-#endif
-}
-
-/*
- * Make space for a new stack register in a particular stack.
- * Returns the pseudo register number of the newly allocated register.
- */
-static int create_stack_reg(jit_gencode_t gen, int reg, int roll_down)
-{
- int first_stack_reg;
- int temp_reg, remap;
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "enter create_stack_reg");
-#endif
-
- /* Find the first pseudo register in the stack */
- while((_jit_reg_info[reg].flags & JIT_REG_START_STACK) == 0)
- {
- --reg;
- }
- first_stack_reg = reg;
-
- /* Find a free pseudo register in the stack */
- for(;;)
+ if(value == regs->descs[1].value)
{
- if(gen->contents[reg].num_values == 0 &&
- !(gen->contents[reg].used_for_temp))
+ flags |= VALUE_INPUT;
+ if(regs->descs[1].used)
{
- break;
+ flags |= VALUE_LIVE | VALUE_USED;
}
- if((_jit_reg_info[reg].flags & JIT_REG_END_STACK) != 0)
+ else if(regs->descs[1].live)
{
- /* None of the registers are free, so we have to spill them all */
- spill_all_between(gen, first_stack_reg, reg);
- reg = first_stack_reg;
- break;
+ flags |= VALUE_LIVE;
+ }
+ else
+ {
+ flags |= VALUE_DEAD;
}
- ++reg;
}
-
- /* Roll the stack remappings down to make room at the top */
- if(roll_down)
+ if(value == regs->descs[2].value)
{
- temp_reg = first_stack_reg - 1;
- do
+ flags |= VALUE_INPUT;
+ if(regs->descs[2].used)
{
- ++temp_reg;
- remap = gen->contents[temp_reg].remap;
- if(remap != -1)
- {
- /* Change the register's position in the stack */
- gen->contents[temp_reg].remap = remap + 1;
- gen->stack_map[remap + 1] = temp_reg;
-
- /* Mark the rolled-down register position as touched */
- jit_reg_set_used(gen->touched, remap + 1);
- }
+ flags |= VALUE_LIVE | VALUE_USED;
+ }
+ else if(regs->descs[2].live)
+ {
+ flags |= VALUE_LIVE;
+ }
+ else
+ {
+ flags |= VALUE_DEAD;
}
- while((_jit_reg_info[temp_reg].flags & JIT_REG_END_STACK) == 0);
- gen->contents[reg].remap = first_stack_reg;
- gen->stack_map[first_stack_reg] = reg;
}
-
- /* Mark the register as touched, in case it needs to be saved */
- jit_reg_set_used(gen->touched, reg);
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "leave create_stack_reg");
-#endif
-
- /* Return the free register to the caller */
- return reg;
+ return flags;
}
/*
- * Free a register, and optionally spill its value.
+ * Check if the register contains any live values.
*/
-static void free_reg_and_spill
- (jit_gencode_t gen, int reg, int value_used, int spill)
+static int
+is_register_alive(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
- int other_reg, posn;
- jit_value_t value;
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "enter free_reg_and_spill");
-#endif
+ int index, usage;
- /* Find the other register in a long pair */
- if(gen->contents[reg].is_long_start)
- {
- other_reg = _jit_reg_info[reg].other_reg;
- gen->contents[reg].is_long_start = 0;
- gen->contents[other_reg].is_long_end = 0;
- }
- else if(gen->contents[reg].is_long_end)
- {
- gen->contents[reg].is_long_end = 0;
- other_reg = reg;
- for(reg = 0; reg < JIT_NUM_REGS; ++reg)
- {
- if(other_reg == _jit_reg_info[reg].other_reg)
- {
- gen->contents[reg].is_long_start = 0;
- break;
- }
- }
- }
- else
+ if(reg < 0)
{
- other_reg = -1;
+ return 0;
}
- /* Spill the register's contents to the local variable frame */
- if(spill && gen->contents[reg].num_values > 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))
{
- for(posn = gen->contents[reg].num_values - 1; posn >= 0; --posn)
+ if(!regs->ternary
+ && regs->descs[0].value
+ && regs->descs[0].value->has_global_register
+ && regs->descs[0].value->global_reg == reg)
{
- value = gen->contents[reg].values[posn];
- if(value->has_global_register)
- {
- if(!(value->in_global_register))
- {
- _jit_gen_spill_reg(gen, reg, other_reg, value);
- value->in_global_register = 1;
- value_used = 1;
- }
- }
- else if(!(value->in_frame))
- {
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
- {
- _jit_gen_spill_reg(gen, reg, other_reg, value);
- }
- else
- {
- _jit_gen_spill_reg
- (gen, gen->contents[reg].remap, -1, value);
- }
- value->in_frame = 1;
- value_used = 1;
- }
- value->in_register = 0;
- value->reg = -1;
+ return 0;
}
+ return 1;
}
- /* The registers do not contain values any more */
- gen->contents[reg].num_values = 0;
- gen->contents[reg].used_for_temp = 0;
- if(other_reg != -1)
- {
- gen->contents[other_reg].num_values = 0;
- gen->contents[other_reg].used_for_temp = 0;
- }
-
- /* If the registers are members of a stack, then readjust the
- stack mappings to compensate for the change */
- if(gen->contents[reg].remap != -1)
+ if(gen->contents[reg].is_long_end)
{
- free_stack_reg(gen, reg);
+ reg = get_long_pair_start(reg);
}
- if(other_reg != -1 && gen->contents[other_reg].remap != -1)
+ for(index = 0; index < gen->contents[reg].num_values; index++)
{
- free_stack_reg(gen, other_reg);
+ usage = value_usage(regs, gen->contents[reg].values[index]);
+ if((usage & VALUE_DEAD) == 0)
+ {
+ return 1;
+ }
}
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "leave free_reg_and_spill");
-#endif
-
- /* Free the register using CPU-specific code */
- _jit_gen_free_reg(gen, reg, other_reg, value_used);
+ return 0;
}
-/*@
- * @deftypefun void _jit_regs_want_reg (jit_gencode_t gen, int reg, int for_long)
- * Tell the register allocator that we want a particular register
- * for a specific purpose. The current contents of the register
- * are spilled. If @code{reg} is part of a register pair, then the
- * other register in the pair will also be spilled. If @code{reg}
- * is a stack register, then it should be the first one.
+/*
+ * 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.
*
- * This is typically used for x86 instructions that require operands
- * to be in certain registers (especially multiplication and division),
- * and we want to make sure that the register is free before we clobber it.
- * It is also used to make space in the x86 FPU for floating-point returns.
+ * 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.
*
- * This may return a different pseudo register number if @code{reg}
- * was a member of a stack and some other register was made free.
- * @end deftypefun
-@*/
-int _jit_regs_want_reg(jit_gencode_t gen, int reg, int for_long)
+ * 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 other_reg;
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
+ int flags;
+
+ if(!regs->descs[index].value)
{
- /* Spill an ordinary register and its pair register */
- free_reg_and_spill(gen, reg, 0, 1);
- if(for_long)
+ return CLOBBER_NONE;
+ }
+
+ if(regs->ternary || !regs->descs[0].value)
+ {
+ /* this is either a ternary or binary or unary note */
+ if(IS_STACK_REG(reg) || regs->descs[index].clobber)
{
- other_reg = _jit_reg_info[reg].other_reg;
- if(other_reg != -1)
- {
- free_reg_and_spill(gen, other_reg, 0, 1);
- }
+ flags = CLOBBER_INPUT_VALUE;
}
else
{
- other_reg = -1;
+ flags = CLOBBER_NONE;
}
-
- /* Mark the register as touched and return it */
- jit_reg_set_used(gen->touched, reg);
- if(other_reg != -1)
+ }
+ else if(index == 0)
+ {
+ /* this is the output value of a binary or unary op */
+ flags = CLOBBER_NONE;
+ if(is_register_alive(gen, regs, reg))
{
- jit_reg_set_used(gen->touched, other_reg);
+ flags |= CLOBBER_REG;
}
- return reg;
+ if(is_register_alive(gen, regs, other_reg))
+ {
+ flags |= CLOBBER_OTHER_REG;
+ }
+ return flags;
}
- else
+ else if(regs->on_stack && !regs->no_pop)
{
- /* If we want a stack register, all we have to do is roll
- everything down to make room for the new value. If the
- stack is full, then we spill the entire stack */
- return create_stack_reg(gen, reg, 0);
+ /* this is a binary or unary stack op -- the input value
+ is either popped or overwritten by the output */
+ flags = CLOBBER_INPUT_VALUE;
}
-}
-
-/*@
- * @deftypefun void _jit_regs_free_reg (jit_gencode_t gen, int reg, int value_used)
- * Free the contents of a pseudo register, without spilling. Used when
- * the contents of a register becomes invalid. If @code{value_used}
- * is non-zero, then it indicates that the value has already been
- * used. On some systems, an explicit instruction is needed to free
- * a register whose value hasn't been used yet (e.g. x86 floating point
- * stack registers).
- * @end deftypefun
-@*/
-void _jit_regs_free_reg(jit_gencode_t gen, int reg, int value_used)
-{
-#ifdef JIT_REG_DEBUG
- printf("enter _jit_regs_free_reg(reg = %d, value_used = %d)\n", reg, value_used);
-#endif
-
- free_reg_and_spill(gen, reg, value_used, 0);
-
-#ifdef JIT_REG_DEBUG
- printf("leave _jit_regs_free_reg\n");
-#endif
-}
-
-/*@
- * @deftypefun void _jit_regs_set_value (jit_gencode_t gen, int reg, jit_value_t value, int still_in_frame)
- * Set pseudo register @code{reg} to record that it currently holds the
- * contents of @code{value}. The value is assumed to already be in
- * the register and no spill occurs. If @code{still_in_frame} is
- * non-zero, then the value is still in the stack frame; otherwise the
- * value is exclusively in the register.
- * @end deftypefun
-@*/
-void _jit_regs_set_value
- (jit_gencode_t gen, int reg, jit_value_t value, int still_in_frame)
-{
- int other_reg;
- int first_stack_reg;
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "enter set_value");
-#endif
-
- /* Get the other register in a pair */
- if(_jit_regs_needs_long_pair(value->type))
+ 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)
{
- other_reg = _jit_reg_info[reg].other_reg;
+ flags = CLOBBER_INPUT_VALUE;
}
else
{
- other_reg = -1;
+ flags = CLOBBER_NONE;
}
- /* Mark the register as touched */
- jit_reg_set_used(gen->touched, reg);
- if(other_reg != -1)
+ if(flags == CLOBBER_NONE)
{
- jit_reg_set_used(gen->touched, other_reg);
+ 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;
+ }
}
- /* Adjust the allocation state to reflect that "reg" contains "value" */
- gen->contents[reg].values[0] = value;
- gen->contents[reg].num_values = 1;
- gen->contents[reg].age = gen->current_age;
- if(other_reg == -1)
+ if(is_register_alive(gen, regs, reg))
{
- gen->contents[reg].is_long_start = 0;
- gen->contents[reg].is_long_end = 0;
- gen->contents[reg].used_for_temp = 0;
+ flags |= CLOBBER_REG;
}
- else
+ if(is_register_alive(gen, regs, other_reg))
{
- gen->contents[reg].is_long_start = 1;
- gen->contents[reg].is_long_end = 0;
- gen->contents[reg].used_for_temp = 0;
- gen->contents[other_reg].num_values = 0;
- gen->contents[other_reg].is_long_start = 0;
- gen->contents[other_reg].is_long_end = 1;
- gen->contents[other_reg].age = gen->current_age;
- gen->contents[other_reg].used_for_temp = 0;
+ flags |= CLOBBER_OTHER_REG;
}
- (gen->current_age)++;
+ return flags;
+}
- /* Set the stack mappings for this register */
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0)
+/*
+ * Assign scratch register.
+ */
+static void
+set_scratch_register(jit_gencode_t gen, _jit_regs_t *regs, int index, int reg)
+{
+ if(reg >= 0)
{
- first_stack_reg = reg;
- while((_jit_reg_info[first_stack_reg].flags & JIT_REG_START_STACK) == 0)
- {
- --first_stack_reg;
- }
- gen->contents[reg].remap = first_stack_reg;
- gen->stack_map[first_stack_reg] = reg;
- }
-
-#ifdef JIT_REG_DEBUG
- dump_regs(gen, "leave set_value");
-#endif
+ regs->scratch[index].reg = reg;
- /* 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 = (short)reg;
+ jit_reg_set_used(gen->touched, reg);
+ jit_reg_set_used(regs->clobber, reg);
+ jit_reg_set_used(regs->assigned, reg);
+ }
}
-/*@
- * @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}. If the register was previously in use,
- * then spill its value first.
- * @end deftypefun
-@*/
-void _jit_regs_set_incoming(jit_gencode_t gen, int reg, jit_value_t value)
+/*
+ * Initialize value descriptor.
+ */
+static void
+init_regdesc(_jit_regs_t *regs, int index)
{
- /* Eject any values that are currently in the register */
- reg = _jit_regs_want_reg(gen, reg, _jit_regs_needs_long_pair(value->type));
+ _jit_regdesc_t *desc;
- /* Record that the value is in "reg", but not in the frame */
- _jit_regs_set_value(gen, reg, value, 0);
+ desc = ®s->descs[index];
+ desc->value = 0;
+ desc->reg = -1;
+ desc->other_reg = -1;
+ desc->stack_reg = -1;
+ desc->regset = jit_regused_init_used;
+ desc->live = 0;
+ desc->used = 0;
+ desc->clobber = 0;
+ desc->early_clobber = 0;
+ desc->duplicate = 0;
+ desc->thrash = 0;
+ desc->save = 0;
+ desc->load = 0;
+ desc->copy = 0;
+ desc->kill = 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)
+/*
+ * Set value information.
+ */
+static void
+set_regdesc_value(_jit_regs_t *regs, int index, jit_value_t value, int flags, int live, int used)
{
- int other_reg;
- int need_pair;
+ _jit_regdesc_t *desc;
-#ifdef JIT_BACKEND_X86
- jit_type_t type;
- type = jit_type_normalize(value->type);
- need_pair = 0;
- if(type)
+ desc = ®s->descs[index];
+ desc->value = value;
+ if(index > 0 || regs->ternary)
{
- /* 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)
+ if((flags & _JIT_REGS_EARLY_CLOBBER) != 0)
{
- need_pair = 1;
+ desc->clobber = 1;
+ desc->early_clobber = 1;
}
- }
- if(value->in_register && value->reg == reg && !need_pair)
-#else
- need_pair = _jit_regs_needs_long_pair(value->type);
- if(value->in_register && value->reg == reg)
-#endif
- {
- /* 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) &&
- !(value->is_temporary))
+ else if((flags & _JIT_REGS_CLOBBER) != 0)
{
- free_reg_and_spill(gen, reg, 1, 1);
+ desc->clobber = 1;
}
-
- /* 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 */
- value->in_register = 0;
- value->reg = -1;
}
- else
+ 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 is_output;
+
+ if(reg >= 0)
{
- /* Force the value out of whatever register it is already in */
- _jit_regs_force_out(gen, value, 0);
+ is_output = (index == 0 && !regs->ternary);
- /* Reload the value into the specified register */
- if(need_pair)
+ regs->descs[index].reg = reg;
+ regs->descs[index].other_reg = other_reg;
+
+ jit_reg_set_used(gen->touched, reg);
+ if(!is_output)
{
- #ifdef JIT_BACKEND_X86
- /* Long values in outgoing registers must be in ECX:EDX,
- not in the ordinary register pairing of ECX:EBX */
- _jit_regs_want_reg(gen, reg, 0);
- other_reg = 2;
- _jit_regs_want_reg(gen, other_reg, 0);
- #else
- _jit_regs_want_reg(gen, reg, 1);
- other_reg = _jit_reg_info[reg].other_reg;
- #endif
- _jit_gen_load_value(gen, reg, other_reg, value);
- jit_reg_set_used(gen->inhibit, reg);
- jit_reg_set_used(gen->inhibit, other_reg);
+ jit_reg_set_used(regs->assigned, reg);
}
- else
+ if(other_reg >= 0)
{
- _jit_regs_want_reg(gen, reg, 0);
- _jit_gen_load_value(gen, reg, -1, value);
- jit_reg_set_used(gen->inhibit, reg);
+ jit_reg_set_used(gen->touched, other_reg);
+ if(!is_output)
+ {
+ jit_reg_set_used(regs->assigned, other_reg);
+ }
}
}
}
-/*@
- * @deftypefun int _jit_regs_is_top (jit_gencode_t gen, jit_value_t value)
- * Determine if @code{value} is currently the in top-most position
- * in the appropriate register stack. Always returns non-zero if
- * @code{value} is in a register, but that register is not part of a
- * register stack. This is used to check if an operand value is
- * already in the right position for a unary operation.
- * @end deftypefun
-@*/
-int _jit_regs_is_top(jit_gencode_t gen, jit_value_t value)
+/*
+ * Determine value flags.
+ */
+static int
+set_regdesc_flags(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int reg, remap;
- if(!(value->in_register))
- {
- return 0;
- }
- reg = value->reg;
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
- {
- return 1;
- }
- remap = gen->contents[reg].remap;
- if(remap != -1 && (_jit_reg_info[remap].flags & JIT_REG_START_STACK) != 0)
+ _jit_regdesc_t *desc;
+ int reg, other_reg;
+ int clobber, clobber_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 1;
}
- return 0;
-}
-/*@
- * @deftypefun int _jit_regs_is_top_two (jit_gencode_t gen, jit_value_t value1, jit_value_t value2)
- * Determine if @code{value1} and @code{value2} are in the top two positions
- * in the appropriate register stack, and @code{value2} is above
- * @code{value1}. Always returns non-zero if @code{value} and
- * @code{value2} are in registers, but those registers are not part
- * of a register stack. This is used to check if the operand values
- * for a binary operation are already in the right positions.
- * @end deftypefun
-@*/
-int _jit_regs_is_top_two
- (jit_gencode_t gen, jit_value_t value1, jit_value_t value2)
-{
- int reg, remap;
- if(!(value1->in_register) || !(value2->in_register))
+ /* Find the registers the value is already in (if any). */
+ if(desc->value->in_register)
{
- return 0;
+ reg = desc->value->reg;
+ if(gen->contents[reg].is_long_start)
+ {
+ other_reg = OTHER_REG(reg);
+ }
+ else
+ {
+ other_reg = -1;
+ }
}
- reg = value2->reg;
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
+ else
{
- reg = value1->reg;
- return ((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0);
+ reg = -1;
+ other_reg = -1;
}
- remap = gen->contents[reg].remap;
- if(remap == -1 || (_jit_reg_info[remap].flags & JIT_REG_START_STACK) == 0)
+
+ /* See if the value clobbers the register it is assigned to. */
+ clobber = clobbers_register(gen, regs, index, desc->reg, desc->other_reg);
+ if((clobber & CLOBBER_INPUT_VALUE) != 0)
{
- return 0;
+ clobber_input = 1;
}
- reg = value1->reg;
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
+ else if(jit_reg_is_used(regs->clobber, desc->reg))
{
- return 1;
+ clobber_input = 1;
}
- return (gen->contents[reg].remap == (remap + 1));
-}
-
-/*
- * Load a value into a register.
- */
-static void load_value(jit_gencode_t gen, int reg, int other_reg,
- jit_value_t value, int destroy)
-{
- _jit_gen_load_value(gen, reg, other_reg, value);
- if(destroy || value->is_constant)
+ else if(desc->other_reg >= 0 && jit_reg_is_used(regs->clobber, desc->other_reg))
{
- /* Mark the register as containing a temporary value */
- gen->contents[reg].used_for_temp = 1;
- jit_reg_set_used(gen->touched, reg);
- if(other_reg != -1)
- {
- gen->contents[reg].is_long_start = 1;
- gen->contents[other_reg].is_long_end = 1;
- gen->contents[other_reg].used_for_temp = 1;
- jit_reg_set_used(gen->touched, other_reg);
- }
+ clobber_input = 1;
}
else
{
- /* Mark the register as containing the value we have loaded */
- if(value->has_global_register)
- _jit_regs_set_value(gen, reg, value, value->in_global_register);
- else
- _jit_regs_set_value(gen, reg, value, value->in_frame);
+ clobber_input = 0;
}
-}
-
-/*
- * Find a free register to hold the contents of a value.
- */
-static int free_register_for_value
- (jit_gencode_t gen, jit_value_t value, int *other_reg)
-{
- int reg, type;
- int suitable_reg, need_pair;
- int suitable_age;
-
- /* Clear the other register before we start */
- *other_reg = -1;
-
- /* Determine if we need a long pair for this value */
- need_pair = _jit_regs_needs_long_pair(value->type);
-
- /* Determine the type of register that we need */
- switch(jit_type_normalize(value->type)->kind)
+ if((clobber & CLOBBER_REG) != 0)
{
- 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_NINT:
- case JIT_TYPE_NUINT:
- case JIT_TYPE_SIGNATURE:
- case JIT_TYPE_PTR:
- {
- type = JIT_REG_WORD;
- }
- break;
+ jit_reg_set_used(regs->clobber, desc->reg);
+ }
+ if((clobber & CLOBBER_OTHER_REG) != 0)
+ {
+ jit_reg_set_used(regs->clobber, desc->other_reg);
+ }
- case JIT_TYPE_LONG:
- case JIT_TYPE_ULONG:
+ /* See if the input value is thrashed by other inputs or clobbered
+ by the output. The allocator tries to avoid thrashing so it may
+ only take place if the register is assigned explicitly. For x87
+ registers the problem of thrashing may be best solved with fxch
+ but as the stack registers are never assigned explicitely there
+ is no such problem for them at all. */
+ if(reg >= 0 && (index > 0 || regs->ternary))
+ {
+ if(index != 0 && regs->ternary && !are_values_equal(desc, ®s->descs[0]))
{
- if(need_pair)
- type = JIT_REG_LONG;
- else
- type = JIT_REG_WORD;
+ if(reg == regs->descs[0].reg
+ || reg == regs->descs[0].other_reg
+ || (other_reg >= 0
+ && (other_reg == regs->descs[0].reg
+ || other_reg == regs->descs[0].other_reg)))
+ {
+ desc->thrash = 1;
+ }
}
- break;
-
- case JIT_TYPE_FLOAT32:
+ if(index != 1 && !are_values_equal(desc, ®s->descs[1]))
{
- type = JIT_REG_FLOAT32;
+ if(reg == regs->descs[1].reg
+ || reg == regs->descs[1].other_reg
+ || (other_reg >= 0
+ && (other_reg == regs->descs[1].reg
+ || other_reg == regs->descs[1].other_reg)))
+ {
+ desc->thrash = 1;
+ }
}
- break;
-
- case JIT_TYPE_FLOAT64:
+ if(index != 2 && !are_values_equal(desc, ®s->descs[2]))
{
- type = JIT_REG_FLOAT64;
+ 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;
+ }
}
- break;
- case JIT_TYPE_NFLOAT:
+ if(desc->thrash)
{
- type = JIT_REG_NFLOAT;
+ reg = -1;
+ other_reg = -1;
+ desc->save = 1;
}
- break;
-
- default: return -1;
}
- /* Search for a free register, ignoring permanent global allocations.
- We also keep track of the oldest suitable register that is not free */
- suitable_reg = -1;
- suitable_age = -1;
- for(reg = 0; reg < JIT_NUM_REGS; ++reg)
+ if(index > 0 || regs->ternary)
{
- if((_jit_reg_info[reg].flags & type) != 0 &&
- !jit_reg_is_used(gen->permanent, reg) &&
- !jit_reg_is_used(gen->inhibit, reg))
+ /* See if the value needs to be loaded or copied or none. */
+ if(desc->value->has_global_register)
{
- if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0)
- {
- /* We always load stack values to the top of the stack */
- reg = create_stack_reg(gen, reg, 1);
- return reg;
- }
- else if(!need_pair)
+ if(desc->value->global_reg != desc->reg
+ && !(reg >= 0 && reg == desc->reg))
{
- if(gen->contents[reg].num_values == 0 &&
- !(gen->contents[reg].used_for_temp) &&
- !(gen->contents[reg].is_long_end))
- {
- return reg;
- }
+ desc->copy = 1;
}
- else
+ }
+ else
+ {
+ if(reg < 0)
{
- *other_reg = _jit_reg_info[reg].other_reg;
- if(gen->contents[reg].num_values == 0 &&
- !(gen->contents[reg].used_for_temp) &&
- gen->contents[*other_reg].num_values == 0 &&
- !(gen->contents[*other_reg].used_for_temp))
- {
- return reg;
- }
+ desc->load = 1;
}
- if(suitable_reg == -1 || gen->contents[reg].age < suitable_age)
+ else if(reg != desc->reg)
{
- /* This is the oldest suitable register of this type */
- suitable_reg = reg;
- suitable_age = gen->contents[reg].age;
+ desc->copy = 1;
}
}
- }
-
- /* If there were no suitable registers at all, then fail */
- if(suitable_reg == -1)
- {
- return -1;
- }
-
- /* Eject the current contents of the register */
- reg = _jit_regs_want_reg(gen, suitable_reg, need_pair);
- if(need_pair)
- {
- *other_reg = _jit_reg_info[reg].other_reg;
- }
- return reg;
-}
-
-/*@
- * @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.
- * @end deftypefun
-@*/
-int _jit_regs_load_value
- (jit_gencode_t gen, jit_value_t value, int destroy, int used_again)
-{
- int reg, other_reg, need_pair;
-
- /* Determine if we need a long pair for this value */
- need_pair = _jit_regs_needs_long_pair(value->type);
- /* If the value is already in a register, then try to use that register */
- if(value->in_register)
- {
- reg = value->reg;
- if(destroy)
+ /* See if the input value needs to be saved before the
+ instruction and if it stays or not in the register
+ after the instruction. */
+ if(desc->value->is_constant)
+ {
+ desc->kill = 1;
+ }
+ else if(reg >= 0)
{
- if(gen->contents[reg].num_values == 1 &&
- (value->in_frame || value->in_global_register || !used_again))
+ if(desc->used)
{
- /* We are the only value in this register, and the
- value is duplicated in the frame, or will never
- be used again in this block. In this case,
- we can disassociate the register from the value
- and just return the register as-is */
- value->in_register = 0;
- gen->contents[reg].num_values = 0;
- gen->contents[reg].used_for_temp = 1;
- gen->contents[reg].age = gen->current_age;
- if(need_pair)
+ if(!desc->copy && clobber_input)
{
- other_reg = _jit_reg_info[reg].other_reg;
- gen->contents[other_reg].used_for_temp = 1;
- gen->contents[other_reg].age = gen->current_age;
+ desc->save = 1;
+ desc->kill = 1;
}
- ++(gen->current_age);
- return reg;
}
else
{
- /* We need to spill the register and then reload it */
- spill_register(gen, reg);
+ if(desc->live)
+ {
+ desc->save = 1;
+ }
+ desc->kill = 1;
}
}
- else
+ else if(desc->load)
{
- if(gen->contents[reg].num_values == 1 &&
- (value->in_frame || value->in_global_register || !used_again))
+ if(desc->used)
{
- /* We are the only value in this register, and the
- value is duplicated in the frame, or will never
- be used again in this block. In this case,
- we can disassociate the register from the value
- and just return the register as-is */
- value->in_register = 0;
- gen->contents[reg].num_values = 0;
- gen->contents[reg].used_for_temp = 1;
+ if(clobber_input)
+ {
+ desc->kill = 1;
+ }
}
- gen->contents[reg].age = gen->current_age;
- if(need_pair)
+ else
{
- other_reg = _jit_reg_info[reg].other_reg;
- gen->contents[other_reg].age = gen->current_age;
+ desc->kill = 1;
}
- ++(gen->current_age);
- return reg;
}
}
- /* 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)
+ /* 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))))
{
- return value->global_reg;
+ desc->kill = 1;
}
- /* Search for a free register to hold the value */
- reg = free_register_for_value(gen, value, &other_reg);
- load_value(gen, reg, other_reg, value, destroy);
- return reg;
-}
-
-/*@
- * @deftypefun int _jit_regs_dest_value (jit_gencode_t gen, jit_value_t value)
- * Get a new register to hold @code{value} as a destination. This cannot
- * be used for stack register destinations (use @code{_jit_regs_new_top}
- * for that).
- * @end deftypefun
-@*/
-int _jit_regs_dest_value(jit_gencode_t gen, jit_value_t value)
-{
- int reg, other_reg;
-
- /* If the value is exclusively in a register already, then use that */
- if(value->in_register)
+ if(IS_STACK_REG(desc->reg))
{
- reg = value->reg;
- if(gen->contents[reg].num_values == 1)
+ if(index > 0 || regs->ternary)
{
- value->in_frame = 0;
- value->in_global_register = 0;
- return reg;
+ ++(regs->wanted_stack_count);
+ if(!(desc->load || desc->copy))
+ {
+ ++(regs->loaded_stack_count);
+ }
}
- free_reg_and_spill(gen, reg, 0, 1);
}
- /* Find a suitable register to hold the destination */
- reg = free_register_for_value(gen, value, &other_reg);
- _jit_regs_set_value(gen, reg, value, 0);
- return 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("save = %d\n", desc->save);
+ printf("load = %d\n", desc->load);
+ printf("copy = %d\n", desc->copy);
+ printf("kill = %d\n", desc->kill);
+#endif
+
+ return 1;
}
/*
- * Determine if "num" stack registers are free in a specific stack.
+ * 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 stack_regs_free(jit_gencode_t gen, int reg, int num)
+static int
+compute_spill_cost(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg)
{
- int first_reg;
+ int cost, index, usage;
+ jit_value_t value;
- /* Find the extents of the stack */
- while((_jit_reg_info[reg].flags & JIT_REG_START_STACK) == 0)
- {
- --reg;
- }
- first_reg = reg;
- while((_jit_reg_info[reg].flags & JIT_REG_END_STACK) == 0)
+ if(gen->contents[reg].is_long_end)
{
- ++reg;
+ reg = get_long_pair_start(reg);
}
- /* Search for free registers */
- while(reg >= first_reg)
+ cost = 0;
+ for(index = 0; index < gen->contents[reg].num_values; index++)
{
- if(gen->contents[reg].num_values == 0 &&
- !(gen->contents[reg].used_for_temp))
+ 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)
{
- --num;
- if(num <= 0)
+ if(value->in_global_register)
{
- return 1;
+ cost += COST_SPILL_CLEAN_GLOBAL;
}
- }
- --reg;
- }
- return 0;
-}
-
-/*@
- * @deftypefun int _jit_regs_load_to_top (jit_gencode_t gen, jit_value_t value, int used_again, int type_reg)
- * Load the contents of @code{value} into a register that is guaranteed to
- * be at the top of its stack. This is the preferred way to set up for a
- * unary operation on a stack-based architecture. Returns the pseudo
- * register that contains the value.
- *
- * When @code{value} is loaded, the "destroy" flag is set so that the
- * unary operation will not affect the original contents of @code{value}.
- * The @code{used_again} flag indicates if @code{value} is used again
- * in the current basic block.
- *
- * The @code{type_reg} parameter should be set to the pseudo register
- * number of a suitable register. This is used to determine which
- * register stack to use for the allocation.
- * @end deftypefun
-@*/
-int _jit_regs_load_to_top(jit_gencode_t gen, jit_value_t value, int used_again, int type_reg)
-{
- int reg;
-
- /* Determine if the value is already in the top-most register */
- if(value->in_register)
- {
- reg = value->reg;
- if((_jit_reg_info[gen->contents[reg].remap].flags
- & JIT_REG_START_STACK) != 0)
+ else
+ {
+ cost += COST_SPILL_DIRTY_GLOBAL;
+ }
+ }
+ else
{
- if(value->in_frame || value->in_global_register || !used_again)
+ if(value->in_frame)
{
- /* Disassociate the value from the register and return */
- value->in_register = 0;
- gen->contents[reg].num_values = 0;
- gen->contents[reg].used_for_temp = 1;
- gen->contents[reg].age = gen->current_age;
- ++(gen->current_age);
- return reg;
+ cost += COST_SPILL_CLEAN;
+ }
+ else
+ {
+ cost += COST_SPILL_DIRTY;
}
}
- spill_all_stack(gen, type_reg);
}
-
- /* If there are free registers of this type, then load the value now */
- if(stack_regs_free(gen, type_reg, 1))
- {
- return _jit_regs_load_value(gen, value, 1, used_again);
- }
-
- /* Spill the entire stack contents, to get things into a known state */
- spill_all_stack(gen, type_reg);
-
- /* Reload the value and return */
- return _jit_regs_load_value(gen, value, 1, used_again);
-}
-
-/*@
- * @deftypefun int _jit_regs_load_to_top_two (jit_gencode_t gen, jit_value_t value, jit_value_t value2, int used_again1, int used_again2, int type_reg)
- * Load the contents of @code{value} and @code{value2} into registers that
- * are guaranteed to be at the top of the relevant register stack.
- * This is the preferred way to set up for a binary operation on a
- * stack-based architecture.
- *
- * Returns the pseudo register that contains @code{value}. The pseudo
- * register that contains @code{value2} is marked as free, because it is
- * assumed that the binary operation will immediately consume its value.
- *
- * When @code{value} are @code{value2} are loaded, the "destroy" flag is
- * set so that the binary operation will not affect their original contents.
- * The @code{used_again1} and @code{used_again2} flags indicate if
- * @code{value} and @code{value2} are used again in the current basic block.
- *
- * The @code{type_reg} parameter should be set to the pseudo register
- * number of a suitable register. This is used to determine which
- * register stack to use for the allocation.
- * @end deftypefun
-@*/
-int _jit_regs_load_to_top_two
- (jit_gencode_t gen, jit_value_t value, jit_value_t value2,
- int used_again1, int used_again2, int type_reg)
-{
- int reg, reg2;
-
- /* Determine if the values are already in the top two registers */
- if(value->in_register && value2->in_register)
+ if(other_reg >= 0)
{
- reg = value->reg;
- reg2 = value2->reg;
- if((_jit_reg_info[gen->contents[reg2].remap].flags
- & JIT_REG_START_STACK) != 0 &&
- gen->contents[reg].remap == (gen->contents[reg2].remap + 1))
+ for(index = 0; index < gen->contents[other_reg].num_values; index++)
{
- if((value->in_frame || value->in_global_register || !used_again1) &&
- (value2->in_frame || value2->in_global_register || !used_again2))
+ value = gen->contents[other_reg].values[index];
+ usage = value_usage(regs, value);
+ if((usage & VALUE_DEAD) != 0)
{
- /* Disassociate the values from the registers and return */
- free_stack_reg(gen, reg2);
- value->in_register = 0;
- value2->in_register = 0;
- gen->contents[reg].num_values = 0;
- gen->contents[reg].used_for_temp = 1;
- gen->contents[reg].age = gen->current_age;
- gen->contents[reg2].num_values = 0;
- gen->contents[reg2].used_for_temp = 0;
- gen->contents[reg2].age = gen->current_age;
- ++(gen->current_age);
- return reg;
+ /* 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;
+ }
}
}
- spill_all_stack(gen, type_reg);
- }
- else if(value2->in_register && !(value->in_register))
- {
- /* We'll probably need to rearrange the stack, so spill first */
- spill_all_stack(gen, type_reg);
- }
-
- /* If there are free registers of this type, then load the values now */
- if(stack_regs_free(gen, type_reg, 2))
- {
- reg = _jit_regs_load_value(gen, value, 1, used_again1);
- reg2 = _jit_regs_load_value(gen, value2, 1, used_again2);
- free_stack_reg(gen, reg2);
- gen->contents[reg2].used_for_temp = 0;
- return reg;
}
-
- /* Spill the entire stack contents, to get things into a known state */
- spill_all_stack(gen, type_reg);
-
- /* Reload the values and return */
- reg = _jit_regs_load_value(gen, value, 1, used_again1);
- reg2 = _jit_regs_load_value(gen, value2, 1, used_again2);
- free_stack_reg(gen, reg2);
- gen->contents[reg2].used_for_temp = 0;
- return reg;
+ return cost;
}
-/*@
- * @deftypefun void _jit_regs_load_to_top_three (jit_gencode_t gen, jit_value_t value, jit_value_t value2, jit_value_t value3, int used_again1, int used_again2, int used_again3, int type_reg)
- * Load three values to the top of a register stack. The values are assumed
- * to be popped by the subsequent operation. This is used by the interpreted
- * back end for things like array stores, that need three values but all
- * of them are discarded after the operation.
- * @end deftypefun
-@*/
-void _jit_regs_load_to_top_three
- (jit_gencode_t gen, jit_value_t value, jit_value_t value2,
- jit_value_t value3, int used_again1, int used_again2,
- int used_again3, int type_reg)
+static int
+thrashes_value(jit_gencode_t gen,
+ _jit_regdesc_t *desc, int reg, int other_reg,
+ _jit_regdesc_t *desc2)
{
- int reg, reg2, reg3;
+ int reg2, other_reg2;
- /* Determine if the values are already in the top three registers */
- if(value->in_register && value2->in_register && value3->in_register)
+#if ALLOW_CLOBBER_GLOBAL
+ if(desc2->value->has_global_register)
{
- reg = value->reg;
- reg2 = value2->reg;
- reg3 = value3->reg;
- if((_jit_reg_info[gen->contents[reg2].remap].flags
- & JIT_REG_START_STACK) != 0 &&
- gen->contents[reg].remap == (gen->contents[reg2].remap + 1) &&
- gen->contents[reg2].remap == (gen->contents[reg3].remap + 1))
- {
- if((value->in_frame || value->in_global_register || !used_again1) &&
- (value2->in_frame || value2->in_global_register ||
- !used_again2) &&
- (value3->in_frame || value3->in_global_register || !used_again3))
+ if(desc2->value->global_reg == reg)
+ {
+ if(desc && desc2->value == desc->value)
{
- /* Disassociate the values from the registers and return */
- free_stack_reg(gen, reg);
- free_stack_reg(gen, reg2);
- free_stack_reg(gen, reg3);
- value->in_register = 0;
- value2->in_register = 0;
- value3->in_register = 0;
- gen->contents[reg].used_for_temp = 0;
- gen->contents[reg2].used_for_temp = 0;
- gen->contents[reg3].used_for_temp = 0;
- return;
+ return 0;
}
+ return 1;
+ }
+ if(desc2->value->global_reg == other_reg)
+ {
+ return 1;
}
}
+#endif
- /* Spill everything out, so that we know where things are */
- spill_all_stack(gen, type_reg);
-
- /* Load the three values that we want onto the stack */
- reg = _jit_regs_load_value(gen, value, 1, used_again1);
- reg2 = _jit_regs_load_value(gen, value2, 1, used_again2);
- reg3 = _jit_regs_load_value(gen, value3, 1, used_again3);
- gen->contents[reg].used_for_temp = 0;
- gen->contents[reg2].used_for_temp = 0;
- gen->contents[reg3].used_for_temp = 0;
-}
-
-/*@
- * @deftypefun int _jit_regs_num_used (jit_gencode_t gen, int type_reg)
- * Get the number of stack registers in use within the register stack
- * indicated by @code{type_reg}.
- * @end deftypefun
-@*/
-int _jit_regs_num_used(jit_gencode_t gen, int type_reg)
-{
- int count;
- while((_jit_reg_info[type_reg].flags & JIT_REG_START_STACK) == 0)
- {
- --type_reg;
- }
- count = 0;
- for(;;)
+ if(desc2->value->in_register)
{
- if(gen->contents[type_reg].num_values > 0 ||
- gen->contents[type_reg].used_for_temp)
+ reg2 = desc2->value->reg;
+ if(reg2 == reg)
+ {
+ if(are_values_equal(desc2, desc))
+ {
+ return 0;
+ }
+ return 1;
+ }
+ if(reg2 == other_reg)
{
- ++count;
+ return 1;
}
- if((_jit_reg_info[type_reg].flags & JIT_REG_END_STACK) == 0)
+ if(gen->contents[reg2].is_long_start)
{
- break;
+ other_reg2 = OTHER_REG(reg2);
+ if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ {
+ return 1;
+ }
}
- ++type_reg;
}
- return count;
+
+ return 0;
}
-/*@
- * @deftypefun int _jit_regs_new_top (jit_gencode_t gen, jit_value_t value, int type_reg)
- * Record that the top of the stack indicated by @code{type_reg} now
- * contains @code{value}. This is slightly different from
- * @code{_jit_regs_set_value}, in that the register wasn't previously
- * allocated to a temporary operand value. Returns the actual stack
- * register that contains @code{value}.
- * @end deftypefun
-@*/
-int _jit_regs_new_top(jit_gencode_t gen, jit_value_t value, int type_reg)
+static int
+choose_scratch_register(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int reg;
-
- /* Create space for the value at the top of the stack */
- reg = create_stack_reg(gen, type_reg, 1);
-
- /* Record the "value" is now in this register */
- value->in_register = 1;
- value->in_frame = 0;
- value->in_global_register = 0;
- value->reg = reg;
- gen->contents[reg].values[0] = value;
- gen->contents[reg].num_values = 1;
+ int reg, type;
+ int use_cost, spill_cost;
+ int suitable_reg;
+ int suitable_cost;
+ int suitable_age;
- /* Return the allocated register to the caller */
- return reg;
-}
+ type = JIT_REG_WORD;
-/*@
- * @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.
- * @end deftypefun
-@*/
-void _jit_regs_force_out(jit_gencode_t gen, jit_value_t value, int is_dest)
-{
- if(value->in_register)
+ suitable_reg = -1;
+ suitable_cost = COST_TOO_MUCH;
+ suitable_age = -1;
+ for(reg = 0; reg < JIT_NUM_REGS; reg++)
{
- if((_jit_reg_info[value->reg].flags & JIT_REG_IN_STACK) == 0)
+ if((_jit_reg_info[reg].flags & type) == 0)
{
- free_reg_and_spill(gen, value->reg, 0, !is_dest);
+ continue;
}
- else
+ if(jit_reg_is_used(regs->assigned, reg))
{
- /* Always do a spill for a stack register */
- spill_register(gen, value->reg);
+ continue;
+ }
+ if(!jit_reg_is_used(regs->scratch[index].regset, reg))
+ {
+ continue;
}
- }
-}
-
-/*
- * 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
-
-/*@
- * @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 ALLOW_CLOBBER_GLOBAL
+ if(jit_reg_is_used(gen->permanent, reg))
{
- if((_jit_reg_info[reg].flags &
- (JIT_REG_FIXED | JIT_REG_CALL_USED)) == 0)
- {
- jit_reg_set_used(gen->permanent, reg);
- }
+ use_cost = COST_CLOBBER_GLOBAL;
}
- return;
- }
+ else
+ {
+ use_cost = 0;
+ }
+#else
+ if(jit_reg_is_used(gen->permanent, reg))
+ {
+ continue;
+ }
+ use_cost = 0;
+#endif
- /* Scan all values within the function, looking for the most used.
- We will replace this with a better allocation strategy later */
- block = func->builder->value_pool.blocks;
- num = (int)(func->builder->value_pool.elems_per_block);
- while(block != 0)
- {
- if(!(block->next))
+ if(regs->ternary && regs->descs[0].value
+ && thrashes_value(gen, 0, reg, -1, ®s->descs[0]))
{
- num = (int)(func->builder->value_pool.elems_in_last);
+ use_cost += COST_THRASH;
}
- for(posn = 0; posn < num; ++posn)
+ else if(regs->descs[1].value
+ && thrashes_value(gen, 0, reg, -1, ®s->descs[1]))
{
- value = (jit_value_t)(block->data + posn *
- sizeof(struct _jit_value));
- if(value->global_candidate && value->usage_count >= JIT_MIN_USED &&
- !(value->is_addressable) && !(value->is_volatile))
- {
- /* Insert this candidate into the list, ordered on count */
- index = 0;
- while(index < num_candidates &&
- value->usage_count <= candidates[index]->usage_count)
- {
- ++index;
- }
- while(index < num_candidates)
- {
- temp = candidates[index];
- candidates[index] = value;
- value = temp;
- ++index;
- }
- if(index < JIT_NUM_GLOBAL_REGS)
- {
- candidates[num_candidates++] = value;
- }
- }
- }
- block = block->next;
- }
-
- /* Allocate registers to the candidates. We allocate from the top-most
- register in the allocation order, because some architectures like
- PPC require global registers to be saved top-down for efficiency */
- reg = JIT_NUM_REGS - 1;
- for(index = 0; index < num_candidates; ++index)
- {
- while(reg >= 0 && (_jit_reg_info[reg].flags & JIT_REG_GLOBAL) == 0)
- {
- --reg;
- }
- candidates[index]->has_global_register = 1;
- candidates[index]->global_reg = (short)reg;
- jit_reg_set_used(gen->touched, reg);
- jit_reg_set_used(gen->permanent, reg);
- --reg;
- }
-
-#endif
-}
-
-/*
- * @deftypefun void _jit_regs_get_reg_pair (jit_gencode_t gen, int not_this1, int not_this2, int not_this3, {int *} reg, {int *} reg2)
- * Get a register pair for temporary operations on "long" values.
- * @end deftypefun
- */
-void _jit_regs_get_reg_pair(jit_gencode_t gen, int not_this1, int not_this2,
- int not_this3, int *reg, int *reg2)
-{
- int index;
- for(index = 0; index < 8; ++index)
- {
- if((_jit_reg_info[index].flags & JIT_REG_WORD) == 0 ||
- jit_reg_is_used(gen->permanent, index))
- {
- continue;
- }
- if(index != not_this1 && index != not_this2 &&
- index != not_this3)
- {
- break;
- }
- }
- *reg = index;
- _jit_regs_want_reg(gen, index, 0);
- if(!reg2)
- {
- return;
- }
- for(; index < 8; ++index)
- {
- if((_jit_reg_info[index].flags & JIT_REG_WORD) == 0 ||
- jit_reg_is_used(gen->permanent, index))
- {
- continue;
- }
- if(index != not_this1 && index != not_this2 &&
- index != not_this3 && index != *reg)
- {
- break;
- }
- }
- if(index >= 8)
- {
- *reg2 = -1;
- }
- else
- {
- *reg2 = index;
- _jit_regs_want_reg(gen, index, 0);
- }
-}
-
-/*
- * New Reg Alloc API
- */
-
-#define IS_STACK_REG(reg) ((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0)
-#define IS_STACK_START(reg) ((_jit_reg_info[reg].flags & JIT_REG_START_STACK) != 0)
-#define IS_STACK_END(reg) ((_jit_reg_info[reg].flags & JIT_REG_END_STACK) != 0)
-#define OTHER_REG(reg) (_jit_reg_info[reg].other_reg)
-
-/* 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 2
-#define COST_SPILL_CLEAN 1
-#define COST_SPILL_CLEAN_GLOBAL 1
-#define COST_GLOBAL_BIAS 1
-#define COST_THRASH 32
-#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
-
-/*
- * For a stack register find the first stack register.
- */
-static int
-get_stack_start(int reg)
-{
- if(IS_STACK_REG(reg))
- {
- while(!IS_STACK_START(reg))
- {
- --reg;
- }
- }
- return reg;
-}
-
-/*
- * Find the stack top given the first stack register,
- */
-static int
-get_stack_top(jit_gencode_t gen, int stack_start)
-{
- if(gen->stack_map[stack_start] < 0)
- {
- return (stack_start - 1);
- }
- return (gen->stack_map[stack_start]);
-}
-
-/*
- * Find the start register of a long 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 == OTHER_REG(reg))
- {
- return reg;
- }
- }
- return -1;
-}
-
-/*
- * Determine the type of register that we need.
- */
-static int
-get_register_type(jit_value_t value, int need_pair)
-{
- switch(jit_type_normalize(value->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_NINT:
- case JIT_TYPE_NUINT:
- case JIT_TYPE_SIGNATURE:
- case JIT_TYPE_PTR:
- return JIT_REG_WORD;
-
- case JIT_TYPE_LONG:
- case JIT_TYPE_ULONG:
- return need_pair ? JIT_REG_LONG : JIT_REG_WORD;
-
- case JIT_TYPE_FLOAT32:
- return JIT_REG_FLOAT32;
-
- case JIT_TYPE_FLOAT64:
- return JIT_REG_FLOAT64;
-
- case JIT_TYPE_NFLOAT:
- return JIT_REG_NFLOAT;
- }
-
- return 0;
-}
-
-/*
- * 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;
-}
-
-
-/*
- * 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. Also the old allocator might sometimes leave a dead
- * value in the register and as of this writing the old allocator is still
- * used by some rules. And just in case if some dead value may creep through
- * the new allocator...
- */
-static int
-value_usage(_jit_regs_t *regs, jit_value_t value)
-{
- int flags;
-
- flags = 0;
- if(value->is_constant)
- {
- flags |= VALUE_DEAD;
- }
- 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;
-}
-
-/*
- * 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(IS_STACK_REG(reg) || regs->descs[index].clobber)
- {
- flags = CLOBBER_INPUT_VALUE;
- }
- else
- {
- flags = CLOBBER_NONE;
- }
- }
- else if(index == 0)
- {
- /* this is the output value of a binary or unary op */
- flags = CLOBBER_NONE;
- if(is_register_alive(gen, regs, reg))
- {
- flags |= CLOBBER_REG;
- }
- if(is_register_alive(gen, regs, other_reg))
- {
- flags |= CLOBBER_OTHER_REG;
- }
- return flags;
- }
- else if(regs->on_stack && !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;
- }
- 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;
- }
- }
-
- if(is_register_alive(gen, regs, reg))
- {
- flags |= CLOBBER_REG;
- }
- if(is_register_alive(gen, regs, other_reg))
- {
- flags |= CLOBBER_OTHER_REG;
- }
- 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);
- }
-}
-
-/*
- * Initialize value descriptor.
- */
-static void
-init_regdesc(_jit_regs_t *regs, int index)
-{
- _jit_regdesc_t *desc;
-
- desc = ®s->descs[index];
- desc->value = 0;
- desc->reg = -1;
- desc->other_reg = -1;
- desc->stack_reg = -1;
- desc->regset = jit_regused_init_used;
- desc->live = 0;
- desc->used = 0;
- desc->clobber = 0;
- desc->early_clobber = 0;
- desc->duplicate = 0;
- desc->thrash = 0;
- desc->save = 0;
- desc->load = 0;
- desc->copy = 0;
- desc->kill = 0;
-}
-
-/*
- * Set value information.
- */
-static void
-set_regdesc_value(_jit_regs_t *regs, int index, jit_value_t value, int flags, int live, int used)
-{
- _jit_regdesc_t *desc;
-
- desc = ®s->descs[index];
- desc->value = value;
- if(index > 0 || regs->ternary)
- {
- if((flags & _JIT_REGS_EARLY_CLOBBER) != 0)
- {
- desc->clobber = 1;
- desc->early_clobber = 1;
- }
- else if((flags & _JIT_REGS_CLOBBER) != 0)
- {
- desc->clobber = 1;
- }
- }
- 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 is_output;
-
- if(reg >= 0)
- {
- is_output = (index == 0 && !regs->ternary);
-
- regs->descs[index].reg = reg;
- regs->descs[index].other_reg = other_reg;
-
- jit_reg_set_used(gen->touched, reg);
- if(!is_output)
- {
- jit_reg_set_used(regs->assigned, reg);
- }
- if(other_reg >= 0)
- {
- jit_reg_set_used(gen->touched, other_reg);
- if(!is_output)
- {
- jit_reg_set_used(regs->assigned, other_reg);
- }
- }
- }
-}
-
-/*
- * Determine value flags.
- */
-static int
-set_regdesc_flags(jit_gencode_t gen, _jit_regs_t *regs, int index)
-{
- _jit_regdesc_t *desc;
- int reg, other_reg, stack_start;
- int clobber, clobber_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 1;
- }
-
- /* Find the registers 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 = OTHER_REG(reg);
- }
- else
- {
- other_reg = -1;
- }
- }
- else
- {
- reg = -1;
- other_reg = -1;
- }
-
- /* See if the value clobbers the register it is assigned to. */
- clobber = clobbers_register(gen, regs, index, desc->reg, desc->other_reg);
- if((clobber & CLOBBER_INPUT_VALUE) != 0)
- {
- clobber_input = 1;
- }
- else if(jit_reg_is_used(regs->clobber, desc->reg))
- {
- clobber_input = 1;
- }
- else if(desc->other_reg >= 0 && jit_reg_is_used(regs->clobber, desc->other_reg))
- {
- clobber_input = 1;
- }
- else
- {
- clobber_input = 0;
- }
- 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);
- }
-
-#if 0
- /* See if the input value is clobbered by the output. */
- if(index > 0 && !regs->ternary
- && (desc->reg == regs->descs[0].reg
- || desc->reg == regs->descs[0].other_reg
- || (desc->other_reg >= 0
- && (desc->other_reg == regs->descs[0].reg
- || desc->other_reg == regs->descs[0].other_reg))))
- {
- clobber_input = 1;
- }
-#endif
-
- /* See if the input value is thrashed by other inputs or clobbered
- by the output. The allocator tries to avoid thrashing so it may
- only take place if the register is assigned explicitly. For x87
- registers the problem of thrashing may be best solved with fxch
- but as the stack registers are never assigned explicitely there
- is no such problem for them at all. */
- if(reg >= 0 && (index > 0 || regs->ternary))
- {
- if(index != 0 && regs->ternary && !are_values_equal(desc, ®s->descs[0]))
- {
- if(reg == regs->descs[0].reg
- || reg == regs->descs[0].other_reg
- || (other_reg >= 0
- && (other_reg == regs->descs[0].reg
- || other_reg == regs->descs[0].other_reg)))
- {
- desc->thrash = 1;
- }
- }
- if(index != 1 && !are_values_equal(desc, ®s->descs[1]))
- {
- if(reg == regs->descs[1].reg
- || reg == regs->descs[1].other_reg
- || (other_reg >= 0
- && (other_reg == regs->descs[1].reg
- || other_reg == regs->descs[1].other_reg)))
- {
- desc->thrash = 1;
- }
- }
- 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;
- desc->save = 1;
- }
- }
-
- if(index > 0 || regs->ternary)
- {
- /* See if the value needs to be loaded or copied or none. */
- if(desc->value->has_global_register)
- {
- if(desc->value->global_reg != desc->reg
- && !(reg >= 0 && reg == desc->reg))
- {
- desc->copy = 1;
- }
- }
- else
- {
- if(reg < 0)
- {
- desc->load = 1;
- }
- else if(reg != desc->reg)
- {
- desc->copy = 1;
- }
- }
-
- /* See if the input value needs to be saved before the
- instruction and if it stays or not in the register
- after the instruction. */
- if(desc->value->is_constant)
- {
- desc->kill = 1;
- }
- else if(reg >= 0)
- {
- if(desc->used)
- {
- if(!desc->copy && clobber_input)
- {
- desc->save = 1;
- desc->kill = 1;
- }
- }
- else
- {
- if(desc->live)
- {
- desc->save = 1;
- }
- desc->kill = 1;
- }
+ use_cost += COST_THRASH;
}
- else if(desc->load)
+ else if(regs->descs[2].value
+ && thrashes_value(gen, 0, reg, -1, ®s->descs[2]))
{
- if(desc->used)
- {
- if(clobber_input)
- {
- desc->kill = 1;
- }
- }
- else
- {
- desc->kill = 1;
- }
+ use_cost += COST_THRASH;
}
- }
-
- /* 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;
- }
- if(IS_STACK_REG(desc->reg))
- {
- stack_start = get_stack_start(desc->reg);
- if(regs->stack_start < 0)
- {
- regs->stack_start = stack_start;
- }
- else if(stack_start != regs->stack_start)
- {
- return 0;
- }
+ spill_cost = compute_spill_cost(gen, regs, reg, -1);
- if(index > 0 || regs->ternary)
+ if((use_cost + spill_cost) < suitable_cost
+ || (spill_cost > 0 && (use_cost + spill_cost) == suitable_cost
+ && gen->contents[reg].age < suitable_age))
{
- ++(regs->wanted_stack_count);
- if(!(desc->load || desc->copy))
- {
- ++(regs->loaded_stack_count);
- }
+ suitable_reg = reg;
+ suitable_cost = use_cost + spill_cost;
+ suitable_age = gen->contents[reg].age;
}
}
-#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("save = %d\n", desc->save);
- printf("load = %d\n", desc->load);
- printf("copy = %d\n", desc->copy);
- printf("kill = %d\n", desc->kill);
-#endif
+ if(suitable_reg >= 0)
+ {
+ set_scratch_register(gen, regs, index, suitable_reg);
+ return 1;
+ }
- return 1;
+ return 0;
}
-/*
- * 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)
+choose_output_register(jit_gencode_t gen, _jit_regs_t *regs)
{
- int cost, index, usage;
- jit_value_t value;
+ int type, need_pair;
+ int reg, other_reg;
+ int use_cost, spill_cost;
+ int suitable_reg, suitable_other_reg;
+ int suitable_cost;
+ int suitable_age;
- if(gen->contents[reg].is_long_end)
+ need_pair = _jit_regs_needs_long_pair(regs->descs[0].value->type);
+ type = get_register_type(regs->descs[0].value, need_pair);
+ if(!type)
{
- reg = get_long_pair_start(reg);
+ return 0;
}
- cost = 0;
- for(index = 0; index < gen->contents[reg].num_values; index++)
+ suitable_reg = -1;
+ suitable_other_reg = -1;
+ suitable_cost = COST_TOO_MUCH;
+ suitable_age = -1;
+ for(reg = 0; reg < JIT_NUM_REGS; reg++)
{
- value = gen->contents[reg].values[index];
- usage = value_usage(regs, value);
- if((usage & VALUE_DEAD) != 0)
+ if((_jit_reg_info[reg].flags & type) == 0)
{
- /* the value is not spilled */
continue;
}
- if((usage & VALUE_LIVE) != 0 && (usage & VALUE_USED) == 0)
+ if(jit_reg_is_used(gen->inhibit, reg))
{
- /* 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(!jit_reg_is_used(regs->descs[0].regset, reg))
{
- if(value->in_global_register)
- {
- cost += COST_SPILL_CLEAN_GLOBAL;
- }
- else
+ continue;
+ }
+
+ if(need_pair)
+ {
+ other_reg = OTHER_REG(reg);
+ if(jit_reg_is_used(gen->inhibit, other_reg))
{
- cost += COST_SPILL_DIRTY_GLOBAL;
+ continue;
}
}
else
{
- if(value->in_frame)
+ other_reg = -1;
+ }
+
+ /* It is not allowed to assign an output value to a global register
+ unless it is the very value the global register contains. */
+ if(jit_reg_is_used(gen->permanent, reg))
+ {
+ if(regs->descs[0].value->has_global_register
+ && regs->descs[0].value->global_reg == reg)
{
- cost += COST_SPILL_CLEAN;
+ use_cost = 0;
}
else
{
- cost += COST_SPILL_DIRTY;
+ continue;
}
}
- }
- if(other_reg >= 0)
- {
- for(index = 0; index < gen->contents[other_reg].num_values; index++)
+ else
{
- value = gen->contents[other_reg].values[index];
- usage = value_usage(regs, value);
- if((usage & VALUE_DEAD) != 0)
+ if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
{
- /* the value is not spilled */
continue;
}
- if((usage & VALUE_LIVE) != 0 && (usage & VALUE_USED) == 0)
+ if(regs->descs[0].value->has_global_register)
{
- /* the value has to be spilled anyway */
- /* NOTE: This is true for local register allocation,
- review for future global allocator. */
- continue;
+ use_cost = COST_GLOBAL_BIAS;
}
- if(value->has_global_register)
+ else
{
- if(value->in_global_register)
- {
- cost += COST_SPILL_CLEAN_GLOBAL;
- }
- else
- {
- cost += COST_SPILL_DIRTY_GLOBAL;
- }
+ use_cost = 0;
+ }
+ }
+
+ if(regs->free_dest)
+ {
+ /* noop */
+ }
+ else if(regs->descs[1].value
+ && regs->descs[1].value->in_register
+ && regs->descs[1].value->reg == reg)
+ {
+ /* noop */
+ }
+ else if(regs->descs[2].value
+ && regs->descs[2].value->in_register
+ && regs->descs[2].value->reg == reg)
+ {
+ if(regs->commutative || regs->x87_arith)
+ {
+ /* noop */
}
else
{
- if(value->in_frame)
- {
- cost += COST_SPILL_CLEAN;
- }
- else
- {
- cost += COST_SPILL_DIRTY;
- }
+ use_cost += COST_THRASH;
}
}
+ else
+ {
+ use_cost += COST_COPY;
+ }
+
+ spill_cost = compute_spill_cost(gen, regs, reg, other_reg);
+
+ if((use_cost + spill_cost) < suitable_cost
+ || (spill_cost > 0 && (use_cost + spill_cost) == suitable_cost
+ && gen->contents[reg].age < suitable_age))
+ {
+ suitable_reg = reg;
+ suitable_other_reg = other_reg;
+ suitable_cost = use_cost + spill_cost;
+ suitable_age = gen->contents[reg].age;
+ }
}
- return cost;
+
+ if(suitable_reg >= 0)
+ {
+ set_regdesc_register(gen, regs, 0, suitable_reg, suitable_other_reg);
+ return 1;
+ }
+
+ return 0;
}
-static int
-thrashes_value(jit_gencode_t gen,
- _jit_regdesc_t *desc, int reg, int other_reg,
- _jit_regdesc_t *desc2)
+/*
+ * Select the best argument order for binary ops. The posibility to select
+ * the order exists only for commutative ops and for some x87 floating point
+ * instructions. Those x87 instructions have variants with reversed argument
+ * order or reversed destination register. Also they have variants that
+ * either do or do not pop the stack top.
+ */
+static void
+choose_input_order(jit_gencode_t gen, _jit_regs_t *regs)
{
- int reg2, other_reg2;
+ _jit_regdesc_t temp_desc;
+ int keep1, keep2;
-#if ALLOW_CLOBBER_GLOBAL
- if(desc2->value->has_global_register)
+ if(regs->ternary || regs->free_dest || !regs->descs[0].value)
{
- if(desc2->value->global_reg == reg)
+ regs->dest_input_index = 0;
+ return;
+ }
+
+ if(regs->descs[2].value
+ && regs->descs[2].value->in_register
+ && regs->descs[2].value->reg == regs->descs[0].reg
+ && regs->descs[2].value != regs->descs[1].value)
+ {
+ if(regs->on_stack && regs->x87_arith)
{
- if(desc && desc2->value == desc->value)
- {
- return 0;
- }
- return 1;
+ regs->no_pop = 1;
+ regs->reverse_dest = 1;
+ regs->dest_input_index = 2;
}
- if(desc2->value->global_reg == other_reg)
+ else
{
- return 1;
+ if(regs->commutative)
+ {
+ temp_desc = regs->descs[1];
+ regs->descs[1] = regs->descs[2];
+ regs->descs[2] = temp_desc;
+ }
+ regs->dest_input_index = 1;
}
}
-#endif
+ else if(regs->descs[1].value)
+ {
+ regs->dest_input_index = 1;
+ }
+ else
+ {
+ regs->dest_input_index = 0;
+ }
- if(desc2->value->in_register)
+ /* Choose between pop and no-pop instructions. */
+ if(regs->on_stack && regs->x87_arith && !regs->no_pop
+ && !regs->clobber_all && !regs->clobber_stack
+ && regs->descs[1].value && regs->descs[2].value)
{
- reg2 = desc2->value->reg;
- if(reg2 == reg)
+ /* Determine if we might want to keep either of input values
+ in registers after the instruction completion. */
+ if(regs->descs[1].value->in_register)
{
- if(are_values_equal(desc2, desc))
- {
- return 0;
- }
- return 1;
+ keep1 = is_register_alive(gen, regs, regs->descs[1].value->reg);
}
- if(reg2 == other_reg)
+ else
{
- return 1;
+ keep1 = (regs->descs[1].used
+ && (regs->descs[1].value != regs->descs[0].value)
+ && !regs->descs[1].clobber);
}
- if(gen->contents[reg2].is_long_start)
+ if(regs->descs[2].value->in_register)
{
- other_reg2 = OTHER_REG(reg2);
- if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
- {
- return 1;
- }
+ keep2 = is_register_alive(gen, regs, regs->descs[2].value->reg);
+ }
+ else
+ {
+ keep2 = (regs->descs[2].used
+ && (regs->descs[2].value != regs->descs[0].value)
+ && !regs->descs[2].clobber);
}
- }
- return 0;
+ regs->no_pop = (keep1 || keep2);
+ }
}
static int
-choose_scratch_register(jit_gencode_t gen, _jit_regs_t *regs, int index)
+choose_input_register(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int reg, type;
+ _jit_regdesc_t *desc;
+ _jit_regdesc_t *desc2;
+ int type, need_pair;
+ int reg, other_reg;
int use_cost, spill_cost;
- int suitable_reg;
+ int suitable_reg, suitable_other_reg;
int suitable_cost;
int suitable_age;
+ int clobber;
- type = JIT_REG_WORD;
+ desc = ®s->descs[index];
+ if(!desc->value)
+ {
+ return 0;
+ }
+
+ need_pair = _jit_regs_needs_long_pair(desc->value->type);
+ type = get_register_type(desc->value, need_pair);
+ if(!type)
+ {
+ return 0;
+ }
+
+ 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 = 0; reg < JIT_NUM_REGS; reg++)
{
continue;
}
- if(!jit_reg_is_used(regs->scratch[index].regset, reg))
+ if(!jit_reg_is_used(desc->regset, reg))
{
continue;
}
-#if ALLOW_CLOBBER_GLOBAL
- if(jit_reg_is_used(gen->permanent, reg))
+ if(need_pair)
{
- use_cost = COST_CLOBBER_GLOBAL;
+ other_reg = OTHER_REG(reg);
+ if(jit_reg_is_used(regs->assigned, other_reg))
+ {
+ continue;
+ }
}
else
+ {
+ other_reg = -1;
+ }
+
+ if((desc->value->in_global_register && desc->value->global_reg == reg)
+ || (desc->value->in_register && desc->value->reg == reg))
{
use_cost = 0;
}
-#else
- if(jit_reg_is_used(gen->permanent, reg))
+ else
{
- continue;
+ use_cost = COST_COPY;
+ }
+ if(desc2->value->has_global_register && desc2->value->global_reg != reg)
+ {
+ use_cost += COST_GLOBAL_BIAS;
}
- use_cost = 0;
-#endif
- if(regs->ternary && regs->descs[0].value
- && thrashes_value(gen, 0, reg, -1, ®s->descs[0]))
+ 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(regs->descs[1].value
- && thrashes_value(gen, 0, reg, -1, ®s->descs[1]))
+ else if(index != 1 && regs->descs[1].value
+ && thrashes_value(gen, desc, reg, other_reg, ®s->descs[1]))
{
use_cost += COST_THRASH;
}
- else if(regs->descs[2].value
- && thrashes_value(gen, 0, reg, -1, ®s->descs[2]))
+ else if(index != 2 && regs->descs[2].value
+ && thrashes_value(gen, desc, reg, other_reg, ®s->descs[2]))
{
use_cost += COST_THRASH;
}
- spill_cost = compute_spill_cost(gen, regs, reg, -1);
+ 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 !ALLOW_CLOBBER_GLOBAL
+ if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
+ {
+ continue;
+ }
+#endif
+ if(jit_reg_is_used(regs->clobber, reg)
+ || (other_reg >= 0 && jit_reg_is_used(regs->clobber, other_reg)))
+ {
+ spill_cost = 0;
+ }
+ else
+ {
+ spill_cost = compute_spill_cost(gen, regs, reg, other_reg);
+ }
+#if ALLOW_CLOBBER_GLOBAL
+ if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
+ {
+ spill_cost += COST_CLOBBER_GLOBAL;
+ }
+#endif
+ }
+ else
+ {
+ spill_cost = 0;
+ }
if((use_cost + spill_cost) < suitable_cost
|| (spill_cost > 0 && (use_cost + spill_cost) == suitable_cost
&& 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 + spill_cost;
suitable_age = gen->contents[reg].age;
}
if(suitable_reg >= 0)
{
- set_scratch_register(gen, regs, index, suitable_reg);
+ set_regdesc_register(gen, regs, index, suitable_reg, suitable_other_reg);
return 1;
}
-
- return 0;
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+static void
+check_duplicate_value(_jit_regs_t *regs, _jit_regdesc_t *desc1, _jit_regdesc_t *desc2)
+{
+ if((!regs->on_stack || regs->x87_arith)
+ && are_values_equal(desc1, desc2)
+ && desc1->reg >= 0 && desc2->reg < 0
+ && !desc1->early_clobber && !desc2->early_clobber)
+ {
+ desc2->reg = desc1->reg;
+ desc2->other_reg = desc1->other_reg;
+ desc2->duplicate = 1;
+ }
}
-static int
-choose_output_register(jit_gencode_t gen, _jit_regs_t *regs)
+#ifdef JIT_REG_STACK
+static void
+adjust_assignment(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int type, need_pair;
- int reg, other_reg;
- int use_cost, spill_cost;
- int suitable_reg, suitable_other_reg;
- int suitable_cost;
- int suitable_age;
+ _jit_regdesc_t *desc;
- need_pair = _jit_regs_needs_long_pair(regs->descs[0].value->type);
- type = get_register_type(regs->descs[0].value, need_pair);
- if(!type)
+ desc = ®s->descs[index];
+ if(!desc->value || !IS_STACK_REG(desc->reg))
{
- return 0;
+ return;
}
- suitable_reg = -1;
- suitable_other_reg = -1;
- suitable_cost = COST_TOO_MUCH;
- suitable_age = -1;
- for(reg = 0; reg < JIT_NUM_REGS; reg++)
+ if(regs->wanted_stack_count == 1)
{
- if((_jit_reg_info[reg].flags & type) == 0)
- {
- continue;
- }
- if(jit_reg_is_used(gen->inhibit, reg))
- {
- continue;
- }
- if(!jit_reg_is_used(regs->descs[0].regset, reg))
+ /* either a unary op or a binary op with duplicate value */
+ desc->reg = gen->reg_stack_top - regs->loaded_stack_count;
+ }
+ else if(regs->wanted_stack_count == 2)
+ {
+ /* a binary op */
+
+ /* find the input value the output goes to */
+ if(index == 0)
{
- continue;
+ index = regs->dest_input_index;
}
- if(need_pair)
+ if(regs->x87_arith && desc->value->in_register && !desc->copy)
{
- other_reg = OTHER_REG(reg);
- if(jit_reg_is_used(gen->inhibit, other_reg))
- {
- continue;
- }
+ desc->reg = desc->value->reg;
}
else
{
- other_reg = -1;
+ desc->reg = gen->reg_stack_top - regs->loaded_stack_count + index - 1;
}
+ }
+}
+#endif
- /* It is not allowed to assign an output value to a global register
- unless it is the very value the global register contains. */
- if(jit_reg_is_used(gen->permanent, reg))
- {
- if(regs->descs[0].value->has_global_register
- && regs->descs[0].value->global_reg == reg)
- {
- use_cost = 0;
- }
- else
- {
- continue;
- }
- }
- else
+#ifdef JIT_REG_STACK
+static void
+select_stack_order(jit_gencode_t gen, _jit_regs_t *regs)
+{
+ _jit_regdesc_t *desc1;
+ _jit_regdesc_t *desc2;
+ _jit_regdesc_t temp_desc;
+ int top_index;
+
+ /* Choose instruction that results into fewer exchanges. */
+ if(regs->on_stack && regs->no_pop && (regs->commutative || regs->reversible))
+ {
+ desc1 = ®s->descs[1];
+ desc2 = ®s->descs[2];
+
+ if(desc1->value->in_register && !desc1->copy
+ && desc2->value->in_register && !desc2->copy)
{
- if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
+ /* Is any of the input values on the stack top? */
+ if(desc1->value->reg == (gen->reg_stack_top - 1))
{
- continue;
+ top_index = 1;
}
- if(regs->descs[0].value->has_global_register)
+ else if(desc2->value->reg == (gen->reg_stack_top - 1))
{
- use_cost = COST_GLOBAL_BIAS;
+ top_index = 2;
}
else
{
- use_cost = 0;
+ /* TODO: See if the next instruction wants output
+ or remaining input to be on the stack top. */
+ top_index = 2;
}
}
-
- if(regs->free_dest)
+ else if(desc1->value->in_register && !desc1->copy)
{
- /* noop */
+ top_index = 2;
}
- else if(regs->descs[1].value
- && regs->descs[1].value->in_register
- && regs->descs[1].value->reg == reg)
+ else if(desc2->value->in_register && !desc2->copy)
{
- /* noop */
+ top_index = 1;
}
- else if(regs->descs[2].value
- && regs->descs[2].value->in_register
- && regs->descs[2].value->reg == reg)
+ else
{
- if(regs->commutative || regs->x87_arith)
+ /* TODO: see if the next instruction wants output or remaining
+ input to be on the stack top. */
+ top_index = 2;
+ }
+
+ if(top_index == 1)
+ {
+ if(regs->reversible)
{
- /* noop */
+ regs->reverse_args = 1;
}
- else
+ else /*if(regs->commutative)*/
{
- use_cost += COST_THRASH;
+ temp_desc = *desc1;
+ *desc1 = *desc2;
+ *desc2 = temp_desc;
}
+ regs->reverse_dest ^= 1;
}
- else
- {
- use_cost += COST_COPY;
- }
+ }
+}
+#endif
- spill_cost = compute_spill_cost(gen, regs, reg, other_reg);
+/*
+ * 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
- if((use_cost + spill_cost) < suitable_cost
- || (spill_cost > 0 && (use_cost + spill_cost) == suitable_cost
- && gen->contents[reg].age < suitable_age))
- {
- suitable_reg = reg;
- suitable_other_reg = other_reg;
- suitable_cost = use_cost + spill_cost;
- suitable_age = gen->contents[reg].age;
- }
+ 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;
}
+}
- if(suitable_reg >= 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)
{
- set_regdesc_register(gen, regs, 0, suitable_reg, suitable_other_reg);
- return 1;
+ value->in_register = 0;
+ value->in_global_register = 1;
+ return;
}
- return 0;
+ if(value->is_constant)
+ {
+ still_in_frame = 0;
+ }
+
+ gen->contents[reg].values[0] = value;
+ gen->contents[reg].num_values = 1;
+ 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;
}
/*
- * Select the best argument order for binary ops. The posibility to select
- * the order exists only for commutative ops and for some x87 floating point
- * instructions. Those x87 instructions have variants with reversed argument
- * order or reversed destination register. Also they have variants that
- * either do or do not pop the stack top.
+ * Disassociate value with register.
*/
static void
-choose_input_order(jit_gencode_t gen, _jit_regs_t *regs)
+unbind_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg)
{
- _jit_regdesc_t temp_desc;
- int keep1, keep2;
+ int index;
- if(regs->ternary || regs->free_dest || !regs->descs[0].value)
+#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)
{
- regs->dest_input_index = 0;
return;
}
- if(regs->descs[2].value
- && regs->descs[2].value->in_register
- && regs->descs[2].value->reg == regs->descs[0].reg
- && regs->descs[2].value != regs->descs[1].value)
+ value->in_register = 0;
+ value->reg = -1;
+
+ for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
{
- if(regs->on_stack && regs->x87_arith)
- {
- regs->no_pop = 1;
- regs->reverse_dest = 1;
- regs->dest_input_index = 2;
- }
- else
+ if(gen->contents[reg].values[index] == value)
{
- if(regs->commutative)
+ --(gen->contents[reg].num_values);
+ for(; index < gen->contents[reg].num_values; index++)
{
- temp_desc = regs->descs[1];
- regs->descs[1] = regs->descs[2];
- regs->descs[2] = temp_desc;
+ gen->contents[reg].values[index] = gen->contents[reg].values[index + 1];
}
- regs->dest_input_index = 1;
+ break;
}
}
- else if(regs->descs[1].value)
- {
- regs->dest_input_index = 1;
- }
- else
- {
- regs->dest_input_index = 0;
- }
- /* Choose between pop and no-pop instructions. */
- if(regs->on_stack && regs->x87_arith && !regs->no_pop
- && !regs->clobber_all && !regs->clobber_stack
- && regs->descs[1].value && regs->descs[2].value)
+ if(gen->contents[reg].num_values == 0 && other_reg >= 0)
{
- /* Determine if we might want to keep either of input values
- in registers after the instruction completion. */
- if(regs->descs[1].value->in_register)
- {
- keep1 = is_register_alive(gen, regs, regs->descs[1].value->reg);
- }
- else
- {
- keep1 = (regs->descs[1].used
- && (regs->descs[1].value != regs->descs[0].value)
- && !regs->descs[1].clobber);
- }
- if(regs->descs[2].value->in_register)
- {
- keep2 = is_register_alive(gen, regs, regs->descs[2].value->reg);
- }
- else
- {
- keep2 = (regs->descs[2].used
- && (regs->descs[2].value != regs->descs[0].value)
- && !regs->descs[2].clobber);
- }
-
- regs->no_pop = (keep1 || keep2);
+ gen->contents[reg].is_long_start = 0;
+ gen->contents[other_reg].is_long_end = 0;
}
}
-static int
-choose_input_register(jit_gencode_t gen, _jit_regs_t *regs, int index)
+/*
+ * 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)
{
- _jit_regdesc_t *desc;
- _jit_regdesc_t *desc2;
- int type, need_pair;
- int reg, other_reg;
- int use_cost, spill_cost;
- int suitable_reg, suitable_other_reg;
- int suitable_cost;
- int suitable_age;
- int clobber;
+ int top, index;
+ int num_values, used_for_temp, age;
+ jit_value_t value1, value2;
- desc = ®s->descs[index];
- if(!desc->value)
- {
- return 0;
- }
+#ifdef JIT_REG_DEBUG
+ printf("exch_stack_top(reg = %d, pop = %d)\n", reg, pop);
+#endif
- need_pair = _jit_regs_needs_long_pair(desc->value->type);
- type = get_register_type(desc->value, need_pair);
- if(!type)
+ if(!IS_STACK_REG(reg))
{
- return 0;
+ return;
}
- if(index == regs->dest_input_index)
+ /* Find the top of the stack. */
+ top = gen->reg_stack_top - 1;
+
+ if(pop)
{
- desc2 = ®s->descs[0];
+ /* Generate move/pop-top instruction. */
+ _jit_gen_move_top(gen, reg);
+ --(gen->reg_stack_top);
}
else
{
- desc2 = desc;
+ /* Generate exchange instruction. */
+ _jit_gen_exch_top(gen, reg);
}
- suitable_reg = -1;
- suitable_other_reg = -1;
- suitable_cost = COST_TOO_MUCH;
- suitable_age = -1;
- for(reg = 0; reg < JIT_NUM_REGS; reg++)
+ /* Update information about the contents of the registers. */
+ for(index = 0;
+ index < gen->contents[reg].num_values || index < gen->contents[top].num_values;
+ index++)
{
- if((_jit_reg_info[reg].flags & type) == 0)
- {
- continue;
- }
- if(jit_reg_is_used(regs->assigned, reg))
- {
- continue;
- }
- if(!jit_reg_is_used(desc->regset, reg))
- {
- continue;
- }
-
- if(need_pair)
- {
- other_reg = OTHER_REG(reg);
- if(jit_reg_is_used(regs->assigned, other_reg))
- {
- continue;
- }
- }
- else
- {
- other_reg = -1;
- }
-
- 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;
- }
+ value1 = (index < gen->contents[top].num_values
+ ? gen->contents[top].values[index] : 0);
+ value2 = (index < gen->contents[reg].num_values
+ ? gen->contents[reg].values[index] : 0);
- 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]))
+ if(value1)
{
- use_cost += COST_THRASH;
+ value1->reg = reg;
}
+ gen->contents[reg].values[index] = value1;
- clobber = clobbers_register(gen, regs, index, reg, other_reg);
- if((clobber & CLOBBER_INPUT_VALUE) != 0)
+ if(pop)
{
- if(desc->used)
+ if(value2)
{
- use_cost += COST_SPILL_CLEAN;
+ value2->reg = -1;
}
+ gen->contents[top].values[index] = 0;
}
- if((clobber & (CLOBBER_REG | CLOBBER_OTHER_REG)) != 0)
+ else
{
- if(jit_reg_is_used(gen->permanent, reg))
- {
- continue;
- }
-#if !ALLOW_CLOBBER_GLOBAL
- if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
- {
- continue;
- }
-#endif
- if(jit_reg_is_used(regs->clobber, reg)
- || (other_reg >= 0 && jit_reg_is_used(regs->clobber, other_reg)))
- {
- spill_cost = 0;
- }
- else
- {
- spill_cost = compute_spill_cost(gen, regs, reg, other_reg);
- }
-#if ALLOW_CLOBBER_GLOBAL
- if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
+ if(value2)
{
- spill_cost += COST_CLOBBER_GLOBAL;
+ value2->reg = top;
}
-#endif
- }
- else
- {
- spill_cost = 0;
- }
-
- if((use_cost + spill_cost) < suitable_cost
- || (spill_cost > 0 && (use_cost + spill_cost) == suitable_cost
- && 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 + spill_cost;
- suitable_age = gen->contents[reg].age;
+ gen->contents[top].values[index] = value2;
}
}
- if(suitable_reg >= 0)
+ if(pop)
{
- set_regdesc_register(gen, regs, index, suitable_reg, suitable_other_reg);
- return 1;
+ num_values = 0;
+ used_for_temp = 0;
+ age = 0;
}
-
- return 0;
-}
-
-/*
- * 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.
- */
-static void
-check_duplicate_value(_jit_regs_t *regs, _jit_regdesc_t *desc1, _jit_regdesc_t *desc2)
-{
- if((!regs->on_stack || regs->x87_arith)
- && are_values_equal(desc1, desc2)
- && desc1->reg >= 0 && desc2->reg < 0
- && !desc1->early_clobber && !desc2->early_clobber)
- {
- desc2->reg = desc1->reg;
- desc2->other_reg = desc1->other_reg;
- desc2->duplicate = 1;
+ 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
-adjust_assignment(jit_gencode_t gen, _jit_regs_t *regs, int index)
+free_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg, int temp)
{
- _jit_regdesc_t *desc;
+#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)\n", reg, other_reg);
+#endif
- desc = ®s->descs[index];
- if(!desc->value || !IS_STACK_REG(desc->reg))
+ /* Never free global registers. */
+ if(value->has_global_register && value->global_reg == reg)
{
return;
}
- if(regs->wanted_stack_count == 1)
+ if(gen->contents[reg].num_values == 1)
{
- /* either a unary op or binary x87 op with duplicate value */
- desc->reg = regs->current_stack_top - regs->loaded_stack_count + 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);
+ }
+#endif
}
- else if(regs->wanted_stack_count == 2)
- {
- /* a binary op */
- /* find the input value the output goes to */
- if(index == 0)
+ 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)\n", reg, other_reg);
+#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)
{
- index = 1 + (regs->reverse_dest ^ regs->reverse_args);
+ return;
}
- if(regs->x87_arith && desc->value->in_register && !desc->copy)
+ if(!value->in_global_register)
{
- desc->reg = desc->value->reg;
+ _jit_gen_spill_reg(gen, reg, other_reg, value);
+ value->in_global_register = 1;
}
- else
+ if(free)
{
- desc->reg = regs->current_stack_top - regs->loaded_stack_count + index;
+ unbind_value(gen, value, reg, other_reg);
}
+ return;
}
- else if(regs->wanted_stack_count == 3)
+
+ /* Take care of constants and values that are already in frame. */
+ if(value->is_constant || value->in_frame)
{
- /* a ternary op */
- desc->reg = regs->current_stack_top - regs->loaded_stack_count + index + 1;
+ if(free)
+ {
+ free_value(gen, value, reg, other_reg, (free == 2));
+ }
+ return;
}
-}
-
-static void
-select_stack_order(jit_gencode_t gen, _jit_regs_t *regs)
-{
- _jit_regdesc_t *desc1;
- _jit_regdesc_t *desc2;
- _jit_regdesc_t temp_desc;
- int top_index;
- /* Choose instruction that results into fewer exchanges. */
- if(regs->on_stack && regs->no_pop && (regs->commutative || regs->reversible))
+ /* Now really save the value into the frame. */
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(reg))
{
- desc1 = ®s->descs[1];
- desc2 = ®s->descs[2];
+ int top;
- if(desc1->value->in_register && !desc1->copy
- && desc2->value->in_register && !desc2->copy)
- {
- /* Is any of the input values is on the stack top? */
- if(desc1->value->reg == regs->current_stack_top)
- {
- top_index = 1;
- }
- else if(desc1->value->reg == regs->current_stack_top)
- {
- top_index = 2;
- }
- else
- {
- /* TODO: See if the next instruction wants output
- or remaining input to be on the stack top. */
- top_index = 2;
- }
- }
- else if(desc1->value->in_register && !desc1->copy)
+ /* 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)
{
- top_index = 2;
+ exch_stack_top(gen, reg, 0);
}
- else if(desc2->value->in_register && !desc2->copy)
+
+ if(free && gen->contents[top].num_values == 1)
{
- top_index = 1;
+ _jit_gen_spill_top(gen, top, value, 1);
+ --(gen->reg_stack_top);
}
else
{
- /* TODO: see if the next instruction wants output or remaining
- input to be on the stack top. */
- top_index = 2;
+ _jit_gen_spill_top(gen, top, value, 0);
}
+ }
+ else
+#endif
+ {
+ _jit_gen_spill_reg(gen, reg, other_reg, value);
+ }
- if(top_index == 1)
- {
- if(regs->reversible)
- {
- regs->reverse_args = 1;
- }
- else /*if(regs->commutative)*/
- {
- temp_desc = *desc1;
- *desc1 = *desc2;
- *desc2 = temp_desc;
- }
- regs->reverse_dest ^= 1;
- }
+ if(free)
+ {
+ unbind_value(gen, value, reg, other_reg);
}
+ value->in_frame = 1;
}
+/*
+ * Spill a specific register.
+ */
static void
-remap_stack_up(jit_gencode_t gen, int stack_start, int reg)
+spill_register(jit_gencode_t gen, int reg)
{
- int index;
+ int other_reg, index;
+ jit_value_t value;
#ifdef JIT_REG_DEBUG
- printf("remap_stack_up(stack_start = %d, reg = %d)\n", stack_start, reg);
+ printf("spill_register(reg = %d)\n", reg);
#endif
- for(index = stack_start; index < reg; index++)
+ /* Find the other register in a long pair */
+ if(gen->contents[reg].is_long_start)
{
- if(gen->contents[index].remap >= 0)
- {
- ++(gen->contents[index].remap);
- gen->stack_map[gen->contents[index].remap] = index;
- }
+ other_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);
}
- gen->contents[reg].remap = stack_start;
- gen->stack_map[stack_start] = reg;
}
+/*
+ * Spill a register clobbered by the instruction.
+ */
static void
-remap_stack_down(jit_gencode_t gen, int stack_start, int reg)
+spill_clobbered_register(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
- int index;
+ int other_reg, index, usage;
+ jit_value_t value;
#ifdef JIT_REG_DEBUG
- printf("remap_stack_down(stack_start = %d, reg = %d)\n", stack_start, reg);
+ printf("spill_clobbered_register(reg = %d)\n", reg);
#endif
- gen->stack_map[gen->contents[stack_start].remap] = -1;
- for(index = stack_start; index < reg; index++)
+ /* Find the other register in a long pair */
+ if(gen->contents[reg].is_long_start)
+ {
+ other_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;
+ }
+
+ /* Spill register contents in two passes. First free values that
+ do not reqiure spilling then spill those that do. This approach
+ is only useful in case a stack register contains both kinds of
+ values and the last value is one that does not require spilling.
+ This way we may save one free instruction. */
+ if(IS_STACK_REG(reg))
+ {
+ 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_INPUT) == 0
+ && ((usage & VALUE_DEAD) != 0 || value->in_frame))
+ {
+ free_value(gen, value, reg, other_reg, 0);
+ }
+ }
+ }
+ for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
{
- if(gen->contents[index].remap >= 0)
+ 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
{
- --(gen->contents[index].remap);
- gen->stack_map[gen->contents[index].remap] = index;
+ if((usage & VALUE_INPUT) == 0)
+ {
+ free_value(gen, value, reg, other_reg, 0);
+ }
}
}
- gen->contents[reg].remap = -1;
}
-/*
- * Associate a temporary with register.
- */
static void
-bind_temporary(jit_gencode_t gen, int reg, int other_reg)
+update_age(jit_gencode_t gen, _jit_regdesc_t *desc)
{
-#ifdef JIT_REG_DEBUG
- printf("bind_temporary(reg = %d, other_reg = %d)\n", reg, other_reg);
-#endif
+ int reg, other_reg;
- 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;
+ reg = desc->value->reg;
+ if(gen->contents[reg].is_long_start)
+ {
+ other_reg = OTHER_REG(reg);
+ }
+ else
+ {
+ other_reg = -1;
+ }
+
+ gen->contents[reg].age = gen->current_age;
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;
+ gen->contents[other_reg].age = gen->current_age;
}
+ ++(gen->current_age);
}
-/*
- * 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)
+save_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
-#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);
+ _jit_regdesc_t *desc;
+ int reg, other_reg;
+
+#ifdef JIT_REG_DEBUG
+ printf("save_input_value(%d)\n", index);
#endif
- if(value->has_global_register && value->global_reg == reg)
+ desc = ®s->descs[index];
+ if(!(desc->value && desc->value->in_register && desc->save))
{
- value->in_register = 0;
- value->in_global_register = 1;
return;
}
- if(value->is_constant)
- {
- still_in_frame = 0;
- }
-
- gen->contents[reg].values[0] = value;
- gen->contents[reg].num_values = 1;
- 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)
+ reg = desc->value->reg;
+ if(gen->contents[reg].is_long_start)
{
- gen->contents[reg].is_long_start = 0;
+ other_reg = OTHER_REG(reg);
}
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;
+ other_reg = -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)
+ if(desc->thrash)
{
- value->in_global_register = still_in_frame;
+ save_value(gen, desc->value, reg, other_reg, 1);
}
else
{
- value->in_frame = still_in_frame;
+ save_value(gen, desc->value, reg, other_reg, 0);
}
- value->reg = reg;
}
-/*
- * Disassociate value with register.
- */
static void
-unbind_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg)
+free_output_value(jit_gencode_t gen, _jit_regs_t *regs)
{
- int index;
+ _jit_regdesc_t *desc;
+ int reg, other_reg;
#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);
+ printf("free_output_value()\n");
#endif
- if(!value->in_register || value->reg != reg)
+ desc = ®s->descs[0];
+ if(!(desc->value && desc->value->in_register))
{
return;
}
-
- value->in_register = 0;
- value->reg = -1;
-
- for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
+ if(desc->value == regs->descs[1].value || desc->value == regs->descs[2].value)
{
- 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;
- }
+ return;
}
- if(gen->contents[reg].num_values == 0 && other_reg >= 0)
+ reg = desc->value->reg;
+ if(gen->contents[reg].is_long_start)
{
- gen->contents[reg].is_long_start = 0;
- gen->contents[other_reg].is_long_end = 0;
+ other_reg = OTHER_REG(reg);
+ }
+ else
+ {
+ other_reg = -1;
}
+
+ free_value(gen, desc->value, reg, other_reg, 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.
- */
static void
-exch_stack_top(jit_gencode_t gen, int reg, int pop)
+load_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int stack_start, top, index;
- int num_values, used_for_temp, age;
- jit_value_t value1, value2;
+ _jit_regdesc_t *desc;
#ifdef JIT_REG_DEBUG
- printf("exch_stack_top(reg = %d, pop = %d)\n", reg, pop);
+ printf("load_input_value(%d)\n", index);
#endif
- if(!IS_STACK_REG(reg))
+ desc = ®s->descs[index];
+ if(!desc->value || desc->duplicate)
{
return;
}
- /* Find the top of the stack. */
- stack_start = get_stack_start(reg);
- top = get_stack_top(gen, stack_start);
-
- if(pop)
- {
- /* Generate move/pop-top instruction. */
- _jit_gen_move_top(gen, reg);
- remap_stack_down(gen, stack_start, top);
- }
- else
+ if(desc->value->has_global_register)
{
- /* Generate exchange instruction. */
- _jit_gen_exch_top(gen, reg);
+ if(desc->value->in_global_register && desc->value->global_reg == desc->reg)
+ {
+ return;
+ }
+ if(desc->value->in_register && desc->value->reg == desc->reg)
+ {
+ update_age(gen, desc);
+ return;
+ }
+ _jit_gen_load_value(gen, desc->reg, desc->other_reg, desc->value);
}
-
- /* Update information about the contents of the registers. */
- for(index = 0;
- index < gen->contents[reg].num_values || index < gen->contents[top].num_values;
- index++)
+ else if(desc->value->in_register)
{
- value1 = (index < gen->contents[top].num_values
- ? gen->contents[top].values[index] : 0);
- value2 = (index < gen->contents[reg].num_values
- ? gen->contents[reg].values[index] : 0);
-
- if(value1)
+ if(desc->value->reg == desc->reg)
{
- value1->reg = reg;
+ update_age(gen, desc);
+ if(IS_STACK_REG(desc->reg))
+ {
+ desc->stack_reg = desc->reg;
+ }
+ return;
}
- gen->contents[reg].values[index] = value1;
- if(pop)
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(desc->reg))
{
- if(value2)
- {
- value2->reg = -1;
- }
- gen->contents[top].values[index] = 0;
+ _jit_gen_load_value(gen, gen->reg_stack_top, -1, desc->value);
+ desc->stack_reg = gen->reg_stack_top++;
+ bind_temporary(gen, desc->stack_reg, -1);
}
else
+#endif
{
- if(value2)
- {
- value2->reg = top;
- }
- gen->contents[top].values[index] = value2;
+ _jit_gen_load_value(gen, desc->reg, desc->other_reg, desc->value);
+ bind_temporary(gen, desc->reg, desc->other_reg);
}
}
-
- if(pop)
- {
- 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;
-}
-
-/*
- * Drop value from register.
- */
-static void
-free_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg)
-{
-#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)\n", reg, other_reg);
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(desc->reg))
+ {
+ _jit_gen_load_value(gen, gen->reg_stack_top, -1, desc->value);
+ desc->stack_reg = gen->reg_stack_top++;
+ bind_value(gen, desc->value, desc->stack_reg, -1, 1);
+ }
+ else
#endif
-
- /* Never free global registers. */
- if(value->has_global_register && value->global_reg == reg)
- {
- return;
- }
-
- /* Free stack register. */
- if(IS_STACK_REG(reg) && gen->contents[reg].num_values == 1)
- {
- exch_stack_top(gen, reg, 1);
+ {
+ _jit_gen_load_value(gen, desc->reg, desc->other_reg, desc->value);
+ bind_value(gen, desc->value, desc->reg, desc->other_reg, 1);
+ }
}
-
- 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.
- */
+#ifdef JIT_REG_STACK
static void
-save_value(jit_gencode_t gen, jit_value_t value, int reg, int other_reg, int free)
+move_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int stack_start, top;
+ _jit_regdesc_t *desc;
+ int src_reg, dst_reg;
#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)\n", reg, other_reg);
+ printf("move_input_value(%d)\n", index);
#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);
- }
+ desc = ®s->descs[index];
+ if(!desc->value || desc->duplicate || !desc->value->in_register)
+ {
return;
}
-
- /* Take care of constants and values that are already in frame. */
- if(value->is_constant || value->in_frame)
+ if(!IS_STACK_REG(desc->value->reg))
{
- if(free)
- {
- free_value(gen, value, reg, other_reg);
- }
return;
}
- /* Now really save the value into frame. */
- if(IS_STACK_REG(reg))
- {
- /* Find the top of the stack. */
- stack_start = get_stack_start(reg);
- top = get_stack_top(gen, stack_start);
-
- /* Move the value on the stack top if it is already not there. */
- if(top != reg)
- {
- exch_stack_top(gen, reg, 0);
- }
-
- if(free && gen->contents[top].num_values == 1)
- {
- _jit_gen_spill_top(gen, top, value, 1);
- remap_stack_down(gen, stack_start, top);
- }
- else
+ if(desc->copy)
+ {
+ src_reg = desc->stack_reg;
+ if(src_reg < 0)
{
- _jit_gen_spill_top(gen, top, value, 0);
+ return;
}
}
else
{
- _jit_gen_spill_reg(gen, reg, other_reg, value);
+ src_reg = desc->value->reg;
}
- if(free)
+ if(desc->reg < gen->reg_stack_top)
{
- unbind_value(gen, value, reg, other_reg);
+ dst_reg = desc->reg;
+ }
+ else
+ {
+ dst_reg = gen->reg_stack_top - 1;
+ }
+
+ if(src_reg != dst_reg)
+ {
+ if(src_reg != (gen->reg_stack_top - 1))
+ {
+ exch_stack_top(gen, src_reg, 0);
+ }
+ if(dst_reg != (gen->reg_stack_top - 1))
+ {
+ exch_stack_top(gen, dst_reg, 0);
+ }
}
- value->in_frame = 1;
}
+#endif
-/*
- * Spill regular (non-stack) register.
- */
static void
-spill_reg(jit_gencode_t gen, _jit_regs_t *regs, int reg)
+abort_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- int other_reg, index, usage;
- jit_value_t value;
+ _jit_regdesc_t *desc;
+ int reg, other_reg;
#ifdef JIT_REG_DEBUG
- printf("spill_reg(reg = %d)\n", reg);
+ printf("abort_input_value(%d)\n", index);
#endif
- /* Find the other register in a long pair */
- if(gen->contents[reg].is_long_start)
- {
- other_reg = OTHER_REG(reg);
- }
- else if(gen->contents[reg].is_long_end)
- {
- other_reg = reg;
- reg = get_long_pair_start(reg);
- }
- else
+ desc = ®s->descs[index];
+ if(!desc->value || desc->duplicate)
{
- other_reg = -1;
+ return;
}
- /* Spill register contents in two passes. First free values that
- do not reqiure spilling then spill those that do. This approach
- is only useful in case a stack register contains both kinds of
- values and the last value is one that does not require spilling.
- This way we may save one free instruction. */
- if(IS_STACK_REG(reg))
+ if(desc->load && desc->value->in_register)
{
- for(index = gen->contents[reg].num_values - 1; index >= 0; --index)
+ reg = desc->value->reg;
+ if(gen->contents[reg].is_long_start)
{
- value = gen->contents[reg].values[index];
- usage = value_usage(regs, value);
- if((usage & VALUE_INPUT) == 0
- && ((usage & VALUE_DEAD) != 0 || value->in_frame))
- {
- free_value(gen, value, reg, other_reg);
- }
+ other_reg = OTHER_REG(reg);
}
- }
- 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)
+ else
{
- if((usage & VALUE_INPUT) == 0)
- {
- save_value(gen, value, reg, other_reg, 1);
- }
- else
- {
- save_value(gen, value, reg, other_reg, 0);
- }
+ other_reg = -1;
}
- else
+ unbind_value(gen, desc->value, reg, other_reg);
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(reg))
{
- if((usage & VALUE_INPUT) == 0)
- {
- free_value(gen, value, reg, other_reg);
- }
+ --(gen->reg_stack_top);
}
+#endif
}
}
static void
-update_age(jit_gencode_t gen, _jit_regdesc_t *desc)
+commit_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
+ _jit_regdesc_t *desc;
int reg, other_reg;
- reg = desc->value->reg;
- if(gen->contents[reg].is_long_start)
+#ifdef JIT_REG_DEBUG
+ printf("commit_input_value(%d)\n", index);
+#endif
+
+ desc = ®s->descs[index];
+ if(!desc->value || desc->duplicate)
{
- other_reg = OTHER_REG(reg);
+ return;
}
- else
+
+ if(desc->copy)
{
- other_reg = -1;
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(desc->reg))
+ {
+ --(gen->reg_stack_top);
+ }
+#endif
+ gen->contents[desc->reg].used_for_temp = 0;
+ if(desc->other_reg >= 0)
+ {
+ gen->contents[desc->other_reg].used_for_temp = 0;
+ }
}
- gen->contents[reg].age = gen->current_age;
- if(other_reg >= 0)
+ if(desc->kill && desc->value->in_register)
{
- gen->contents[other_reg].age = gen->current_age;
+ reg = desc->value->reg;
+ if(gen->contents[reg].is_long_start)
+ {
+ other_reg = OTHER_REG(reg);
+ }
+ else
+ {
+ other_reg = -1;
+ }
+ unbind_value(gen, desc->value, reg, other_reg);
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(reg))
+ {
+ --(gen->reg_stack_top);
+ }
+#endif
}
- ++(gen->current_age);
+
+#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
-save_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
+commit_output_value(jit_gencode_t gen, _jit_regs_t *regs)
{
_jit_regdesc_t *desc;
- int reg, other_reg;
#ifdef JIT_REG_DEBUG
- printf("save_input_value(%d)\n", index);
+ printf("commit_output_value()\n");
#endif
- desc = ®s->descs[index];
- if(!(desc->value && desc->value->in_register && desc->save))
+ desc = ®s->descs[0];
+ if(!desc->value)
{
return;
}
- reg = desc->value->reg;
- if(gen->contents[reg].is_long_start)
- {
- other_reg = OTHER_REG(reg);
- }
- else
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(desc->reg))
{
- other_reg = -1;
+ ++(gen->reg_stack_top);
}
+#endif
+ bind_value(gen, desc->value, desc->reg, desc->other_reg, 0);
- if(desc->thrash)
+ if(!desc->used)
{
- save_value(gen, desc->value, reg, other_reg, 1);
+ 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
+ else if(desc->kill)
{
- save_value(gen, desc->value, reg, other_reg, 0);
+ save_value(gen, desc->value, desc->reg, desc->other_reg, 1);
}
-}
-
-static void
-free_output_value(jit_gencode_t gen, _jit_regs_t *regs)
-{
- _jit_regdesc_t *desc;
- int reg, other_reg;
#ifdef JIT_REG_DEBUG
- printf("free_output_value()\n");
+ 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
+}
- desc = ®s->descs[0];
- if(!(desc->value && desc->value->in_register))
+/*@
+ * @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)
{
- return;
+ for(reg = 0; reg < JIT_NUM_REGS; reg++)
+ {
+ if(strcmp(_jit_reg_info[reg].name, name) == 0)
+ {
+ return reg;
+ }
+ }
}
- if(desc->value == regs->descs[1].value || desc->value == regs->descs[2].value)
+ return -1;
+}
+
+/*@
+ * @deftypefun int _jit_regs_needs_long_pair (jit_type_t type)
+ * Determine if a type requires a long register pair.
+ * @end deftypefun
+@*/
+int _jit_regs_needs_long_pair(jit_type_t type)
+{
+#if defined(JIT_NATIVE_INT32) && !defined(JIT_BACKEND_INTERP)
+ type = jit_type_normalize(type);
+ if(type)
{
- return;
+ if(type->kind == JIT_TYPE_LONG || type->kind == JIT_TYPE_ULONG)
+ {
+ return 1;
+ }
}
+ return 0;
+#else
+ /* We don't register pairs on 64-bit platforms or the interpreter */
+ return 0;
+#endif
+}
- reg = desc->value->reg;
- if(gen->contents[reg].is_long_start)
+/*@
+ * @deftypefun int _jit_regs_get_cpu (jit_gencode_t gen, int reg, int *other_reg)
+ * Get the CPU register that corresponds to a pseudo register.
+ * "other_reg" will be set to the other register in a pair,
+ * or -1 if the register is not part of a pair.
+ * @end deftypefun
+@*/
+int _jit_regs_get_cpu(jit_gencode_t gen, int reg, int *other_reg)
+{
+ int cpu_reg, other;
+
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(reg))
{
- other_reg = OTHER_REG(reg);
+ reg = gen->reg_stack_top - reg;
+ cpu_reg = _jit_reg_info[reg].cpu_reg;
+ other = -1;
+ }
+ else
+#endif
+ {
+ cpu_reg = _jit_reg_info[reg].cpu_reg;
+ if(gen->contents[reg].is_long_start)
+ {
+ other = _jit_reg_info[reg].other_reg;
+ other = _jit_reg_info[other].cpu_reg;
+ }
+ else
+ {
+ other = -1;
+ }
}
- else
+ if(other_reg)
{
- other_reg = -1;
+ *other_reg = other;
}
-
- free_value(gen, desc->value, reg, other_reg);
+ return cpu_reg;
}
-static void
-load_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
+/*@
+ * @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)
{
- _jit_regdesc_t *desc;
-
-#ifdef JIT_REG_DEBUG
- printf("load_input_value(%d)\n", index);
-#endif
+#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;
- desc = ®s->descs[index];
- if(!desc->value || desc->duplicate)
+ /* 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(desc->value->has_global_register)
+ /* 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)
{
- if(desc->value->in_global_register && desc->value->global_reg == desc->reg)
- {
- return;
- }
- if(desc->value->in_register && desc->value->reg == desc->reg)
+ for(reg = 0; reg < JIT_NUM_REGS; ++reg)
{
- update_age(gen, desc);
- return;
+ if((_jit_reg_info[reg].flags &
+ (JIT_REG_FIXED | JIT_REG_CALL_USED)) == 0)
+ {
+ jit_reg_set_used(gen->permanent, reg);
+ }
}
- _jit_gen_load_value(gen, desc->reg, desc->other_reg, desc->value);
+ return;
}
- else if(desc->value->in_register)
+
+ /* Scan all values within the function, looking for the most used.
+ We will replace this with a better allocation strategy later */
+ block = func->builder->value_pool.blocks;
+ num = (int)(func->builder->value_pool.elems_per_block);
+ while(block != 0)
{
- if(desc->value->reg == desc->reg)
+ if(!(block->next))
{
- update_age(gen, desc);
- if(IS_STACK_REG(desc->reg))
+ num = (int)(func->builder->value_pool.elems_in_last);
+ }
+ for(posn = 0; posn < num; ++posn)
+ {
+ value = (jit_value_t)(block->data + posn *
+ sizeof(struct _jit_value));
+ if(value->global_candidate && value->usage_count >= JIT_MIN_USED &&
+ !(value->is_addressable) && !(value->is_volatile))
{
- desc->stack_reg = desc->reg;
+ /* Insert this candidate into the list, ordered on count */
+ index = 0;
+ while(index < num_candidates &&
+ value->usage_count <= candidates[index]->usage_count)
+ {
+ ++index;
+ }
+ while(index < num_candidates)
+ {
+ temp = candidates[index];
+ candidates[index] = value;
+ value = temp;
+ ++index;
+ }
+ if(index < JIT_NUM_GLOBAL_REGS)
+ {
+ candidates[num_candidates++] = value;
+ }
}
- return;
}
+ block = block->next;
+ }
- if(IS_STACK_REG(desc->reg))
+ /* Allocate registers to the candidates. We allocate from the top-most
+ register in the allocation order, because some architectures like
+ PPC require global registers to be saved top-down for efficiency */
+ reg = JIT_NUM_REGS - 1;
+ for(index = 0; index < num_candidates; ++index)
+ {
+ while(reg >= 0 && (_jit_reg_info[reg].flags & JIT_REG_GLOBAL) == 0)
{
- desc->stack_reg = ++(regs->current_stack_top);
- _jit_gen_load_value(gen, desc->stack_reg, -1, desc->value);
- bind_temporary(gen, desc->stack_reg, -1);
- remap_stack_up(gen, regs->stack_start, desc->stack_reg);
+ --reg;
}
- else
+ candidates[index]->has_global_register = 1;
+ candidates[index]->global_reg = (short)reg;
+ jit_reg_set_used(gen->touched, reg);
+ jit_reg_set_used(gen->permanent, reg);
+ --reg;
+ }
+
+#endif
+}
+
+/*@
+ * @deftypefun void _jit_regs_init_for_block (jit_gencode_t gen)
+ * Initialize the register allocation state for a new block.
+ * @end deftypefun
+@*/
+void _jit_regs_init_for_block(jit_gencode_t gen)
+{
+ int reg;
+ gen->current_age = 1;
+ for(reg = 0; reg < JIT_NUM_REGS; ++reg)
+ {
+ /* Clear everything except permanent and fixed registers */
+ if(!jit_reg_is_used(gen->permanent, reg) &&
+ (_jit_reg_info[reg].flags & JIT_REG_FIXED) == 0)
{
- _jit_gen_load_value(gen, desc->reg, desc->other_reg, desc->value);
- bind_temporary(gen, desc->reg, desc->other_reg);
+ gen->contents[reg].num_values = 0;
+ gen->contents[reg].is_long_start = 0;
+ gen->contents[reg].is_long_end = 0;
+ gen->contents[reg].age = 0;
+ gen->contents[reg].used_for_temp = 0;
}
+#ifdef JIT_REG_STACK
+ gen->reg_stack_top = JIT_REG_STACK_START;
+#endif
}
- else
+ gen->inhibit = jit_regused_init;
+}
+
+/*@
+ * @deftypefun void _jit_regs_spill_all (jit_gencode_t gen)
+ * Spill all of the temporary registers to memory locations.
+ * Normally used at the end of a block, but may also be used in
+ * situations where a value must be in a certain register and
+ * it is too hard to swap things around to put it there.
+ * @end deftypefun
+@*/
+void
+_jit_regs_spill_all(jit_gencode_t gen)
+{
+ int reg;
+
+#ifdef JIT_REG_DEBUG
+ printf("enter _jit_regs_spill_all\n");
+#endif
+
+ for(reg = 0; reg < JIT_NUM_REGS; reg++)
{
- if(IS_STACK_REG(desc->reg))
+ /* Skip this register if it is permanent or fixed */
+ if(jit_reg_is_used(gen->permanent, reg) ||
+ (_jit_reg_info[reg].flags & JIT_REG_FIXED) != 0)
{
- desc->stack_reg = ++(regs->current_stack_top);
- _jit_gen_load_value(gen, desc->stack_reg, -1, desc->value);
- bind_value(gen, desc->value, desc->stack_reg, -1, 1);
- remap_stack_up(gen, regs->stack_start, desc->stack_reg);
+ 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
{
- _jit_gen_load_value(gen, desc->reg, desc->other_reg, desc->value);
- bind_value(gen, desc->value, desc->reg, desc->other_reg, 1);
+ spill_register(gen, reg);
}
}
-}
-
-static void
-move_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
-{
- _jit_regdesc_t *desc;
- int src_reg, dst_reg;
#ifdef JIT_REG_DEBUG
- printf("move_input_value(%d)\n", index);
+ printf("leave _jit_regs_spill_all\n");
#endif
+}
- desc = ®s->descs[index];
- if(!desc->value || desc->duplicate || !desc->value->in_register)
+/*@
+ * @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}. If the register was previously in use,
+ * then spill its value first.
+ * @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 long pair */
+ if(_jit_regs_needs_long_pair(value->type))
{
- return;
+ other_reg = OTHER_REG(reg);
}
- if(!IS_STACK_REG(desc->value->reg))
+ else
{
- return;
+ other_reg = -1;
}
- if(desc->copy)
+ /* Eject any values that are currently in the register */
+ spill_register(gen, reg);
+ if(other_reg >= 0)
{
- src_reg = desc->stack_reg;
- if(src_reg < 0)
- {
- return;
- }
+ spill_register(gen, other_reg);
}
- else
+
+ /* Record that the value is in "reg", but not in the frame */
+#ifdef JIT_REG_STACK
+ if(IS_STACK_REG(reg))
{
- src_reg = desc->value->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;
- if(desc->reg <= regs->current_stack_top)
+ type = jit_type_normalize(value->type);
+ if(type)
{
- dst_reg = desc->reg;
+ /* 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 if(regs->ternary && index == 2
- && regs->descs[0].value && regs->descs[1].value
- && !regs->descs[0].value->in_register && regs->descs[1].value->in_register)
+#else
+ if(_jit_regs_needs_long_pair(value->type))
{
- dst_reg = regs->current_stack_top - 1;
+ other_reg = OTHER_REG(reg);
}
else
{
- dst_reg = regs->current_stack_top;
+ other_reg = -1;
}
+#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);
+ }
- if(src_reg != dst_reg)
+ /* 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
{
- if(src_reg != regs->current_stack_top)
+ /* Reload the value into the specified register */
+ spill_register(gen, reg);
+ if(other_reg >= 0)
{
- exch_stack_top(gen, src_reg, 0);
+ spill_register(gen, other_reg);
}
- if(dst_reg != regs->current_stack_top)
+
+ _jit_gen_load_value(gen, reg, other_reg, value);
+
+ jit_reg_set_used(gen->inhibit, reg);
+ if(other_reg > 0)
{
- exch_stack_top(gen, dst_reg, 0);
+ jit_reg_set_used(gen->inhibit, other_reg);
}
}
}
-static void
-abort_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
+/*@
+ * @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.
+ * @end deftypefun
+@*/
+void _jit_regs_force_out(jit_gencode_t gen, jit_value_t value, int is_dest)
{
- _jit_regdesc_t *desc;
int reg, other_reg;
-
-#ifdef JIT_REG_DEBUG
- printf("abort_input_value(%d)\n", index);
-#endif
-
- desc = ®s->descs[index];
- if(!desc->value || desc->duplicate)
+ if(value->in_register)
{
- return;
- }
+ reg = value->reg;
- if(desc->load && desc->value->in_register)
- {
- reg = desc->value->reg;
- if(gen->contents[reg].is_long_start)
+ /* Find the other register in a long pair */
+ if(_jit_regs_needs_long_pair(value->type))
{
other_reg = OTHER_REG(reg);
}
other_reg = -1;
}
- if(IS_STACK_REG(reg))
+ if(is_dest)
{
- unbind_value(gen, desc->value, reg, -1);
- remap_stack_down(gen, regs->stack_start, reg);
+ free_value(gen, value, reg, other_reg, 0);
}
else
{
- unbind_value(gen, desc->value, reg, other_reg);
+ save_value(gen, value, reg, other_reg, 1);
}
}
}
-static void
-commit_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index)
+/*@
+ * @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.
+ * @end deftypefun
+@*/
+int
+_jit_regs_load_value(jit_gencode_t gen, jit_value_t value, int destroy, int used_again)
{
- _jit_regdesc_t *desc;
+ int type, need_pair;
int reg, other_reg;
+ int spill_cost;
+ int suitable_reg, suitable_other_reg;
+ int suitable_cost;
+ int suitable_age;
-#ifdef JIT_REG_DEBUG
- printf("commit_input_value(%d)\n", index);
-#endif
-
- desc = ®s->descs[index];
- if(!desc->value || desc->duplicate)
+ /* 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;
+ return value->global_reg;
}
- if(desc->copy)
+ need_pair = _jit_regs_needs_long_pair(value->type);
+
+ /* If the value is already in a register, then try to use that register */
+ if(value->in_register && (!destroy || !used_again))
{
- if(IS_STACK_REG(desc->reg))
- {
- remap_stack_down(gen, regs->stack_start, desc->reg);
- }
- gen->contents[desc->reg].used_for_temp = 0;
- if(desc->other_reg >= 0)
+ reg = value->reg;
+ if(!used_again)
{
- gen->contents[desc->other_reg].used_for_temp = 0;
+ if(need_pair)
+ {
+ other_reg = OTHER_REG(reg);
+ }
+ else
+ {
+ other_reg = -1;
+ }
+ free_value(gen, value, reg, other_reg, 1);
}
+ return reg;
}
- if(desc->kill && desc->value->in_register)
+ type = get_register_type(value, need_pair);
+ if(!type)
{
- reg = desc->value->reg;
- if(gen->contents[reg].is_long_start)
+ return 0;
+ }
+
+ suitable_reg = -1;
+ suitable_other_reg = -1;
+ suitable_cost = COST_TOO_MUCH;
+ suitable_age = -1;
+ for(reg = 0; reg < JIT_NUM_REGS; reg++)
+ {
+ if((_jit_reg_info[reg].flags & type) == 0)
{
- other_reg = OTHER_REG(reg);
+ continue;
}
- else
+ if(jit_reg_is_used(gen->inhibit, reg))
{
- other_reg = -1;
+ continue;
+ }
+ if(jit_reg_is_used(gen->permanent, reg))
+ {
+ continue;
}
- if(IS_STACK_REG(reg))
+ if(need_pair)
{
- unbind_value(gen, desc->value, reg, -1);
- remap_stack_down(gen, regs->stack_start, reg);
+ other_reg = OTHER_REG(reg);
+ if(jit_reg_is_used(gen->inhibit, other_reg))
+ {
+ continue;
+ }
+ if(jit_reg_is_used(gen->permanent, other_reg))
+ {
+ continue;
+ }
}
else
{
- unbind_value(gen, desc->value, reg, other_reg);
+ 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
-}
-
-static void
-commit_output_value(jit_gencode_t gen, _jit_regs_t *regs)
-{
- _jit_regdesc_t *desc;
-#ifdef JIT_REG_DEBUG
- printf("commit_output_value()\n");
-#endif
+ spill_cost = compute_spill_cost(gen, 0, reg, other_reg);
- desc = ®s->descs[0];
- if(!desc->value)
- {
- return;
+ if(spill_cost < suitable_cost
+ || (spill_cost == suitable_cost
+ && spill_cost > 0 && gen->contents[reg].age < suitable_age))
+ {
+ suitable_reg = reg;
+ suitable_other_reg = other_reg;
+ suitable_cost = spill_cost;
+ suitable_age = gen->contents[reg].age;
+ }
}
- if(IS_STACK_REG(desc->reg))
- {
- bind_value(gen, desc->value, desc->reg, -1, 0);
- remap_stack_up(gen, regs->stack_start, desc->reg);
- }
- else
- {
- bind_value(gen, desc->value, desc->reg, desc->other_reg, 0);
- }
- if(!desc->used)
+ if(suitable_reg >= 0)
{
- if(desc->live)
+ spill_register(gen, suitable_reg);
+ if(suitable_other_reg >= 0)
{
- save_value(gen, desc->value, desc->reg, desc->other_reg, 1);
+ spill_register(gen, suitable_other_reg);
+ }
+
+ _jit_gen_load_value(gen, suitable_reg, suitable_other_reg, value);
+
+ if(!destroy && !used_again)
+ {
+ bind_value(gen, value, suitable_reg, suitable_other_reg, 1);
}
else
{
- free_value(gen, desc->value, desc->reg, desc->other_reg);
+ bind_temporary(gen, suitable_reg, suitable_other_reg);
}
}
- 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
+ return suitable_reg;
}
void
regs->assigned = gen->inhibit;
- regs->stack_start = -1;
- regs->current_stack_top = 0;
regs->wanted_stack_count = 0;
regs->loaded_stack_count = 0;
}
int
_jit_regs_gen(jit_gencode_t gen, _jit_regs_t *regs)
{
- int reg, stack_start, top;
+ int reg;
#ifdef JIT_REG_DEBUG
dump_regs(gen, "enter _jit_regs_gen");
#endif
/* Spill clobbered registers. */
- stack_start = 0;
for(reg = 0; reg < JIT_NUM_REGS; reg++)
{
if((_jit_reg_info[reg].flags & JIT_REG_FIXED))
continue;
}
- /* Remember this register if it is the start of a stack */
- if(IS_STACK_START(reg))
- {
- stack_start = reg;
- }
-
if(!jit_reg_is_used(regs->clobber, reg))
{
continue;
continue;
}
+#ifdef JIT_REG_STACK
/* 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 */
if(IS_STACK_REG(reg))
{
- top = get_stack_top(gen, stack_start);
- while(top > reg && jit_reg_is_used(regs->clobber, top))
+ int top = gen->reg_stack_top - 1;
+
+ /* spill top registers if there are any that needs to be */
+ for(; top > reg && jit_reg_is_used(regs->clobber, top); top--)
{
- spill_reg(gen, regs, top);
- /* If one of the input values is on the top
- spill_reg() will not pop it. */
+ spill_clobbered_register(gen, regs, top);
+ /* If an input value is on the top then it stays there
+ and the top does not change. */
if(gen->contents[top].num_values > 0)
{
break;
}
- top = get_stack_top(gen, stack_start);
}
+ /* If the top register was not spilled then exchange it with
+ the current register. */
if(top > reg)
{
exch_stack_top(gen, reg, 0);
}
- if(top >= stack_start)
+ /* Finally spill the register */
+ if(top >= JIT_REG_STACK_START)
{
- spill_reg(gen, regs, top);
+ spill_clobbered_register(gen, regs, top);
}
}
else
+#endif
{
- spill_reg(gen, regs, reg);
+ spill_clobbered_register(gen, regs, reg);
}
}
- /* Save input values if necessary and free output value if it is in a register */
+ /* Save input values if necessary and free the output value if it is in a register */
if(regs->ternary)
{
save_input_value(gen, regs, 0);
save_input_value(gen, regs, 1);
save_input_value(gen, regs, 2);
- /* Adjust assignment of stack registers. */
+#ifdef JIT_REG_STACK
if(regs->wanted_stack_count > 0)
{
- regs->current_stack_top = get_stack_top(gen, regs->stack_start);
-
+ /* Adjust assignment of stack registers. */
adjust_assignment(gen, regs, 0);
adjust_assignment(gen, regs, 1);
adjust_assignment(gen, regs, 2);
select_stack_order(gen, regs);
- }
- /* Shuffle the values that are already on the register stack. */
- if(regs->loaded_stack_count > 0)
- {
- if(regs->ternary)
+ /* Shuffle the values that are already on the register stack. */
+ if(regs->loaded_stack_count > 0 && !regs->x87_arith)
{
- if(regs->descs[0].value && regs->descs[0].value->in_register)
+ move_input_value(gen, regs, 1);
+ move_input_value(gen, regs, 2);
+ }
+
+ /* Load and shuffle the remaining values. */
+ if(regs->reverse_args)
+ {
+ load_input_value(gen, regs, 2);
+ move_input_value(gen, regs, 2);
+ load_input_value(gen, regs, 1);
+ move_input_value(gen, regs, 1);
+ }
+ else
+ {
+ if(regs->ternary)
{
+ load_input_value(gen, regs, 0);
move_input_value(gen, regs, 0);
- move_input_value(gen, regs, 1);
- move_input_value(gen, regs, 2);
- }
- else
- {
- move_input_value(gen, regs, 2);
- move_input_value(gen, regs, 1);
}
- }
- else if(!regs->x87_arith)
- {
+ load_input_value(gen, regs, 1);
move_input_value(gen, regs, 1);
+ load_input_value(gen, regs, 2);
move_input_value(gen, regs, 2);
}
}
-
- /* Load and shuffle the remaining values. */
- if(regs->x87_arith && regs->reverse_args)
- {
- load_input_value(gen, regs, 2);
- move_input_value(gen, regs, 2);
- load_input_value(gen, regs, 1);
- move_input_value(gen, regs, 1);
- }
else
+#endif
{
+ /* Load flat registers. */
if(regs->ternary)
{
load_input_value(gen, regs, 0);
- move_input_value(gen, regs, 0);
}
load_input_value(gen, regs, 1);
- move_input_value(gen, regs, 1);
load_input_value(gen, regs, 2);
- move_input_value(gen, regs, 2);
}
#ifdef JIT_REG_DEBUG
dump_regs(gen, "enter _jit_regs_commit");
#endif
- if(regs->x87_arith && regs->reverse_args)
+#ifdef JIT_REG_STACK
+ if(regs->reverse_args)
{
commit_input_value(gen, regs, 1);
commit_input_value(gen, regs, 2);
}
else
+#endif
{
commit_input_value(gen, regs, 2);
commit_input_value(gen, regs, 1);
void
_jit_regs_abort(jit_gencode_t gen, _jit_regs_t *regs)
{
+ abort_input_value(gen, regs, 2);
+ abort_input_value(gen, regs, 1);
if(regs->ternary)
{
abort_input_value(gen, regs, 0);
}
- abort_input_value(gen, regs, 1);
- abort_input_value(gen, regs, 2);
}
unsigned char *
gen->posn.ptr = inst;
_jit_regs_commit(gen, regs);
}
-
-/*@
- * @deftypefun void _jit_regs_lookup (char *name)
- * Get register by 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_info[reg].name, name) == 0)
- {
- return reg;
- }
- }
- }
- return -1;
-}