/* Compute register class preferences for pseudo-registers.
- Copyright (C) 1987, 1988, 1991, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 91, 92, 93, 94, 1996 Free Software Foundation, Inc.
This file is part of GNU CC.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
/* This file contains two passes of the compiler: reg_scan and reg_class.
#include "recog.h"
#include "reload.h"
#include "real.h"
+#include "bytecode.h"
#ifndef REGISTER_MOVE_COST
#define REGISTER_MOVE_COST(x, y) 2
#ifndef MEMORY_MOVE_COST
#define MEMORY_MOVE_COST(x) 4
#endif
+
+/* If we have auto-increment or auto-decrement and we can have secondary
+ reloads, we are not allowed to use classes requiring secondary
+ reloads for pseudos auto-incremented since reload can't handle it. */
+
+#ifdef AUTO_INC_DEC
+#if defined(SECONDARY_INPUT_RELOAD_CLASS) || defined(SECONDARY_OUTPUT_RELOAD_CLASS)
+#define FORBIDDEN_INC_DEC_CLASSES
+#endif
+#endif
\f
/* Register tables used by many passes. */
HARD_REG_SET call_used_reg_set;
+/* HARD_REG_SET of registers we want to avoid caller saving. */
+HARD_REG_SET losing_caller_save_reg_set;
+
/* Data for initializing the above. */
static char initial_call_used_regs[] = CALL_USED_REGISTERS;
/* For each reg class, a HARD_REG_SET saying which registers are in it. */
-HARD_REG_SET reg_class_contents[] = REG_CLASS_CONTENTS;
+HARD_REG_SET reg_class_contents[N_REG_CLASSES];
+
+/* The same information, but as an array of unsigned ints. We copy from
+ these unsigned ints to the table above. We do this so the tm.h files
+ do not have to be aware of the wordsize for machines with <= 64 regs. */
+
+#define N_REG_INTS \
+ ((FIRST_PSEUDO_REGISTER + (HOST_BITS_PER_INT - 1)) / HOST_BITS_PER_INT)
+
+static unsigned int_reg_class_contents[N_REG_CLASSES][N_REG_INTS]
+ = REG_CLASS_CONTENTS;
/* For each reg class, number of regs it contains. */
char *reg_names[] = REGISTER_NAMES;
+/* For each hard register, the widest mode object that it can contain.
+ This will be a MODE_INT mode if the register can hold integers. Otherwise
+ it will be a MODE_FLOAT or a MODE_CC mode, whichever is valid for the
+ register. */
+
+enum machine_mode reg_raw_mode[FIRST_PSEUDO_REGISTER];
+
/* Indexed by n, gives number of times (REG n) is set or clobbered.
This information remains valid for the rest of the compilation
of the current function; it is used to control register allocation.
static int may_move_cost[N_REG_CLASSES][N_REG_CLASSES];
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+
+/* These are the classes that regs which are auto-incremented or decremented
+ cannot be put in. */
+
+static int forbidden_inc_dec_class[N_REG_CLASSES];
+
+/* Indexed by n, is non-zero if (REG n) is used in an auto-inc or auto-dec
+ context. */
+
+static char *in_inc_dec;
+
+#endif /* FORBIDDEN_INC_DEC_CLASSES */
+
/* Function called only once to initialize the above data on reg usage.
Once this is done, various switches may override. */
{
register int i, j;
+ /* First copy the register information from the initial int form into
+ the regsets. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ CLEAR_HARD_REG_SET (reg_class_contents[i]);
+
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (int_reg_class_contents[i][j / HOST_BITS_PER_INT]
+ & ((unsigned) 1 << (j % HOST_BITS_PER_INT)))
+ SET_HARD_REG_BIT (reg_class_contents[i], j);
+ }
+
bcopy (initial_fixed_regs, fixed_regs, sizeof fixed_regs);
bcopy (initial_call_used_regs, call_used_regs, sizeof call_used_regs);
bzero (global_regs, sizeof global_regs);
/* Compute number of hard regs in each class. */
- bzero (reg_class_size, sizeof reg_class_size);
+ bzero ((char *) reg_class_size, sizeof reg_class_size);
for (i = 0; i < N_REG_CLASSES; i++)
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
if (TEST_HARD_REG_BIT (reg_class_contents[i], j))
/* After switches have been processed, which perhaps alter
`fixed_regs' and `call_used_regs', convert them to HARD_REG_SETs. */
-void
+static void
init_reg_sets_1 ()
{
register int i;
CONDITIONAL_REGISTER_USAGE;
#endif
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (global_regs[i])
- {
- if (call_used_regs[i] && ! fixed_regs[i])
- warning ("call-clobbered register used for global register variable");
- fixed_regs[i] = 1;
- /* Prevent saving/restoring of this reg. */
- call_used_regs[i] = 1;
- }
-
/* Initialize "constant" tables. */
CLEAR_HARD_REG_SET (fixed_reg_set);
CLEAR_HARD_REG_SET (call_fixed_reg_set);
bcopy (fixed_regs, call_fixed_regs, sizeof call_fixed_regs);
-#ifdef STRUCT_VALUE_REGNUM
- call_fixed_regs[STRUCT_VALUE_REGNUM] = 1;
-#endif
-#ifdef STATIC_CHAIN_REGNUM
- call_fixed_regs[STATIC_CHAIN_REGNUM] = 1;
-#endif
n_non_fixed_regs = 0;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
- if (FUNCTION_VALUE_REGNO_P (i))
- call_fixed_regs[i] = 1;
if (fixed_regs[i])
SET_HARD_REG_BIT (fixed_reg_set, i);
else
SET_HARD_REG_BIT (call_used_reg_set, i);
if (call_fixed_regs[i])
SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (i)))
+ SET_HARD_REG_BIT (losing_caller_save_reg_set, i);
+ }
+}
+
+/* Compute the table of register modes.
+ These values are used to record death information for individual registers
+ (as opposed to a multi-register mode). */
+
+static void
+init_reg_modes ()
+{
+ register int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ reg_raw_mode[i] = choose_hard_reg_mode (i, 1);
+
+ /* If we couldn't find a valid mode, fall back to `word_mode'.
+ ??? We assume `word_mode' has already been initialized.
+ ??? One situation in which we need to do this is on the mips where
+ HARD_REGNO_NREGS (fpreg, [SD]Fmode) returns 2. Ideally we'd like
+ to use DF mode for the even registers and VOIDmode for the odd
+ (for the cpu models where the odd ones are inaccessible). */
+ if (reg_raw_mode[i] == VOIDmode)
+ reg_raw_mode[i] = word_mode;
}
}
+/* Finish initializing the register sets and
+ initialize the register modes. */
+
+void
+init_regs ()
+{
+ /* This finishes what was started by init_reg_sets, but couldn't be done
+ until after register usage was specified. */
+ if (!output_bytecode)
+ init_reg_sets_1 ();
+
+ init_reg_modes ();
+}
+
+/* Return a machine mode that is legitimate for hard reg REGNO and large
+ enough to save nregs. If we can't find one, return VOIDmode. */
+
+enum machine_mode
+choose_hard_reg_mode (regno, nregs)
+ int regno;
+ int nregs;
+{
+ enum machine_mode found_mode = VOIDmode, mode;
+
+ /* We first look for the largest integer mode that can be validly
+ held in REGNO. If none, we look for the largest floating-point mode.
+ If we still didn't find a valid mode, try CCmode. */
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_NREGS (regno, mode) == nregs
+ && HARD_REGNO_MODE_OK (regno, mode))
+ found_mode = mode;
+
+ if (found_mode != VOIDmode)
+ return found_mode;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_NREGS (regno, mode) == nregs
+ && HARD_REGNO_MODE_OK (regno, mode))
+ found_mode = mode;
+
+ if (found_mode != VOIDmode)
+ return found_mode;
+
+ if (HARD_REGNO_NREGS (regno, CCmode) == nregs
+ && HARD_REGNO_MODE_OK (regno, CCmode))
+ return CCmode;
+
+ /* We can't find a mode valid for this register. */
+ return VOIDmode;
+}
+
/* Specify the usage characteristics of the register named NAME.
It should be a fixed register if FIXED and a
call-used register if CALL_USED. */
{
int i;
+ if (output_bytecode)
+ {
+ warning ("request to mark `%s' as %s ignored by bytecode compiler",
+ name, call_used ? "call-used" : "fixed");
+ return;
+ }
+
/* Decode the name and update the primary form of
the register info. */
warning ("unknown register name: %s", name);
}
}
+
+/* Mark register number I as global. */
+
+void
+globalize_reg (i)
+ int i;
+{
+ if (global_regs[i])
+ {
+ warning ("register used for two global register variables");
+ return;
+ }
+
+ if (call_used_regs[i] && ! fixed_regs[i])
+ warning ("call-clobbered register used for global register variable");
+
+ global_regs[i] = 1;
+
+ /* If already fixed, nothing else to do. */
+ if (fixed_regs[i])
+ return;
+
+ fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 1;
+ n_non_fixed_regs--;
+
+ SET_HARD_REG_BIT (fixed_reg_set, i);
+ SET_HARD_REG_BIT (call_used_reg_set, i);
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+}
\f
/* Now the data and code for the `regclass' pass, which happens
just before local-alloc. */
static int loop_cost;
-static int copy_cost ();
-static void record_reg_classes ();
-static void record_address_regs ();
-
+static void record_reg_classes PROTO((int, int, rtx *, enum machine_mode *,
+ char **, rtx));
+static int copy_cost PROTO((rtx, enum machine_mode,
+ enum reg_class, int));
+static void record_address_regs PROTO((rtx, enum reg_class, int));
+static auto_inc_dec_reg_p PROTO((rtx, enum machine_mode));
+static void reg_scan_mark_refs PROTO((rtx, rtx, int));
/* Return the reg_class in which pseudo reg number REGNO is best allocated.
This function is sometimes called before the info has been computed.
init_recog ();
+ costs = (struct costs *) alloca (nregs * sizeof (struct costs));
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+
+ in_inc_dec = (char *) alloca (nregs);
+
+ /* Initialize information about which register classes can be used for
+ pseudos that are auto-incremented or auto-decremented. It would
+ seem better to put this in init_reg_sets, but we need to be able
+ to allocate rtx, which we can't do that early. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ rtx r = gen_rtx (REG, VOIDmode, 0);
+ enum machine_mode m;
+
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[i], j))
+ {
+ REGNO (r) = j;
+
+ for (m = VOIDmode; (int) m < (int) MAX_MACHINE_MODE;
+ m = (enum machine_mode) ((int) m + 1))
+ if (HARD_REGNO_MODE_OK (j, m))
+ {
+ PUT_MODE (r, m);
+
+ /* If a register is not directly suitable for an
+ auto-increment or decrement addressing mode and
+ requires secondary reloads, disallow its class from
+ being used in such addresses. */
+
+ if ((0
+#ifdef SECONDARY_RELOAD_CLASS
+ || (SECONDARY_RELOAD_CLASS (BASE_REG_CLASS, m, r)
+ != NO_REGS)
+#else
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ || (SECONDARY_INPUT_RELOAD_CLASS (BASE_REG_CLASS, m, r)
+ != NO_REGS)
+#endif
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ || (SECONDARY_OUTPUT_RELOAD_CLASS (BASE_REG_CLASS, m, r)
+ != NO_REGS)
+#endif
+#endif
+ )
+ && ! auto_inc_dec_reg_p (r, m))
+ forbidden_inc_dec_class[i] = 1;
+ }
+ }
+ }
+#endif /* FORBIDDEN_INC_DEC_CLASSES */
+
init_cost.mem_cost = 10000;
for (i = 0; i < N_REG_CLASSES; i++)
init_cost.cost[i] = 10000;
{
/* Zero out our accumulation of the cost of each class for each reg. */
- costs = (struct costs *) alloca (nregs * sizeof (struct costs));
- bzero (costs, nregs * sizeof (struct costs));
+ bzero ((char *) costs, nregs * sizeof (struct costs));
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ bzero (in_inc_dec, nregs);
+#endif
loop_depth = 0, loop_cost = 1;
int noperands;
/* Show that an insn inside a loop is likely to be executed three
- times more than insns outside a loop. This is much more agressive
+ times more than insns outside a loop. This is much more aggressive
than the assumptions made elsewhere and is being tried as an
experiment. */
{
decode_asm_operands (PATTERN (insn), recog_operand, NULL_PTR,
constraints, modes);
- nalternatives = n_occurrences (',', constraints[0]) + 1;
+ nalternatives = (noperands == 0 ? 0
+ : n_occurrences (',', constraints[0]) + 1);
}
else
{
basic_block_head[b] = newinsn;
}
- /* This makes one more setting of new insns's dest. */
+ /* This makes one more setting of new insns's dest. */
reg_n_sets[REGNO (recog_operand[0])]++;
*recog_operand_loc[1] = recog_operand[0];
continue;
}
- /* If this is setting a pseudo from another pseudo or the
- sum of a pseudo and a constant integer and the other
- pseudo is known to be a pointer, set the destination to
- be a pointer as well.
-
- Likewise if it is setting the destination from an address
- or from a value equivalent to an address or to the sum of
- an address and something else.
-
- But don't do any of this if the pseudo corresponds to
- a user variable since it should have already been set
- as a pointer based on the type.
-
- There is no point in doing this during our second
- pass since not enough should have changed. */
-
- if (pass == 0 && set != 0 && GET_CODE (SET_DEST (set)) == REG
- && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
- && ! REG_USERVAR_P (SET_DEST (set))
- && ! REGNO_POINTER_FLAG (REGNO (SET_DEST (set)))
- && ((GET_CODE (SET_SRC (set)) == REG
- && REGNO_POINTER_FLAG (REGNO (SET_SRC (set))))
- || ((GET_CODE (SET_SRC (set)) == PLUS
- || GET_CODE (SET_SRC (set)) == LO_SUM)
- && (GET_CODE (XEXP (SET_SRC (set), 1))
- == CONST_INT)
- && GET_CODE (XEXP (SET_SRC (set), 0)) == REG
- && REGNO_POINTER_FLAG (REGNO (XEXP (SET_SRC (set), 0))))
- || GET_CODE (SET_SRC (set)) == CONST
- || GET_CODE (SET_SRC (set)) == SYMBOL_REF
- || GET_CODE (SET_SRC (set)) == LABEL_REF
- || (GET_CODE (SET_SRC (set)) == HIGH
- && (GET_CODE (XEXP (SET_SRC (set), 0)) == CONST
- || (GET_CODE (XEXP (SET_SRC (set), 0))
- == SYMBOL_REF)
- || (GET_CODE (XEXP (SET_SRC (set), 0))
- == LABEL_REF)))
- || ((GET_CODE (SET_SRC (set)) == PLUS
- || GET_CODE (SET_SRC (set)) == LO_SUM)
- && (GET_CODE (XEXP (SET_SRC (set), 1)) == CONST
- || (GET_CODE (XEXP (SET_SRC (set), 1))
- == SYMBOL_REF)
- || (GET_CODE (XEXP (SET_SRC (set), 1))
- == LABEL_REF)))
- || ((note = find_reg_note (insn, REG_EQUAL, 0)) != 0
- && (GET_CODE (XEXP (note, 0)) == CONST
- || GET_CODE (XEXP (note, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (note, 0)) == LABEL_REF))))
- REGNO_POINTER_FLAG (REGNO (SET_DEST (set))) = 1;
-
for (i = 0; i < noperands; i++)
{
constraints[i]
}
/* Check for commutative in a separate loop so everything will
- have been initialized. Don't bother doing anything if the
- second operand is a constant since that is the case
- for which the constraints should have been written. */
+ have been initialized. We must do this even if one operand
+ is a constant--see addsi3 in m68k.md. */
- for (i = 0; i < noperands; i++)
- if (constraints[i][0] == '%'
- && ! CONSTANT_P (recog_operand[i+1]))
+ for (i = 0; i < noperands - 1; i++)
+ if (constraints[i][0] == '%')
{
char *xconstraints[MAX_RECOG_OPERANDS];
int j;
for (class = (int) ALL_REGS - 1; class > 0; class--)
{
- /* Ignore classes that are too small for this operand. */
+ /* Ignore classes that are too small for this operand or
+ invalid for a operand that was auto-incremented. */
if (CLASS_MAX_NREGS (class, PSEUDO_REGNO_MODE (i))
- > reg_class_size[class])
+ > reg_class_size[class]
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ || (in_inc_dec[i] && forbidden_inc_dec_class[class])
+#endif
+ )
;
else if (p->cost[class] < best_cost)
{
if (pass == 1 || ! flag_expensive_optimizations)
for (class = 0; class < N_REG_CLASSES; class++)
if (p->cost[class] < p->mem_cost
- && (reg_class_size[reg_class_subunion[(int) alt][class]]
- > reg_class_size[(int) alt]))
+ && (reg_class_size[(int) reg_class_subunion[(int) alt][class]]
+ > reg_class_size[(int) alt])
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ && ! (in_inc_dec[i] && forbidden_inc_dec_class[class])
+#endif
+ )
alt = reg_class_subunion[(int) alt][class];
/* If we don't add any classes, nothing to try. */
if (alt == best)
- alt = (int) NO_REGS;
+ alt = NO_REGS;
/* We cast to (int) because (char) hits bugs in some compilers. */
prefclass[i] = (int) best;
int alt;
enum op_type {OP_READ, OP_WRITE, OP_READ_WRITE} op_types[MAX_RECOG_OPERANDS];
int i, j;
+ rtx set;
/* By default, each operand is an input operand. */
continue;
}
+ if (*p == '%')
+ p++;
+
/* If this alternative is only relevant when this operand
matches a previous operand, we do different things depending
on whether this operand is a pseudo-reg or not. */
if (GET_CODE (op) != REG || REGNO (op) < FIRST_PSEUDO_REGISTER)
{
/* If this matches the other operand, we have no added
- cost. */
+ cost and we win. */
if (rtx_equal_p (ops[j], op))
- ;
+ win = 1;
+ /* If we can put the other operand into a register, add to
+ the cost of this alternative the cost to copy this
+ operand to the register used for the other operand. */
+
+ else if (classes[j] != NO_REGS)
+ alt_cost += copy_cost (op, mode, classes[j], 1), win = 1;
+ }
+ else if (GET_CODE (ops[j]) != REG
+ || REGNO (ops[j]) < FIRST_PSEUDO_REGISTER)
+ {
+ /* This op is a pseudo but the one it matches is not. */
+
/* If we can't put the other operand into a register, this
alternative can't be used. */
- else if (classes[j] == NO_REGS)
+ if (classes[j] == NO_REGS)
alt_fail = 1;
/* Otherwise, add to the cost of this alternative the cost
- to copy this operand to the register used for the other
+ to copy the other operand to the register used for this
operand. */
else
- alt_cost += copy_cost (op, mode, classes[j], 1);
+ alt_cost += copy_cost (ops[j], mode, classes[j], 1);
}
-
else
{
/* The costs of this operand are the same as that of the
instruction. */
this_op_costs[i] = this_op_costs[j];
- if (! find_reg_note (insn, REG_DEAD, op))
+ if (REGNO (ops[i]) != REGNO (ops[j])
+ && ! find_reg_note (insn, REG_DEAD, op))
alt_cost += 2;
- }
- continue;
+ /* This is in place of ordinary cost computation
+ for this operand, so skip to the end of the
+ alternative (should be just one character). */
+ while (*p && *p++ != ',')
+ ;
+
+ constraints[i] = p;
+ continue;
+ }
}
/* Scan all the constraint letters. See if the operand matches
break;
case 'm': case 'o': case 'V':
- /* It doesn't seem worth distingishing between offsettable
+ /* It doesn't seem worth distinguishing between offsettable
and non-offsettable addresses here. */
allows_mem = 1;
if (GET_CODE (op) == MEM)
break;
case 'E':
+#ifndef REAL_ARITHMETIC
/* Match any floating double constant, but only if
we can examine the bits of it reliably. */
if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
|| HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
&& GET_MODE (op) != VOIDmode && ! flag_pretend_float)
break;
+#endif
if (GET_CODE (op) == CONST_DOUBLE)
win = 1;
break;
else if (win
|| (GET_CODE (op) == REG
- && reg_fits_class_p (op, classes[i], 0, mode)))
+ && reg_fits_class_p (op, classes[i], 0, GET_MODE (op))))
;
/* If registers are valid, the cost of this alternative includes
(qq->cost[class] + alt_cost) * scale);
}
}
+
+ /* If this insn is a single set copying operand 1 to operand 0
+ and one is a pseudo with the other a hard reg that is in its
+ own register class, set the cost of that register class to -1. */
+
+ if ((set = single_set (insn)) != 0
+ && ops[0] == SET_DEST (set) && ops[1] == SET_SRC (set)
+ && GET_CODE (ops[0]) == REG && GET_CODE (ops[1]) == REG)
+ for (i = 0; i <= 1; i++)
+ if (REGNO (ops[i]) >= FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (ops[!i]);
+ enum machine_mode mode = GET_MODE (ops[!i]);
+ int class;
+ int nr;
+
+ if (regno >= FIRST_PSEUDO_REGISTER && prefclass != 0
+ && (reg_class_size[prefclass[regno]]
+ == CLASS_MAX_NREGS (prefclass[regno], mode)))
+ op_costs[i].cost[prefclass[regno]] = -1;
+ else if (regno < FIRST_PSEUDO_REGISTER)
+ for (class = 0; class < N_REG_CLASSES; class++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[class], regno)
+ && reg_class_size[class] == CLASS_MAX_NREGS (class, mode))
+ {
+ if (reg_class_size[class] == 1)
+ op_costs[i].cost[class] = -1;
+ else
+ {
+ for (nr = 0; nr < HARD_REGNO_NREGS(regno, mode); nr++)
+ {
+ if (!TEST_HARD_REG_BIT (reg_class_contents[class], regno + nr))
+ break;
+ }
+
+ if (nr == HARD_REGNO_NREGS(regno,mode))
+ op_costs[i].cost[class] = -1;
+ }
+ }
+ }
}
\f
/* Compute the cost of loading X into (if TO_P is non-zero) or from (if
secondary_class = SECONDARY_INPUT_RELOAD_CLASS (class, mode, x);
#endif
-#ifdef SECONARY_OUTPUT_RELOAD_CLASS
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
if (! to_p)
secondary_class = SECONDARY_OUTPUT_RELOAD_CLASS (class, mode, x);
#endif
if (secondary_class != NO_REGS)
return (move_cost[(int) secondary_class][(int) class]
+ copy_cost (x, mode, secondary_class, 2));
-#endif /* HAVE_SECONARY_RELOADS */
+#endif /* HAVE_SECONDARY_RELOADS */
/* For memory, use the memory move cost, for (hard) registers, use the
cost to move between the register classes, and use 2 for everything
case PRE_DEC:
/* Double the importance of a pseudo register that is incremented
or decremented, since it would take two extra insns
- if it ends up in the wrong place. */
+ if it ends up in the wrong place. If the operand is a pseudo,
+ show it is being used in an INC_DEC context. */
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ if (GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER)
+ in_inc_dec[REGNO (XEXP (x, 0))] = 1;
+#endif
record_address_regs (XEXP (x, 0), class, 2 * scale);
break;
}
}
}
+\f
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+
+/* Return 1 if REG is valid as an auto-increment memory reference
+ to an object of MODE. */
+
+static
+auto_inc_dec_reg_p (reg, mode)
+ rtx reg;
+ enum machine_mode mode;
+{
+#ifdef HAVE_POST_INCREMENT
+ if (memory_address_p (mode, gen_rtx (POST_INC, Pmode, reg)))
+ return 1;
+#endif
+
+#ifdef HAVE_POST_DECREMENT
+ if (memory_address_p (mode, gen_rtx (POST_DEC, Pmode, reg)))
+ return 1;
+#endif
+
+#ifdef HAVE_PRE_INCREMENT
+ if (memory_address_p (mode, gen_rtx (PRE_INC, Pmode, reg)))
+ return 1;
+#endif
+
+#ifdef HAVE_PRE_DECREMENT
+ if (memory_address_p (mode, gen_rtx (PRE_DEC, Pmode, reg)))
+ return 1;
+#endif
+
+ return 0;
+}
+#endif
+
#endif /* REGISTER_CONSTRAINTS */
\f
/* This is the `regscan' pass of the compiler, run just before cse
int *regno_last_uid;
+/* Indexed by pseudo register number, gives uid of last insn using the reg
+ or mentioning it in a note (as of the time reg_scan is called). */
+
+int *regno_last_note_uid;
+
/* Record the number of registers we used when we allocated the above two
tables. If we are called again with more than this, we must re-allocate
the tables. */
static int highest_regno_in_uid_map;
/* Maximum number of parallel sets and clobbers in any insn in this fn.
- Always at least 3, since the combiner could put that many togetherm
+ Always at least 3, since the combiner could put that many together
and we want this to remain correct for all the remaining passes. */
int max_parallel;
-void reg_scan_mark_refs ();
-
void
reg_scan (f, nregs, repeat)
rtx f;
= (int *) oballoc (highest_regno_in_uid_map * sizeof (int));
regno_last_uid
= (int *) oballoc (highest_regno_in_uid_map * sizeof (int));
+ regno_last_note_uid
+ = (int *) oballoc (highest_regno_in_uid_map * sizeof (int));
reg_n_sets
= (short *) oballoc (highest_regno_in_uid_map * sizeof (short));
}
- bzero (regno_first_uid, highest_regno_in_uid_map * sizeof (int));
- bzero (regno_last_uid, highest_regno_in_uid_map * sizeof (int));
- bzero (reg_n_sets, highest_regno_in_uid_map * sizeof (short));
+ bzero ((char *) regno_first_uid, highest_regno_in_uid_map * sizeof (int));
+ bzero ((char *) regno_last_uid, highest_regno_in_uid_map * sizeof (int));
+ bzero ((char *) regno_last_note_uid,
+ highest_regno_in_uid_map * sizeof (int));
+ bzero ((char *) reg_n_sets, highest_regno_in_uid_map * sizeof (short));
max_parallel = 3;
if (GET_CODE (PATTERN (insn)) == PARALLEL
&& XVECLEN (PATTERN (insn), 0) > max_parallel)
max_parallel = XVECLEN (PATTERN (insn), 0);
- reg_scan_mark_refs (PATTERN (insn), INSN_UID (insn));
+ reg_scan_mark_refs (PATTERN (insn), insn, 0);
+
+ if (REG_NOTES (insn))
+ reg_scan_mark_refs (REG_NOTES (insn), insn, 1);
}
}
-void
-reg_scan_mark_refs (x, uid)
+/* X is the expression to scan. INSN is the insn it appears in.
+ NOTE_FLAG is nonzero if X is from INSN's notes rather than its body. */
+
+static void
+reg_scan_mark_refs (x, insn, note_flag)
rtx x;
- int uid;
+ rtx insn;
+ int note_flag;
{
register enum rtx_code code = GET_CODE (x);
register rtx dest;
+ register rtx note;
switch (code)
{
{
register int regno = REGNO (x);
- regno_last_uid[regno] = uid;
+ regno_last_note_uid[regno] = INSN_UID (insn);
+ if (!note_flag)
+ regno_last_uid[regno] = INSN_UID (insn);
if (regno_first_uid[regno] == 0)
- regno_first_uid[regno] = uid;
+ regno_first_uid[regno] = INSN_UID (insn);
}
break;
+ case EXPR_LIST:
+ if (XEXP (x, 0))
+ reg_scan_mark_refs (XEXP (x, 0), insn, note_flag);
+ if (XEXP (x, 1))
+ reg_scan_mark_refs (XEXP (x, 1), insn, note_flag);
+ break;
+
+ case INSN_LIST:
+ if (XEXP (x, 1))
+ reg_scan_mark_refs (XEXP (x, 1), insn, note_flag);
+ break;
+
case SET:
/* Count a set of the destination if it is a register. */
for (dest = SET_DEST (x);
if (GET_CODE (dest) == REG)
reg_n_sets[REGNO (dest)]++;
- /* ... fall through ... */
+ /* If this is setting a pseudo from another pseudo or the sum of a
+ pseudo and a constant integer and the other pseudo is known to be
+ a pointer, set the destination to be a pointer as well.
+
+ Likewise if it is setting the destination from an address or from a
+ value equivalent to an address or to the sum of an address and
+ something else.
+
+ But don't do any of this if the pseudo corresponds to a user
+ variable since it should have already been set as a pointer based
+ on the type. */
+
+ if (GET_CODE (SET_DEST (x)) == REG
+ && REGNO (SET_DEST (x)) >= FIRST_PSEUDO_REGISTER
+ && ! REG_USERVAR_P (SET_DEST (x))
+ && ! REGNO_POINTER_FLAG (REGNO (SET_DEST (x)))
+ && ((GET_CODE (SET_SRC (x)) == REG
+ && REGNO_POINTER_FLAG (REGNO (SET_SRC (x))))
+ || ((GET_CODE (SET_SRC (x)) == PLUS
+ || GET_CODE (SET_SRC (x)) == LO_SUM)
+ && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+ && GET_CODE (XEXP (SET_SRC (x), 0)) == REG
+ && REGNO_POINTER_FLAG (REGNO (XEXP (SET_SRC (x), 0))))
+ || GET_CODE (SET_SRC (x)) == CONST
+ || GET_CODE (SET_SRC (x)) == SYMBOL_REF
+ || GET_CODE (SET_SRC (x)) == LABEL_REF
+ || (GET_CODE (SET_SRC (x)) == HIGH
+ && (GET_CODE (XEXP (SET_SRC (x), 0)) == CONST
+ || GET_CODE (XEXP (SET_SRC (x), 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 0)) == LABEL_REF))
+ || ((GET_CODE (SET_SRC (x)) == PLUS
+ || GET_CODE (SET_SRC (x)) == LO_SUM)
+ && (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == SYMBOL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF))
+ || ((note = find_reg_note (insn, REG_EQUAL, 0)) != 0
+ && (GET_CODE (XEXP (note, 0)) == CONST
+ || GET_CODE (XEXP (note, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (note, 0)) == LABEL_REF))))
+ REGNO_POINTER_FLAG (REGNO (SET_DEST (x))) = 1;
+
+ /* ... fall through ... */
default:
{
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- reg_scan_mark_refs (XEXP (x, i), uid);
+ reg_scan_mark_refs (XEXP (x, i), insn, note_flag);
else if (fmt[i] == 'E' && XVEC (x, i) != 0)
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- reg_scan_mark_refs (XVECEXP (x, i, j), uid);
+ reg_scan_mark_refs (XVECEXP (x, i, j), insn, note_flag);
}
}
}