From: Aleksey Demakov Date: Thu, 14 Sep 2006 06:27:08 +0000 (+0000) Subject: remove "old" register allocator; X-Git-Tag: before.move.to.git~193 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=56e8a52ef824632520a2b525fe274b5c2cf508b9;p=francis%2Flibjit.git remove "old" register allocator; --- diff --git a/ChangeLog b/ChangeLog index 0a4cfce..2abc9c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-09-14 Aleksey Demakov + + * jit/jit-reg-alloc.h, jit/jit-reg-alloc.c: remove "old" register + allocation code. + + * configure.in: remove --enable-new-reg-alloc option. + + * jit/jit-rules-x86.ins: simplify JIT_OP_RETURN_LONG rule, lift code + from load_small_struct() function to JIT_OP_RETURN_SMALL_STRUCT rule. + * jit/jit-rules-x86.c: remove load_small_struct() function. + 2006-09-09 Aleksey Demakov * jit/jit-reg-alloc.h, jit/jit-reg-alloc.c: @@ -7,7 +18,7 @@ 2006-09-02 Aleksey Demakov - * jit/jit-interp.c (_jit_run_function): + * jit/jit-interp.c (_jit_run_function): * jit/jit-rules-interp.c (_jit_gen_insn): remove last traces of register allocation from the interpreter. diff --git a/jit/jit-reg-alloc.c b/jit/jit-reg-alloc.c index a5608a4..d0bf074 100644 --- a/jit/jit-reg-alloc.c +++ b/jit/jit-reg-alloc.c @@ -32,113 +32,71 @@ mostly don't have to worry about it: @*/ -/*@ - * @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; } @@ -147,11 +105,12 @@ static void dump_regs(jit_gencode_t gen, const char *name) { 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) { @@ -166,2421 +125,1179 @@ static void dump_regs(jit_gencode_t gen, const char *name) { 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++) @@ -2593,51 +1310,102 @@ choose_scratch_register(jit_gencode_t gen, _jit_regs_t *regs, int index) { 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; } @@ -2645,1189 +1413,1345 @@ choose_scratch_register(jit_gencode_t gen, _jit_regs_t *regs, int index) 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); } @@ -3836,132 +2760,146 @@ abort_input_value(jit_gencode_t gen, _jit_regs_t *regs, int index) 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 @@ -4016,8 +2954,6 @@ _jit_regs_init(jit_gencode_t gen, _jit_regs_t *regs, int flags) regs->assigned = gen->inhibit; - regs->stack_start = -1; - regs->current_stack_top = 0; regs->wanted_stack_count = 0; regs->loaded_stack_count = 0; } @@ -4291,14 +3227,13 @@ _jit_regs_assign(jit_gencode_t gen, _jit_regs_t *regs) 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)) @@ -4306,12 +3241,6 @@ _jit_regs_gen(jit_gencode_t gen, _jit_regs_t *regs) 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; @@ -4332,40 +3261,46 @@ _jit_regs_gen(jit_gencode_t gen, _jit_regs_t *regs) 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); @@ -4377,61 +3312,54 @@ _jit_regs_gen(jit_gencode_t gen, _jit_regs_t *regs) 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 @@ -4471,12 +3399,14 @@ _jit_regs_commit(jit_gencode_t gen, _jit_regs_t *regs) 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); @@ -4507,12 +3437,12 @@ _jit_regs_commit(jit_gencode_t gen, _jit_regs_t *regs) 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 * @@ -4559,25 +3489,3 @@ _jit_regs_end(jit_gencode_t gen, _jit_regs_t *regs, unsigned char *inst) 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; -} diff --git a/jit/jit-reg-alloc.h b/jit/jit-reg-alloc.h index 55f0323..ed76751 100644 --- a/jit/jit-reg-alloc.h +++ b/jit/jit-reg-alloc.h @@ -27,43 +27,6 @@ extern "C" { #endif -void _jit_regs_init_for_block(jit_gencode_t gen); -int _jit_regs_needs_long_pair(jit_type_t type); -int _jit_regs_get_cpu(jit_gencode_t gen, int reg, int *other_reg); -void _jit_regs_spill_all(jit_gencode_t gen); -int _jit_regs_want_reg(jit_gencode_t gen, int reg, int for_long); -void _jit_regs_free_reg(jit_gencode_t gen, int reg, int value_used); -void _jit_regs_set_value - (jit_gencode_t gen, int reg, jit_value_t value, int still_in_frame); -void _jit_regs_set_incoming(jit_gencode_t gen, int reg, jit_value_t value); -void _jit_regs_set_outgoing(jit_gencode_t gen, int reg, jit_value_t value); -int _jit_regs_is_top(jit_gencode_t gen, jit_value_t value); -int _jit_regs_is_top_two - (jit_gencode_t gen, jit_value_t value1, jit_value_t value2); -int _jit_regs_load_value - (jit_gencode_t gen, jit_value_t value, int destroy, int used_again); -int _jit_regs_dest_value(jit_gencode_t gen, jit_value_t value); -int _jit_regs_load_to_top - (jit_gencode_t gen, jit_value_t value, int used_again, int type_reg); -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); -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); -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); -void _jit_regs_get_reg_pair(jit_gencode_t gen, int not_this1, int not_this2, - int not_this3, int *reg, int *reg2); - - -/* - * New Reg Alloc API - */ - /* * The maximum number of values per instruction. */ @@ -140,6 +103,10 @@ typedef struct */ typedef struct { + _jit_regdesc_t descs[_JIT_REGS_VALUE_MAX]; + _jit_scratch_t scratch[_JIT_REGS_SCRATCH_MAX]; + int num_scratch; + unsigned clobber_all : 1; unsigned clobber_stack : 1; unsigned ternary : 1; @@ -155,22 +122,33 @@ typedef struct unsigned reverse_dest : 1; unsigned reverse_args : 1; - _jit_regdesc_t descs[_JIT_REGS_VALUE_MAX]; - _jit_scratch_t scratch[_JIT_REGS_SCRATCH_MAX]; - int num_scratch; - + /* The input value index that is going to be overwritten + by the destination value. For ordinary binary and unary + opcodes it is equal to 1, for notes and three-address + opcodes it is equal to 0, and for some x87 instructions + it could be equal to 2. */ int dest_input_index; jit_regused_t assigned; jit_regused_t clobber; - int stack_start; - int current_stack_top; int wanted_stack_count; int loaded_stack_count; } _jit_regs_t; +int _jit_regs_lookup(char *name); +int _jit_regs_needs_long_pair(jit_type_t type); +int _jit_regs_get_cpu(jit_gencode_t gen, int reg, int *other_reg); + +void _jit_regs_alloc_global(jit_gencode_t gen, jit_function_t func); +void _jit_regs_init_for_block(jit_gencode_t gen); +void _jit_regs_spill_all(jit_gencode_t gen); +void _jit_regs_set_incoming(jit_gencode_t gen, int reg, jit_value_t value); +void _jit_regs_set_outgoing(jit_gencode_t gen, int reg, jit_value_t value); +void _jit_regs_force_out(jit_gencode_t gen, jit_value_t value, int is_dest); +int _jit_regs_load_value(jit_gencode_t gen, jit_value_t value, int destroy, int used_again); + 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); @@ -205,8 +183,6 @@ int _jit_regs_value1_other(_jit_regs_t *regs); int _jit_regs_value2_other(_jit_regs_t *regs); int _jit_regs_scratch(_jit_regs_t *regs, int index); -int _jit_regs_lookup(char *name); - #ifdef __cplusplus }; #endif diff --git a/jit/jit-rules-x86.c b/jit/jit-rules-x86.c index 2d1fca5..11107b8 100644 --- a/jit/jit-rules-x86.c +++ b/jit/jit-rules-x86.c @@ -434,140 +434,6 @@ static int get_temp_reg(int reg1, int reg2, int reg3) return X86_EDI; } -/* - * Load a small structure from a pointer into registers. - */ -static unsigned char *load_small_struct - (unsigned char *inst, int reg, int other_reg, - int base_reg, jit_nint offset, jit_nint size, int save_temp) -{ - int temp_reg; - switch(size) - { - case 1: - { - x86_widen_membase(inst, reg, base_reg, offset, 0, 0); - } - break; - - case 2: - { - x86_widen_membase(inst, reg, base_reg, offset, 0, 1); - } - break; - - case 3: - { - temp_reg = get_temp_reg(reg, -1, base_reg); - if(save_temp || temp_reg >= X86_EBX) - { - x86_push_reg(inst, temp_reg); - } - x86_widen_membase(inst, temp_reg, base_reg, offset, 0, 1); - x86_widen_membase(inst, reg, base_reg, offset + 2, 0, 0); - x86_shift_reg_imm(inst, X86_SHL, reg, 16); - x86_alu_reg_reg(inst, X86_OR, reg, temp_reg); - if(save_temp || temp_reg >= X86_EBX) - { - x86_pop_reg(inst, temp_reg); - } - } - break; - - case 4: - { - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - } - break; - - case 5: - { - if(reg != base_reg) - { - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 0); - } - else - { - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 0); - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - } - } - break; - - case 6: - { - if(reg != base_reg) - { - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 1); - } - else - { - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 1); - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - } - } - break; - - case 7: - { - temp_reg = get_temp_reg(reg, other_reg, base_reg); - if(save_temp || temp_reg >= X86_EBX) - { - x86_push_reg(inst, temp_reg); - } - if(reg != base_reg && other_reg != base_reg) - { - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 1); - x86_widen_membase(inst, temp_reg, base_reg, offset + 6, 0, 0); - x86_shift_reg_imm(inst, X86_SHL, temp_reg, 16); - x86_alu_reg_reg(inst, X86_OR, other_reg, temp_reg); - } - else if(reg != base_reg) - { - /* other_reg == base_reg */ - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - x86_widen_membase(inst, temp_reg, base_reg, offset + 6, 0, 0); - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 1); - x86_shift_reg_imm(inst, X86_SHL, temp_reg, 16); - x86_alu_reg_reg(inst, X86_OR, other_reg, temp_reg); - } - else - { - /* reg == base_reg */ - x86_widen_membase(inst, other_reg, base_reg, offset + 4, 0, 1); - x86_widen_membase(inst, temp_reg, base_reg, offset + 6, 0, 0); - x86_shift_reg_imm(inst, X86_SHL, temp_reg, 16); - x86_alu_reg_reg(inst, X86_OR, other_reg, temp_reg); - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - } - if(save_temp || temp_reg >= X86_EBX) - { - x86_pop_reg(inst, temp_reg); - } - } - break; - - case 8: - { - if(reg != base_reg) - { - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - x86_mov_reg_membase(inst, other_reg, base_reg, offset + 4, 4); - } - else - { - x86_mov_reg_membase(inst, other_reg, base_reg, offset + 4, 4); - x86_mov_reg_membase(inst, reg, base_reg, offset, 4); - } - } - break; - } - return inst; -} - /* * Store a byte value to a membase address. */ @@ -769,8 +635,7 @@ void _jit_gen_free_reg(jit_gencode_t gen, int reg, static int fp_stack_index(jit_gencode_t gen, int reg) { - int top = gen->stack_map[X86_REG_ST0]; - return top - reg; + return gen->reg_stack_top - reg - 1; } void diff --git a/jit/jit-rules-x86.h b/jit/jit-rules-x86.h index 6d68a3b..bff1b3a 100644 --- a/jit/jit-rules-x86.h +++ b/jit/jit-rules-x86.h @@ -39,19 +39,21 @@ extern "C" { {"edi", 7, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ {"ebp", 4, -1, JIT_REG_FRAME | JIT_REG_FIXED}, \ {"esp", 5, -1, JIT_REG_STACK_PTR | JIT_REG_FIXED | JIT_REG_CALL_USED}, \ - {"st", 0, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | \ - JIT_REG_START_STACK | JIT_REG_IN_STACK}, \ + {"st", 0, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ {"st1", 1, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ {"st2", 2, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ {"st3", 3, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ {"st4", 4, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ {"st5", 5, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ {"st6", 6, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ - {"st7", 7, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | \ - JIT_REG_END_STACK | JIT_REG_IN_STACK}, + {"st7", 7, -1, JIT_REG_X86_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, #define JIT_NUM_REGS 16 #define JIT_NUM_GLOBAL_REGS 3 +#define JIT_REG_STACK 1 +#define JIT_REG_STACK_START 8 +#define JIT_REG_STACK_END 15 + /* * Define to 1 if we should always load values into registers * before operating on them. i.e. the CPU does not have reg-mem diff --git a/jit/jit-rules-x86.ins b/jit/jit-rules-x86.ins index 2182046..84ba026 100644 --- a/jit/jit-rules-x86.ins +++ b/jit/jit-rules-x86.ins @@ -1344,25 +1344,13 @@ JIT_OP_RETURN: inst = jump_to_epilog(gen, inst, block); } -JIT_OP_RETURN_INT: unary_branch +JIT_OP_RETURN_INT: unary_note [reg("eax")] -> { inst = jump_to_epilog(gen, inst, block); } -JIT_OP_RETURN_LONG: unary_branch - [imm] -> { - x86_mov_reg_imm(inst, X86_EAX, - ((jit_int *)(insn->value1->address))[0]); - x86_mov_reg_imm(inst, X86_EDX, - ((jit_int *)(insn->value1->address))[1]); - inst = jump_to_epilog(gen, inst, block); - } - [lreg] -> { - if($1 != X86_EAX) - { - x86_mov_reg_reg(inst, X86_EAX, $1, 4); - x86_mov_reg_reg(inst, X86_EDX, %1, 4); - } +JIT_OP_RETURN_LONG: unary_note + [lreg("eax":"edx")] -> { inst = jump_to_epilog(gen, inst, block); } @@ -1381,11 +1369,100 @@ JIT_OP_RETURN_NFLOAT: unary_note, stack, only inst = jump_to_epilog(gen, inst, block); } -JIT_OP_RETURN_SMALL_STRUCT: unary_branch - [reg] -> { - inst = load_small_struct - (inst, X86_EAX, X86_EDX, $1, 0, - jit_value_get_nint_constant(insn->value2), 0); +JIT_OP_RETURN_SMALL_STRUCT: binary_note + [reg, imm] -> { + switch($2) + { + case 1: + x86_widen_membase(inst, X86_EAX, $1, 0, 0, 0); + break; + + case 2: + x86_widen_membase(inst, X86_EAX, $1, 0, 0, 1); + break; + + case 3: + if(X86_EAX == $1) + { + x86_widen_membase(inst, X86_EDX, $1, 0, 0, 1); + x86_widen_membase(inst, X86_EAX, $1, 2, 0, 0); + } + else + { + x86_widen_membase(inst, X86_EAX, $1, 2, 0, 0); + x86_widen_membase(inst, X86_EDX, $1, 0, 0, 1); + } + x86_shift_reg_imm(inst, X86_SHL, X86_EAX, 16); + x86_alu_reg_reg(inst, X86_OR, X86_EAX, X86_EDX); + break; + + case 4: + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + break; + + case 5: + if(X86_EAX == $1) + { + x86_widen_membase(inst, X86_EDX, $1, 4, 0, 0); + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + } + else + { + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + x86_widen_membase(inst, X86_EDX, $1, 4, 0, 0); + } + break; + + case 6: + if(X86_EAX == $1) + { + x86_widen_membase(inst, X86_EDX, $1, 4, 0, 1); + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + } + else + { + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + x86_widen_membase(inst, X86_EDX, $1, 4, 0, 1); + } + break; + + case 7: + if(X86_EAX == $1) + { + x86_widen_membase(inst, X86_ECX, $1, 4, 0, 1); + x86_widen_membase(inst, X86_EDX, $1, 6, 0, 0); + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + } + else if(X86_ECX == $1) + { + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + x86_widen_membase(inst, X86_EDX, $1, 6, 0, 0); + x86_widen_membase(inst, X86_ECX, $1, 4, 0, 1); + } + else + { + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + x86_widen_membase(inst, X86_ECX, $1, 4, 0, 1); + x86_widen_membase(inst, X86_EDX, $1, 6, 0, 0); + } + x86_shift_reg_imm(inst, X86_SHL, X86_EDX, 16); + x86_alu_reg_reg(inst, X86_OR, X86_EDX, X86_ECX); + break; + + case 8: + if(X86_EAX == $1) + { + x86_mov_reg_membase(inst, X86_EDX, $1, 4, 4); + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + } + else + { + x86_mov_reg_membase(inst, X86_EAX, $1, 0, 4); + x86_mov_reg_membase(inst, X86_EDX, $1, 4, 4); + } + break; + } + inst = jump_to_epilog(gen, inst, block); } diff --git a/jit/jit-rules.h b/jit/jit-rules.h index d3050e1..b3eb32f 100644 --- a/jit/jit-rules.h +++ b/jit/jit-rules.h @@ -70,10 +70,8 @@ typedef struct #define JIT_REG_STACK_PTR (1 << 6) /* Contains CPU stack pointer */ #define JIT_REG_FIXED (1 << 7) /* Fixed use; not for allocation */ #define JIT_REG_CALL_USED (1 << 8) /* Destroyed by a call */ -#define JIT_REG_START_STACK (1 << 9) /* Stack of stack-like allocation */ -#define JIT_REG_END_STACK (1 << 10) /* End of stack-like allocation */ -#define JIT_REG_IN_STACK (1 << 11) /* Middle of stack-like allocation */ -#define JIT_REG_GLOBAL (1 << 12) /* Candidate for global allocation */ +#define JIT_REG_IN_STACK (1 << 9) /* Middle of stack-like allocation */ +#define JIT_REG_GLOBAL (1 << 10) /* Candidate for global allocation */ #define JIT_REG_ALL (JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT32 | \ JIT_REG_FLOAT64 | JIT_REG_NFLOAT) @@ -120,7 +118,10 @@ struct jit_regcontents { /* List of values that are currently stored in this register */ jit_value_t values[JIT_MAX_REG_VALUES]; - short num_values; + int num_values; + + /* Current age of this register. Older registers are reclaimed first */ + int age; /* Flag that indicates if this register is holding the first word of a double-word long value (32-bit platforms only) */ @@ -130,15 +131,9 @@ struct jit_regcontents word of a double-word long value (32-bit platforms only) */ char is_long_end; - /* Current age of this register. Older registers are reclaimed first */ - int age; - - /* Remapped version of this register, when used in a stack */ - short remap; - /* Flag that indicates if the register holds a valid value, but there are no actual "jit_value_t" objects associated */ - short used_for_temp; + char used_for_temp; }; /* @@ -152,13 +147,15 @@ struct jit_gencode jit_regused_t inhibit; /* Temporarily inhibited registers */ jit_cache_posn posn; /* Current cache output position */ jit_regcontents_t contents[JIT_NUM_REGS]; /* Contents of each register */ - int current_age;/* Current age value for registers */ - int stack_map[JIT_NUM_REGS]; /* Reverse stack mappings */ + int current_age; /* Current age value for registers */ +#ifdef JIT_REG_STACK + int reg_stack_top; /* Current register stack top */ +#endif #ifdef jit_extra_gen_state jit_extra_gen_state; /* CPU-specific extra information */ #endif - void *epilog_fixup; /* Fixup list for function epilogs */ - int stack_changed; /* Stack top changed since entry */ + void *epilog_fixup; /* Fixup list for function epilogs */ + int stack_changed; /* Stack top changed since entry */ }; /*