+2004-06-02 Rhys Weatherley <rweather@southern-storm.com.au>
+
+ * jit/jit-function.c, jit/jit-insn.c, jit/jit-internal.h,
+ jit/jit-reg-alloc.c, jit/jit-reg-alloc.h, jit/jit-rules-arm.c,
+ jit/jit-rules-arm.h, jit/jit-rules-interp.c, jit/jit-rules-interp.h,
+ jit/jit-rules-x86.c, jit/jit-rules-x86.h, jit/jit-rules.h,
+ jit/jit-value.c, tools/gen-sel-parser.y: implement a simple global
+ register allocation policy, based on usage counts.
+
2004-06-01 Rhys Weatherley <rweather@southern-storm.com.au>
* jit/jit-cache.c, jit/jit-elf-read.c, tools/gen-apply.c:
insn->value1->in_register = 0;
insn->value1->in_frame = 1;
insn->value1->has_frame_offset = 1;
+ insn->value1->has_global_register = 0; /* Disable globals */
}
break;
/* Compute liveness and "next use" information for this function */
_jit_function_compute_liveness(func);
+ /* Allocate global registers to variables within the function */
+ _jit_regs_alloc_global(&gen, func);
+
/* We may need to perform output twice, if the first attempt fails
due to a lack of space in the current method cache page */
do
jit_insn_t insn;
/* Bail out if there is something wrong with the parameters */
- if(!func || !jit_func)
+ if(!_jit_function_ensure_builder(func) || !jit_func)
{
return 0;
}
jit_insn_t insn;
/* Bail out if there is something wrong with the parameters */
- if(!func || !value || !signature)
+ if(!_jit_function_ensure_builder(func) || !value || !signature)
{
return 0;
}
jit_insn_t insn;
/* Bail out if there is something wrong with the parameters */
- if(!func || !value || !signature)
+ if(!_jit_function_ensure_builder(func) || !value || !signature)
{
return 0;
}
jit_insn_t insn;
/* Bail out if there is something wrong with the parameters */
- if(!func || !native_func || !signature)
+ if(!_jit_function_ensure_builder(func) || !native_func || !signature)
{
return 0;
}
{
jit_block_t block;
jit_type_t type;
- short is_temporary : 1;
- short is_local : 1;
- short is_volatile : 1;
- short is_addressable : 1;
- short is_constant : 1;
- short is_nint_constant : 1;
- short has_address : 1;
- short free_address : 1;
- short in_register : 1;
- short in_frame : 1;
- short live : 1;
- short next_use : 1;
- short has_frame_offset : 1;
+ int is_temporary : 1;
+ int is_local : 1;
+ int is_volatile : 1;
+ int is_addressable : 1;
+ int is_constant : 1;
+ int is_nint_constant : 1;
+ int has_address : 1;
+ int free_address : 1;
+ int in_register : 1;
+ int in_frame : 1;
+ int in_global_register : 1;
+ int live : 1;
+ int next_use : 1;
+ int has_frame_offset : 1;
+ int global_candidate : 1;
+ int has_global_register : 1;
short reg;
+ short global_reg;
jit_nint address;
jit_nint frame_offset;
+ jit_nuint usage_count;
};
#define JIT_INVALID_FRAME_OFFSET ((jit_nint)0x7FFFFFFF)
posn >= 0; --posn)
{
value = gen->contents[real_reg].values[posn];
- if(!(value->in_frame))
+ 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)
{
for(posn = gen->contents[reg].num_values - 1; posn >= 0; --posn)
{
value = gen->contents[reg].values[posn];
- if(!(value->in_frame))
+ 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)
{
/* Adjust the value to reflect that it is in "reg", and maybe the frame */
value->in_register = 1;
- value->in_frame = still_in_frame;
+ if(value->has_global_register)
+ value->in_global_register = still_in_frame;
+ else
+ value->in_frame = still_in_frame;
value->reg = (short)reg;
}
else
{
/* Mark the register as containing the value we have loaded */
- _jit_regs_set_value(gen, reg, value, value->in_frame);
+ 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);
}
}
if(destroy)
{
if(gen->contents[reg].num_values == 1 &&
- (value->in_frame || !used_again))
+ (value->in_frame || value->in_global_register || !used_again))
{
/* We are the only value in this register, and the
value is duplicated in the frame, or will never
if(gen->contents[reg].num_values == 1)
{
value->in_frame = 0;
+ value->in_global_register = 0;
return reg;
}
free_reg_and_spill(gen, reg, 0, 1);
if((_jit_reg_info[gen->contents[reg].remap].flags
& JIT_REG_START_STACK) != 0)
{
- if(value->in_frame || !used_again)
+ if(value->in_frame || value->in_global_register || !used_again)
{
/* Disassociate the value from the register and return */
value->in_register = 0;
& JIT_REG_START_STACK) != 0 &&
gen->contents[reg].remap == (gen->contents[reg2].remap + 1))
{
- if((value->in_frame || !used_again1) &&
- (value2->in_frame || !used_again2))
+ if((value->in_frame || value->in_global_register || !used_again1) &&
+ (value2->in_frame || value2->in_global_register || !used_again2))
{
/* Disassociate the values from the registers and return */
free_stack_reg(gen, reg2);
gen->contents[reg].remap == (gen->contents[reg2].remap + 1) &&
gen->contents[reg2].remap == (gen->contents[reg3].remap + 1))
{
- if((value->in_frame || !used_again1) &&
- (value2->in_frame || !used_again2) &&
- (value3->in_frame || !used_again3))
+ 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))
{
/* Disassociate the values from the registers and return */
free_stack_reg(gen, reg);
/* 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;
}
}
}
+
+/*
+ * 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;
+ }
+
+ /* 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))
+ {
+ 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))
+ {
+ /* 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);
+ }
+
+#endif
+}
int _jit_regs_num_used(jit_gencode_t gen, int type_reg);
int _jit_regs_new_top(jit_gencode_t gen, jit_value_t value, int type_reg);
void _jit_regs_force_out(jit_gencode_t gen, jit_value_t value, int is_dest);
+void _jit_regs_alloc_global(jit_gencode_t gen, jit_function_t func);
#ifdef __cplusplus
};
/* Nothing to do here for ARM */
}
+int _jit_gen_is_global_candidate(jit_type_t type)
+{
+ switch(jit_type_remove_tags(type)->kind)
+ {
+ case JIT_TYPE_INT:
+ case JIT_TYPE_UINT:
+ case JIT_TYPE_NINT:
+ case JIT_TYPE_NUINT:
+ case JIT_TYPE_PTR:
+ case JIT_TYPE_SIGNATURE: return 1;
+ }
+ return 0;
+}
+
#endif /* JIT_BACKEND_ARM */
{"f1", 1, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED}, \
{"f2", 2, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED}, \
{"f3", 3, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED},
-#define JIT_NUM_REGS 20
+#define JIT_NUM_REGS 20
+#define JIT_NUM_GLOBAL_REGS 3
/*
* Define to 1 if we should always load values into registers
The rule file may also have definitions of the following macros:
@table @code
+@item JIT_NUM_GLOBAL_REGS
+The number of registers that are used for global register allocation.
+Set to zero if global register allocation should not be used.
+
@item JIT_ALWAYS_REG_REG
Define this to 1 if arithmetic operations must always be performed
on registers. Define this to 0 if register/memory and memory/register
gen->working_area = 0;
}
+/*@
+ * @deftypefun int _jit_gen_is_global_candidate (jit_type_t type)
+ * Determine if @code{type} is a candidate for allocation within
+ * global registers.
+ * @end deftypefun
+@*/
+int _jit_gen_is_global_candidate(jit_type_t type)
+{
+ /* Global register allocation is not used by the interpreter */
+ return 0;
+}
+
#endif /* JIT_BACKEND_INTERP */
JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \
{"r15", 15, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \
JIT_REG_CALL_USED | JIT_REG_END_STACK | JIT_REG_IN_STACK},
-#define JIT_NUM_REGS 16
+#define JIT_NUM_REGS 16
+#define JIT_NUM_GLOBAL_REGS 0
/*
* Define to 1 if we should always load values into registers
/* Make sure that we have sufficient space */
jit_cache_setup_output(16);
+ /* If the value is associated with a global register, then copy to that */
+ if(value->has_global_register)
+ {
+ reg = _jit_reg_info[reg].cpu_reg;
+ other_reg = _jit_reg_info[value->global_reg].cpu_reg;
+ x86_mov_reg_reg(inst, other_reg, reg, sizeof(void *));
+ jit_cache_end_output();
+ return;
+ }
+
/* Fix the value in place within the local variable frame */
_jit_gen_fix_value(value);
break;
}
}
+ else if(value->has_global_register)
+ {
+ /* Load the value out of a global register */
+ x86_mov_reg_reg(inst, _jit_reg_info[reg].cpu_reg,
+ _jit_reg_info[value->global_reg].cpu_reg,
+ sizeof(void *));
+ }
else
{
/* Fix the position of the value in the stack frame */
/* Nothing to do here for x86 */
}
+int _jit_gen_is_global_candidate(jit_type_t type)
+{
+ switch(jit_type_remove_tags(type)->kind)
+ {
+ case JIT_TYPE_INT:
+ case JIT_TYPE_UINT:
+ case JIT_TYPE_NINT:
+ case JIT_TYPE_NUINT:
+ case JIT_TYPE_PTR:
+ case JIT_TYPE_SIGNATURE: return 1;
+ }
+ return 0;
+}
+
#endif /* JIT_BACKEND_X86 */
{"st6", 6, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \
{"st7", 7, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_END_STACK | \
JIT_REG_IN_STACK},
-#define JIT_NUM_REGS 16
+#define JIT_NUM_REGS 16
+#define JIT_NUM_GLOBAL_REGS 3
/*
* Define to 1 if we should always load values into registers
jit_block_t block, jit_insn_t insn);
void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block);
void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block);
+int _jit_gen_is_global_candidate(jit_type_t type);
/*
* Determine the byte number within a "jit_int" where the low
{
return;
}
+ ++(value->usage_count);
if(value->is_temporary)
{
if(value->block->func != func)
value->is_temporary = 0;
value->is_local = 1;
value->live = 1;
+ if(_jit_gen_is_global_candidate(value->type))
+ {
+ value->global_candidate = 1;
+ }
}
}
else if(value->is_local && value->block->func != func)
printf("\telse\n");
printf("\t{\n");
printf("\t\t_jit_gen_spill_reg(gen, reg, -1, insn->dest);\n");
- printf("\t\tinsn->dest->in_frame = 1;\n");
+ printf("\t\tif(insn->dest->has_global_register)\n");
+ printf("\t\t\tinsn->dest->in_global_register = 1;\n");
+ printf("\t\telse\n");
+ printf("\t\t\tinsn->dest->in_frame = 1;\n");
printf("\t\t_jit_regs_free_reg(gen, reg, 1);\n");
printf("\t}\n");
}