+2006-06-17 Aleksey Demakov <ademakov@gmail.com>
+
+ * jit/jit-reg-alloc.h, jit/jit-reg-alloc.c: implement support of the
+ JIT_REGS_FREE_DEST flag for 3-argument instructions. Tweak reg alloc
+ API.
+
+ * tools/gen-rules-parser.y: adjust for reg alloc API changes. Use
+ '$0' pattern element to refer to destination register in 3-argument
+ instructions.
+
+ * jit/jit-rules-x86.ins: rewrite JIT_OP_IDIV, JIT_OP_IDIV_UN,
+ JIT_OP_IREM, JIT_OP_IREM_UN, JIT_OP_ADDRESS_OF_LABEL,
+ JIT_OP_LOAD_RELATIVE_FLOAT32, JIT_OP_LOAD_RELATIVE_FLOAT32,
+ JIT_OP_LOAD_RELATIVE_NFLOAT, JIT_OP_LOAD_ELEMENT_FLOAT32,
+ JIT_OP_LOAD_ELEMENT_FLOAT64, JIT_OP_LOAD_ELEMENT_NFLOAT rules.
+
2006-06-16 Aleksey Demakov <ademakov@gmail.com>
* jit/jit-rules-x86.sel, jit/jit-rules-x86.ins: fix JIT_OP_IDIV and
#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
-#define ALLOW_CLOBBER_GLOBAL 1
+#ifdef JIT_BACKEND_X86
+# define ALLOW_CLOBBER_GLOBAL 1
+#else
+# define ALLOW_CLOBBER_GLOBAL 0
+#endif
/* Value usage flags. */
#define VALUE_INPUT 1
static int
are_values_equal(_jit_regdesc_t *desc1, _jit_regdesc_t *desc2)
{
- if(desc1->value && desc2->value)
+ if(desc1 && desc2 && desc1->value && desc2->value)
{
if(desc1->value == desc2->value)
{
/*
- * Check if the value is used in and after the current instruction.
+ * 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.
*
- * The value is dead if it is the output value of the instruction and hence
- * just about to be recomputed or if it is one of the input values and its
- * usage flags are not set. Otherwise the value is alive.
+ * 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)
}
/*
- * Determine the effect of assigning the value to a register. It finds out
- * if the old register contents is clobbered and so needs to be spilled and
- * if the newly assigned value will be clobbered by the instruction.
+ * Determine the effect of assigning a value to a register on the values
+ * the register contains. The following factors are taken into account:
+ for
+ * the instruction
+ *. It
+ * checks if the register contains any other values that might be clobbered
+ * by using the register or if the value in question is clobbered itself by
+ * the instruction.
*/
static int
clobbers_register(jit_gencode_t gen, _jit_regs_t *regs, int index, int reg, int other_reg)
else
{
/* find out the input value that is going to be overwritten by the output */
- if(!regs->descs[2].value)
+ if(regs->free_dest)
+ {
+ out_index = 0;
+ }
+ else if(!regs->descs[2].value)
{
/* a unary op */
out_index = 1;
}
}
- /* does input value clobber the register? */
+ /* is input value clobbered? */
if(regs->descs[index].clobber)
{
clobber = 1;
}
- if(!clobber)
+ if(clobber)
+ {
+ flags = CLOBBER_INPUT_VALUE;
+ }
+ else
{
if(regs->descs[index].value->has_global_register
&& regs->descs[index].value->global_reg == reg)
{
return CLOBBER_NONE;
}
- }
-
- flags = CLOBBER_NONE;
- if(clobber)
- {
- flags |= CLOBBER_INPUT_VALUE;
+ flags = CLOBBER_NONE;
}
if(is_alive)
{
}
/*
- * Set assigned and clobber flags for a register.
+ * Assign scratch register.
*/
static void
-set_register_flags(
- jit_gencode_t gen,
- _jit_regs_t *regs,
- int reg,
- int clobber_reg,
- int clobber_input)
+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);
- if(clobber_reg)
- {
- jit_reg_set_used(regs->spill, reg);
- }
- if(clobber_input)
- {
- jit_reg_set_used(regs->clobber, reg);
- }
}
}
+/*
+ * Initialize value descriptor.
+ */
static void
init_regdesc(_jit_regs_t *regs, int index)
{
desc->clobber = 0;
desc->early_clobber = 0;
desc->duplicate = 0;
+ desc->thrash = 0;
desc->save = 0;
desc->load = 0;
desc->copy = 0;
}
/*
- * Initialize register descriptor.
+ * 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;
+ _jit_regdesc_t *desc;
desc = ®s->descs[index];
desc->value = value;
}
/*
- * Set assigned and clobber flags for the register descriptor.
+ * 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 clobber;
-
if(reg >= 0)
{
regs->descs[index].reg = reg;
regs->descs[index].other_reg = other_reg;
- clobber = clobbers_register(gen, regs, index, reg, other_reg);
- set_register_flags(gen, regs, reg,
- (clobber & CLOBBER_REG),
- (clobber & CLOBBER_INPUT_VALUE));
+ jit_reg_set_used(gen->touched, reg);
+ jit_reg_set_used(regs->assigned, reg);
if(other_reg >= 0)
{
- set_register_flags(gen, regs, other_reg,
- (clobber & CLOBBER_OTHER_REG),
- (clobber & CLOBBER_INPUT_VALUE));
+ jit_reg_set_used(gen->touched, other_reg);
+ jit_reg_set_used(regs->assigned, other_reg);
}
}
}
+/*
+ * Determine value flags.
+ */
static int
-collect_register_info(jit_gencode_t gen, _jit_regs_t *regs, int index)
+set_regdesc_flags(jit_gencode_t gen, _jit_regs_t *regs, int index)
{
- _jit_regdesc_t *desc;
+ _jit_regdesc_t *desc;
int reg, other_reg;
- int stack_start;
+ int clobber, stack_start;
#ifdef JIT_REG_DEBUG
- printf("collect_register_info(index = %d)\n", index);
+ printf("set_regdesc_flags(index = %d)\n", index);
#endif
desc = ®s->descs[index];
return 1;
}
+ /* Find the registers the value is already in (if any). */
if(desc->value->in_register)
{
reg = desc->value->reg;
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_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);
+ }
+
+ /* 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)
+ {
+ 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)))
+ {
+ if(regs->ternary)
+ {
+ if(!are_values_equal(desc, ®s->descs[0]))
+ {
+ desc->thrash = 1;
+ }
+ }
+ else
+ {
+ if(desc->value != regs->descs[0].value)
+ {
+ clobber |= CLOBBER_INPUT_VALUE;
+ }
+ }
+ }
+ }
+ 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
- && !(desc->value->in_register && reg == desc->reg))
+ && !(reg >= 0 && reg == desc->reg))
{
desc->copy = 1;
}
}
else
{
- if(!desc->value->in_register)
+ if(reg < 0)
{
desc->load = 1;
}
{
desc->kill = 1;
}
- else if(desc->value->in_register)
+ else if(reg >= 0)
{
if(desc->used)
{
- if(jit_reg_is_used(regs->clobber, reg)
- || (other_reg >= 0
- && jit_reg_is_used(regs->clobber, other_reg)))
+ if(!desc->copy && (clobber & CLOBBER_INPUT_VALUE) != 0)
{
desc->save = 1;
desc->kill = 1;
{
if(desc->used)
{
- if(jit_reg_is_used(regs->clobber, desc->reg)
- || (desc->other_reg >= 0
- && jit_reg_is_used(regs->clobber, desc->other_reg)))
+ if((clobber & CLOBBER_INPUT_VALUE) != 0)
{
desc->kill = 1;
}
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("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);
}
/*
- * To avoid circular movements of input values in place of each other
- * check if the current value overwrites any of the others.
+ * Check to see if loading one input value into the given register
+ * clobbers any other input values.
*/
static int
-thrashes_register(jit_gencode_t gen, _jit_regs_t *regs,
- _jit_regdesc_t *desc, int reg, int other_reg)
+thrashes_register(jit_gencode_t gen, _jit_regs_t *regs, int index, int reg, int other_reg)
{
+ _jit_regdesc_t *desc;
int reg2, other_reg2;
- if(regs->ternary && regs->descs[0].value && regs->descs[0].value->in_register
- && !(desc && are_values_equal(®s->descs[0], desc)))
+
+ if(index < 0)
+ {
+ desc = 0;
+ }
+ else
{
- reg2 = regs->descs[0].value->reg;
- if(reg2 == reg || reg2 == other_reg)
+ if(index == 0 && !regs->ternary)
+ {
+ return 0;
+ }
+ desc = ®s->descs[index];
+ if(!desc->value)
+ {
+ return 0;
+ }
+ }
+
+ if(index != 0 && regs->ternary && regs->descs[0].value)
+ {
+ if(!(desc && regs->descs[0].value == desc->value)
+ && regs->descs[0].value->has_global_register
+ && regs->descs[0].value->global_reg == reg)
{
return 1;
}
- if(gen->contents[reg2].is_long_start)
+ if(regs->descs[0].value->in_register
+ && !are_values_equal(®s->descs[0], desc))
{
- other_reg2 = OTHER_REG(reg2);
- if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ reg2 = regs->descs[0].value->reg;
+ if(reg2 == reg || reg2 == other_reg)
{
return 1;
}
+ if(gen->contents[reg2].is_long_start)
+ {
+ other_reg2 = OTHER_REG(reg2);
+ if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ {
+ return 1;
+ }
+ }
}
}
- if(regs->descs[1].value && regs->descs[1].value->in_register
- && !(desc && are_values_equal(®s->descs[1], desc)))
+ if(index != 1 && regs->descs[1].value)
{
- reg2 = regs->descs[1].value->reg;
- if(reg2 == reg || reg2 == other_reg)
+ if(!(desc && regs->descs[1].value == desc->value)
+ && regs->descs[1].value->has_global_register
+ && regs->descs[1].value->global_reg == reg)
{
return 1;
}
- if(gen->contents[reg2].is_long_start)
+ if(regs->descs[1].value->in_register
+ && !are_values_equal(®s->descs[1], desc))
{
- other_reg2 = OTHER_REG(reg2);
- if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ reg2 = regs->descs[1].value->reg;
+ if(reg2 == reg || reg2 == other_reg)
{
return 1;
}
+ if(gen->contents[reg2].is_long_start)
+ {
+ other_reg2 = OTHER_REG(reg2);
+ if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ {
+ return 1;
+ }
+ }
}
}
- if(regs->descs[2].value && regs->descs[2].value->in_register
- && !(desc && are_values_equal(®s->descs[2], desc)))
+ if(index != 2 && regs->descs[2].value)
{
- reg2 = regs->descs[2].value->reg;
- if(reg2 == reg || reg2 == other_reg)
+ if(!(desc && regs->descs[2].value == desc->value)
+ && regs->descs[2].value->has_global_register
+ && regs->descs[2].value->global_reg == reg)
{
return 1;
}
- if(gen->contents[reg2].is_long_start)
+ if(regs->descs[2].value->in_register
+ && !are_values_equal(®s->descs[2], desc))
{
- other_reg2 = OTHER_REG(reg2);
- if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ reg2 = regs->descs[2].value->reg;
+ if(reg2 == reg || reg2 == other_reg)
{
return 1;
}
+ if(gen->contents[reg2].is_long_start)
+ {
+ other_reg2 = OTHER_REG(reg2);
+ if(other_reg2 == reg /*|| other_reg2 == other_reg*/)
+ {
+ 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)
{
}
if(value->has_global_register)
{
- if(!value->in_global_register)
+ if(value->in_global_register)
{
- cost += 1;
+ cost += COST_SPILL_CLEAN_GLOBAL;
+ }
+ else
+ {
+ cost += COST_SPILL_DIRTY_GLOBAL;
}
}
else
{
- if(!value->in_frame)
+ if(value->in_frame)
+ {
+ cost += COST_SPILL_CLEAN;
+ }
+ else
{
- cost += 10;
+ cost += COST_SPILL_DIRTY;
}
}
}
*
*/
static int
-use_cheapest_register(jit_gencode_t gen, _jit_regs_t *regs, int index, jit_regused_t regset)
+use_cheapest_register(jit_gencode_t gen, _jit_regs_t *regs, int index, jit_regused_t allowed)
{
_jit_regdesc_t *desc;
- int output;
- int type;
- int need_pair;
+ _jit_regdesc_t *desc2;
+ _jit_regdesc_t *desc3;
+ int is_output, output_index;
+ int type, need_pair;
int reg, other_reg;
int cost, copy_cost;
int suitable_reg;
int suitable_cost;
int suitable_age;
+ int clobber;
if(index >= 0)
{
{
return -1;
}
- output = (index == 0 && !regs->ternary);
+
+ if(regs->ternary || regs->descs[0].value == 0)
+ {
+ is_output = 0;
+ output_index = 0;
+ }
+ else
+ {
+ is_output = (index == 0);
+ if(regs->free_dest)
+ {
+ output_index = 0;
+ }
+ else
+ {
+ output_index = 1 + (regs->reverse_dest ^ regs->reverse_args);
+ }
+ }
+
need_pair = _jit_regs_needs_long_pair(desc->value->type);
type = get_register_type(desc->value, need_pair);
if(!type)
else
{
desc = 0;
- output = 0;
+ is_output = 0;
+ output_index = 0;
need_pair = 0;
type = JIT_REG_WORD;
}
{
continue;
}
-
- if(!jit_reg_is_used(regset, reg)
+ if(!jit_reg_is_used(allowed, reg)
|| jit_reg_is_used(gen->inhibit, reg)
|| jit_reg_is_used(regs->assigned, reg))
{
{
continue;
}
- else if(thrashes_register(gen, regs, desc, reg, -1))
+ else if(thrashes_register(gen, regs, index, reg, -1))
{
continue;
}
- copy_cost = 0;
cost = compute_spill_cost(gen, regs, reg, -1);
+ copy_cost = 0;
}
- else if(desc->value->has_global_register)
+ else
{
- if(reg == desc->value->global_reg)
+ if(need_pair)
{
- if(clobbers_register(gen, regs, index, reg, -1))
+ other_reg = OTHER_REG(reg);
+ if(jit_reg_is_used(gen->inhibit, other_reg)
+ || jit_reg_is_used(regs->assigned, other_reg))
{
continue;
}
- copy_cost = ((output | desc->value->in_global_register) == 0);
- cost = 0;
}
- else if(jit_reg_is_used(gen->permanent, reg))
- {
- continue;
- }
- else if(thrashes_register(gen, regs, desc, reg, -1))
+ else
{
- continue;
+ other_reg = -1;
}
- else if(desc->value->in_register && reg == desc->value->reg)
+
+ clobber = clobbers_register(gen, regs, index, reg, other_reg);
+ if((clobber & ~CLOBBER_INPUT_VALUE) != 0)
{
- copy_cost = ((output | desc->value->in_global_register) != 0);
- if(clobbers_register(gen, regs, index, reg, -1))
+ if(jit_reg_is_used(gen->permanent, reg))
+ {
+ continue;
+ }
+#if !ALLOW_LONGS_USE_GLOBAL
+ if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
{
- cost = compute_spill_cost(gen, regs, reg, -1);
+ continue;
}
- else
+#endif
+ if(jit_reg_is_used(regs->clobber, reg)
+ || (other_reg >= 0 && jit_reg_is_used(regs->clobber, other_reg)))
{
cost = 0;
}
+ else
+ {
+ cost = compute_spill_cost(gen, regs, reg, other_reg);
+ }
+#if ALLOW_LONGS_USE_GLOBAL
+ if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
+ {
+ cost += COST_CLOBBER_GLOBAL;
+ }
+#endif
}
else
{
- copy_cost = 1;
- cost = compute_spill_cost(gen, regs, reg, -1);
- }
- }
- else
- {
- if(jit_reg_is_used(gen->permanent, reg))
- {
- continue;
+ cost = 0;
}
- if(need_pair)
+ if(thrashes_register(gen, regs,
+ is_output ? output_index : index,
+ reg, other_reg))
{
- other_reg = OTHER_REG(reg);
-
- if(jit_reg_is_used(gen->inhibit, other_reg)
- || jit_reg_is_used(regs->assigned, other_reg))
+ if(jit_reg_is_used(gen->permanent, reg))
{
continue;
}
-#if !ALLOW_CLOBBER_GLOBAL
- if(jit_reg_is_used(gen->permanent, other_reg))
+ if(other_reg >= 0 && jit_reg_is_used(gen->permanent, other_reg))
{
continue;
}
-#endif
- }
- else
- {
- other_reg = -1;
+ cost += COST_THRASH;
+ if(other_reg >= 0)
+ {
+ cost += COST_THRASH;
+ }
}
- if(thrashes_register(gen, regs, desc, reg, other_reg))
+ if(is_output)
{
- continue;
+ if(output_index && regs->descs[output_index].value)
+ {
+ desc2 = ®s->descs[output_index];
+ }
+ else
+ {
+ desc2 = 0;
+ }
+ desc3 = ®s->descs[0];
}
- if(desc->value->in_register)
+ else
{
- if(reg == desc->value->reg)
+ desc2 = desc;
+ if(output_index && index == output_index)
{
- if(clobbers_register(gen, regs, index, reg, other_reg)
- && !(jit_reg_is_used(regs->clobber, reg)
- || (other_reg >= 0
- && jit_reg_is_used(regs->clobber, other_reg))))
- {
- copy_cost = 0;
- cost = compute_spill_cost(gen, regs, reg, other_reg);
- }
- else
- {
- copy_cost = 0;
- cost = 0;
- }
+ desc3 = ®s->descs[0];
}
else
{
- copy_cost = 1;
- cost = compute_spill_cost(gen, regs, reg, other_reg);
+ desc3 = desc;
}
}
- else
+ if(!desc2
+ || (desc2->value->in_global_register && desc2->value->global_reg == reg)
+ || (desc2->value->in_register && desc2->value->reg == reg))
{
copy_cost = 0;
- 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))
+ else
{
- cost += COST_CLOBBER_GLOBAL;
+ copy_cost = COST_COPY;
+ }
+ if(desc3->value->has_global_register && desc3->value->global_reg != reg)
+ {
+ cost += COST_GLOBAL_BIAS;
}
-#endif
}
-#if COST_COPY != 1
- if(copy_cost)
- {
- copy_cost = COST_COPY;
- }
-#endif
if((cost + copy_cost) < suitable_cost
|| (cost > 0 && (cost + copy_cost) == suitable_cost
&& gen->contents[reg].age < suitable_age))
}
reg = suitable_reg;
- if(reg >= 0)
+ if(desc && reg >= 0)
{
- if(desc)
+ if(need_pair)
{
- if(need_pair)
- {
- other_reg = OTHER_REG(reg);
- }
- else
- {
- other_reg = -1;
- }
- set_regdesc_register(gen, regs, index, reg, other_reg);
+ other_reg = OTHER_REG(reg);
}
else
{
- set_register_flags(gen, regs, reg, 1, 0);
+ other_reg = -1;
}
+ set_regdesc_register(gen, regs, index, reg, other_reg);
}
return reg;
int keep1, keep2;
int out_index;
- if(regs->ternary || !(regs->commutative || regs->x87_arith))
+ if(regs->ternary || regs->free_dest || !(regs->commutative || regs->x87_arith))
{
/* Bail out on ternary or non-commutative and non-x87 instruction. */
return;
}
/* Determine if we might want to keep either of input values
- in registers after the operation completion. */
+ in registers after the instruction completion. */
if(regs->clobber_all)
{
keep1 = 0;
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;
other_reg = -1;
}
- save_value(gen, desc->value, reg, other_reg, 0);
+ if(desc->thrash)
+ {
+ save_value(gen, desc->value, reg, other_reg, 1);
+ }
+ else
+ {
+ save_value(gen, desc->value, reg, other_reg, 0);
+ }
}
static void
}
void
-_jit_regs_init(_jit_regs_t *regs, int flags)
+_jit_regs_init(jit_gencode_t gen, _jit_regs_t *regs, int flags)
{
int index;
}
regs->num_scratch = 0;
- regs->assigned = jit_regused_init;
+ /* Set clobber flags. */
regs->clobber = jit_regused_init;
- regs->spill = jit_regused_init;
+ if(regs->clobber_all)
+ {
+ for(index = 0; index < JIT_NUM_REGS; index++)
+ {
+ if((_jit_reg_info[index].flags & JIT_REG_FIXED)
+ || jit_reg_is_used(gen->permanent, index))
+ {
+ continue;
+ }
+ jit_reg_set_used(regs->clobber, index);
+ }
+ }
+
+ regs->assigned = jit_regused_init;
regs->stack_start = -1;
regs->current_stack_top = 0;
}
void
-_jit_regs_set_dest(_jit_regs_t *regs, int reg, int other_reg)
+_jit_regs_set_dest(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg)
{
- regs->descs[0].reg = reg;
- regs->descs[0].other_reg = other_reg;
+ if(reg >= 0 && !IS_STACK_REG(reg))
+ {
+ set_regdesc_register(gen, regs, 0, reg, other_reg);
+ }
}
void
-_jit_regs_set_value1(_jit_regs_t *regs, int reg, int other_reg)
+_jit_regs_set_value1(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg)
{
- regs->descs[1].reg = reg;
- regs->descs[1].other_reg = other_reg;
+ if(reg >= 0 && !IS_STACK_REG(reg))
+ {
+ set_regdesc_register(gen, regs, 1, reg, other_reg);
+ }
}
void
-_jit_regs_set_value2(_jit_regs_t *regs, int reg, int other_reg)
+_jit_regs_set_value2(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg)
{
- regs->descs[2].reg = reg;
- regs->descs[2].other_reg = other_reg;
+ if(reg >= 0 && !IS_STACK_REG(reg))
+ {
+ set_regdesc_register(gen, regs, 2, reg, other_reg);
+ }
}
void
-_jit_regs_add_scratch(_jit_regs_t *regs, int reg)
+_jit_regs_add_scratch(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
if(regs->num_scratch < _JIT_REGS_SCRATCH_MAX)
{
- regs->scratch[regs->num_scratch++].reg = reg;
+ if(reg < 0)
+ {
+ ++regs->num_scratch;
+ }
+ else if(!IS_STACK_REG(reg))
+ {
+ set_scratch_register(gen, regs, regs->num_scratch++, reg);
+ }
}
}
void
-_jit_regs_set_clobber(_jit_regs_t *regs, int reg)
+_jit_regs_set_clobber(jit_gencode_t gen, _jit_regs_t *regs, int reg)
{
- jit_reg_set_used(regs->clobber, reg);
+ if(reg >= 0)
+ {
+ jit_reg_set_used(regs->clobber, reg);
+ }
}
void
int
_jit_regs_assign(jit_gencode_t gen, _jit_regs_t *regs)
{
- int index, out_index;
+ int reg, index, out_index;
#ifdef JIT_REG_DEBUG
printf("_jit_regs_assign()\n");
#endif
- /* Set clobber flags. */
- if(regs->clobber_all)
- {
- for(index = 0; index < JIT_NUM_REGS; index++)
- {
- if((_jit_reg_info[index].flags & JIT_REG_FIXED)
- || jit_reg_is_used(gen->permanent, index))
- {
- continue;
- }
- jit_reg_set_used(regs->clobber, index);
- }
- }
-
- /* Spill all clobbered registers. */
- regs->spill = regs->clobber;
-
/* Process pre-assigned registers. */
- if(regs->descs[0].reg >= 0)
- {
- if(IS_STACK_REG(regs->descs[0].reg))
- {
- return 0;
- }
- set_regdesc_register(gen, regs, 0,
- regs->descs[0].reg,
- regs->descs[0].other_reg);
- }
- if(regs->descs[1].reg >= 0)
- {
- if(IS_STACK_REG(regs->descs[1].reg))
- {
- return 0;
- }
- set_regdesc_register(gen, regs, 1,
- regs->descs[1].reg,
- regs->descs[1].other_reg);
- }
- if(regs->descs[2].reg >= 0)
- {
- if(IS_STACK_REG(regs->descs[2].reg))
- {
- return 0;
- }
- set_regdesc_register(gen, regs, 2,
- regs->descs[2].reg,
- regs->descs[2].other_reg);
- }
-
- for(index = 0; index < regs->num_scratch; index++)
- {
- if(regs->scratch[index].reg >= 0)
- {
- if(IS_STACK_REG(regs->scratch[index].reg))
- {
- return 0;
- }
- set_register_flags(gen, regs, regs->scratch[index].reg, 1, 0);
- }
- }
for(index = 0; index < regs->num_scratch; index++)
{
if(regs->scratch[index].reg < 0
&& regs->scratch[index].regset != jit_regused_init_used)
{
- regs->scratch[index].reg = use_cheapest_register(
- gen, regs, -1, regs->scratch[index].regset);
- if(regs->scratch[index].reg < 0)
+ reg = use_cheapest_register(gen, regs, -1, regs->scratch[index].regset);
+ if(reg < 0)
{
return 0;
}
+ set_scratch_register(gen, regs, index, reg);
}
}
check_duplicate_value(regs, ®s->descs[0], ®s->descs[1]);
check_duplicate_value(regs, ®s->descs[0], ®s->descs[2]);
}
- else if(regs->descs[0].value && regs->descs[out_index].value)
+ else if(!regs->free_dest && regs->descs[0].reg >= 0 && regs->descs[out_index].value)
{
- if(regs->descs[0].reg < 0 && regs->descs[out_index].reg < 0)
- {
- if(regs->descs[out_index].value->in_register
- && gen->contents[regs->descs[out_index].value->reg].num_values == 1
- && !(/*regs->descs[out_index].live ||*/regs->descs[out_index].used))
- {
- /* NOTE: The last condition makes sense for local register
- allocation, review it for future global allocator. */
- use_cheapest_register(
- gen, regs, out_index, regs->descs[out_index].regset);
- if(regs->descs[out_index].reg < 0)
- {
- return 0;
- }
- }
- else if(regs->descs[0].value->has_global_register
- || (regs->descs[0].value->in_register
- && gen->contents[regs->descs[0].value->reg].num_values == 1))
- {
- use_cheapest_register(gen, regs, 0, regs->descs[0].regset);
- if(regs->descs[0].reg < 0)
- {
- return 0;
- }
- }
- }
- if(regs->descs[0].reg >= 0)
- {
- set_regdesc_register(gen, regs, out_index,
- regs->descs[0].reg,
- regs->descs[0].other_reg);
- }
+ set_regdesc_register(gen, regs, out_index,
+ regs->descs[0].reg,
+ regs->descs[0].other_reg);
}
if(regs->descs[1].value && regs->descs[1].reg < 0)
{
}
if(regs->descs[0].value && regs->descs[0].reg < 0)
{
- if(regs->descs[out_index].reg < 0)
+ if(regs->free_dest || regs->descs[out_index].reg < 0)
{
use_cheapest_register(gen, regs, 0, regs->descs[0].regset);
if(regs->descs[0].reg < 0)
{
if(regs->scratch[index].reg < 0)
{
- regs->scratch[index].reg = use_cheapest_register(
- gen, regs, -1, jit_regused_init_used);
- if(regs->scratch[index].reg < 0)
+ reg = use_cheapest_register(gen, regs, -1, jit_regused_init_used);
+ if(reg < 0)
{
return 0;
}
+ set_scratch_register(gen, regs, index, reg);
}
}
/* Collect information about registers. */
- if(!collect_register_info(gen, regs, 0))
+ if(!set_regdesc_flags(gen, regs, 0))
{
return 0;
}
- if(!collect_register_info(gen, regs, 1))
+ if(!set_regdesc_flags(gen, regs, 1))
{
return 0;
}
- if(!collect_register_info(gen, regs, 2))
+ if(!set_regdesc_flags(gen, regs, 2))
{
return 0;
}
stack_start = reg;
}
- if(!jit_reg_is_used(regs->spill, reg))
+ if(!jit_reg_is_used(regs->clobber, reg))
{
continue;
}
if(regs->branch)
{
/* After the branch is taken there is no way
- to load global register back. */
+ to load the global register back. */
return 0;
}
_jit_gen_spill_global(gen, reg, 0);
if(IS_STACK_REG(reg))
{
top = get_stack_top(gen, stack_start);
- while(top > reg && jit_reg_is_used(regs->spill, top))
+ while(top > reg && jit_reg_is_used(regs->clobber, top))
{
spill_reg(gen, regs, top);
/* If one of the input values is on the top
/* Load clobbered global registers. */
for(reg = JIT_NUM_REGS - 1; reg >= 0; reg--)
{
- if(jit_reg_is_used(regs->spill, reg) && jit_reg_is_used(gen->permanent, reg))
+ if(jit_reg_is_used(regs->clobber, reg) && jit_reg_is_used(gen->permanent, reg))
{
_jit_gen_load_global(gen, reg, 0);
}
unsigned clobber : 1;
unsigned early_clobber : 1;
unsigned duplicate : 1;
+ unsigned thrash : 1;
unsigned save : 1;
unsigned load : 1;
unsigned copy : 1;
jit_regused_t assigned;
jit_regused_t clobber;
- jit_regused_t spill;
int stack_start;
int current_stack_top;
int loaded_stack_count;
} _jit_regs_t;
-void _jit_regs_init(_jit_regs_t *regs, int flags);
+void _jit_regs_init(jit_gencode_t gen, _jit_regs_t *regs, int flags);
void _jit_regs_init_dest(_jit_regs_t *regs, jit_insn_t insn, int flags);
void _jit_regs_init_value1(_jit_regs_t *regs, jit_insn_t insn, int flags);
void _jit_regs_init_value2(_jit_regs_t *regs, jit_insn_t insn, int flags);
-void _jit_regs_set_dest(_jit_regs_t *regs, int reg, int other_reg);
-void _jit_regs_set_value1(_jit_regs_t *regs, int reg, int other_reg);
-void _jit_regs_set_value2(_jit_regs_t *regs, int reg, int other_reg);
-void _jit_regs_add_scratch(_jit_regs_t *regs, int reg);
-void _jit_regs_set_clobber(_jit_regs_t *regs, int reg);
+void _jit_regs_set_dest(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg);
+void _jit_regs_set_value1(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg);
+void _jit_regs_set_value2(jit_gencode_t gen, _jit_regs_t *regs, int reg, int other_reg);
+void _jit_regs_add_scratch(jit_gencode_t gen, _jit_regs_t *regs, int reg);
+void _jit_regs_set_clobber(jit_gencode_t gen, _jit_regs_t *regs, int reg);
void _jit_regs_set_dest_from(_jit_regs_t *regs, jit_regused_t regset);
void _jit_regs_set_value1_from(_jit_regs_t *regs, jit_regused_t regset);
int _jit_regs_select(_jit_regs_t *regs);
void _jit_regs_commit(jit_gencode_t gen, _jit_regs_t *regs);
void _jit_regs_abort(jit_gencode_t gen, _jit_regs_t *regs);
+
unsigned char *_jit_regs_inst_ptr(jit_gencode_t gen, int space);
unsigned char *_jit_regs_begin(jit_gencode_t gen, _jit_regs_t *regs, int space);
void _jit_regs_end(jit_gencode_t gen, _jit_regs_t *regs, unsigned char *inst);
x86_imul_reg_reg(inst, $1, $2);
}
-/* Spill before division to ensure that the arguments end up in
- EAX and ECX, and that EDX is free */
-JIT_OP_IDIV: binary, spill_before, more_space
- [reg, imm] -> {
- switch($2)
- {
- case 0:
- {
- inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
- }
- break;
-
- case 1: break;
-
- case -1:
- {
- /* Dividing by -1 gives an exception if the argument
- is minint, or simply negates for other values */
- unsigned char *patch;
- x86_alu_reg_imm(inst, X86_CMP, $1, jit_min_int);
- patch = inst;
- x86_branch8(inst, X86_CC_NE, 0, 0);
- inst = throw_builtin(inst, func, JIT_RESULT_ARITHMETIC);
- x86_patch(patch, inst);
- x86_neg_reg(inst, $1);
- }
- break;
-
- case 2:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 1);
- }
- break;
-
- case 4:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 2);
- }
- break;
-
- case 8:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 3);
- }
- break;
-
- case 16:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 4);
- }
- break;
-
- case 32:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 5);
- }
- break;
-
- case 64:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 6);
- }
- break;
-
- case 128:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 7);
- }
- break;
-
- case 256:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 8);
- }
- break;
-
- case 512:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 9);
- }
- break;
-
- case 1024:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 10);
- }
- break;
-
- case 2048:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 11);
- }
- break;
-
- case 4096:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 12);
- }
- break;
-
- case 8192:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 13);
- }
- break;
-
- case 16384:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 14);
- }
- break;
-
- case 32768:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 15);
- }
- break;
-
- case 65536:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 16);
- }
- break;
-
- case 0x00020000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 17);
- }
- break;
-
- case 0x00040000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 18);
- }
- break;
-
- case 0x00080000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 19);
- }
- break;
-
- case 0x00100000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 20);
- }
- break;
-
- case 0x00200000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 21);
- }
- break;
-
- case 0x00400000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 22);
- }
- break;
-
- case 0x00800000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 23);
- }
- break;
-
- case 0x01000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 24);
- }
- break;
-
- case 0x02000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 25);
- }
- break;
-
- case 0x04000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 26);
- }
- break;
-
- case 0x08000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 27);
- }
- break;
-
- case 0x10000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 28);
- }
- break;
-
- case 0x20000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 29);
- }
- break;
-
- case 0x40000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 30);
- }
- break;
-
- case (jit_nint)0x80000000:
- {
- x86_shift_reg_imm(inst, X86_SAR, $1, 31);
- }
- break;
-
- default:
- {
- x86_mov_reg_imm(inst, X86_ECX, $2);
- x86_cdq(inst);
- x86_div_reg(inst, X86_ECX, 1);
- }
- break;
- }
- }
- [reg, reg] -> {
- unsigned char *patch, *patch2;
- x86_alu_reg_reg(inst, X86_OR, $2, $2);
- patch = inst;
- x86_branch8(inst, X86_CC_NE, 0, 0);
+JIT_OP_IDIV: more_space
+ [any, immzero] -> {
inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
- x86_patch(patch, inst);
- x86_alu_reg_imm(inst, X86_CMP, $2, -1);
- patch = inst;
- x86_branch8(inst, X86_CC_NE, 0, 0);
+ }
+ [reg, imm, if("$2 == 1")] -> {
+ }
+ [reg, imm, if("$2 == -1")] -> {
+ /* Dividing by -1 gives an exception if the argument
+ is minint, or simply negates for other values */
+ unsigned char *patch;
x86_alu_reg_imm(inst, X86_CMP, $1, jit_min_int);
- patch2 = inst;
+ patch = inst;
x86_branch8(inst, X86_CC_NE, 0, 0);
inst = throw_builtin(inst, func, JIT_RESULT_ARITHMETIC);
x86_patch(patch, inst);
- x86_patch(patch2, inst);
- x86_cdq(inst);
- x86_div_reg(inst, $2, 1);
- }
-
-JIT_OP_IDIV_UN: binary, spill_before, more_space
- [reg, imm] -> {
- switch($2)
- {
- case 0:
- {
- inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
- }
- break;
-
- case 1: break;
-
- case 2:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 1);
- }
- break;
-
- case 4:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 2);
- }
- break;
-
- case 8:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 3);
- }
- break;
-
- case 16:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 4);
- }
- break;
-
- case 32:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 5);
- }
- break;
-
- case 64:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 6);
- }
- break;
-
- case 128:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 7);
- }
- break;
-
- case 256:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 8);
- }
- break;
-
- case 512:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 9);
- }
- break;
-
- case 1024:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 10);
- }
- break;
-
- case 2048:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 11);
- }
- break;
-
- case 4096:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 12);
- }
- break;
-
- case 8192:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 13);
- }
- break;
-
- case 16384:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 14);
- }
- break;
-
- case 32768:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 15);
- }
- break;
-
- case 65536:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 16);
- }
- break;
-
- case 0x00020000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 17);
- }
- break;
-
- case 0x00040000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 18);
- }
- break;
-
- case 0x00080000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 19);
- }
- break;
-
- case 0x00100000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 20);
- }
- break;
-
- case 0x00200000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 21);
- }
- break;
-
- case 0x00400000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 22);
- }
- break;
-
- case 0x00800000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 23);
- }
- break;
-
- case 0x01000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 24);
- }
- break;
-
- case 0x02000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 25);
- }
- break;
-
- case 0x04000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 26);
- }
- break;
-
- case 0x08000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 27);
- }
- break;
-
- case 0x10000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 28);
- }
- break;
-
- case 0x20000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 29);
- }
- break;
-
- case 0x40000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 30);
- }
- break;
-
- case (jit_nint)0x80000000:
- {
- x86_shift_reg_imm(inst, X86_SHR, $1, 31);
- }
- break;
+ x86_neg_reg(inst, $1);
+ }
+ [reg, imm, if("(((jit_nuint)$2) & (((jit_nuint)$2) - 1)) == 0")] -> {
+ /* x & (x - 1) is equal to zero if x is a power of 2 */
+ jit_nuint shift, value;
+ for(shift = 0, value = $2; value; value >>= 1)
+ {
+ ++shift;
+ }
+ x86_shift_reg_imm(inst, X86_SAR, $1, shift);
+ }
+ [reg("eax"), imm, scratch("?", "edx")] -> {
+ x86_mov_reg_imm(inst, $3, $2);
+ x86_cdq(inst);
+ x86_div_reg(inst, $3, 1);
+ }
+ [reg("eax"), reg, scratch("edx")] -> {
+ unsigned char *patch, *patch2;
+ x86_alu_reg_reg(inst, X86_OR, $2, $2);
+ patch = inst;
+ x86_branch8(inst, X86_CC_NE, 0, 0);
+ inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
+ x86_patch(patch, inst);
+ x86_alu_reg_imm(inst, X86_CMP, $2, -1);
+ patch = inst;
+ x86_branch8(inst, X86_CC_NE, 0, 0);
+ x86_alu_reg_imm(inst, X86_CMP, $1, jit_min_int);
+ patch2 = inst;
+ x86_branch8(inst, X86_CC_NE, 0, 0);
+ inst = throw_builtin(inst, func, JIT_RESULT_ARITHMETIC);
+ x86_patch(patch, inst);
+ x86_patch(patch2, inst);
+ x86_cdq(inst);
+ x86_div_reg(inst, $2, 1);
+ }
- default:
- {
- x86_mov_reg_imm(inst, X86_ECX, $2);
- x86_clear_reg(inst, X86_EDX);
- x86_div_reg(inst, X86_ECX, 0);
- }
- break;
+JIT_OP_IDIV_UN: more_space
+ [any, immzero] -> {
+ inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
+ }
+ [reg, imm, if("$2 == 1")] -> {
+ }
+ [reg, imm, if("(((jit_nuint)$2) & (((jit_nuint)$2) - 1)) == 0")] -> {
+ /* x & (x - 1) is equal to zero if x is a power of 2 */
+ jit_nuint shift, value;
+ for(shift = 0, value = $2; value; value >>= 1)
+ {
+ ++shift;
}
+ x86_shift_reg_imm(inst, X86_SAR, $1, shift);
}
- [reg, reg] -> {
+ [reg("eax"), imm, scratch("?", "edx")] -> {
+ x86_mov_reg_imm(inst, $3, $2);
+ x86_clear_reg(inst, X86_EDX);
+ x86_div_reg(inst, $3, 0);
+ }
+ [reg("eax"), reg, scratch("edx")] -> {
unsigned char *patch;
x86_alu_reg_reg(inst, X86_OR, $2, $2);
patch = inst;
x86_div_reg(inst, $2, 0);
}
-JIT_OP_IREM: binary, spill_before, more_space
- [reg, imm] -> {
- switch($2)
- {
- case 0:
- {
- inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
- }
- break;
-
- case 1:
- {
- x86_clear_reg(inst, $1);
- }
- break;
-
- case -1:
- {
- /* Dividing by -1 gives an exception if the argument
- is minint, or simply gives a remainder of zero */
- unsigned char *patch;
- x86_alu_reg_imm(inst, X86_CMP, $1, jit_min_int);
- patch = inst;
- x86_branch8(inst, X86_CC_NE, 0, 0);
- inst = throw_builtin(inst, func, JIT_RESULT_ARITHMETIC);
- x86_patch(patch, inst);
- x86_clear_reg(inst, $1);
- }
- break;
-
- default:
- {
- x86_mov_reg_imm(inst, X86_ECX, $2);
- x86_cdq(inst);
- x86_div_reg(inst, X86_ECX, 1);
- /* TODO: rearrange register assignments to avoid the move */
- x86_mov_reg_reg(inst, X86_EAX, X86_EDX, 4);
- }
- break;
- }
+JIT_OP_IREM: more_space
+ [any, immzero] -> {
+ inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
}
- [reg, reg] -> {
+ [reg, imm, if("$2 == 1")] -> {
+ x86_clear_reg(inst, $1);
+ }
+ [reg, imm, if("$2 == -1")] -> {
+ /* Dividing by -1 gives an exception if the argument
+ is minint, or simply gives a remainder of zero */
+ unsigned char *patch;
+ x86_alu_reg_imm(inst, X86_CMP, $1, jit_min_int);
+ patch = inst;
+ x86_branch8(inst, X86_CC_NE, 0, 0);
+ inst = throw_builtin(inst, func, JIT_RESULT_ARITHMETIC);
+ x86_patch(patch, inst);
+ x86_clear_reg(inst, $1);
+ }
+ [=reg("edx"), *reg("eax"), imm, scratch("?")] -> {
+ x86_mov_reg_imm(inst, $3, $2);
+ x86_cdq(inst);
+ x86_div_reg(inst, $3, 1);
+ }
+ [=reg("edx"), *reg("eax"), reg] -> {
unsigned char *patch, *patch2;
x86_alu_reg_reg(inst, X86_OR, $2, $2);
patch = inst;
x86_patch(patch2, inst);
x86_cdq(inst);
x86_div_reg(inst, $2, 1);
- x86_mov_reg_reg(inst, X86_EAX, X86_EDX, 4);
}
-JIT_OP_IREM_UN: binary, spill_before, more_space
- [reg, imm] -> {
- switch($2)
- {
- case 0:
- {
- inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
- }
- break;
-
- case 1:
- {
- x86_clear_reg(inst, $1);
- }
- break;
-
- case 2:
- case 4:
- case 8:
- case 16:
- case 32:
- case 64:
- case 128:
- case 256:
- case 512:
- case 1024:
- case 2048:
- case 4096:
- case 8192:
- case 16384:
- case 32768:
- case 65536:
- case 0x00020000:
- case 0x00040000:
- case 0x00080000:
- case 0x00100000:
- case 0x00200000:
- case 0x00400000:
- case 0x00800000:
- case 0x01000000:
- case 0x02000000:
- case 0x04000000:
- case 0x08000000:
- case 0x10000000:
- case 0x20000000:
- case 0x40000000:
- case (jit_nint)0x80000000:
- {
- x86_alu_reg_imm(inst, X86_AND, $1, $2 - 1);
- }
- break;
-
- default:
- {
- x86_mov_reg_imm(inst, X86_ECX, $2);
- x86_clear_reg(inst, X86_EDX);
- x86_div_reg(inst, X86_ECX, 0);
- x86_mov_reg_reg(inst, X86_EAX, X86_EDX, 4);
- }
- break;
- }
+JIT_OP_IREM_UN: more_space
+ [any, immzero] -> {
+ inst = throw_builtin(inst, func, JIT_RESULT_DIVISION_BY_ZERO);
}
- [reg, reg] -> {
+ [reg, imm, if("$2 == 1")] -> {
+ x86_clear_reg(inst, $1);
+ }
+ [reg, imm, if("(((jit_nuint)$2) & (((jit_nuint)$2) - 1)) == 0")] -> {
+ /* x & (x - 1) is equal to zero if x is a power of 2 */
+ x86_alu_reg_imm(inst, X86_AND, $1, $2 - 1);
+ }
+ [=reg("edx"), *reg("eax"), imm, scratch("?")] -> {
+ x86_mov_reg_imm(inst, $3, $2);
+ x86_clear_reg(inst, X86_EDX);
+ x86_div_reg(inst, $3, 0);
+ }
+ [=reg("edx"), *reg("eax"), reg] -> {
unsigned char *patch;
x86_alu_reg_reg(inst, X86_OR, $2, $2);
patch = inst;
x86_patch(patch, inst);
x86_clear_reg(inst, X86_EDX);
x86_div_reg(inst, $2, 0);
- x86_mov_reg_reg(inst, X86_EAX, X86_EDX, 4);
}
JIT_OP_INEG: unary
TODO();
}
-JIT_OP_ADDRESS_OF_LABEL: manual
- [] -> {
- unsigned char *inst;
- jit_block_t block;
- int reg;
-
- reg = _jit_regs_dest_value(gen, insn->dest);
-
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
- reg = _jit_reg_info[reg].cpu_reg;
-
+JIT_OP_ADDRESS_OF_LABEL:
+ [=reg] -> {
block = jit_block_from_label(func, (jit_label_t)(insn->value1));
if(block->address)
{
- x86_mov_reg_imm(inst, reg, block->address);
+ x86_mov_reg_imm(inst, $0, block->address);
}
else
{
/* Output a placeholder and record on the block's fixup list */
- x86_mov_reg_imm(inst, reg, (int)(block->fixup_absolute_list));
+ x86_mov_reg_imm(inst, $0, (int)(block->fixup_absolute_list));
block->fixup_absolute_list = (void *)(inst - 4);
}
-
- gen->posn.ptr = inst;
}
/*
gen->posn.ptr = inst;
}
-JIT_OP_LOAD_RELATIVE_FLOAT32: manual
- [] -> {
- unsigned char *inst;
- int reg = _jit_regs_load_value
- (gen, insn->value1, 0,
- (insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
- JIT_INSN_VALUE1_LIVE)));
- _jit_regs_new_top(gen, insn->dest, 8);
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
- reg = _jit_reg_info[reg].cpu_reg;
- x86_fld_membase(inst, reg, insn->value2->address, 0);
- gen->posn.ptr = inst;
+JIT_OP_LOAD_RELATIVE_FLOAT32:
+ [=freg, reg, imm] -> {
+ x86_fld_membase(inst, $1, $2, 0);
}
-JIT_OP_LOAD_RELATIVE_FLOAT64: manual
- [] -> {
- unsigned char *inst;
- int reg = _jit_regs_load_value
- (gen, insn->value1, 0,
- (insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
- JIT_INSN_VALUE1_LIVE)));
- _jit_regs_new_top(gen, insn->dest, 8);
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
- reg = _jit_reg_info[reg].cpu_reg;
- x86_fld_membase(inst, reg, insn->value2->address, 1);
- gen->posn.ptr = inst;
+JIT_OP_LOAD_RELATIVE_FLOAT64:
+ [=freg, reg, imm] -> {
+ x86_fld_membase(inst, $1, $2, 1);
}
-
-JIT_OP_LOAD_RELATIVE_NFLOAT: manual
- [] -> {
- unsigned char *inst;
- int reg = _jit_regs_load_value
- (gen, insn->value1, 0,
- (insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
- JIT_INSN_VALUE1_LIVE)));
- _jit_regs_new_top(gen, insn->dest, 8);
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
- reg = _jit_reg_info[reg].cpu_reg;
- if(sizeof(jit_nfloat) != sizeof(jit_float64))
- {
- x86_fld80_membase(inst, reg, insn->value2->address);
- }
- else
- {
- x86_fld_membase(inst, reg, insn->value2->address, 1);
- }
- gen->posn.ptr = inst;
+
+JIT_OP_LOAD_RELATIVE_NFLOAT:
+ [=freg, reg, imm, if("sizeof(jit_nfloat) != sizeof(jit_float64)")] -> {
+ x86_fld80_membase(inst, $1, $2);
+ }
+ [=freg, reg, imm, if("sizeof(jit_nfloat) == sizeof(jit_float64)")] -> {
+ x86_fld_membase(inst, $1, $2, 1);
}
JIT_OP_LOAD_RELATIVE_STRUCT: unary_branch, more_space
gen->posn.ptr = inst;
}
-JIT_OP_LOAD_ELEMENT_FLOAT32: manual
- [] -> {
- unsigned char *inst;
- int reg, reg2;
-
- reg = _jit_regs_load_value(gen, insn->value1, 0,
- (insn->flags & (JIT_INSN_VALUE1_NEXT_USE | JIT_INSN_VALUE1_LIVE)));
- reg2 = _jit_regs_load_value(gen, insn->value2, 0,
- (insn->flags & (JIT_INSN_VALUE2_NEXT_USE | JIT_INSN_VALUE2_LIVE)));
- _jit_regs_new_top(gen, insn->dest, 8);
-
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
-
- reg = _jit_reg_info[reg].cpu_reg;
- reg2 = _jit_reg_info[reg2].cpu_reg;
-
- x86_fld_memindex(inst, reg, 0, reg2, 2, 0);
-
- gen->posn.ptr = inst;
+JIT_OP_LOAD_ELEMENT_FLOAT32:
+ [=freg, reg, reg] -> {
+ x86_fld_memindex(inst, $1, 0, $2, 2, 0);
}
-JIT_OP_LOAD_ELEMENT_FLOAT64: manual
- [] -> {
- unsigned char *inst;
- int reg, reg2, reg3;
-
- reg = _jit_regs_load_value(gen, insn->value1, 0,
- (insn->flags & (JIT_INSN_VALUE1_NEXT_USE | JIT_INSN_VALUE1_LIVE)));
- reg2 = _jit_regs_load_value(gen, insn->value2, 0,
- (insn->flags & (JIT_INSN_VALUE2_NEXT_USE | JIT_INSN_VALUE2_LIVE)));
- reg3 = _jit_regs_new_top(gen, insn->dest, 8);
-
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
-
- reg = _jit_reg_info[reg].cpu_reg;
- reg2 = _jit_reg_info[reg2].cpu_reg;
-
- x86_fld_memindex(inst, reg, 0, reg2, 3, 1);
-
- gen->posn.ptr = inst;
+JIT_OP_LOAD_ELEMENT_FLOAT64:
+ [=freg, reg, reg] -> {
+ x86_fld_memindex(inst, $1, 0, $2, 3, 1);
}
-JIT_OP_LOAD_ELEMENT_NFLOAT: manual
- [] -> {
- unsigned char *inst;
- int reg, reg2, reg3;
-
- reg = _jit_regs_load_value
- (gen, insn->value1, 0,
- (insn->flags & (JIT_INSN_VALUE1_NEXT_USE | JIT_INSN_VALUE1_LIVE)));
- reg2 = _jit_regs_load_value
- (gen, insn->value2, sizeof(jit_nfloat) != sizeof(jit_float64),
- (insn->flags & (JIT_INSN_VALUE2_NEXT_USE | JIT_INSN_VALUE2_LIVE)));
- reg3 = _jit_regs_new_top(gen, insn->dest, 8);
-
- inst = gen->posn.ptr;
- if(!jit_cache_check_for_n(&(gen->posn), 32))
- {
- jit_cache_mark_full(&(gen->posn));
- return;
- }
-
- reg = _jit_reg_info[reg].cpu_reg;
- reg2 = _jit_reg_info[reg2].cpu_reg;
-
- if(sizeof(jit_nfloat) != sizeof(jit_float64))
- {
- /* lea reg2, [reg2 + reg2 * 2] */
- x86_lea_memindex(inst, reg2, reg2, 0, reg2, 1);
- /* fld [reg2 * 4] */
- x86_fld80_memindex(inst, reg, 0, reg2, 2);
- }
- else
- {
- x86_fld_memindex(inst, reg, 0, reg2, 3, 1);
- }
-
- gen->posn.ptr = inst;
+JIT_OP_LOAD_ELEMENT_NFLOAT:
+ [=freg, reg, +reg, if("sizeof(jit_nfloat) != sizeof(jit_float64)")] -> {
+ /* lea $2, [$2 + $2 * 2] */
+ x86_lea_memindex(inst, $2, $2, 0, $2, 1);
+ /* fld [$2 * 4] */
+ x86_fld80_memindex(inst, $1, 0, $2, 2);
+ }
+ [=freg, reg, reg, if("sizeof(jit_nfloat) == sizeof(jit_float64)")] -> {
+ x86_fld_memindex(inst, $1, 0, $2, 3, 1);
}
JIT_OP_STORE_ELEMENT_BYTE: ternary
char *code,
char *names[MAX_PATTERN],
char *other_names[MAX_PATTERN],
+ int free_dest,
int in_line)
{
+ char first;
int index;
/* Output the clause code */
}
while(*code != '\0')
{
- if(*code == '$' && code[1] >= '1' && code[1] <= '9')
+ first = free_dest ? '0' : '1';
+ if(*code == '$' && code[1] >= first && code[1] <= (first + MAX_PATTERN))
{
- index = code[1] - '1';
+ index = code[1] - first;
printf(names[index]);
code += 2;
}
- else if(*code == '%' && code[1] >= '1' && code[1] <= '9')
+ else if(*code == '%' && code[1] >= first && code[1] <= (first + MAX_PATTERN))
{
- index = code[1] - '1';
+ index = code[1] - first;
printf(other_names[index]);
code += 2;
}
gensel_output_clause_code(
gensel_clause_t clause,
char *names[MAX_PATTERN],
- char *other_names[MAX_PATTERN])
+ char *other_names[MAX_PATTERN],
+ int free_dest)
{
/* Output the line number information from the original file */
#if 0
printf("#line %ld \"%s\"\n", clause->linenum, clause->filename);
#endif
- gensel_output_code(clause->pattern, clause->code, names, other_names, 0);
+ gensel_output_code(clause->pattern, clause->code, names, other_names, free_dest, 0);
}
/*
printf("\t_jit_regs_spill_all(gen);\n");
}
gensel_init_names(MAX_PATTERN, names, other_names);
- gensel_output_clause_code(clauses, names, other_names);
+ gensel_output_clause_code(clauses, names, other_names, 0);
return;
}
gensel_output_code(
clause->pattern,
pattern->values->value,
- names, other_names, 1);
+ names, other_names, free_dest, 1);
printf(")");
seen_option = 1;
break;
if(contains_registers)
{
seen_option = 0;
- printf("\t\t_jit_regs_init(®s, ");
+ printf("\t\t_jit_regs_init(gen, ®s, ");
if(clobber_all)
{
seen_option = 1;
{
if(pattern->values->type == GENSEL_VALUE_STRING)
{
- printf("\t\t_jit_regs_set_%s(®s, _jit_regs_lookup(\"%s\"), -1);\n",
+ printf("\t\t_jit_regs_set_%s(gen, ®s, _jit_regs_lookup(\"%s\"), -1);\n",
args[index], pattern->values->value);
}
else
{
if(pattern->values->next && pattern->values->next->value)
{
- printf("\t\t_jit_regs_set_%s(®s, _jit_regs_lookup(\"%s\"), _jit_regs_lookup(\"%s\"));\n",
+ printf("\t\t_jit_regs_set_%s(gen, ®s, _jit_regs_lookup(\"%s\"), _jit_regs_lookup(\"%s\"));\n",
args[index], pattern->values->value, pattern->values->next->value);
}
else
{
- printf("\t\t_jit_regs_set_%s(®s, _jit_regs_lookup(\"%s\"), -1);\n",
+ printf("\t\t_jit_regs_set_%s(gen, ®s, _jit_regs_lookup(\"%s\"), -1);\n",
args[index], pattern->values->value);
}
}
{
if(values->type == GENSEL_VALUE_STRING)
{
- printf("\t\t_jit_regs_add_scratch(®s, _jit_regs_lookup(\"%s\"));\n",
+ printf("\t\t_jit_regs_add_scratch(gen, ®s, _jit_regs_lookup(\"%s\"));\n",
values->value);
}
else
}
else
{
- printf("\t\t_jit_regs_add_scratch(®s, -1);\n");
+ printf("\t\t_jit_regs_add_scratch(gen, ®s, -1);\n");
}
++regs;
++index;
{
if(values->value && strcmp(values->value, "*") != 0)
{
- printf("\t\t_jit_regs_set_clobber(®s, _jit_regs_lookup(\"%s\"));\n",
+ printf("\t\t_jit_regs_set_clobber(gen, ®s, _jit_regs_lookup(\"%s\"));\n",
values->value);
}
values = values->next;
gensel_output_code(
clause->pattern,
space->values->value,
- names, other_names, 1);
+ names, other_names, free_dest, 1);
printf(")");
}
else
}
gensel_build_var_index(clause->pattern, names, other_names);
- gensel_output_clause_code(clause, names, other_names);
+ gensel_output_clause_code(clause, names, other_names, free_dest);
/* Copy "inst" back into the generation context */
if(gensel_new_inst_type)