From: Rhys Weatherley Date: Wed, 2 Jun 2004 07:17:23 +0000 (+0000) Subject: Implement a simple global register allocation policy, based on usage counts. X-Git-Tag: r.0.0.4~59 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=f24dd004ca6683be547b170488405a7a8885e38d;p=francis%2Flibjit.git Implement a simple global register allocation policy, based on usage counts. --- diff --git a/ChangeLog b/ChangeLog index 436fa3c..992ede1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,13 @@ +2004-06-02 Rhys Weatherley + + * 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 * jit/jit-cache.c, jit/jit-elf-read.c, tools/gen-apply.c: diff --git a/jit/jit-function.c b/jit/jit-function.c index cdb8a92..8eef76f 100644 --- a/jit/jit-function.c +++ b/jit/jit-function.c @@ -475,6 +475,7 @@ static void compile_block(jit_gencode_t gen, jit_function_t func, 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; @@ -581,6 +582,9 @@ int jit_function_compile(jit_function_t func) /* 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 diff --git a/jit/jit-insn.c b/jit/jit-insn.c index 28bf18f..3c83e4d 100644 --- a/jit/jit-insn.c +++ b/jit/jit-insn.c @@ -4991,7 +4991,7 @@ jit_value_t jit_insn_call 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; } @@ -5143,7 +5143,7 @@ jit_value_t jit_insn_call_indirect 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; } @@ -5257,7 +5257,7 @@ jit_value_t jit_insn_call_indirect_vtable 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; } @@ -5366,7 +5366,7 @@ jit_value_t jit_insn_call_native 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; } diff --git a/jit/jit-internal.h b/jit/jit-internal.h index 92e3a26..ffb21f7 100644 --- a/jit/jit-internal.h +++ b/jit/jit-internal.h @@ -195,22 +195,27 @@ struct _jit_value { 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) diff --git a/jit/jit-reg-alloc.c b/jit/jit-reg-alloc.c index 2352484..c003095 100644 --- a/jit/jit-reg-alloc.c +++ b/jit/jit-reg-alloc.c @@ -252,7 +252,16 @@ static void spill_all_between(jit_gencode_t gen, int first, int last) 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) { @@ -503,7 +512,16 @@ static void free_reg_and_spill 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) { @@ -696,7 +714,10 @@ void _jit_regs_set_value /* 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; } @@ -817,7 +838,10 @@ static void load_value(jit_gencode_t gen, int reg, int other_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); } } @@ -963,7 +987,7 @@ int _jit_regs_load_value 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 @@ -1026,6 +1050,7 @@ int _jit_regs_dest_value(jit_gencode_t gen, jit_value_t value) 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); @@ -1100,7 +1125,7 @@ int _jit_regs_load_to_top(jit_gencode_t gen, jit_value_t value, int used_again, 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; @@ -1163,8 +1188,8 @@ int _jit_regs_load_to_top_two & 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); @@ -1235,9 +1260,10 @@ void _jit_regs_load_to_top_three 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); @@ -1315,6 +1341,7 @@ int _jit_regs_new_top(jit_gencode_t gen, jit_value_t value, int type_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; @@ -1345,3 +1372,91 @@ void _jit_regs_force_out(jit_gencode_t gen, jit_value_t value, int is_dest) } } } + +/* + * 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 +} diff --git a/jit/jit-reg-alloc.h b/jit/jit-reg-alloc.h index 01a1c65..cc781bf 100644 --- a/jit/jit-reg-alloc.h +++ b/jit/jit-reg-alloc.h @@ -55,6 +55,7 @@ void _jit_regs_load_to_top_three 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 }; diff --git a/jit/jit-rules-arm.c b/jit/jit-rules-arm.c index 2e7e1cd..63c08bc 100644 --- a/jit/jit-rules-arm.c +++ b/jit/jit-rules-arm.c @@ -827,4 +827,18 @@ void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) /* 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 */ diff --git a/jit/jit-rules-arm.h b/jit/jit-rules-arm.h index 4ec5f60..20bd76b 100644 --- a/jit/jit-rules-arm.h +++ b/jit/jit-rules-arm.h @@ -55,7 +55,8 @@ extern "C" { {"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 diff --git a/jit/jit-rules-interp.c b/jit/jit-rules-interp.c index 7d089e7..4d7b81f 100644 --- a/jit/jit-rules-interp.c +++ b/jit/jit-rules-interp.c @@ -128,6 +128,10 @@ This register is a candidate for global register allocation. 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 @@ -1669,4 +1673,16 @@ void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) 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 */ diff --git a/jit/jit-rules-interp.h b/jit/jit-rules-interp.h index 8f4505e..a6d0215 100644 --- a/jit/jit-rules-interp.h +++ b/jit/jit-rules-interp.h @@ -63,7 +63,8 @@ extern "C" { 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 diff --git a/jit/jit-rules-x86.c b/jit/jit-rules-x86.c index 5ef3de9..86d33e0 100644 --- a/jit/jit-rules-x86.c +++ b/jit/jit-rules-x86.c @@ -904,6 +904,16 @@ void _jit_gen_spill_reg(jit_gencode_t gen, int reg, /* 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); @@ -1067,6 +1077,13 @@ void _jit_gen_load_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 */ @@ -1482,4 +1499,18 @@ void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) /* 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 */ diff --git a/jit/jit-rules-x86.h b/jit/jit-rules-x86.h index c0feb39..b144f54 100644 --- a/jit/jit-rules-x86.h +++ b/jit/jit-rules-x86.h @@ -47,7 +47,8 @@ extern "C" { {"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 diff --git a/jit/jit-rules.h b/jit/jit-rules.h index f23ed9a..f73ab04 100644 --- a/jit/jit-rules.h +++ b/jit/jit-rules.h @@ -191,6 +191,7 @@ void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, 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 diff --git a/jit/jit-value.c b/jit/jit-value.c index 7f0db7c..b24a31f 100644 --- a/jit/jit-value.c +++ b/jit/jit-value.c @@ -604,6 +604,7 @@ void jit_value_ref(jit_function_t func, jit_value_t value) { return; } + ++(value->usage_count); if(value->is_temporary) { if(value->block->func != func) @@ -626,6 +627,10 @@ void jit_value_ref(jit_function_t func, jit_value_t value) 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) diff --git a/tools/gen-sel-parser.y b/tools/gen-sel-parser.y index 7c70215..dd6e2c1 100644 --- a/tools/gen-sel-parser.y +++ b/tools/gen-sel-parser.y @@ -734,7 +734,10 @@ Rule 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"); }