diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 830b2efad74..5f6d4a35450 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,33 @@ +2014-09-22 Richard Sandiford + + * hard-reg-set.h: Include hash-table.h. + (target_hard_regs): Add a finalize method and a x_simplifiable_subregs + field. + * target-globals.c (target_globals::~target_globals): Call + hard_regs->finalize. + * rtl.h (subreg_shape): New structure. + (shape_of_subreg): New function. + (simplifiable_subregs): Declare. + * reginfo.c (simplifiable_subreg): New structure. + (simplifiable_subregs_hasher): Likewise. + (simplifiable_subregs): New function. + (invalid_mode_changes): Delete. + (alid_mode_changes, valid_mode_changes_obstack): New variables. + (record_subregs_of_mode): Remove subregs_of_mode parameter. + Record valid mode changes in valid_mode_changes. + (find_subregs_of_mode): Remove subregs_of_mode parameter. + Update calls to record_subregs_of_mode. + (init_subregs_of_mode): Remove invalid_mode_changes and bitmap + handling. Initialize new variables. Update call to + find_subregs_of_mode. + (invalid_mode_change_p): Check new variables instead of + invalid_mode_changes. + (finish_subregs_of_mode): Finalize new variables instead of + invalid_mode_changes. + (target_hard_regs::finalize): New function. + * ira-costs.c (print_allocno_costs): Call invalid_mode_change_p + even when CLASS_CANNOT_CHANGE_MODE is undefined. + 2014-09-22 Richard Sandiford * combine.c (subst): Use simplify_subreg_regno rather than diff --git a/gcc/hard-reg-set.h b/gcc/hard-reg-set.h index 401fea1bab8..c32516c6913 100644 --- a/gcc/hard-reg-set.h +++ b/gcc/hard-reg-set.h @@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_HARD_REG_SET_H #define GCC_HARD_REG_SET_H +#include "hash-table.h" + /* Define the type of a set of hard registers. */ /* HARD_REG_ELT_TYPE is a typedef of the unsigned integral type which @@ -613,7 +615,11 @@ hard_reg_set_iter_next (hard_reg_set_iterator *iter, unsigned *regno) extern char global_regs[FIRST_PSEUDO_REGISTER]; +struct simplifiable_subregs_hasher; + struct target_hard_regs { + void finalize (); + /* The set of registers that actually exist on the current target. */ HARD_REG_SET x_accessible_reg_set; @@ -688,6 +694,10 @@ struct target_hard_regs { /* Vector indexed by hardware reg giving its name. */ const char *x_reg_names[FIRST_PSEUDO_REGISTER]; + + /* Records which registers can form a particular subreg, with the subreg + being identified by its outer mode, inner mode and offset. */ + hash_table *x_simplifiable_subregs; }; extern struct target_hard_regs default_target_hard_regs; diff --git a/gcc/ira-costs.c b/gcc/ira-costs.c index 34da9a73ba3..38d0e0edeee 100644 --- a/gcc/ira-costs.c +++ b/gcc/ira-costs.c @@ -1438,10 +1438,7 @@ print_allocno_costs (FILE *f) { rclass = cost_classes[k]; if (contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (regno)] -#ifdef CANNOT_CHANGE_MODE_CLASS - && ! invalid_mode_change_p (regno, (enum reg_class) rclass) -#endif - ) + && ! invalid_mode_change_p (regno, (enum reg_class) rclass)) { fprintf (f, " %s:%d", reg_class_names[rclass], COSTS (costs, i)->cost[k]); @@ -1480,10 +1477,7 @@ print_pseudo_costs (FILE *f) { rclass = cost_classes[k]; if (contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (regno)] -#ifdef CANNOT_CHANGE_MODE_CLASS - && ! invalid_mode_change_p (regno, (enum reg_class) rclass) -#endif - ) + && ! invalid_mode_change_p (regno, (enum reg_class) rclass)) fprintf (f, " %s:%d", reg_class_names[rclass], COSTS (costs, regno)->cost[k]); } @@ -1725,10 +1719,7 @@ find_costs_and_classes (FILE *dump_file) /* Ignore classes that are too small or invalid for this operand. */ if (! contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (i)] -#ifdef CANNOT_CHANGE_MODE_CLASS - || invalid_mode_change_p (i, (enum reg_class) rclass) -#endif - ) + || invalid_mode_change_p (i, (enum reg_class) rclass)) continue; if (i_costs[k] < best_cost) { @@ -1822,10 +1813,7 @@ find_costs_and_classes (FILE *dump_file) /* Ignore classes that are too small or invalid for this operand. */ if (! contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (i)] -#ifdef CANNOT_CHANGE_MODE_CLASS - || invalid_mode_change_p (i, (enum reg_class) rclass) -#endif - ) + || invalid_mode_change_p (i, (enum reg_class) rclass)) ; else if (total_a_costs[k] < best_cost) { diff --git a/gcc/reginfo.c b/gcc/reginfo.c index 8fd5a68660c..226152278b4 100644 --- a/gcc/reginfo.c +++ b/gcc/reginfo.c @@ -54,6 +54,24 @@ along with GCC; see the file COPYING3. If not see int max_regno; +/* Used to cache the results of simplifiable_subregs. SHAPE is the input + parameter and SIMPLIFIABLE_REGS is the result. */ +struct simplifiable_subreg +{ + simplifiable_subreg (const subreg_shape &); + + subreg_shape shape; + HARD_REG_SET simplifiable_regs; +}; + +struct simplifiable_subregs_hasher : typed_noop_remove +{ + typedef simplifiable_subreg value_type; + typedef subreg_shape compare_type; + + static inline hashval_t hash (const value_type *); + static inline bool equal (const value_type *, const compare_type *); +}; struct target_hard_regs default_target_hard_regs; struct target_regs default_target_regs; @@ -1193,64 +1211,102 @@ reg_classes_intersect_p (reg_class_t c1, reg_class_t c2) } +inline hashval_t +simplifiable_subregs_hasher::hash (const value_type *value) +{ + return value->shape.unique_id (); +} + +inline bool +simplifiable_subregs_hasher::equal (const value_type *value, + const compare_type *compare) +{ + return value->shape == *compare; +} + +inline simplifiable_subreg::simplifiable_subreg (const subreg_shape &shape_in) + : shape (shape_in) +{ + CLEAR_HARD_REG_SET (simplifiable_regs); +} + +/* Return the set of hard registers that are able to form the subreg + described by SHAPE. */ + +const HARD_REG_SET & +simplifiable_subregs (const subreg_shape &shape) +{ + if (!this_target_hard_regs->x_simplifiable_subregs) + this_target_hard_regs->x_simplifiable_subregs + = new hash_table (30); + simplifiable_subreg **slot + = (this_target_hard_regs->x_simplifiable_subregs + ->find_slot_with_hash (&shape, shape.unique_id (), INSERT)); + + if (!*slot) + { + simplifiable_subreg *info = new simplifiable_subreg (shape); + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + if (HARD_REGNO_MODE_OK (i, shape.inner_mode) + && simplify_subreg_regno (i, shape.inner_mode, shape.offset, + shape.outer_mode) >= 0) + SET_HARD_REG_BIT (info->simplifiable_regs, i); + *slot = info; + } + return (*slot)->simplifiable_regs; +} /* Passes for keeping and updating info about modes of registers inside subregisters. */ -#ifdef CANNOT_CHANGE_MODE_CLASS - -static bitmap invalid_mode_changes; +static HARD_REG_SET **valid_mode_changes; +static obstack valid_mode_changes_obstack; static void -record_subregs_of_mode (rtx subreg, bitmap subregs_of_mode) +record_subregs_of_mode (rtx subreg) { - enum machine_mode mode; unsigned int regno; if (!REG_P (SUBREG_REG (subreg))) return; regno = REGNO (SUBREG_REG (subreg)); - mode = GET_MODE (subreg); - if (regno < FIRST_PSEUDO_REGISTER) return; - if (bitmap_set_bit (subregs_of_mode, - regno * NUM_MACHINE_MODES + (unsigned int) mode)) + if (valid_mode_changes[regno]) + AND_HARD_REG_SET (*valid_mode_changes[regno], + simplifiable_subregs (shape_of_subreg (subreg))); + else { - unsigned int rclass; - for (rclass = 0; rclass < N_REG_CLASSES; rclass++) - if (!bitmap_bit_p (invalid_mode_changes, - regno * N_REG_CLASSES + rclass) - && CANNOT_CHANGE_MODE_CLASS (PSEUDO_REGNO_MODE (regno), - mode, (enum reg_class) rclass)) - bitmap_set_bit (invalid_mode_changes, - regno * N_REG_CLASSES + rclass); + valid_mode_changes[regno] + = XOBNEW (&valid_mode_changes_obstack, HARD_REG_SET); + COPY_HARD_REG_SET (*valid_mode_changes[regno], + simplifiable_subregs (shape_of_subreg (subreg))); } } /* Call record_subregs_of_mode for all the subregs in X. */ static void -find_subregs_of_mode (rtx x, bitmap subregs_of_mode) +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, subregs_of_mode); + 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), subregs_of_mode); + 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), subregs_of_mode); + find_subregs_of_mode (XVECEXP (x, i, j)); } } } @@ -1260,46 +1316,38 @@ init_subregs_of_mode (void) { basic_block bb; rtx_insn *insn; - bitmap_obstack srom_obstack; - bitmap subregs_of_mode; - gcc_assert (invalid_mode_changes == NULL); - invalid_mode_changes = BITMAP_ALLOC (NULL); - bitmap_obstack_initialize (&srom_obstack); - subregs_of_mode = BITMAP_ALLOC (&srom_obstack); + gcc_obstack_init (&valid_mode_changes_obstack); + valid_mode_changes = XCNEWVEC (HARD_REG_SET *, max_reg_num ()); FOR_EACH_BB_FN (bb, cfun) FOR_BB_INSNS (bb, insn) if (NONDEBUG_INSN_P (insn)) - find_subregs_of_mode (PATTERN (insn), subregs_of_mode); - - BITMAP_FREE (subregs_of_mode); - bitmap_obstack_release (&srom_obstack); + find_subregs_of_mode (PATTERN (insn)); } /* Return 1 if REGNO has had an invalid mode change in CLASS from FROM mode. */ bool -invalid_mode_change_p (unsigned int regno, - enum reg_class rclass) +invalid_mode_change_p (unsigned int regno, enum reg_class rclass) { - return bitmap_bit_p (invalid_mode_changes, - regno * N_REG_CLASSES + (unsigned) rclass); + return (valid_mode_changes[regno] + && !hard_reg_set_intersect_p (reg_class_contents[rclass], + *valid_mode_changes[regno])); } void finish_subregs_of_mode (void) { - BITMAP_FREE (invalid_mode_changes); -} -#else -void -init_subregs_of_mode (void) -{ -} -void -finish_subregs_of_mode (void) -{ + XDELETEVEC (valid_mode_changes); + obstack_finish (&valid_mode_changes_obstack); } -#endif /* CANNOT_CHANGE_MODE_CLASS */ +/* Free all data attached to the structure. This isn't a destructor because + we don't want to run on exit. */ + +void +target_hard_regs::finalize () +{ + delete x_simplifiable_subregs; +} diff --git a/gcc/rtl.h b/gcc/rtl.h index 93df69152c2..e73f7315438 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -1831,6 +1831,64 @@ costs_add_n_insns (struct full_rtx_costs *c, int n) c->size += COSTS_N_INSNS (n); } +/* Describes the shape of a subreg: + + inner_mode == the mode of the SUBREG_REG + offset == the SUBREG_BYTE + outer_mode == the mode of the SUBREG itself. */ +struct subreg_shape { + subreg_shape (enum machine_mode, unsigned int, enum machine_mode); + bool operator == (const subreg_shape &) const; + bool operator != (const subreg_shape &) const; + unsigned int unique_id () const; + + enum machine_mode inner_mode; + unsigned int offset; + enum machine_mode outer_mode; +}; + +inline +subreg_shape::subreg_shape (enum machine_mode inner_mode_in, + unsigned int offset_in, + enum machine_mode outer_mode_in) + : inner_mode (inner_mode_in), offset (offset_in), outer_mode (outer_mode_in) +{} + +inline bool +subreg_shape::operator == (const subreg_shape &other) const +{ + return (inner_mode == other.inner_mode + && offset == other.offset + && outer_mode == other.outer_mode); +} + +inline bool +subreg_shape::operator != (const subreg_shape &other) const +{ + return !operator == (other); +} + +/* Return an integer that uniquely identifies this shape. Structures + like rtx_def assume that a mode can fit in an 8-bit bitfield and no + current mode is anywhere near being 65536 bytes in size, so the + id comfortably fits in an int. */ + +inline unsigned int +subreg_shape::unique_id () const +{ + STATIC_ASSERT (MAX_MACHINE_MODE <= 256); + return (int) inner_mode + ((int) outer_mode << 8) + (offset << 16); +} + +/* Return the shape of a SUBREG rtx. */ + +static inline subreg_shape +shape_of_subreg (const_rtx x) +{ + return subreg_shape (GET_MODE (SUBREG_REG (x)), + SUBREG_BYTE (x), GET_MODE (x)); +} + /* Information about an address. This structure is supposed to be able to represent all supported target addresses. Please extend it if it is not yet general enough. */ @@ -2727,6 +2785,9 @@ extern bool val_signbit_known_clear_p (enum machine_mode, /* In reginfo.c */ extern enum machine_mode choose_hard_reg_mode (unsigned int, unsigned int, bool); +#ifdef HARD_CONST +extern const HARD_REG_SET &simplifiable_subregs (const subreg_shape &); +#endif /* In emit-rtl.c */ extern rtx set_for_reg_notes (rtx); diff --git a/gcc/target-globals.c b/gcc/target-globals.c index 43f9f4a0bb1..52ac2c02dfc 100644 --- a/gcc/target-globals.c +++ b/gcc/target-globals.c @@ -125,6 +125,7 @@ target_globals::~target_globals () /* default_target_globals points to static data so shouldn't be freed. */ if (this != &default_target_globals) { + hard_regs->finalize (); XDELETE (flag_state); XDELETE (regs); XDELETE (recog);