diff options
Diffstat (limited to 'gcc/regclass.c')
-rw-r--r-- | gcc/regclass.c | 490 |
1 files changed, 202 insertions, 288 deletions
diff --git a/gcc/regclass.c b/gcc/regclass.c index 23fd0928bea..0c86dbfc8c2 100644 --- a/gcc/regclass.c +++ b/gcc/regclass.c @@ -48,6 +48,12 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #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); @@ -241,20 +247,6 @@ static char *in_inc_dec; 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. */ @@ -263,6 +255,26 @@ static int no_global_reg_vars = 0; /* 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. */ @@ -827,10 +839,6 @@ static struct costs init_cost; 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; @@ -848,7 +856,7 @@ static void record_address_regs (enum machine_mode, rtx, int, enum rtx_code, #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, unsigned int); +static void reg_scan_mark_refs (rtx, rtx); /* Wrapper around REGNO_OK_FOR_INDEX_P, to allow pseudo registers. */ @@ -896,11 +904,14 @@ reg_alternate_class (int regno) /* 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; @@ -911,7 +922,27 @@ regclass_init (void) /* 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 */ +}; + + /* Dump register costs. */ static void @@ -1096,7 +1127,7 @@ init_reg_autoinc (void) 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)) @@ -1137,9 +1168,14 @@ regclass (rtx f, int nregs) 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 @@ -1197,9 +1233,6 @@ regclass (rtx f, int nregs) `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); @@ -2068,243 +2101,56 @@ auto_inc_dec_reg_p (rtx reg, enum machine_mode mode) } #endif -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. */ - -void -allocate_reg_info (size_t num_regs, int new_p, int renumber_p) -{ - 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) - { - 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) - { - reg_n_info = VEC_alloc (reg_info_p, heap, regno_allocated); - VEC_safe_grow_cleared (reg_info_p, heap, reg_n_info, - regno_allocated); - renumber = xmalloc (size_renumber); - reg_pref_buffer = XNEWVEC (struct reg_pref, regno_allocated); - } - else - { - size_t old_length = VEC_length (reg_info_p, reg_n_info); - if (old_length < regno_allocated) - { - VEC_safe_grow_cleared (reg_info_p, heap, reg_n_info, - regno_allocated); - } - else if (regno_allocated < old_length) - { - VEC_truncate (reg_info_p, 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; - } - - reg_n_max = num_regs; - if (min < num_regs) - { - /* 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++) - { - VEC_replace (reg_info_p, 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; - } - } - } - - /* 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) + if (reg_pref) { - struct reg_info_data *reg_data; - struct reg_info_data *reg_next; - - VEC_free (reg_info_p, heap, 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); + reg_pref = NULL; + } - free (reg_pref_buffer); - reg_pref_buffer = (struct reg_pref *) 0; - reg_info_head = (struct reg_info_data *) 0; - renumber = (short *) 0; + if (reg_renumber) + { + free (reg_renumber); + reg_renumber = NULL; } - regno_allocated = 0; - reg_n_max = 0; } -/* Clear the information stored for REGNO. */ -void -clear_reg_info_regno (unsigned int regno) -{ - if (regno < regno_allocated) - memset (VEC_index (reg_info_p, reg_n_info, regno), 0, sizeof (reg_info)); -} -/* 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, 0); - + reg_scan_mark_refs (PATTERN (insn), insn); if (REG_NOTES (insn)) - reg_scan_mark_refs (REG_NOTES (insn), insn, 1, 0); + reg_scan_mark_refs (REG_NOTES (insn), insn); } - max_parallel += max_set_parallel; - timevar_pop (TV_REG_SCAN); } -/* Update 'regscan' information by looking at the insns - from FIRST to LAST. Some new REGs have been created, - and any REG with number greater than OLD_MAX_REGNO is - such a REG. We only update information for those. */ - -void -reg_scan_update (rtx first, rtx last, unsigned int old_max_regno) -{ - rtx insn; - - allocate_reg_info (max_reg_num (), FALSE, FALSE); - - for (insn = first; insn != last; 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, old_max_regno); - - if (REG_NOTES (insn)) - reg_scan_mark_refs (REG_NOTES (insn), insn, 1, old_max_regno); - } -} /* 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. 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, unsigned int min_regno) +reg_scan_mark_refs (rtx x, rtx insn) { enum rtx_code code; rtx dest; @@ -2325,50 +2171,24 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) case LABEL_REF: case ADDR_VEC: case ADDR_DIFF_VEC: - return; - case REG: - { - unsigned int regno = REGNO (x); - - if (regno >= min_regno) - { - if (!note_flag) - REGNO_LAST_UID (regno) = INSN_UID (insn); - if (REGNO_FIRST_UID (regno) == 0) - REGNO_FIRST_UID (regno) = INSN_UID (insn); - /* If we are called by reg_scan_update() (indicated by min_regno - being set), we also need to update the reference count. */ - if (min_regno) - REG_N_REFS (regno)++; - } - } - break; + return; case EXPR_LIST: if (XEXP (x, 0)) - reg_scan_mark_refs (XEXP (x, 0), insn, note_flag, min_regno); + reg_scan_mark_refs (XEXP (x, 0), insn); if (XEXP (x, 1)) - reg_scan_mark_refs (XEXP (x, 1), insn, note_flag, min_regno); + 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, min_regno); + reg_scan_mark_refs (XEXP (x, 1), insn); break; case CLOBBER: - { - rtx reg = XEXP (x, 0); - if (REG_P (reg) - && REGNO (reg) >= min_regno) - { - 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, min_regno); - } + if (MEM_P (XEXP (x, 0))) + reg_scan_mark_refs (XEXP (XEXP (x, 0), 0), insn); break; case SET: @@ -2379,18 +2199,6 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) 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) - && REGNO (dest) >= min_regno) - { - 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. @@ -2405,13 +2213,12 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) if (REG_P (SET_DEST (x)) && REGNO (SET_DEST (x)) >= FIRST_PSEUDO_REGISTER - && REGNO (SET_DEST (x)) >= min_regno /* If the destination pseudo is set more than once, then other sets might not be to a pointer value (consider access to a 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)) @@ -2441,7 +2248,7 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) /* 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); @@ -2451,9 +2258,9 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) || (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); } @@ -2466,12 +2273,12 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') - reg_scan_mark_refs (XEXP (x, i), insn, note_flag, min_regno); + 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, min_regno); + reg_scan_mark_refs (XVECEXP (x, i, j), insn); } } } @@ -2527,16 +2334,8 @@ som_eq (const void *x, const void *y) 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; @@ -2567,6 +2366,53 @@ record_subregs_of_mode (rtx subreg) 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. */ @@ -2579,6 +2425,7 @@ cannot_change_mode_set_regs (HARD_REG_SET *used, enum machine_mode from, 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) @@ -2604,6 +2451,7 @@ invalid_mode_change_p (unsigned int regno, enum reg_class class, 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) @@ -2617,6 +2465,72 @@ invalid_mode_change_p (unsigned int regno, enum reg_class class, 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" |