/* Compute register class preferences for pseudo-registers.
Copyright (C) 1987, 1988, 1991, 1992, 1993, 1994, 1995, 1996
- 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
This file is part of GCC.
#include "timevar.h"
#include "hashtab.h"
#include "target.h"
+#include "tree-pass.h"
+#include "df.h"
+
+/* Maximum register number used in this function, plus one. */
+
+int max_regno;
static void init_reg_sets_1 (void);
static void init_reg_autoinc (void);
static GTY(()) rtx top_of_stack[MAX_MACHINE_MODE];
-/* Linked list of reg_info structures allocated for reg_n_info array.
- Grouping all of the allocated structures together in one lump
- means only one call to bzero to clear them, rather than n smaller
- calls. */
-struct reg_info_data {
- struct reg_info_data *next; /* next set of reg_info structures */
- size_t min_index; /* minimum index # */
- size_t max_index; /* maximum index # */
- char used_p; /* nonzero if this has been used previously */
- reg_info data[1]; /* beginning of the reg_info data */
-};
-
-static struct reg_info_data *reg_info_head;
-
/* No more global register variables may be declared; true once
regclass has been initialized. */
/* Specify number of hard registers given machine mode occupy. */
unsigned char hard_regno_nregs[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
+/* Given a register bitmap, turn on the bits in a HARD_REG_SET that
+ correspond to the hard registers, if any, set in that map. This
+ could be done far more efficiently by having all sorts of special-cases
+ with moving single words, but probably isn't worth the trouble. */
+
+void
+reg_set_to_hard_reg_set (HARD_REG_SET *to, bitmap from)
+{
+ unsigned i;
+ bitmap_iterator bi;
+
+ EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi)
+ {
+ if (i >= FIRST_PSEUDO_REGISTER)
+ return;
+ SET_HARD_REG_BIT (*to, i);
+ }
+}
+
+
/* Function called only once to initialize the above data on reg usage.
Once this is done, various switches may override. */
COPY_HARD_REG_SET (c, reg_class_contents[i]);
IOR_HARD_REG_SET (c, reg_class_contents[j]);
for (k = 0; k < N_REG_CLASSES; k++)
- {
- GO_IF_HARD_REG_SUBSET (reg_class_contents[k], c,
- subclass1);
- continue;
-
- subclass1:
- /* Keep the largest subclass. */ /* SPEE 900308 */
- GO_IF_HARD_REG_SUBSET (reg_class_contents[k],
- reg_class_contents[(int) reg_class_subunion[i][j]],
- subclass2);
+ if (hard_reg_set_subset_p (reg_class_contents[k], c)
+ && !hard_reg_set_subset_p (reg_class_contents[k],
+ reg_class_contents
+ [(int) reg_class_subunion[i][j]]))
reg_class_subunion[i][j] = (enum reg_class) k;
- subclass2:
- ;
- }
}
}
COPY_HARD_REG_SET (c, reg_class_contents[i]);
IOR_HARD_REG_SET (c, reg_class_contents[j]);
for (k = 0; k < N_REG_CLASSES; k++)
- GO_IF_HARD_REG_SUBSET (c, reg_class_contents[k], superclass);
+ if (hard_reg_set_subset_p (c, reg_class_contents[k]))
+ break;
- superclass:
reg_class_superunion[i][j] = (enum reg_class) k;
}
}
continue;
for (j = i + 1; j < N_REG_CLASSES; j++)
- {
- enum reg_class *p;
-
- GO_IF_HARD_REG_SUBSET (reg_class_contents[i], reg_class_contents[j],
- subclass);
- continue;
- subclass:
- /* Reg class I is a subclass of J.
- Add J to the table of superclasses of I. */
- p = ®_class_superclasses[i][0];
- while (*p != LIM_REG_CLASSES) p++;
- *p = (enum reg_class) j;
- /* Add I to the table of superclasses of J. */
- p = ®_class_subclasses[j][0];
- while (*p != LIM_REG_CLASSES) p++;
- *p = (enum reg_class) i;
- }
+ if (hard_reg_set_subset_p (reg_class_contents[i],
+ reg_class_contents[j]))
+ {
+ /* Reg class I is a subclass of J.
+ Add J to the table of superclasses of I. */
+ enum reg_class *p;
+
+ p = ®_class_superclasses[i][0];
+ while (*p != LIM_REG_CLASSES) p++;
+ *p = (enum reg_class) j;
+ /* Add I to the table of superclasses of J. */
+ p = ®_class_subclasses[j][0];
+ while (*p != LIM_REG_CLASSES) p++;
+ *p = (enum reg_class) i;
+ }
}
/* Initialize "constant" tables. */
/* Structure used to record preferences of given pseudo. */
struct reg_pref
{
- /* (enum reg_class) prefclass is the preferred class. */
+ /* (enum reg_class) prefclass is the preferred class. May be
+ NO_REGS if no class is better than memory. */
char prefclass;
/* altclass is a register class that we should use for allocating
static struct reg_pref *reg_pref;
-/* Allocated buffers for reg_pref. */
-
-static struct reg_pref *reg_pref_buffer;
-
/* Frequency of executions of current insn. */
static int frequency;
#ifdef FORBIDDEN_INC_DEC_CLASSES
static int auto_inc_dec_reg_p (rtx, enum machine_mode);
#endif
-static void reg_scan_mark_refs (rtx, rtx, int);
+static void reg_scan_mark_refs (rtx, rtx);
/* Wrapper around REGNO_OK_FOR_INDEX_P, to allow pseudo registers. */
/* Initialize some global data for this pass. */
-void
+static unsigned int
regclass_init (void)
{
int i;
+ if (df)
+ df_compute_regs_ever_live (true);
+
init_cost.mem_cost = 10000;
for (i = 0; i < N_REG_CLASSES; i++)
init_cost.cost[i] = 10000;
/* No more global register variables may be declared. */
no_global_reg_vars = 1;
+ return 1;
}
+
+struct tree_opt_pass pass_regclass_init =
+{
+ "regclass", /* name */
+ NULL, /* gate */
+ regclass_init, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ 'k' /* letter */
+};
+
+
\f
/* Dump register costs. */
static void
there. */
static rtx
-scan_one_insn (rtx insn, int pass)
+scan_one_insn (rtx insn, int pass ATTRIBUTE_UNUSED)
{
enum rtx_code pat_code;
rtx set, note;
return insn;
}
- /* Improve handling of two-address insns such as
- (set X (ashift CONST Y)) where CONST must be made to
- match X. Change it into two insns: (set X CONST)
- (set X (ashift X Y)). If we left this for reloading, it
- would probably get three insns because X and Y might go
- in the same place. This prevents X and Y from receiving
- the same hard reg.
-
- We can only do this if the modes of operands 0 and 1
- (which might not be the same) are tieable and we only need
- do this during our first pass. */
-
- if (pass == 0 && optimize
- && recog_data.n_operands >= 3
- && recog_data.constraints[1][0] == '0'
- && recog_data.constraints[1][1] == 0
- && CONSTANT_P (recog_data.operand[1])
- && ! rtx_equal_p (recog_data.operand[0], recog_data.operand[1])
- && ! rtx_equal_p (recog_data.operand[0], recog_data.operand[2])
- && REG_P (recog_data.operand[0])
- && MODES_TIEABLE_P (GET_MODE (recog_data.operand[0]),
- recog_data.operand_mode[1]))
- {
- rtx previnsn = prev_real_insn (insn);
- rtx dest
- = gen_lowpart (recog_data.operand_mode[1],
- recog_data.operand[0]);
- rtx newinsn
- = emit_insn_before (gen_move_insn (dest, recog_data.operand[1]), insn);
-
- /* If this insn was the start of a basic block,
- include the new insn in that block.
- We need not check for code_label here;
- while a basic block can start with a code_label,
- INSN could not be at the beginning of that block. */
- if (previnsn == 0 || JUMP_P (previnsn))
- {
- basic_block b;
- FOR_EACH_BB (b)
- if (insn == BB_HEAD (b))
- BB_HEAD (b) = newinsn;
- }
-
- /* This makes one more setting of new insns's dest. */
- REG_N_SETS (REGNO (recog_data.operand[0]))++;
- REG_N_REFS (REGNO (recog_data.operand[0]))++;
- REG_FREQ (REGNO (recog_data.operand[0])) += frequency;
-
- *recog_data.operand_loc[1] = recog_data.operand[0];
- REG_N_REFS (REGNO (recog_data.operand[0]))++;
- REG_FREQ (REGNO (recog_data.operand[0])) += frequency;
- for (i = recog_data.n_dups - 1; i >= 0; i--)
- if (recog_data.dup_num[i] == 1)
- {
- *recog_data.dup_loc[i] = recog_data.operand[0];
- REG_N_REFS (REGNO (recog_data.operand[0]))++;
- REG_FREQ (REGNO (recog_data.operand[0])) += frequency;
- }
-
- return PREV_INSN (newinsn);
- }
-
record_operand_costs (insn, op_costs, reg_pref);
/* Now add the cost for each operand to the total costs for
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
if (TEST_HARD_REG_BIT (reg_class_contents[i], j))
{
- REGNO (r) = j;
+ SET_REGNO (r, j);
for (m = VOIDmode; (int) m < (int) MAX_MACHINE_MODE;
m = (enum machine_mode) ((int) m + 1))
rtx insn;
int i;
int pass;
+ max_regno = max_reg_num ();
init_recog ();
+ reg_renumber = xmalloc (max_regno * sizeof (short));
+ reg_pref = XCNEWVEC (struct reg_pref, max_regno);
+ memset (reg_renumber, -1, max_regno * sizeof (short));
+
costs = XNEWVEC (struct costs, nregs);
#ifdef FORBIDDEN_INC_DEC_CLASSES
`prefclass'. Record in `altclass' the largest register
class any of whose registers is better than memory. */
- if (pass == 0)
- reg_pref = reg_pref_buffer;
-
if (dump_file)
{
dump_regclass (dump_file);
int class;
struct costs *p = &costs[i];
+ if (regno_reg_rtx[i] == NULL)
+ continue;
+
/* In non-optimizing compilation REG_N_REFS is not initialized
yet. */
if (optimize && !REG_N_REFS (i) && !REG_N_SETS (i))
best = reg_class_subunion[(int) best][class];
}
+ /* If no register class is better than memory, use memory. */
+ if (p->mem_cost < best_cost)
+ best = NO_REGS;
+
/* Record the alternate register class; i.e., a class for which
every register in it is better than using memory. If adding a
class would make a smaller class (i.e., no union of just those
to what we would add if this register were not in the
appropriate class. */
- if (reg_pref)
+ if (reg_pref && reg_pref[REGNO (op)].prefclass != NO_REGS)
alt_cost
+= (may_move_in_cost[mode]
[(unsigned char) reg_pref[REGNO (op)].prefclass]
to what we would add if this register were not in the
appropriate class. */
- if (reg_pref)
+ if (reg_pref && reg_pref[REGNO (op)].prefclass != NO_REGS)
alt_cost
+= (may_move_in_cost[mode]
[(unsigned char) reg_pref[REGNO (op)].prefclass]
unsigned int regno = REGNO (ops[!i]);
enum machine_mode mode = GET_MODE (ops[!i]);
int class;
- unsigned int nr;
- if (regno >= FIRST_PSEUDO_REGISTER && reg_pref != 0)
+ if (regno >= FIRST_PSEUDO_REGISTER && reg_pref != 0
+ && reg_pref[regno].prefclass != NO_REGS)
{
enum reg_class pref = reg_pref[regno].prefclass;
{
if (reg_class_size[class] == 1)
op_costs[i].cost[class] = -1;
- else
- {
- for (nr = 0; nr < (unsigned) hard_regno_nregs[regno][mode]; nr++)
- {
- if (! TEST_HARD_REG_BIT (reg_class_contents[class],
- regno + nr))
- break;
- }
-
- if (nr == (unsigned) hard_regno_nregs[regno][mode])
- op_costs[i].cost[class] = -1;
- }
+ else if (in_hard_reg_set_p (reg_class_contents[class],
+ mode, regno))
+ op_costs[i].cost[class] = -1;
}
}
}
}
#endif
\f
-static short *renumber;
-static size_t regno_allocated;
-static unsigned int reg_n_max;
-
-/* Allocate enough space to hold NUM_REGS registers for the tables used for
- reg_scan and flow_analysis that are indexed by the register number. If
- NEW_P is nonzero, initialize all of the registers, otherwise only
- initialize the new registers allocated. The same table is kept from
- function to function, only reallocating it when we need more room. If
- RENUMBER_P is nonzero, allocate the reg_renumber array also. */
-
+/* Free up the space allocated by allocate_reg_info. */
void
-allocate_reg_info (size_t num_regs, int new_p, int renumber_p)
+free_reg_info (void)
{
- size_t size_info;
- size_t size_renumber;
- size_t min = (new_p) ? 0 : reg_n_max;
- struct reg_info_data *reg_data;
-
- if (num_regs > regno_allocated)
+ if (reg_pref)
{
- size_t old_allocated = regno_allocated;
-
- regno_allocated = num_regs + (num_regs / 20); /* Add some slop space. */
- size_renumber = regno_allocated * sizeof (short);
-
- if (!reg_n_info)
- {
- VARRAY_REG_INIT (reg_n_info, regno_allocated, "reg_n_info");
- renumber = xmalloc (size_renumber);
- reg_pref_buffer = XNEWVEC (struct reg_pref, regno_allocated);
- }
- else
- {
- VARRAY_GROW (reg_n_info, regno_allocated);
-
- if (new_p) /* If we're zapping everything, no need to realloc. */
- {
- free ((char *) renumber);
- free ((char *) reg_pref);
- renumber = xmalloc (size_renumber);
- reg_pref_buffer = XNEWVEC (struct reg_pref, regno_allocated);
- }
-
- else
- {
- renumber = xrealloc (renumber, size_renumber);
- reg_pref_buffer = (struct reg_pref *) xrealloc (reg_pref_buffer,
- regno_allocated
- * sizeof (struct reg_pref));
- }
- }
-
- size_info = (regno_allocated - old_allocated) * sizeof (reg_info)
- + sizeof (struct reg_info_data) - sizeof (reg_info);
- reg_data = xcalloc (size_info, 1);
- reg_data->min_index = old_allocated;
- reg_data->max_index = regno_allocated - 1;
- reg_data->next = reg_info_head;
- reg_info_head = reg_data;
+ free (reg_pref);
+ reg_pref = NULL;
}
- reg_n_max = num_regs;
- if (min < num_regs)
+ if (reg_renumber)
{
- /* Loop through each of the segments allocated for the actual
- reg_info pages, and set up the pointers, zero the pages, etc. */
- for (reg_data = reg_info_head;
- reg_data && reg_data->max_index >= min;
- reg_data = reg_data->next)
- {
- size_t min_index = reg_data->min_index;
- size_t max_index = reg_data->max_index;
- size_t max = MIN (max_index, num_regs);
- size_t local_min = min - min_index;
- size_t i;
-
- if (reg_data->min_index > num_regs)
- continue;
-
- if (min < min_index)
- local_min = 0;
- if (!reg_data->used_p) /* page just allocated with calloc */
- reg_data->used_p = 1; /* no need to zero */
- else
- memset (®_data->data[local_min], 0,
- sizeof (reg_info) * (max - min_index - local_min + 1));
-
- for (i = min_index+local_min; i <= max; i++)
- {
- VARRAY_REG (reg_n_info, i) = ®_data->data[i-min_index];
- REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN;
- renumber[i] = -1;
- reg_pref_buffer[i].prefclass = (char) NO_REGS;
- reg_pref_buffer[i].altclass = (char) NO_REGS;
- }
- }
+ free (reg_renumber);
+ reg_renumber = NULL;
}
-
- /* If {pref,alt}class have already been allocated, update the pointers to
- the newly realloced ones. */
- if (reg_pref)
- reg_pref = reg_pref_buffer;
-
- if (renumber_p)
- reg_renumber = renumber;
}
-/* Free up the space allocated by allocate_reg_info. */
-void
-free_reg_info (void)
-{
- if (reg_n_info)
- {
- struct reg_info_data *reg_data;
- struct reg_info_data *reg_next;
-
- VARRAY_FREE (reg_n_info);
- for (reg_data = reg_info_head; reg_data; reg_data = reg_next)
- {
- reg_next = reg_data->next;
- free ((char *) reg_data);
- }
-
- free (reg_pref_buffer);
- reg_pref_buffer = (struct reg_pref *) 0;
- reg_info_head = (struct reg_info_data *) 0;
- renumber = (short *) 0;
- }
- regno_allocated = 0;
- reg_n_max = 0;
-}
\f
-/* This is the `regscan' pass of the compiler, run just before cse
- and again just before loop.
-
- It finds the first and last use of each pseudo-register
- and records them in the vectors regno_first_uid, regno_last_uid
- and counts the number of sets in the vector reg_n_sets.
-
- REPEAT is nonzero the second time this is called. */
-
-/* Maximum number of parallel sets and clobbers in any insn in this fn.
- Always at least 3, since the combiner could put that many together
- and we want this to remain correct for all the remaining passes.
- This corresponds to the maximum number of times note_stores will call
- a function for any insn. */
-
-int max_parallel;
-
-/* Used as a temporary to record the largest number of registers in
- PARALLEL in a SET_DEST. This is added to max_parallel. */
-
-static int max_set_parallel;
+/* This is the `regscan' pass of the compiler, run just before cse and
+ again just before loop. It finds the first and last use of each
+ pseudo-register. */
void
-reg_scan (rtx f, unsigned int nregs)
+reg_scan (rtx f, unsigned int nregs ATTRIBUTE_UNUSED)
{
rtx insn;
timevar_push (TV_REG_SCAN);
- allocate_reg_info (nregs, TRUE, FALSE);
- max_parallel = 3;
- max_set_parallel = 0;
-
for (insn = f; insn; insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
- rtx pat = PATTERN (insn);
- if (GET_CODE (pat) == PARALLEL
- && XVECLEN (pat, 0) > max_parallel)
- max_parallel = XVECLEN (pat, 0);
- reg_scan_mark_refs (pat, insn, 0);
-
+ reg_scan_mark_refs (PATTERN (insn), insn);
if (REG_NOTES (insn))
- reg_scan_mark_refs (REG_NOTES (insn), insn, 1);
+ reg_scan_mark_refs (REG_NOTES (insn), insn);
}
- max_parallel += max_set_parallel;
-
timevar_pop (TV_REG_SCAN);
}
+
/* 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. */
+ NOTE_FLAG is nonzero if X is from INSN's notes rather than its body.
+ We should only record information for REGs with numbers
+ greater than or equal to MIN_REGNO. */
+
+extern struct tree_opt_pass *current_pass;
static void
-reg_scan_mark_refs (rtx x, rtx insn, int note_flag)
+reg_scan_mark_refs (rtx x, rtx insn)
{
enum rtx_code code;
rtx dest;
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
- return;
-
case REG:
- {
- unsigned int regno = REGNO (x);
-
- if (!note_flag)
- REGNO_LAST_UID (regno) = INSN_UID (insn);
- if (REGNO_FIRST_UID (regno) == 0)
- REGNO_FIRST_UID (regno) = INSN_UID (insn);
- }
- break;
+ return;
case EXPR_LIST:
if (XEXP (x, 0))
- reg_scan_mark_refs (XEXP (x, 0), insn, note_flag);
+ reg_scan_mark_refs (XEXP (x, 0), insn);
if (XEXP (x, 1))
- reg_scan_mark_refs (XEXP (x, 1), insn, note_flag);
+ reg_scan_mark_refs (XEXP (x, 1), insn);
break;
case INSN_LIST:
if (XEXP (x, 1))
- reg_scan_mark_refs (XEXP (x, 1), insn, note_flag);
+ reg_scan_mark_refs (XEXP (x, 1), insn);
break;
case CLOBBER:
- {
- rtx reg = XEXP (x, 0);
- if (REG_P (reg))
- {
- REG_N_SETS (REGNO (reg))++;
- REG_N_REFS (REGNO (reg))++;
- }
- else if (MEM_P (reg))
- reg_scan_mark_refs (XEXP (reg, 0), insn, note_flag);
- }
+ if (MEM_P (XEXP (x, 0)))
+ reg_scan_mark_refs (XEXP (XEXP (x, 0), 0), insn);
break;
case SET:
dest = XEXP (dest, 0))
;
- /* For a PARALLEL, record the number of things (less the usual one for a
- SET) that are set. */
- if (GET_CODE (dest) == PARALLEL)
- max_set_parallel = MAX (max_set_parallel, XVECLEN (dest, 0) - 1);
-
- if (REG_P (dest))
- {
- REG_N_SETS (REGNO (dest))++;
- REG_N_REFS (REGNO (dest))++;
- }
-
/* 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.
union in two threads of control in the presence of global
optimizations). So only set REG_POINTER on the destination
pseudo if this is the only set of that pseudo. */
- && REG_N_SETS (REGNO (SET_DEST (x))) == 1
+ && DF_REG_DEF_COUNT (REGNO (SET_DEST (x))) == 1
&& ! REG_USERVAR_P (SET_DEST (x))
&& ! REG_POINTER (SET_DEST (x))
&& ((REG_P (SET_SRC (x))
/* If this is setting a register from a register or from a simple
conversion of a register, propagate REG_EXPR. */
- if (REG_P (dest))
+ if (REG_P (dest) && !REG_ATTRS (dest))
{
rtx src = SET_SRC (x);
|| (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)))
src = XEXP (src, 0);
- if (!REG_ATTRS (dest) && REG_P (src))
+ if (REG_P (src))
REG_ATTRS (dest) = REG_ATTRS (src);
- if (!REG_ATTRS (dest) && MEM_P (src))
+ if (MEM_P (src))
set_reg_attrs_from_mem (dest, src);
}
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- reg_scan_mark_refs (XEXP (x, i), insn, note_flag);
+ reg_scan_mark_refs (XEXP (x, i), insn);
else if (fmt[i] == 'E' && XVEC (x, i) != 0)
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- reg_scan_mark_refs (XVECEXP (x, i, j), insn, note_flag);
+ reg_scan_mark_refs (XVECEXP (x, i, j), insn);
}
}
}
int
reg_class_subset_p (enum reg_class c1, enum reg_class c2)
{
- if (c1 == c2) return 1;
-
- if (c2 == ALL_REGS)
- win:
- return 1;
- GO_IF_HARD_REG_SUBSET (reg_class_contents[(int) c1],
- reg_class_contents[(int) c2],
- win);
- return 0;
+ return (c1 == c2
+ || c2 == ALL_REGS
+ || hard_reg_set_subset_p (reg_class_contents[(int) c1],
+ reg_class_contents[(int) c2]));
}
/* Return nonzero if there is a register that is in both C1 and C2. */
int
reg_classes_intersect_p (enum reg_class c1, enum reg_class c2)
{
- HARD_REG_SET c;
-
- if (c1 == c2) return 1;
-
- if (c1 == ALL_REGS || c2 == ALL_REGS)
- return 1;
-
- COPY_HARD_REG_SET (c, reg_class_contents[(int) c1]);
- AND_HARD_REG_SET (c, reg_class_contents[(int) c2]);
-
- GO_IF_HARD_REG_SUBSET (c, reg_class_contents[(int) NO_REGS], lose);
- return 1;
-
- lose:
- return 0;
+ return (c1 == c2
+ || c1 == ALL_REGS
+ || c2 == ALL_REGS
+ || hard_reg_set_intersect_p (reg_class_contents[(int) c1],
+ reg_class_contents[(int) c2]));
}
#ifdef CANNOT_CHANGE_MODE_CLASS
return a->block == b->block;
}
-void
-init_subregs_of_mode (void)
-{
- if (subregs_of_mode)
- htab_empty (subregs_of_mode);
- else
- subregs_of_mode = htab_create (100, som_hash, som_eq, free);
-}
-void
+static void
record_subregs_of_mode (rtx subreg)
{
struct subregs_of_mode_node dummy, *node;
node->modes[mode] |= 1 << (regno & 7);
}
+
+/* Call record_subregs_of_mode for all the subregs in X. */
+
+static void
+find_subregs_of_mode (rtx x)
+{
+ enum rtx_code code = GET_CODE (x);
+ const char * const fmt = GET_RTX_FORMAT (code);
+ int i;
+
+ if (code == SUBREG)
+ record_subregs_of_mode (x);
+
+ /* Time for some deep diving. */
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ find_subregs_of_mode (XEXP (x, i));
+ else if (fmt[i] == 'E')
+ {
+ int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ find_subregs_of_mode (XVECEXP (x, i, j));
+ }
+ }
+}
+
+static unsigned int
+init_subregs_of_mode (void)
+{
+ basic_block bb;
+ rtx insn;
+
+ if (subregs_of_mode)
+ htab_empty (subregs_of_mode);
+ else
+ subregs_of_mode = htab_create (100, som_hash, som_eq, free);
+
+ FOR_EACH_BB (bb)
+ FOR_BB_INSNS (bb, insn)
+ if (INSN_P (insn))
+ find_subregs_of_mode (PATTERN (insn));
+
+ return 0;
+}
+
+
/* Set bits in *USED which correspond to registers which can't change
their mode from FROM to any mode in which REGNO was encountered. */
unsigned char mask;
unsigned int i;
+ gcc_assert (subregs_of_mode);
dummy.block = regno & -8;
node = htab_find_with_hash (subregs_of_mode, &dummy, dummy.block);
if (node == NULL)
mode. */
bool
-invalid_mode_change_p (unsigned int regno, enum reg_class class,
+invalid_mode_change_p (unsigned int regno,
+ enum reg_class class ATTRIBUTE_UNUSED,
enum machine_mode from)
{
struct subregs_of_mode_node dummy, *node;
enum machine_mode to;
unsigned char mask;
+ gcc_assert (subregs_of_mode);
dummy.block = regno & -8;
node = htab_find_with_hash (subregs_of_mode, &dummy, dummy.block);
if (node == NULL)
return false;
}
+
+static unsigned int
+finish_subregs_of_mode (void)
+{
+ htab_delete (subregs_of_mode);
+ subregs_of_mode = 0;
+ return 0;
+}
+#else
+static unsigned int
+init_subregs_of_mode (void)
+{
+ return 0;
+}
+static unsigned int
+finish_subregs_of_mode (void)
+{
+ return 0;
+}
+
#endif /* CANNOT_CHANGE_MODE_CLASS */
+static bool
+gate_subregs_of_mode_init (void)
+{
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ return true;
+#else
+ return false;
+#endif
+}
+
+struct tree_opt_pass pass_subregs_of_mode_init =
+{
+ "subregs_of_mode_init", /* name */
+ gate_subregs_of_mode_init, /* gate */
+ init_subregs_of_mode, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ 0 /* letter */
+};
+
+struct tree_opt_pass pass_subregs_of_mode_finish =
+{
+ "subregs_of_mode_finish", /* name */
+ gate_subregs_of_mode_init, /* gate */
+ finish_subregs_of_mode, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ 0 /* letter */
+};
+
+
+
#include "gt-regclass.h"