mirror of https://github.com/ademakov/libjit
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1284 lines
34 KiB
1284 lines
34 KiB
/*
|
|
* jit-reg-alloc.c - Register allocation routines for the JIT.
|
|
*
|
|
* Copyright (C) 2004 Southern Storm Software, Pty Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "jit-internal.h"
|
|
#include "jit-reg-alloc.h"
|
|
#include <jit/jit-dump.h>
|
|
#include <stdio.h>
|
|
|
|
/*@
|
|
|
|
The @code{libjit} library provides a number of functions for
|
|
performing register allocation within basic blocks so that you
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*@
|
|
* @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;
|
|
#else
|
|
/* We don't register pairs on 64-bit platforms or the interpreter */
|
|
return 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.
|
|
*/
|
|
/*#define JIT_REG_DEBUG 1*/
|
|
#ifdef JIT_REG_DEBUG
|
|
static void dump_regs(jit_gencode_t gen, const char *name)
|
|
{
|
|
int reg;
|
|
unsigned int index;
|
|
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)
|
|
{
|
|
continue;
|
|
}
|
|
printf("\t%s: ", _jit_reg_info[reg].name);
|
|
if(gen->contents[reg].num_values > 0)
|
|
{
|
|
for(index = 0; index < gen->contents[reg].num_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);
|
|
}
|
|
if(gen->contents[reg].used_for_temp)
|
|
{
|
|
printf(", used_for_temp");
|
|
}
|
|
}
|
|
else if(gen->contents[reg].used_for_temp)
|
|
{
|
|
printf("used_for_temp");
|
|
}
|
|
else
|
|
{
|
|
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.
|
|
*/
|
|
static void spill_all_between(jit_gencode_t gen, int first, int last)
|
|
{
|
|
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)
|
|
{
|
|
/* 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)
|
|
{
|
|
first_stack_reg = reg;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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->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;
|
|
}
|
|
}
|
|
|
|
/* Free the register */
|
|
_jit_regs_free_reg(gen, real_reg, value_used);
|
|
}
|
|
|
|
#ifdef JIT_REG_DEBUG
|
|
dump_regs(gen, "leave spill_all_between");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Spill a specific register. If it is in a stack, then all registers
|
|
* above the specific register must also be spilled.
|
|
*/
|
|
static void spill_register(jit_gencode_t gen, int reg)
|
|
{
|
|
if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
|
|
{
|
|
spill_all_between(gen, reg, reg);
|
|
}
|
|
else
|
|
{
|
|
int first_reg;
|
|
reg = gen->contents[reg].remap;
|
|
first_reg = reg;
|
|
while((_jit_reg_info[first_reg].flags & JIT_REG_START_STACK) == 0)
|
|
{
|
|
--first_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);
|
|
}
|
|
|
|
/*@
|
|
* @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)
|
|
{
|
|
spill_all_between(gen, 0, JIT_NUM_REGS - 1);
|
|
}
|
|
|
|
/*
|
|
* Free a register within a stack, and renumber the other stack registers
|
|
* to compensate for the change.
|
|
*/
|
|
static void free_stack_reg(jit_gencode_t gen, int reg)
|
|
{
|
|
int remap;
|
|
|
|
#ifdef JIT_REG_DEBUG
|
|
dump_regs(gen, "enter free_stack_reg");
|
|
#endif
|
|
|
|
/* 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)
|
|
{
|
|
++remap;
|
|
for(;;)
|
|
{
|
|
if(gen->stack_map[remap] == -1)
|
|
{
|
|
/* There are no more active values in this stack */
|
|
gen->stack_map[remap - 1] = -1;
|
|
break;
|
|
}
|
|
else if((_jit_reg_info[remap].flags & JIT_REG_END_STACK) != 0)
|
|
{
|
|
/* 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;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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(gen->contents[reg].num_values == 0 &&
|
|
!(gen->contents[reg].used_for_temp))
|
|
{
|
|
break;
|
|
}
|
|
if((_jit_reg_info[reg].flags & JIT_REG_END_STACK) != 0)
|
|
{
|
|
/* 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;
|
|
}
|
|
++reg;
|
|
}
|
|
|
|
/* Roll the stack remappings down to make room at the top */
|
|
if(roll_down)
|
|
{
|
|
temp_reg = first_stack_reg - 1;
|
|
do
|
|
{
|
|
++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);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Free a register, and optionally spill its value.
|
|
*/
|
|
static void free_reg_and_spill
|
|
(jit_gencode_t gen, int reg, int value_used, int spill)
|
|
{
|
|
int other_reg, posn;
|
|
jit_value_t value;
|
|
|
|
#ifdef JIT_REG_DEBUG
|
|
dump_regs(gen, "enter free_reg_and_spill");
|
|
#endif
|
|
|
|
/* 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
|
|
{
|
|
other_reg = -1;
|
|
}
|
|
|
|
/* Spill the register's contents to the local variable frame */
|
|
if(spill && gen->contents[reg].num_values > 0)
|
|
{
|
|
for(posn = gen->contents[reg].num_values - 1; posn >= 0; --posn)
|
|
{
|
|
value = gen->contents[reg].values[posn];
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
free_stack_reg(gen, reg);
|
|
}
|
|
if(other_reg != -1 && gen->contents[other_reg].remap != -1)
|
|
{
|
|
free_stack_reg(gen, other_reg);
|
|
}
|
|
|
|
#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);
|
|
}
|
|
|
|
/*@
|
|
* @deftypefun void _jit_regs_want_reg (jit_gencode_t gen, int reg)
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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)
|
|
{
|
|
int other_reg;
|
|
if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
|
|
{
|
|
/* Spill an ordinary register and its pair register */
|
|
free_reg_and_spill(gen, reg, 0, 1);
|
|
if(for_long)
|
|
{
|
|
other_reg = _jit_reg_info[reg].other_reg;
|
|
if(other_reg != -1)
|
|
{
|
|
free_reg_and_spill(gen, other_reg, 0, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
other_reg = -1;
|
|
}
|
|
|
|
/* Mark the register as touched and return it */
|
|
jit_reg_set_used(gen->touched, reg);
|
|
if(other_reg != -1)
|
|
{
|
|
jit_reg_set_used(gen->touched, other_reg);
|
|
}
|
|
return reg;
|
|
}
|
|
else
|
|
{
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
/*@
|
|
* @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)
|
|
{
|
|
free_reg_and_spill(gen, reg, value_used, 0);
|
|
}
|
|
|
|
/*@
|
|
* @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))
|
|
{
|
|
other_reg = _jit_reg_info[reg].other_reg;
|
|
}
|
|
else
|
|
{
|
|
other_reg = -1;
|
|
}
|
|
|
|
/* Mark the register as touched */
|
|
jit_reg_set_used(gen->touched, reg);
|
|
if(other_reg != -1)
|
|
{
|
|
jit_reg_set_used(gen->touched, other_reg);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
gen->contents[reg].is_long_start = 0;
|
|
gen->contents[reg].is_long_end = 0;
|
|
gen->contents[reg].used_for_temp = 0;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
(gen->current_age)++;
|
|
|
|
/* Set the stack mappings for this register */
|
|
if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 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
|
|
|
|
/* Adjust the value to reflect that it is in "reg", and maybe the frame */
|
|
value->in_register = 1;
|
|
value->in_frame = still_in_frame;
|
|
value->reg = (short)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)
|
|
{
|
|
/* Eject any values that are currently in the register */
|
|
reg = _jit_regs_want_reg(gen, reg, _jit_regs_needs_long_pair(value->type));
|
|
|
|
/* Record that the value is in "reg", but not in the frame */
|
|
_jit_regs_set_value(gen, reg, value, 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)
|
|
{
|
|
/* TODO */
|
|
}
|
|
|
|
/*@
|
|
* @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)
|
|
{
|
|
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)
|
|
{
|
|
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))
|
|
{
|
|
return 0;
|
|
}
|
|
reg = value2->reg;
|
|
if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
|
|
{
|
|
reg = value1->reg;
|
|
return ((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0);
|
|
}
|
|
remap = gen->contents[reg].remap;
|
|
if(remap == -1 || (_jit_reg_info[remap].flags & JIT_REG_START_STACK) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
reg = value1->reg;
|
|
if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0)
|
|
{
|
|
return 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)
|
|
{
|
|
/* 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);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Mark the register as containing the value we have loaded */
|
|
_jit_regs_set_value(gen, reg, value, value->in_frame);
|
|
}
|
|
}
|
|
|
|
/*@
|
|
* @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, type;
|
|
int suitable_reg, need_pair;
|
|
int suitable_age;
|
|
|
|
/* 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)
|
|
{
|
|
if(gen->contents[reg].num_values == 1 &&
|
|
(value->in_frame || !used_again))
|
|
{
|
|
/* 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)
|
|
{
|
|
other_reg = _jit_reg_info[reg].other_reg;
|
|
gen->contents[other_reg].used_for_temp = 1;
|
|
gen->contents[other_reg].age = gen->current_age;
|
|
}
|
|
++(gen->current_age);
|
|
return reg;
|
|
}
|
|
else
|
|
{
|
|
/* We need to spill the register and then reload it */
|
|
spill_register(gen, reg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gen->contents[reg].age = gen->current_age;
|
|
if(need_pair)
|
|
{
|
|
other_reg = _jit_reg_info[reg].other_reg;
|
|
gen->contents[other_reg].age = gen->current_age;
|
|
}
|
|
++(gen->current_age);
|
|
return reg;
|
|
}
|
|
}
|
|
|
|
/* Determine the type of register that we need */
|
|
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:
|
|
{
|
|
type = JIT_REG_WORD;
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_LONG:
|
|
case JIT_TYPE_ULONG:
|
|
{
|
|
if(need_pair)
|
|
type = JIT_REG_LONG;
|
|
else
|
|
type = JIT_REG_WORD;
|
|
}
|
|
break;
|
|
|
|
case JIT_TYPE_FLOAT32:
|
|
case JIT_TYPE_FLOAT64:
|
|
case JIT_TYPE_NFLOAT:
|
|
{
|
|
type = JIT_REG_FLOAT;
|
|
}
|
|
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((_jit_reg_info[reg].flags & type) != 0 &&
|
|
!jit_reg_is_used(gen->permanent, reg))
|
|
{
|
|
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);
|
|
load_value(gen, reg, -1, value, destroy);
|
|
return reg;
|
|
}
|
|
else if(!need_pair)
|
|
{
|
|
if(gen->contents[reg].num_values == 0 &&
|
|
!(gen->contents[reg].used_for_temp))
|
|
{
|
|
load_value(gen, reg, -1, value, destroy);
|
|
return reg;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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))
|
|
{
|
|
load_value(gen, reg, other_reg, value, destroy);
|
|
return reg;
|
|
}
|
|
}
|
|
if(suitable_reg == -1 || gen->contents[reg].age < suitable_age)
|
|
{
|
|
/* This is the oldest suitable register of this type */
|
|
suitable_reg = reg;
|
|
suitable_age = gen->contents[reg].age;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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, reg, need_pair);
|
|
|
|
/* Load the value into the register */
|
|
if(!need_pair)
|
|
{
|
|
load_value(gen, reg, -1, value, destroy);
|
|
}
|
|
else
|
|
{
|
|
load_value(gen, reg, _jit_reg_info[reg].other_reg, value, destroy);
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
/*
|
|
* Determine if "num" stack registers are free in a specific stack.
|
|
*/
|
|
static int stack_regs_free(jit_gencode_t gen, int reg, int num)
|
|
{
|
|
int first_reg;
|
|
|
|
/* 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)
|
|
{
|
|
++reg;
|
|
}
|
|
|
|
/* Search for free registers */
|
|
while(reg >= first_reg)
|
|
{
|
|
if(gen->contents[reg].num_values == 0 &&
|
|
!(gen->contents[reg].used_for_temp))
|
|
{
|
|
--num;
|
|
if(num <= 0)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
--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)
|
|
{
|
|
if(value->in_frame || !used_again)
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
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))
|
|
{
|
|
if((value->in_frame || !used_again1) &&
|
|
(value2->in_frame || !used_again2))
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*@
|
|
* @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)
|
|
{
|
|
int reg, reg2, reg3;
|
|
|
|
/* Determine if the values are already in the top three registers */
|
|
if(value->in_register && value2->in_register && value3->in_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 || !used_again1) &&
|
|
(value2->in_frame || !used_again2) &&
|
|
(value3->in_frame || !used_again3))
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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(gen->contents[type_reg].num_values > 0 ||
|
|
gen->contents[type_reg].used_for_temp)
|
|
{
|
|
++count;
|
|
}
|
|
if((_jit_reg_info[type_reg].flags & JIT_REG_END_STACK) == 0)
|
|
{
|
|
break;
|
|
}
|
|
++type_reg;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*@
|
|
* @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)
|
|
{
|
|
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->reg = reg;
|
|
gen->contents[reg].values[0] = value;
|
|
gen->contents[reg].num_values = 1;
|
|
|
|
/* Return the allocated register to the caller */
|
|
return reg;
|
|
}
|
|
|