/* Allocate registers within a basic block, for GNU compiler.
- Copyright (C) 1987, 88, 91, 93-97, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
But this is currently disabled since tying in global_alloc is not
yet implemented. */
-/* Pseudos allocated here cannot be reallocated by global.c if the hard
- register is used as a spill register. So we don't allocate such pseudos
+/* Pseudos allocated here can be reallocated by global.c if the hard register
+ is used as a spill register. Currently we don't allocate such pseudos
here if their preferred class is likely to be used by spills. */
#include "config.h"
#include "system.h"
#include "rtl.h"
+#include "tm_p.h"
#include "flags.h"
#include "basic-block.h"
#include "regs.h"
+#include "function.h"
#include "hard-reg-set.h"
#include "insn-config.h"
+#include "insn-attr.h"
#include "recog.h"
#include "output.h"
+#include "toplev.h"
\f
/* Next quantity number available for allocation. */
static int next_qty;
-/* In all the following vectors indexed by quantity number. */
+/* Information we maitain about each quantity. */
+struct qty
+{
+ /* The number of refs to quantity Q. */
-/* Element Q is the hard reg number chosen for quantity Q,
- or -1 if none was found. */
+ int n_refs;
-static short *qty_phys_reg;
+ /* Insn number (counting from head of basic block)
+ where quantity Q was born. -1 if birth has not been recorded. */
-/* We maintain two hard register sets that indicate suggested hard registers
- for each quantity. The first, qty_phys_copy_sugg, contains hard registers
- that are tied to the quantity by a simple copy. The second contains all
- hard registers that are tied to the quantity via an arithmetic operation.
+ int birth;
- The former register set is given priority for allocation. This tends to
- eliminate copy insns. */
+ /* Insn number (counting from head of basic block)
+ where given quantity died. Due to the way tying is done,
+ and the fact that we consider in this pass only regs that die but once,
+ a quantity can die only once. Each quantity's life span
+ is a set of consecutive insns. -1 if death has not been recorded. */
-/* Element Q is a set of hard registers that are suggested for quantity Q by
- copy insns. */
+ int death;
-static HARD_REG_SET *qty_phys_copy_sugg;
+ /* Number of words needed to hold the data in given quantity.
+ This depends on its machine mode. It is used for these purposes:
+ 1. It is used in computing the relative importances of qtys,
+ which determines the order in which we look for regs for them.
+ 2. It is used in rules that prevent tying several registers of
+ different sizes in a way that is geometrically impossible
+ (see combine_regs). */
-/* Element Q is a set of hard registers that are suggested for quantity Q by
- arithmetic insns. */
+ int size;
-static HARD_REG_SET *qty_phys_sugg;
+ /* Number of times a reg tied to given qty lives across a CALL_INSN. */
-/* Element Q is the number of suggested registers in qty_phys_copy_sugg. */
+ int n_calls_crossed;
-static short *qty_phys_num_copy_sugg;
+ /* The register number of one pseudo register whose reg_qty value is Q.
+ This register should be the head of the chain
+ maintained in reg_next_in_qty. */
-/* Element Q is the number of suggested registers in qty_phys_sugg. */
+ int first_reg;
-static short *qty_phys_num_sugg;
+ /* Reg class contained in (smaller than) the preferred classes of all
+ the pseudo regs that are tied in given quantity.
+ This is the preferred class for allocating that quantity. */
-/* Element Q is the number of refs to quantity Q. */
+ enum reg_class min_class;
-static int *qty_n_refs;
+ /* Register class within which we allocate given qty if we can't get
+ its preferred class. */
-/* Element Q is a reg class contained in (smaller than) the
- preferred classes of all the pseudo regs that are tied in quantity Q.
- This is the preferred class for allocating that quantity. */
+ enum reg_class alternate_class;
-static enum reg_class *qty_min_class;
+ /* This holds the mode of the registers that are tied to given qty,
+ or VOIDmode if registers with differing modes are tied together. */
-/* Insn number (counting from head of basic block)
- where quantity Q was born. -1 if birth has not been recorded. */
+ enum machine_mode mode;
-static int *qty_birth;
+ /* the hard reg number chosen for given quantity,
+ or -1 if none was found. */
-/* Insn number (counting from head of basic block)
- where quantity Q died. Due to the way tying is done,
- and the fact that we consider in this pass only regs that die but once,
- a quantity can die only once. Each quantity's life span
- is a set of consecutive insns. -1 if death has not been recorded. */
+ short phys_reg;
-static int *qty_death;
+ /* Nonzero if this quantity has been used in a SUBREG that changes
+ its size. */
-/* Number of words needed to hold the data in quantity Q.
- This depends on its machine mode. It is used for these purposes:
- 1. It is used in computing the relative importances of qtys,
- which determines the order in which we look for regs for them.
- 2. It is used in rules that prevent tying several registers of
- different sizes in a way that is geometrically impossible
- (see combine_regs). */
+ char changes_size;
-static int *qty_size;
+};
-/* This holds the mode of the registers that are tied to qty Q,
- or VOIDmode if registers with differing modes are tied together. */
+static struct qty *qty;
-static enum machine_mode *qty_mode;
+/* These fields are kept separately to speedup their clearing. */
-/* Number of times a reg tied to qty Q lives across a CALL_INSN. */
+/* We maintain two hard register sets that indicate suggested hard registers
+ for each quantity. The first, phys_copy_sugg, contains hard registers
+ that are tied to the quantity by a simple copy. The second contains all
+ hard registers that are tied to the quantity via an arithmetic operation.
-static int *qty_n_calls_crossed;
+ The former register set is given priority for allocation. This tends to
+ eliminate copy insns. */
-/* Register class within which we allocate qty Q if we can't get
- its preferred class. */
+/* Element Q is a set of hard registers that are suggested for quantity Q by
+ copy insns. */
-static enum reg_class *qty_alternate_class;
+static HARD_REG_SET *qty_phys_copy_sugg;
+
+/* Element Q is a set of hard registers that are suggested for quantity Q by
+ arithmetic insns. */
-/* Element Q is the SCRATCH expression for which this quantity is being
- allocated or 0 if this quantity is allocating registers. */
+static HARD_REG_SET *qty_phys_sugg;
-static rtx *qty_scratch_rtx;
+/* Element Q is the number of suggested registers in qty_phys_copy_sugg. */
-/* Element Q is nonzero if this quantity has been used in a SUBREG
- that changes its size. */
+static short *qty_phys_num_copy_sugg;
-static char *qty_changes_size;
+/* Element Q is the number of suggested registers in qty_phys_sugg. */
-/* Element Q is the register number of one pseudo register whose
- reg_qty value is Q, or -1 is this quantity is for a SCRATCH. This
- register should be the head of the chain maintained in reg_next_in_qty. */
+static short *qty_phys_num_sugg;
-static int *qty_first_reg;
/* If (REG N) has been assigned a quantity number, is a register number
of another register assigned the same quantity number, or -1 for the
- end of the chain. qty_first_reg point to the head of this chain. */
+ end of the chain. qty->first_reg point to the head of this chain. */
static int *reg_next_in_qty;
static HARD_REG_SET *regs_live_at;
-int *scratch_block;
-rtx *scratch_list;
-int scratch_list_length;
-static int scratch_index;
-
/* Communicate local vars `insn_number' and `insn'
from `block_alloc' to `reg_is_set', `wipe_dead_reg', and `alloc_qty'. */
static int this_insn_number;
static rtx *reg_equiv_replacement;
-static void alloc_qty PROTO((int, enum machine_mode, int, int));
-static void alloc_qty_for_scratch PROTO((rtx, int, rtx, int, int));
-static void validate_equiv_mem_from_store PROTO((rtx, rtx));
-static int validate_equiv_mem PROTO((rtx, rtx, rtx));
-static int contains_replace_regs PROTO((rtx, char *));
-static int memref_referenced_p PROTO((rtx, rtx));
-static int memref_used_between_p PROTO((rtx, rtx, rtx));
-static void update_equiv_regs PROTO((void));
-static void block_alloc PROTO((int));
-static int qty_sugg_compare PROTO((int, int));
-static int qty_sugg_compare_1 PROTO((const GENERIC_PTR, const GENERIC_PTR));
-static int qty_compare PROTO((int, int));
-static int qty_compare_1 PROTO((const GENERIC_PTR, const GENERIC_PTR));
-static int combine_regs PROTO((rtx, rtx, int, int, rtx, int));
-static int reg_meets_class_p PROTO((int, enum reg_class));
-static int reg_classes_overlap_p PROTO((enum reg_class, enum reg_class,
- int));
-static void update_qty_class PROTO((int, int));
-static void reg_is_set PROTO((rtx, rtx));
-static void reg_is_born PROTO((rtx, int));
-static void wipe_dead_reg PROTO((rtx, int));
-static int find_free_reg PROTO((enum reg_class, enum machine_mode,
+/* Used for communication between update_equiv_regs and no_equiv. */
+static rtx *reg_equiv_init_insns;
+
+/* Nonzero if we recorded an equivalence for a LABEL_REF. */
+static int recorded_label_ref;
+
+static void alloc_qty PARAMS ((int, enum machine_mode, int, int));
+static void validate_equiv_mem_from_store PARAMS ((rtx, rtx, void *));
+static int validate_equiv_mem PARAMS ((rtx, rtx, rtx));
+static int contains_replace_regs PARAMS ((rtx, char *));
+static int memref_referenced_p PARAMS ((rtx, rtx));
+static int memref_used_between_p PARAMS ((rtx, rtx, rtx));
+static void update_equiv_regs PARAMS ((void));
+static void no_equiv PARAMS ((rtx, rtx, void *));
+static void block_alloc PARAMS ((int));
+static int qty_sugg_compare PARAMS ((int, int));
+static int qty_sugg_compare_1 PARAMS ((const PTR, const PTR));
+static int qty_compare PARAMS ((int, int));
+static int qty_compare_1 PARAMS ((const PTR, const PTR));
+static int combine_regs PARAMS ((rtx, rtx, int, int, rtx, int));
+static int reg_meets_class_p PARAMS ((int, enum reg_class));
+static void update_qty_class PARAMS ((int, int));
+static void reg_is_set PARAMS ((rtx, rtx, void *));
+static void reg_is_born PARAMS ((rtx, int));
+static void wipe_dead_reg PARAMS ((rtx, int));
+static int find_free_reg PARAMS ((enum reg_class, enum machine_mode,
int, int, int, int, int));
-static void mark_life PROTO((int, enum machine_mode, int));
-static void post_mark_life PROTO((int, enum machine_mode, int, int, int));
-static int no_conflict_p PROTO((rtx, rtx, rtx));
-static int requires_inout PROTO((char *));
+static void mark_life PARAMS ((int, enum machine_mode, int));
+static void post_mark_life PARAMS ((int, enum machine_mode, int, int, int));
+static int no_conflict_p PARAMS ((rtx, rtx, rtx));
+static int requires_inout PARAMS ((const char *));
\f
/* Allocate a new quantity (new within current basic block)
for register number REGNO which is born at index BIRTH
enum machine_mode mode;
int size, birth;
{
- register int qty = next_qty++;
+ register int qtyno = next_qty++;
- reg_qty[regno] = qty;
+ reg_qty[regno] = qtyno;
reg_offset[regno] = 0;
reg_next_in_qty[regno] = -1;
- qty_first_reg[qty] = regno;
- qty_size[qty] = size;
- qty_mode[qty] = mode;
- qty_birth[qty] = birth;
- qty_n_calls_crossed[qty] = REG_N_CALLS_CROSSED (regno);
- qty_min_class[qty] = reg_preferred_class (regno);
- qty_alternate_class[qty] = reg_alternate_class (regno);
- qty_n_refs[qty] = REG_N_REFS (regno);
- qty_changes_size[qty] = REG_CHANGES_SIZE (regno);
-}
-\f
-/* Similar to `alloc_qty', but allocates a quantity for a SCRATCH rtx
- used as operand N in INSN. We assume here that the SCRATCH is used in
- a CLOBBER. */
-
-static void
-alloc_qty_for_scratch (scratch, n, insn, insn_code_num, insn_number)
- rtx scratch;
- int n;
- rtx insn;
- int insn_code_num, insn_number;
-{
- register int qty;
- enum reg_class class;
- char *p, c;
- int i;
-
-#ifdef REGISTER_CONSTRAINTS
- /* If we haven't yet computed which alternative will be used, do so now.
- Then set P to the constraints for that alternative. */
- if (which_alternative == -1)
- if (! constrain_operands (insn_code_num, 0))
- return;
-
- for (p = insn_operand_constraint[insn_code_num][n], i = 0;
- *p && i < which_alternative; p++)
- if (*p == ',')
- i++;
-
- /* Compute the class required for this SCRATCH. If we don't need a
- register, the class will remain NO_REGS. If we guessed the alternative
- number incorrectly, reload will fix things up for us. */
-
- class = NO_REGS;
- while ((c = *p++) != '\0' && c != ',')
- switch (c)
- {
- case '=': case '+': case '?':
- case '#': case '&': case '!':
- case '*': case '%':
- case '0': case '1': case '2': case '3': case '4':
- case 'm': case '<': case '>': case 'V': case 'o':
- case 'E': case 'F': case 'G': case 'H':
- case 's': case 'i': case 'n':
- case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P':
-#ifdef EXTRA_CONSTRAINT
- case 'Q': case 'R': case 'S': case 'T': case 'U':
-#endif
- case 'p':
- /* These don't say anything we care about. */
- break;
-
- case 'X':
- /* We don't need to allocate this SCRATCH. */
- return;
-
- case 'g': case 'r':
- class = reg_class_subunion[(int) class][(int) GENERAL_REGS];
- break;
-
- default:
- class
- = reg_class_subunion[(int) class][(int) REG_CLASS_FROM_LETTER (c)];
- break;
- }
-
- if (class == NO_REGS)
- return;
-
-#else /* REGISTER_CONSTRAINTS */
-
- class = GENERAL_REGS;
-#endif
-
-
- qty = next_qty++;
-
- qty_first_reg[qty] = -1;
- qty_scratch_rtx[qty] = scratch;
- qty_size[qty] = GET_MODE_SIZE (GET_MODE (scratch));
- qty_mode[qty] = GET_MODE (scratch);
- qty_birth[qty] = 2 * insn_number - 1;
- qty_death[qty] = 2 * insn_number + 1;
- qty_n_calls_crossed[qty] = 0;
- qty_min_class[qty] = class;
- qty_alternate_class[qty] = NO_REGS;
- qty_n_refs[qty] = 1;
- qty_changes_size[qty] = 0;
+ qty[qtyno].first_reg = regno;
+ qty[qtyno].size = size;
+ qty[qtyno].mode = mode;
+ qty[qtyno].birth = birth;
+ qty[qtyno].n_calls_crossed = REG_N_CALLS_CROSSED (regno);
+ qty[qtyno].min_class = reg_preferred_class (regno);
+ qty[qtyno].alternate_class = reg_alternate_class (regno);
+ qty[qtyno].n_refs = REG_N_REFS (regno);
+ qty[qtyno].changes_size = REG_CHANGES_SIZE (regno);
}
\f
/* Main entry point of this file. */
-void
+int
local_alloc ()
{
register int b, i;
int max_qty;
+ /* We need to keep track of whether or not we recorded a LABEL_REF so
+ that we know if the jump optimizer needs to be rerun. */
+ recorded_label_ref = 0;
+
/* Leaf functions and non-leaf functions have different needs.
If defined, let the machine say what kind of ordering we
should use. */
update_equiv_regs ();
/* This sets the maximum number of quantities we can have. Quantity
- numbers start at zero and we can have one for each pseudo plus the
- number of SCRATCHes in the largest block, in the worst case. */
- max_qty = (max_regno - FIRST_PSEUDO_REGISTER) + max_scratch;
+ numbers start at zero and we can have one for each pseudo. */
+ max_qty = (max_regno - FIRST_PSEUDO_REGISTER);
/* Allocate vectors of temporary data.
See the declarations of these variables, above,
for what they mean. */
- /* There can be up to MAX_SCRATCH * N_BASIC_BLOCKS SCRATCHes to allocate.
- Instead of allocating this much memory from now until the end of
- reload, only allocate space for MAX_QTY SCRATCHes. If there are more
- reload will allocate them. */
-
- scratch_list_length = max_qty;
- scratch_list = (rtx *) xmalloc (scratch_list_length * sizeof (rtx));
- bzero ((char *) scratch_list, scratch_list_length * sizeof (rtx));
- scratch_block = (int *) xmalloc (scratch_list_length * sizeof (int));
- bzero ((char *) scratch_block, scratch_list_length * sizeof (int));
- scratch_index = 0;
-
- qty_phys_reg = (short *) alloca (max_qty * sizeof (short));
+ qty = (struct qty *) xmalloc (max_qty * sizeof (struct qty));
qty_phys_copy_sugg
- = (HARD_REG_SET *) alloca (max_qty * sizeof (HARD_REG_SET));
- qty_phys_num_copy_sugg = (short *) alloca (max_qty * sizeof (short));
- qty_phys_sugg = (HARD_REG_SET *) alloca (max_qty * sizeof (HARD_REG_SET));
- qty_phys_num_sugg = (short *) alloca (max_qty * sizeof (short));
- qty_birth = (int *) alloca (max_qty * sizeof (int));
- qty_death = (int *) alloca (max_qty * sizeof (int));
- qty_scratch_rtx = (rtx *) alloca (max_qty * sizeof (rtx));
- qty_first_reg = (int *) alloca (max_qty * sizeof (int));
- qty_size = (int *) alloca (max_qty * sizeof (int));
- qty_mode
- = (enum machine_mode *) alloca (max_qty * sizeof (enum machine_mode));
- qty_n_calls_crossed = (int *) alloca (max_qty * sizeof (int));
- qty_min_class
- = (enum reg_class *) alloca (max_qty * sizeof (enum reg_class));
- qty_alternate_class
- = (enum reg_class *) alloca (max_qty * sizeof (enum reg_class));
- qty_n_refs = (int *) alloca (max_qty * sizeof (int));
- qty_changes_size = (char *) alloca (max_qty * sizeof (char));
-
- reg_qty = (int *) alloca (max_regno * sizeof (int));
- reg_offset = (char *) alloca (max_regno * sizeof (char));
- reg_next_in_qty = (int *) alloca (max_regno * sizeof (int));
+ = (HARD_REG_SET *) xmalloc (max_qty * sizeof (HARD_REG_SET));
+ qty_phys_num_copy_sugg = (short *) xmalloc (max_qty * sizeof (short));
+ qty_phys_sugg = (HARD_REG_SET *) xmalloc (max_qty * sizeof (HARD_REG_SET));
+ qty_phys_num_sugg = (short *) xmalloc (max_qty * sizeof (short));
+
+ reg_qty = (int *) xmalloc (max_regno * sizeof (int));
+ reg_offset = (char *) xmalloc (max_regno * sizeof (char));
+ reg_next_in_qty = (int *) xmalloc(max_regno * sizeof (int));
/* Allocate the reg_renumber array */
allocate_reg_info (max_regno, FALSE, TRUE);
{
for (i = 0; i < next_qty; i++)
{
- qty_scratch_rtx[i] = 0;
CLEAR_HARD_REG_SET (qty_phys_copy_sugg[i]);
qty_phys_num_copy_sugg[i] = 0;
CLEAR_HARD_REG_SET (qty_phys_sugg[i]);
#define CLEAR(vector) \
bzero ((char *) (vector), (sizeof (*(vector))) * next_qty);
- CLEAR (qty_scratch_rtx);
CLEAR (qty_phys_copy_sugg);
CLEAR (qty_phys_num_copy_sugg);
CLEAR (qty_phys_sugg);
next_qty = 0;
block_alloc (b);
-#ifdef USE_C_ALLOCA
- alloca (0);
-#endif
}
+
+ free (qty);
+ free (qty_phys_copy_sugg);
+ free (qty_phys_num_copy_sugg);
+ free (qty_phys_sugg);
+ free (qty_phys_num_sugg);
+
+ free (reg_qty);
+ free (reg_offset);
+ free (reg_next_in_qty);
+
+ return recorded_label_ref;
}
\f
/* Depth of loops we are in while in update_equiv_regs. */
Called via note_stores. */
static void
-validate_equiv_mem_from_store (dest, set)
+validate_equiv_mem_from_store (dest, set, data)
rtx dest;
- rtx set;
+ rtx set ATTRIBUTE_UNUSED;
+ void *data ATTRIBUTE_UNUSED;
{
if ((GET_CODE (dest) == REG
&& reg_overlap_mentioned_p (dest, equiv_mem))
&& ! CONST_CALL_P (insn))
return 0;
- note_stores (PATTERN (insn), validate_equiv_mem_from_store);
+ note_stores (PATTERN (insn), validate_equiv_mem_from_store, NULL);
/* If a register mentioned in MEMREF is modified via an
auto-increment, we lose the equivalence. Do the same if one
char *reg_equiv_replace;
{
int i, j;
- char *fmt;
+ const char *fmt;
enum rtx_code code = GET_CODE (x);
switch (code)
rtx memref;
{
int i, j;
- char *fmt;
+ const char *fmt;
enum rtx_code code = GET_CODE (x);
switch (code)
return 0;
}
\f
+/* Return nonzero if the rtx X is invariant over the current function. */
+int
+function_invariant_p (x)
+ rtx x;
+{
+ if (CONSTANT_P (x))
+ return 1;
+ if (x == frame_pointer_rtx || x == arg_pointer_rtx)
+ return 1;
+ if (GET_CODE (x) == PLUS
+ && (XEXP (x, 0) == frame_pointer_rtx || XEXP (x, 0) == arg_pointer_rtx)
+ && CONSTANT_P (XEXP (x, 1)))
+ return 1;
+ return 0;
+}
+
/* Find registers that are equivalent to a single value throughout the
compilation (either because they can be referenced in memory or are set once
from a single constant). Lower their priority for a register.
static void
update_equiv_regs ()
{
- rtx *reg_equiv_init_insn = (rtx *) alloca (max_regno * sizeof (rtx *));
/* Set when an attempt should be made to replace a register with the
associated reg_equiv_replacement entry at the end of this function. */
- char *reg_equiv_replace
- = (char *) alloca (max_regno * sizeof *reg_equiv_replace);
+ char *reg_equiv_replace;
rtx insn;
int block, depth;
- reg_equiv_replacement = (rtx *) alloca (max_regno * sizeof (rtx *));
-
- bzero ((char *) reg_equiv_init_insn, max_regno * sizeof (rtx *));
- bzero ((char *) reg_equiv_replacement, max_regno * sizeof (rtx *));
- bzero ((char *) reg_equiv_replace, max_regno * sizeof *reg_equiv_replace);
+ reg_equiv_replace = (char *) xcalloc (max_regno, sizeof *reg_equiv_replace);
+ reg_equiv_init_insns = (rtx *) xcalloc (max_regno, sizeof (rtx));
+ reg_equiv_replacement = (rtx *) xcalloc (max_regno, sizeof (rtx));
init_alias_analysis ();
- loop_depth = 1;
+ loop_depth = 0;
/* Scan the insns and find which registers have equivalences. Do this
in a separate scan of the insns because (due to -fcse-follow-jumps)
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
rtx note;
- rtx set = single_set (insn);
+ rtx set;
rtx dest, src;
int regno;
loop_depth--;
}
- /* If this insn contains more (or less) than a single SET, ignore it. */
- if (set == 0)
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_INC)
+ no_equiv (XEXP (note, 0), note, NULL);
+
+ set = single_set (insn);
+
+ /* If this insn contains more (or less) than a single SET,
+ only mark all destinations as having no known equivalence. */
+ if (set == 0)
+ {
+ note_stores (PATTERN (insn), no_equiv, NULL);
+ continue;
+ }
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ int i;
+
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ rtx part = XVECEXP (PATTERN (insn), 0, i);
+ if (part != set)
+ note_stores (part, no_equiv, NULL);
+ }
+ }
+
dest = SET_DEST (set);
src = SET_SRC (set);
If one of the regs in the address is marked as reg_equiv_replace,
then we can't add this REG_EQUIV note. The reg_equiv_replace
optimization may move the set of this register immediately before
- insn, which puts it after reg_equiv_init_insn[regno], and hence
+ insn, which puts it after reg_equiv_init_insns[regno], and hence
the mention in the REG_EQUIV note would be to an uninitialized
pseudo. */
-
- if (GET_CODE (dest) == MEM && GET_CODE (SET_SRC (set)) == REG
- && (regno = REGNO (SET_SRC (set))) >= FIRST_PSEUDO_REGISTER
+ /* ????? This test isn't good enough; we might see a MEM with a use of
+ a pseudo register before we see its setting insn that will cause
+ reg_equiv_replace for that pseudo to be set.
+ Equivalences to MEMs should be made in another pass, after the
+ reg_equiv_replace information has been gathered. */
+
+ if (GET_CODE (dest) == MEM && GET_CODE (src) == REG
+ && (regno = REGNO (src)) >= FIRST_PSEUDO_REGISTER
&& REG_BASIC_BLOCK (regno) >= 0
- && reg_equiv_init_insn[regno] != 0
- && ! find_reg_note (insn, REG_EQUIV, NULL_RTX)
- && ! contains_replace_regs (XEXP (dest, 0), reg_equiv_replace)
- && validate_equiv_mem (reg_equiv_init_insn[regno], SET_SRC (set),
- dest)
- && ! memref_used_between_p (SET_DEST (set),
- reg_equiv_init_insn[regno], insn))
- REG_NOTES (reg_equiv_init_insn[regno])
- = gen_rtx_EXPR_LIST (REG_EQUIV, dest,
- REG_NOTES (reg_equiv_init_insn[regno]));
+ && REG_N_SETS (regno) == 1
+ && reg_equiv_init_insns[regno] != 0
+ && reg_equiv_init_insns[regno] != const0_rtx
+ && ! find_reg_note (XEXP (reg_equiv_init_insns[regno], 0),
+ REG_EQUIV, NULL_RTX)
+ && ! contains_replace_regs (XEXP (dest, 0), reg_equiv_replace))
+ {
+ rtx init_insn = XEXP (reg_equiv_init_insns[regno], 0);
+ if (validate_equiv_mem (init_insn, src, dest)
+ && ! memref_used_between_p (dest, init_insn, insn))
+ REG_NOTES (init_insn)
+ = gen_rtx_EXPR_LIST (REG_EQUIV, dest, REG_NOTES (init_insn));
+ }
/* We only handle the case of a pseudo register being set
- once and only if neither the source nor the destination are
- in a register class that's likely to be spilled. */
+ once, or always to the same value. */
+ /* ??? The mn10200 port breaks if we add equivalences for
+ values that need an ADDRESS_REGS register and set them equivalent
+ to a MEM of a pseudo. The actual problem is in the over-conservative
+ handling of INPADDR_ADDRESS / INPUT_ADDRESS / INPUT triples in
+ calculate_needs, but we traditionally work around this problem
+ here by rejecting equivalences when the destination is in a register
+ that's likely spilled. This is fragile, of course, since the
+ preferred class of a pseudo depends on all instructions that set
+ or use it. */
+
if (GET_CODE (dest) != REG
|| (regno = REGNO (dest)) < FIRST_PSEUDO_REGISTER
- || REG_N_SETS (regno) != 1
- || CLASS_LIKELY_SPILLED_P (reg_preferred_class (REGNO (dest)))
- || (GET_CODE (src) == REG
- && REGNO (src) >= FIRST_PSEUDO_REGISTER
- && CLASS_LIKELY_SPILLED_P (reg_preferred_class (REGNO (src)))))
- continue;
+ || reg_equiv_init_insns[regno] == const0_rtx
+ || (CLASS_LIKELY_SPILLED_P (reg_preferred_class (regno))
+ && GET_CODE (src) == MEM))
+ {
+ /* This might be seting a SUBREG of a pseudo, a pseudo that is
+ also set somewhere else to a constant. */
+ note_stores (set, no_equiv, NULL);
+ continue;
+ }
+ /* Don't handle the equivalence if the source is in a register
+ class that's likely to be spilled. */
+ if (GET_CODE (src) == REG
+ && REGNO (src) >= FIRST_PSEUDO_REGISTER
+ && CLASS_LIKELY_SPILLED_P (reg_preferred_class (REGNO (src))))
+ {
+ no_equiv (dest, set, NULL);
+ continue;
+ }
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
-#ifdef DONT_RECORD_EQUIVALENCE
- /* Allow the target to reject promotions of some REG_EQUAL notes to
- REG_EQUIV notes.
-
- In some cases this can improve register allocation if the existence
- of the REG_EQUIV note is likely to increase the lifetime of a register
- that is likely to be spilled.
-
- It may also be necessary if the target can't handle certain constant
- expressions appearing randomly in insns, but for whatever reason
- those expressions must be considered legitimate constant expressions
- to prevent them from being forced into memory. */
- if (note && DONT_RECORD_EQUIVALENCE (note))
- note = NULL;
-#endif
-
+ if (REG_N_SETS (regno) != 1
+ && (! note
+ || ! function_invariant_p (XEXP (note, 0))
+ || (reg_equiv_replacement[regno]
+ && ! rtx_equal_p (XEXP (note, 0),
+ reg_equiv_replacement[regno]))))
+ {
+ no_equiv (dest, set, NULL);
+ continue;
+ }
/* Record this insn as initializing this register. */
- reg_equiv_init_insn[regno] = insn;
+ reg_equiv_init_insns[regno]
+ = gen_rtx_INSN_LIST (VOIDmode, insn, reg_equiv_init_insns[regno]);
/* If this register is known to be equal to a constant, record that
it is always equivalent to the constant. */
- if (note && CONSTANT_P (XEXP (note, 0)))
+ if (note && function_invariant_p (XEXP (note, 0)))
PUT_MODE (note, (enum machine_mode) REG_EQUIV);
/* If this insn introduces a "constant" register, decrease the priority
{
int regno = REGNO (dest);
+ /* Record whether or not we created a REG_EQUIV note for a LABEL_REF.
+ We might end up substituting the LABEL_REF for uses of the
+ pseudo here or later. That kind of transformation may turn an
+ indirect jump into a direct jump, in which case we must rerun the
+ jump optimizer to ensure that the JUMP_LABEL fields are valid. */
+ if (GET_CODE (XEXP (note, 0)) == LABEL_REF
+ || (GET_CODE (XEXP (note, 0)) == CONST
+ && GET_CODE (XEXP (XEXP (note, 0), 0)) == PLUS
+ && (GET_CODE (XEXP (XEXP (XEXP (note, 0), 0), 0))
+ == LABEL_REF)))
+ recorded_label_ref = 1;
+
+
reg_equiv_replacement[regno] = XEXP (note, 0);
/* Don't mess with things live during setjmp. */
/* Keep track of which basic block we are in. */
if (block + 1 < n_basic_blocks
- && basic_block_head[block + 1] == insn)
+ && BLOCK_HEAD (block + 1) == insn)
++block;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
if (! reg_equiv_replace[regno])
continue;
- equiv_insn = reg_equiv_init_insn[regno];
+ /* reg_equiv_replace[REGNO] gets set only when
+ REG_N_REFS[REGNO] is 2, i.e. the register is set
+ once and used once. (If it were only set, but not used,
+ flow would have deleted the setting insns.) Hence
+ there can only be one insn in reg_equiv_init_insns. */
+ equiv_insn = XEXP (reg_equiv_init_insns[regno], 0);
if (validate_replace_rtx (regno_reg_rtx[regno],
reg_equiv_replacement[regno], insn))
emit_insn_before (copy_rtx (PATTERN (equiv_insn)), insn);
REG_NOTES (PREV_INSN (insn)) = REG_NOTES (equiv_insn);
+ REG_NOTES (equiv_insn) = 0;
PUT_CODE (equiv_insn, NOTE);
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (equiv_insn) = 0;
- REG_NOTES (equiv_insn) = 0;
if (block < 0)
REG_BASIC_BLOCK (regno) = 0;
REG_N_CALLS_CROSSED (regno) = 0;
REG_LIVE_LENGTH (regno) = 2;
- if (block >= 0 && insn == basic_block_head[block])
- basic_block_head[block] = PREV_INSN (insn);
+ if (block >= 0 && insn == BLOCK_HEAD (block))
+ BLOCK_HEAD (block) = PREV_INSN (insn);
for (l = 0; l < n_basic_blocks; l++)
- CLEAR_REGNO_REG_SET (basic_block_live_at_start[l], regno);
+ CLEAR_REGNO_REG_SET (BASIC_BLOCK (l)->global_live_at_start,
+ regno);
}
}
}
}
+
+ /* Clean up. */
+ end_alias_analysis ();
+ free (reg_equiv_replace);
+ free (reg_equiv_init_insns);
+ free (reg_equiv_replacement);
+}
+
+/* Mark REG as having no known equivalence.
+ Some instructions might have been proceessed before and furnished
+ with REG_EQUIV notes for this register; these notes will have to be
+ removed.
+ STORE is the piece of RTL that does the non-constant / conflicting
+ assignment - a SET, CLOBBER or REG_INC note. It is currently not used,
+ but needs to be there because this function is called from note_stores. */
+static void
+no_equiv (reg, store, data)
+ rtx reg, store ATTRIBUTE_UNUSED;
+ void *data ATTRIBUTE_UNUSED;
+{
+ int regno;
+ rtx list;
+
+ if (GET_CODE (reg) != REG)
+ return;
+ regno = REGNO (reg);
+ list = reg_equiv_init_insns[regno];
+ if (list == const0_rtx)
+ return;
+ for (; list; list = XEXP (list, 1))
+ {
+ rtx insn = XEXP (list, 0);
+ remove_note (insn, find_reg_note (insn, REG_EQUIV, NULL_RTX));
+ }
+ reg_equiv_init_insns[regno] = const0_rtx;
+ reg_equiv_replacement[regno] = NULL_RTX;
}
\f
/* Allocate hard regs to the pseudo regs used only within block number B.
int max_uid = get_max_uid ();
int *qty_order;
int no_conflict_combined_regno = -1;
- /* Counter to prevent allocating more SCRATCHes than can be stored
- in SCRATCH_LIST. */
- int scratches_allocated = scratch_index;
/* Count the instructions in the basic block. */
- insn = basic_block_end[b];
+ insn = BLOCK_END (b);
while (1)
{
if (GET_CODE (insn) != NOTE)
if (++insn_count > max_uid)
abort ();
- if (insn == basic_block_head[b])
+ if (insn == BLOCK_HEAD (b))
break;
insn = PREV_INSN (insn);
}
/* +2 to leave room for a post_mark_life at the last insn and for
the birth of a CLOBBER in the first insn. */
- regs_live_at = (HARD_REG_SET *) alloca ((2 * insn_count + 2)
- * sizeof (HARD_REG_SET));
- bzero ((char *) regs_live_at, (2 * insn_count + 2) * sizeof (HARD_REG_SET));
+ regs_live_at = (HARD_REG_SET *) xcalloc ((2 * insn_count + 2),
+ sizeof (HARD_REG_SET));
/* Initialize table of hardware registers currently live. */
- REG_SET_TO_HARD_REG_SET (regs_live, basic_block_live_at_start[b]);
+ REG_SET_TO_HARD_REG_SET (regs_live, BASIC_BLOCK (b)->global_live_at_start);
/* This loop scans the instructions of the basic block
and assigns quantities to registers.
It computes which registers to tie. */
- insn = basic_block_head[b];
+ insn = BLOCK_HEAD (b);
while (1)
{
- register rtx body = PATTERN (insn);
-
if (GET_CODE (insn) != NOTE)
insn_number++;
{
register rtx link, set;
register int win = 0;
- register rtx r0, r1;
+ register rtx r0, r1 = NULL_RTX;
int combined_regno = -1;
int i;
- int insn_code_number = recog_memoized (insn);
this_insn_number = insn_number;
this_insn = insn;
- if (insn_code_number >= 0)
- insn_extract (insn);
+ extract_insn (insn);
which_alternative = -1;
/* Is this insn suitable for tying two registers?
If tying is done, WIN is set nonzero. */
- if (insn_code_number >= 0
-#ifdef REGISTER_CONSTRAINTS
- && insn_n_operands[insn_code_number] > 1
- && insn_operand_constraint[insn_code_number][0][0] == '='
- && insn_operand_constraint[insn_code_number][0][1] != '&'
-#else
- && GET_CODE (PATTERN (insn)) == SET
- && rtx_equal_p (SET_DEST (PATTERN (insn)), recog_operand[0])
-#endif
- )
+ if (optimize
+ && recog_data.n_operands > 1
+ && recog_data.constraints[0][0] == '='
+ && recog_data.constraints[0][1] != '&')
{
-#ifdef REGISTER_CONSTRAINTS
/* If non-negative, is an operand that must match operand 0. */
int must_match_0 = -1;
/* Counts number of alternatives that require a match with
operand 0. */
int n_matching_alts = 0;
- for (i = 1; i < insn_n_operands[insn_code_number]; i++)
+ for (i = 1; i < recog_data.n_operands; i++)
{
- char *p = insn_operand_constraint[insn_code_number][i];
+ const char *p = recog_data.constraints[i];
int this_match = (requires_inout (p));
n_matching_alts += this_match;
- if (this_match == insn_n_alternatives[insn_code_number])
+ if (this_match == recog_data.n_alternatives)
must_match_0 = i;
}
-#endif
- r0 = recog_operand[0];
- for (i = 1; i < insn_n_operands[insn_code_number]; i++)
+ r0 = recog_data.operand[0];
+ for (i = 1; i < recog_data.n_operands; i++)
{
-#ifdef REGISTER_CONSTRAINTS
/* Skip this operand if we found an operand that
must match operand 0 and this operand isn't it
and can't be made to be it by commutativity. */
if (must_match_0 >= 0 && i != must_match_0
&& ! (i == must_match_0 + 1
- && insn_operand_constraint[insn_code_number][i-1][0] == '%')
+ && recog_data.constraints[i-1][0] == '%')
&& ! (i == must_match_0 - 1
- && insn_operand_constraint[insn_code_number][i][0] == '%'))
+ && recog_data.constraints[i][0] == '%'))
continue;
/* Likewise if each alternative has some operand that
operand that doesn't list operand 0 since we know that
the operand always conflicts with operand 0. We
ignore commutatity in this case to keep things simple. */
- if (n_matching_alts == insn_n_alternatives[insn_code_number]
- && (0 == requires_inout
- (insn_operand_constraint[insn_code_number][i])))
+ if (n_matching_alts == recog_data.n_alternatives
+ && 0 == requires_inout (recog_data.constraints[i]))
continue;
-#endif
- r1 = recog_operand[i];
+ r1 = recog_data.operand[i];
/* If the operand is an address, find a register in it.
There may be more than one register, but we only try one
of them. */
- if (
-#ifdef REGISTER_CONSTRAINTS
- insn_operand_constraint[insn_code_number][i][0] == 'p'
-#else
- insn_operand_address_p[insn_code_number][i]
-#endif
- )
+ if (recog_data.constraints[i][0] == 'p')
while (GET_CODE (r1) == PLUS || GET_CODE (r1) == MULT)
r1 = XEXP (r1, 0);
can only be in the same register as the output, give
priority to an equivalence found from that insn. */
int may_save_copy
- = ((SET_DEST (body) == r0 && SET_SRC (body) == r1)
-#ifdef REGISTER_CONSTRAINTS
- || (r1 == recog_operand[i] && must_match_0 >= 0)
-#endif
- );
+ = (r1 == recog_data.operand[i] && must_match_0 >= 0);
if (GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)
win = combine_regs (r1, r0, may_save_copy,
destination register won't have had a quantity number
assigned, since that would prevent combining. */
- if (GET_CODE (PATTERN (insn)) == CLOBBER
+ if (optimize
+ && GET_CODE (PATTERN (insn)) == CLOBBER
&& (r0 = XEXP (PATTERN (insn), 0),
GET_CODE (r0) == REG)
&& (link = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_DEAD
&& GET_CODE (XEXP (link, 0)) == REG
- && combined_regno != REGNO (XEXP (link, 0))
- && (no_conflict_combined_regno != REGNO (XEXP (link, 0))
- || ! find_reg_note (insn, REG_NO_CONFLICT, XEXP (link, 0))))
+ && combined_regno != (int) REGNO (XEXP (link, 0))
+ && (no_conflict_combined_regno != (int) REGNO (XEXP (link, 0))
+ || ! find_reg_note (insn, REG_NO_CONFLICT,
+ XEXP (link, 0))))
wipe_dead_reg (XEXP (link, 0), 0);
/* Allocate qty numbers for all registers local to this block
that are born (set) in this instruction.
A pseudo that already has a qty is not changed. */
- note_stores (PATTERN (insn), reg_is_set);
+ note_stores (PATTERN (insn), reg_is_set, NULL);
/* If anything is set in this insn and then unused, mark it as dying
after this insn, so it will conflict with our outputs. This
&& GET_CODE (XEXP (link, 0)) == REG)
wipe_dead_reg (XEXP (link, 0), 1);
- /* Allocate quantities for any SCRATCH operands of this insn. */
-
- if (insn_code_number >= 0)
- for (i = 0; i < insn_n_operands[insn_code_number]; i++)
- if (GET_CODE (recog_operand[i]) == SCRATCH
- && scratches_allocated++ < scratch_list_length)
- alloc_qty_for_scratch (recog_operand[i], i, insn,
- insn_code_number, insn_number);
-
/* If this is an insn that has a REG_RETVAL note pointing at a
CLOBBER insn, we have reached the end of a REG_NO_CONFLICT
block, so clear any register number that combined within it. */
IOR_HARD_REG_SET (regs_live_at[2 * insn_number], regs_live);
IOR_HARD_REG_SET (regs_live_at[2 * insn_number + 1], regs_live);
- if (insn == basic_block_end[b])
+ if (insn == BLOCK_END (b))
break;
insn = NEXT_INSN (insn);
number of suggested registers they need so we allocate those with
the most restrictive needs first. */
- qty_order = (int *) alloca (next_qty * sizeof (int));
+ qty_order = (int *) xmalloc (next_qty * sizeof (int));
for (i = 0; i < next_qty; i++)
qty_order[i] = i;
{
q = qty_order[i];
if (qty_phys_num_sugg[q] != 0 || qty_phys_num_copy_sugg[q] != 0)
- qty_phys_reg[q] = find_free_reg (qty_min_class[q], qty_mode[q], q,
- 0, 1, qty_birth[q], qty_death[q]);
+ qty[q].phys_reg = find_free_reg (qty[q].min_class, qty[q].mode, q,
+ 0, 1, qty[q].birth, qty[q].death);
else
- qty_phys_reg[q] = -1;
+ qty[q].phys_reg = -1;
}
/* Order the qtys so we assign them registers in order of
for (i = 0; i < next_qty; i++)
{
q = qty_order[i];
- if (qty_phys_reg[q] < 0)
+ if (qty[q].phys_reg < 0)
{
+#ifdef INSN_SCHEDULING
+ /* These values represent the adjusted lifetime of a qty so
+ that it conflicts with qtys which appear near the start/end
+ of this qty's lifetime.
+
+ The purpose behind extending the lifetime of this qty is to
+ discourage the register allocator from creating false
+ dependencies.
+
+ The adjustment value is choosen to indicate that this qty
+ conflicts with all the qtys in the instructions immediately
+ before and after the lifetime of this qty.
+
+ Experiments have shown that higher values tend to hurt
+ overall code performance.
+
+ If allocation using the extended lifetime fails we will try
+ again with the qty's unadjusted lifetime. */
+ int fake_birth = MAX (0, qty[q].birth - 2 + qty[q].birth % 2);
+ int fake_death = MIN (insn_number * 2 + 1,
+ qty[q].death + 2 - qty[q].death % 2);
+#endif
+
if (N_REG_CLASSES > 1)
{
- qty_phys_reg[q] = find_free_reg (qty_min_class[q],
- qty_mode[q], q, 0, 0,
- qty_birth[q], qty_death[q]);
- if (qty_phys_reg[q] >= 0)
+#ifdef INSN_SCHEDULING
+ /* We try to avoid using hard registers allocated to qtys which
+ are born immediately after this qty or die immediately before
+ this qty.
+
+ This optimization is only appropriate when we will run
+ a scheduling pass after reload and we are not optimizing
+ for code size. */
+ if (flag_schedule_insns_after_reload
+ && !optimize_size
+ && !SMALL_REGISTER_CLASSES)
+ {
+
+ qty[q].phys_reg = find_free_reg (qty[q].min_class,
+ qty[q].mode, q, 0, 0,
+ fake_birth, fake_death);
+ if (qty[q].phys_reg >= 0)
+ continue;
+ }
+#endif
+ qty[q].phys_reg = find_free_reg (qty[q].min_class,
+ qty[q].mode, q, 0, 0,
+ qty[q].birth, qty[q].death);
+ if (qty[q].phys_reg >= 0)
continue;
}
- if (qty_alternate_class[q] != NO_REGS)
- qty_phys_reg[q] = find_free_reg (qty_alternate_class[q],
- qty_mode[q], q, 0, 0,
- qty_birth[q], qty_death[q]);
+#ifdef INSN_SCHEDULING
+ /* Similarly, avoid false dependencies. */
+ if (flag_schedule_insns_after_reload
+ && !optimize_size
+ && !SMALL_REGISTER_CLASSES
+ && qty[q].alternate_class != NO_REGS)
+ qty[q].phys_reg = find_free_reg (qty[q].alternate_class,
+ qty[q].mode, q, 0, 0,
+ fake_birth, fake_death);
+#endif
+ if (qty[q].alternate_class != NO_REGS)
+ qty[q].phys_reg = find_free_reg (qty[q].alternate_class,
+ qty[q].mode, q, 0, 0,
+ qty[q].birth, qty[q].death);
}
}
to the pseudo regs belonging to the qtys. */
for (q = 0; q < next_qty; q++)
- if (qty_phys_reg[q] >= 0)
+ if (qty[q].phys_reg >= 0)
{
- for (i = qty_first_reg[q]; i >= 0; i = reg_next_in_qty[i])
- reg_renumber[i] = qty_phys_reg[q] + reg_offset[i];
- if (qty_scratch_rtx[q])
- {
- if (GET_CODE (qty_scratch_rtx[q]) == REG)
- abort ();
- qty_scratch_rtx[q] = gen_rtx_REG (GET_MODE (qty_scratch_rtx[q]),
- qty_phys_reg[q]);
- scratch_block[scratch_index] = b;
- scratch_list[scratch_index++] = qty_scratch_rtx[q];
-
- }
+ for (i = qty[q].first_reg; i >= 0; i = reg_next_in_qty[i])
+ reg_renumber[i] = qty[q].phys_reg + reg_offset[i];
}
+
+ /* Clean up. */
+ free (regs_live_at);
+ free (qty_order);
}
\f
/* Compare two quantities' priority for getting real registers.
QTY_CMP_PRI is also used by qty_sugg_compare. */
#define QTY_CMP_PRI(q) \
- ((int) (((double) (floor_log2 (qty_n_refs[q]) * qty_n_refs[q] * qty_size[q]) \
- / (qty_death[q] - qty_birth[q])) * 10000))
+ ((int) (((double) (floor_log2 (qty[q].n_refs) * qty[q].n_refs * qty[q].size) \
+ / (qty[q].death - qty[q].birth)) * 10000))
static int
qty_compare (q1, q2)
static int
qty_compare_1 (q1p, q2p)
- const GENERIC_PTR q1p;
- const GENERIC_PTR q2p;
+ const PTR q1p;
+ const PTR q2p;
{
- register int q1 = *(int *)q1p, q2 = *(int *)q2p;
+ register int q1 = *(const int *)q1p, q2 = *(const int *)q2p;
register int tem = QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
if (tem != 0)
static int
qty_sugg_compare_1 (q1p, q2p)
- const GENERIC_PTR q1p;
- const GENERIC_PTR q2p;
+ const PTR q1p;
+ const PTR q2p;
{
- register int q1 = *(int *)q1p, q2 = *(int *)q2p;
+ register int q1 = *(const int *)q1p, q2 = *(const int *)q2p;
register int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
if (tem != 0)
/* Do not combine with a smaller already-assigned object
if that smaller object is already combined with something bigger. */
|| (ssize > usize && ureg >= FIRST_PSEUDO_REGISTER
- && usize < qty_size[reg_qty[ureg]])
+ && usize < qty[reg_qty[ureg]].size)
/* Can't combine if SREG is not a register we can allocate. */
|| (sreg >= FIRST_PSEUDO_REGISTER && reg_qty[sreg] == -1)
/* Don't combine with a pseudo mentioned in a REG_NO_CONFLICT note.
|| ureg == sreg
/* Don't try to connect two different hardware registers. */
|| (ureg < FIRST_PSEUDO_REGISTER && sreg < FIRST_PSEUDO_REGISTER)
+ /* Don't use a hard reg that might be spilled. */
+ || (ureg < FIRST_PSEUDO_REGISTER
+ && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (ureg)))
+ || (sreg < FIRST_PSEUDO_REGISTER
+ && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (sreg)))
/* Don't connect two different machine modes if they have different
implications as to which registers may be used. */
|| !MODES_TIEABLE_P (GET_MODE (usedreg), GET_MODE (setreg)))
are compatible. */
if ((already_dead || find_regno_note (insn, REG_DEAD, ureg))
- && reg_meets_class_p (sreg, qty_min_class[reg_qty[ureg]]))
+ && reg_meets_class_p (sreg, qty[reg_qty[ureg]].min_class))
{
/* Add SREG to UREG's quantity. */
sqty = reg_qty[ureg];
reg_qty[sreg] = sqty;
reg_offset[sreg] = reg_offset[ureg] + offset;
- reg_next_in_qty[sreg] = qty_first_reg[sqty];
- qty_first_reg[sqty] = sreg;
+ reg_next_in_qty[sreg] = qty[sqty].first_reg;
+ qty[sqty].first_reg = sreg;
- /* If SREG's reg class is smaller, set qty_min_class[SQTY]. */
+ /* If SREG's reg class is smaller, set qty[SQTY].min_class. */
update_qty_class (sqty, sreg);
/* Update info about quantity SQTY. */
- qty_n_calls_crossed[sqty] += REG_N_CALLS_CROSSED (sreg);
- qty_n_refs[sqty] += REG_N_REFS (sreg);
+ qty[sqty].n_calls_crossed += REG_N_CALLS_CROSSED (sreg);
+ qty[sqty].n_refs += REG_N_REFS (sreg);
if (usize < ssize)
{
register int i;
- for (i = qty_first_reg[sqty]; i >= 0; i = reg_next_in_qty[i])
+ for (i = qty[sqty].first_reg; i >= 0; i = reg_next_in_qty[i])
reg_offset[i] -= offset;
- qty_size[sqty] = ssize;
- qty_mode[sqty] = GET_MODE (setreg);
+ qty[sqty].size = ssize;
+ qty[sqty].mode = GET_MODE (setreg);
}
}
else
|| reg_class_subset_p (class, rclass));
}
-/* Return 1 if the two specified classes have registers in common.
- If CALL_SAVED, then consider only call-saved registers. */
-
-static int
-reg_classes_overlap_p (c1, c2, call_saved)
- register enum reg_class c1;
- register enum reg_class c2;
- int call_saved;
-{
- HARD_REG_SET c;
- int i;
-
- COPY_HARD_REG_SET (c, reg_class_contents[(int) c1]);
- AND_HARD_REG_SET (c, reg_class_contents[(int) c2]);
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (TEST_HARD_REG_BIT (c, i)
- && (! call_saved || ! call_used_regs[i]))
- return 1;
-
- return 0;
-}
-
-/* Update the class of QTY assuming that REG is being tied to it. */
+/* Update the class of QTYNO assuming that REG is being tied to it. */
static void
-update_qty_class (qty, reg)
- int qty;
+update_qty_class (qtyno, reg)
+ int qtyno;
int reg;
{
enum reg_class rclass = reg_preferred_class (reg);
- if (reg_class_subset_p (rclass, qty_min_class[qty]))
- qty_min_class[qty] = rclass;
+ if (reg_class_subset_p (rclass, qty[qtyno].min_class))
+ qty[qtyno].min_class = rclass;
rclass = reg_alternate_class (reg);
- if (reg_class_subset_p (rclass, qty_alternate_class[qty]))
- qty_alternate_class[qty] = rclass;
+ if (reg_class_subset_p (rclass, qty[qtyno].alternate_class))
+ qty[qtyno].alternate_class = rclass;
if (REG_CHANGES_SIZE (reg))
- qty_changes_size[qty] = 1;
+ qty[qtyno].changes_size = 1;
}
\f
/* Handle something which alters the value of an rtx REG.
carry info from `block_alloc'. */
static void
-reg_is_set (reg, setter)
+reg_is_set (reg, setter, data)
rtx reg;
rtx setter;
+ void *data ATTRIBUTE_UNUSED;
{
/* Note that note_stores will only pass us a SUBREG if it is a SUBREG of
a hard register. These may actually not exist any more. */
/* If this register has a quantity number, show that it isn't dead. */
if (reg_qty[regno] >= 0)
- qty_death[reg_qty[regno]] = -1;
+ qty[reg_qty[regno]].death = -1;
}
}
/* If this insn has multiple results,
and the dead reg is used in one of the results,
extend its life to after this insn,
- so it won't get allocated together with any other result of this insn. */
+ so it won't get allocated together with any other result of this insn.
+
+ It is unsafe to use !single_set here since it will ignore an unused
+ output. Just because an output is unused does not mean the compiler
+ can assume the side effect will not occur. Consider if REG appears
+ in the address of an output and we reload the output. If we allocate
+ REG to the same hard register as an unused output we could set the hard
+ register before the output reload insn. */
if (GET_CODE (PATTERN (this_insn)) == PARALLEL
- && !single_set (this_insn))
+ && multiple_sets (this_insn))
{
int i;
for (i = XVECLEN (PATTERN (this_insn), 0) - 1; i >= 0; i--)
}
else if (reg_qty[regno] >= 0)
- qty_death[reg_qty[regno]] = 2 * this_insn_number + output_p;
+ qty[reg_qty[regno]].death = 2 * this_insn_number + output_p;
}
\f
/* Find a block of SIZE words of hard regs in reg_class CLASS
and still free between insn BORN_INDEX and insn DEAD_INDEX,
and return the number of the first of them.
Return -1 if such a block cannot be found.
- If QTY crosses calls, insist on a register preserved by calls,
+ If QTYNO crosses calls, insist on a register preserved by calls,
unless ACCEPT_CALL_CLOBBERED is nonzero.
If JUST_TRY_SUGGESTED is non-zero, only try to see if the suggested
register is available. If not, return -1. */
static int
-find_free_reg (class, mode, qty, accept_call_clobbered, just_try_suggested,
+find_free_reg (class, mode, qtyno, accept_call_clobbered, just_try_suggested,
born_index, dead_index)
enum reg_class class;
enum machine_mode mode;
- int qty;
+ int qtyno;
int accept_call_clobbered;
int just_try_suggested;
int born_index, dead_index;
/* Don't let a pseudo live in a reg across a function call
if we might get a nonlocal goto. */
if (current_function_has_nonlocal_label
- && qty_n_calls_crossed[qty] > 0)
+ && qty[qtyno].n_calls_crossed > 0)
return -1;
if (accept_call_clobbered)
COPY_HARD_REG_SET (used, call_fixed_reg_set);
- else if (qty_n_calls_crossed[qty] == 0)
+ else if (qty[qtyno].n_calls_crossed == 0)
COPY_HARD_REG_SET (used, fixed_reg_set);
else
COPY_HARD_REG_SET (used, call_used_reg_set);
This is true of any register that can be eliminated. */
#ifdef ELIMINABLE_REGS
- for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+ for (i = 0; i < (int)(sizeof eliminables / sizeof eliminables[0]); i++)
SET_HARD_REG_BIT (used, eliminables[i].from);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
/* If FRAME_POINTER_REGNUM is not a real register, then protect the one
#endif
#ifdef CLASS_CANNOT_CHANGE_SIZE
- if (qty_changes_size[qty])
+ if (qty[qtyno].changes_size)
IOR_HARD_REG_SET (used,
reg_class_contents[(int) CLASS_CANNOT_CHANGE_SIZE]);
#endif
if (just_try_suggested)
{
- if (qty_phys_num_copy_sugg[qty] != 0)
- IOR_COMPL_HARD_REG_SET (first_used, qty_phys_copy_sugg[qty]);
+ if (qty_phys_num_copy_sugg[qtyno] != 0)
+ IOR_COMPL_HARD_REG_SET (first_used, qty_phys_copy_sugg[qtyno]);
else
- IOR_COMPL_HARD_REG_SET (first_used, qty_phys_sugg[qty]);
+ IOR_COMPL_HARD_REG_SET (first_used, qty_phys_sugg[qtyno]);
}
/* If all registers are excluded, we can't do anything. */
int regno = i;
#endif
if (! TEST_HARD_REG_BIT (first_used, regno)
- && HARD_REGNO_MODE_OK (regno, mode))
+ && HARD_REGNO_MODE_OK (regno, mode)
+ && (qty[qtyno].n_calls_crossed == 0
+ || accept_call_clobbered
+ || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
{
register int j;
register int size1 = HARD_REGNO_NREGS (regno, mode);
/* If it would be profitable to allocate a call-clobbered register
and save and restore it around calls, do that. */
- if (just_try_suggested && qty_phys_num_copy_sugg[qty] != 0
- && qty_phys_num_sugg[qty] != 0)
+ if (just_try_suggested && qty_phys_num_copy_sugg[qtyno] != 0
+ && qty_phys_num_sugg[qtyno] != 0)
{
/* Don't try the copy-suggested regs again. */
- qty_phys_num_copy_sugg[qty] = 0;
- return find_free_reg (class, mode, qty, accept_call_clobbered, 1,
+ qty_phys_num_copy_sugg[qtyno] = 0;
+ return find_free_reg (class, mode, qtyno, accept_call_clobbered, 1,
born_index, dead_index);
}
if (! accept_call_clobbered
&& flag_caller_saves
&& ! just_try_suggested
- && qty_n_calls_crossed[qty] != 0
- && CALLER_SAVE_PROFITABLE (qty_n_refs[qty], qty_n_calls_crossed[qty]))
+ && qty[qtyno].n_calls_crossed != 0
+ && CALLER_SAVE_PROFITABLE (qty[qtyno].n_refs, qty[qtyno].n_calls_crossed))
{
- i = find_free_reg (class, mode, qty, 1, 0, born_index, dead_index);
+ i = find_free_reg (class, mode, qtyno, 1, 0, born_index, dead_index);
if (i >= 0)
caller_save_needed = 1;
return i;
static int
no_conflict_p (insn, r0, r1)
- rtx insn, r0, r1;
+ rtx insn, r0 ATTRIBUTE_UNUSED, r1;
{
int ok = 0;
rtx note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
return ok;
}
\f
-#ifdef REGISTER_CONSTRAINTS
-
/* Return the number of alternatives for which the constraint string P
indicates that the operand must be equal to operand 0 and that no register
is acceptable. */
static int
requires_inout (p)
- char *p;
+ const char *p;
{
char c;
int found_zero = 0;
case '=': case '+': case '?':
case '#': case '&': case '!':
case '*': case '%':
- case '1': case '2': case '3': case '4':
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
case 'm': case '<': case '>': case 'V': case 'o':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
return num_matching_alts;
}
-#endif /* REGISTER_CONSTRAINTS */
\f
void
dump_local_alloc (file)