mbox series

[0/9] Make HARD_REG_SETs easier to use

Message ID mptv9u18o2j.fsf@arm.com
Headers show
Series Make HARD_REG_SETs easier to use | expand

Message

Richard Sandiford Sept. 9, 2019, 3:52 p.m. UTC
I have a series of patches that allows several ABIs to be used
interoperably within the same translation unit.  Part of that involves
removing our reliance on global register sets that describe "the ABI".

One of the difficulties is that we have several global sets that contain
related information.  E.g.:

   call_used_reg_set == regs_invalidated_by_call | fixed_reg_set

call_fixed_reg_set is derived from call_used_reg_set.
no_caller_save_reg_set is in turn derived from call_fixed_reg_set.

I don't think there's a strong efficiency reason for having a global
cache of things like call_used_reg_set above.  On most targets the
"|" operation is two (parallel) 64-bit logical ORs of nearby data and
so should be very cheap to compute.  I think the sets are more there
for convenience.  And I think the main reason they're convenient is
that HARD_REG_SET is so unwieldly to use.  E.g. if you want to do:

   a &= b | c;

you have to write:

   HARD_REG_SET tmp;
   COPY_HARD_REG_SET (tmp, b);
   IOR_HARD_REG_SET (tmp, c);
   AND_HARD_REG_SET (a, tmp);

Again, each of these operations is individually very cheap on most
targets (a couple of moves or logic ops each).  But because it takes
so much code to write, there's a strong temptation to cache "b | c"
if it's used often.

This series therefore provides the following operators for HARD_REG_SET
and converts existing code to use them:

  = ~ & &= | |= == !=

The reason for picking (only) these is that they work on plain integer
bitmasks too, and so we can continue to treat HARD_REG_SET as a plain
integer when FIRST_PSEUDO_REGISTER is small enough.  As noted at the
end of the covering note for the first patch, that might be overly
conservative, since the sets aren't passed by value all that often
and so the performance gain is probably very slight in practice.
I nevertheless stopped here because the series seems like a strict
improvement compared to the status quo and is all that I need for the
motivating follow-on series.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  Also tested by
compiling one target for each CPU directory and making sure that
there were no changes in assembly code for gcc.c-torture, gcc.dg
and g++.dg.  (This was before the eBPF port went in, but that's
conveniently free of HARD_REG_SETs.)

Richard

Comments

Richard Sandiford Sept. 9, 2019, 3:53 p.m. UTC | #1
We have two styles of HARD_REG_SET: a single integer based on
HOST_WIDEST_FAST_INT (used when FIRST_PSEUDO_REGISTER is small enough)
or an array of integers.  One of the nice properties of this arrangement
is that:

  void foo (const HARD_REG_SET);

is passed by value as an integer when the set is small enough and
by reference otherwise.

(This is in constrast to "const HARD_REG_SET &", which would always
be passed by reference, and in contrast to passing a structure wrapper
like "struct s { T elts[1]; }" by value, where the structure might be
passed like a T or by reference, depending on the ABI.)

However, one of the disadvantages of using an array is that simple
assignment doesn't work.  We need to use COPY_HARD_REG_SET instead.

This patch uses a structure wrapper around the array, and preserves
the above "nice property" using a new const_hard_reg_set typedef.
The patch also removes the manual unrolling for small array sizes;
I think these days we can rely on the compiler to do that for us.

This meant fixing two port-specific quirks:

- epiphany passed NULL as a HARD_REG_SET whose value doesn't matter.
  The patch passes the NO_REGS set instead.

- ia64 reused TEST_HARD_REG_BIT and SET_HARD_REG_BIT for arrays that
  are bigger than HARD_REG_SET.  The patch just open-codes them.

The patch is probably being too conservative.  Very few places actually
take advantage of the "nice property" above, and we could have a
cleaner interface if we used a structure wrapper for all cases.


2019-09-09  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* hard-reg-set.h (HARD_REG_SET): Define using a typedef rather
	than a #define.  Use a structure rather than an array as the
	fallback definition.  Remove special cases for low array sizes.
	(const_hard_reg_set): New typedef.
	(hard_reg_set_subset_p): Use it instead of "const HARD_REG_SET".
	(hard_reg_set_equal_p, hard_reg_set_intersect_p): Likewise.
	(hard_reg_set_empty_p): Likewise.
	(SET_HARD_REG_BIT): Use a function rather than a macro to
	handle the case in which HARD_REG_SET is a structure.
	(CLEAR_HARD_REG_BIT, TEST_HARD_REG_BIT, CLEAR_HARD_REG_SET)
	(SET_HARD_REG_SET, COPY_HARD_REG_SET, COMPL_HARD_REG_SET)
	(AND_HARD_REG_SET, AND_COMPL_HARD_REG_SET, IOR_HARD_REG_SET)
	(IOR_COMPL_HARD_REG_SET): Likewise.
	(hard_reg_set_iterator::pset): Constify the pointer target.
	(hard_reg_set_iter_init): Take a const_hard_reg_set instead
	of a "const HARD_REG_SET".  Update the handling of non-integer
	HARD_REG_SETs.
	* recog.h: Test HARD_CONST instead of CLEAR_HARD_REG_SET.
	* reload.h: Likewise.
	* rtl.h (choose_hard_reg_mode): Remove unnecessary line break.
	* regs.h (in_hard_reg_set_p): Take a const_hard_reg_set instead
	of a "const HARD_REG_SET".
	(overlaps_hard_reg_set_p, range_overlaps_hard_reg_set_p): Likewise.
	(range_in_hard_reg_set_p): Likewise.
	* ira-costs.c (restrict_cost_classes): Likewise.
	* shrink-wrap.c (move_insn_for_shrink_wrap): Likewise.
	* config/epiphany/resolve-sw-modes.c (pass_resolve_sw_modes::execute):
	Pass a NO_REGS HARD_REG_SET rather than NULL to emit_set_fp_mode.
	* config/ia64/ia64.c (rws_insn): In the CHECKING_P version,
	use unsigned HOST_WIDEST_FAST_INT rather than HARD_REG_ELT_TYPE.
	(rws_insn_set, rws_insn_test): In the CHECKING_P version,
	take an unsigned int and open-code the HARD_REG_SET operations.

Index: gcc/hard-reg-set.h
===================================================================
*** gcc/hard-reg-set.h	2019-09-09 16:53:27.837266363 +0100
--- gcc/hard-reg-set.h	2019-09-09 16:53:27.829266420 +0100
*************** typedef unsigned HOST_WIDEST_FAST_INT HA
*** 42,55 ****
  
  #if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_WIDEST_FAST_INT
  
! #define HARD_REG_SET HARD_REG_ELT_TYPE
  
  #else
  
  #define HARD_REG_SET_LONGS \
   ((FIRST_PSEUDO_REGISTER + HOST_BITS_PER_WIDEST_FAST_INT - 1)	\
    / HOST_BITS_PER_WIDEST_FAST_INT)
! typedef HARD_REG_ELT_TYPE HARD_REG_SET[HARD_REG_SET_LONGS];
  
  #endif
  
--- 42,61 ----
  
  #if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_WIDEST_FAST_INT
  
! typedef HARD_REG_ELT_TYPE HARD_REG_SET;
! typedef const HARD_REG_SET const_hard_reg_set;
  
  #else
  
  #define HARD_REG_SET_LONGS \
   ((FIRST_PSEUDO_REGISTER + HOST_BITS_PER_WIDEST_FAST_INT - 1)	\
    / HOST_BITS_PER_WIDEST_FAST_INT)
! 
! struct HARD_REG_SET
! {
!   HARD_REG_ELT_TYPE elts[HARD_REG_SET_LONGS];
! };
! typedef const HARD_REG_SET &const_hard_reg_set;
  
  #endif
  
*************** #define HARD_CONST(X) ((HARD_REG_ELT_TYP
*** 98,104 ****
  
  #define UHOST_BITS_PER_WIDE_INT ((unsigned) HOST_BITS_PER_WIDEST_FAST_INT)
  
! #ifdef HARD_REG_SET
  
  #define SET_HARD_REG_BIT(SET, BIT)  \
   ((SET) |= HARD_CONST (1) << (BIT))
--- 104,110 ----
  
  #define UHOST_BITS_PER_WIDE_INT ((unsigned) HOST_BITS_PER_WIDEST_FAST_INT)
  
! #if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_WIDEST_FAST_INT
  
  #define SET_HARD_REG_BIT(SET, BIT)  \
   ((SET) |= HARD_CONST (1) << (BIT))
*************** #define AND_HARD_REG_SET(TO, FROM) ((TO)
*** 119,513 ****
  #define AND_COMPL_HARD_REG_SET(TO, FROM) ((TO) &= ~ (FROM))
  
  static inline bool
! hard_reg_set_subset_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
    return (x & ~y) == HARD_CONST (0);
  }
  
  static inline bool
! hard_reg_set_equal_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
    return x == y;
  }
  
  static inline bool
! hard_reg_set_intersect_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
    return (x & y) != HARD_CONST (0);
  }
  
  static inline bool
! hard_reg_set_empty_p (const HARD_REG_SET x)
  {
    return x == HARD_CONST (0);
  }
  
  #else
  
! #define SET_HARD_REG_BIT(SET, BIT)		\
!   ((SET)[(BIT) / UHOST_BITS_PER_WIDE_INT]	\
!    |= HARD_CONST (1) << ((BIT) % UHOST_BITS_PER_WIDE_INT))
! 
! #define CLEAR_HARD_REG_BIT(SET, BIT)		\
!   ((SET)[(BIT) / UHOST_BITS_PER_WIDE_INT]	\
!    &= ~(HARD_CONST (1) << ((BIT) % UHOST_BITS_PER_WIDE_INT)))
! 
! #define TEST_HARD_REG_BIT(SET, BIT)		\
!   (!!((SET)[(BIT) / UHOST_BITS_PER_WIDE_INT]	\
!       & (HARD_CONST (1) << ((BIT) % UHOST_BITS_PER_WIDE_INT))))
! 
! #if FIRST_PSEUDO_REGISTER <= 2*HOST_BITS_PER_WIDEST_FAST_INT
! #define CLEAR_HARD_REG_SET(TO)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      scan_tp_[0] = 0;						\
!      scan_tp_[1] = 0; } while (0)
! 
! #define SET_HARD_REG_SET(TO)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      scan_tp_[0] = -1;						\
!      scan_tp_[1] = -1; } while (0)
! 
! #define COPY_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] = scan_fp_[0];					\
!      scan_tp_[1] = scan_fp_[1]; } while (0)
! 
! #define COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] = ~ scan_fp_[0];				\
!      scan_tp_[1] = ~ scan_fp_[1]; } while (0)
! 
! #define AND_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] &= scan_fp_[0];				\
!      scan_tp_[1] &= scan_fp_[1]; } while (0)
! 
! #define AND_COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] &= ~ scan_fp_[0];				\
!      scan_tp_[1] &= ~ scan_fp_[1]; } while (0)
! 
! #define IOR_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] |= scan_fp_[0];				\
!      scan_tp_[1] |= scan_fp_[1]; } while (0)
! 
! #define IOR_COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] |= ~ scan_fp_[0];				\
!      scan_tp_[1] |= ~ scan_fp_[1]; } while (0)
! 
! static inline bool
! hard_reg_set_subset_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return (x[0] & ~y[0]) == 0 && (x[1] & ~y[1]) == 0;
  }
  
! static inline bool
! hard_reg_set_equal_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return x[0] == y[0] && x[1] == y[1];
  }
  
! static inline bool
! hard_reg_set_intersect_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return (x[0] & y[0]) != 0 || (x[1] & y[1]) != 0;
  }
  
! static inline bool
! hard_reg_set_empty_p (const HARD_REG_SET x)
  {
!   return x[0] == 0 && x[1] == 0;
  }
  
! #else
! #if FIRST_PSEUDO_REGISTER <= 3*HOST_BITS_PER_WIDEST_FAST_INT
! #define CLEAR_HARD_REG_SET(TO)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      scan_tp_[0] = 0;						\
!      scan_tp_[1] = 0;						\
!      scan_tp_[2] = 0; } while (0)
! 
! #define SET_HARD_REG_SET(TO)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      scan_tp_[0] = -1;						\
!      scan_tp_[1] = -1;						\
!      scan_tp_[2] = -1; } while (0)
! 
! #define COPY_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] = scan_fp_[0];					\
!      scan_tp_[1] = scan_fp_[1];					\
!      scan_tp_[2] = scan_fp_[2]; } while (0)
! 
! #define COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] = ~ scan_fp_[0];				\
!      scan_tp_[1] = ~ scan_fp_[1];				\
!      scan_tp_[2] = ~ scan_fp_[2]; } while (0)
! 
! #define AND_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] &= scan_fp_[0];				\
!      scan_tp_[1] &= scan_fp_[1];				\
!      scan_tp_[2] &= scan_fp_[2]; } while (0)
! 
! #define AND_COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] &= ~ scan_fp_[0];				\
!      scan_tp_[1] &= ~ scan_fp_[1];				\
!      scan_tp_[2] &= ~ scan_fp_[2]; } while (0)
! 
! #define IOR_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] |= scan_fp_[0];				\
!      scan_tp_[1] |= scan_fp_[1];				\
!      scan_tp_[2] |= scan_fp_[2]; } while (0)
! 
! #define IOR_COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] |= ~ scan_fp_[0];				\
!      scan_tp_[1] |= ~ scan_fp_[1];				\
!      scan_tp_[2] |= ~ scan_fp_[2]; } while (0)
! 
! static inline bool
! hard_reg_set_subset_p (const HARD_REG_SET x, const HARD_REG_SET y)
! {
!   return ((x[0] & ~y[0]) == 0
! 	  && (x[1] & ~y[1]) == 0
! 	  && (x[2] & ~y[2]) == 0);
! }
! 
! static inline bool
! hard_reg_set_equal_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return x[0] == y[0] && x[1] == y[1] && x[2] == y[2];
  }
  
! static inline bool
! hard_reg_set_intersect_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return ((x[0] & y[0]) != 0
! 	  || (x[1] & y[1]) != 0
! 	  || (x[2] & y[2]) != 0);
  }
  
! static inline bool
! hard_reg_set_empty_p (const HARD_REG_SET x)
  {
!   return x[0] == 0 && x[1] == 0 && x[2] == 0;
  }
  
! #else
! #if FIRST_PSEUDO_REGISTER <= 4*HOST_BITS_PER_WIDEST_FAST_INT
! #define CLEAR_HARD_REG_SET(TO)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      scan_tp_[0] = 0;						\
!      scan_tp_[1] = 0;						\
!      scan_tp_[2] = 0;						\
!      scan_tp_[3] = 0; } while (0)
! 
! #define SET_HARD_REG_SET(TO)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      scan_tp_[0] = -1;						\
!      scan_tp_[1] = -1;						\
!      scan_tp_[2] = -1;						\
!      scan_tp_[3] = -1; } while (0)
! 
! #define COPY_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] = scan_fp_[0];					\
!      scan_tp_[1] = scan_fp_[1];					\
!      scan_tp_[2] = scan_fp_[2];					\
!      scan_tp_[3] = scan_fp_[3]; } while (0)
! 
! #define COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] = ~ scan_fp_[0];				\
!      scan_tp_[1] = ~ scan_fp_[1];				\
!      scan_tp_[2] = ~ scan_fp_[2];				\
!      scan_tp_[3] = ~ scan_fp_[3]; } while (0)
! 
! #define AND_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] &= scan_fp_[0];				\
!      scan_tp_[1] &= scan_fp_[1];				\
!      scan_tp_[2] &= scan_fp_[2];				\
!      scan_tp_[3] &= scan_fp_[3]; } while (0)
! 
! #define AND_COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] &= ~ scan_fp_[0];				\
!      scan_tp_[1] &= ~ scan_fp_[1];				\
!      scan_tp_[2] &= ~ scan_fp_[2];				\
!      scan_tp_[3] &= ~ scan_fp_[3]; } while (0)
! 
! #define IOR_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] |= scan_fp_[0];				\
!      scan_tp_[1] |= scan_fp_[1];				\
!      scan_tp_[2] |= scan_fp_[2];				\
!      scan_tp_[3] |= scan_fp_[3]; } while (0)
! 
! #define IOR_COMPL_HARD_REG_SET(TO, FROM)  \
! do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
!      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
!      scan_tp_[0] |= ~ scan_fp_[0];				\
!      scan_tp_[1] |= ~ scan_fp_[1];				\
!      scan_tp_[2] |= ~ scan_fp_[2];				\
!      scan_tp_[3] |= ~ scan_fp_[3]; } while (0)
! 
! static inline bool
! hard_reg_set_subset_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return ((x[0] & ~y[0]) == 0
! 	  && (x[1] & ~y[1]) == 0
! 	  && (x[2] & ~y[2]) == 0
! 	  && (x[3] & ~y[3]) == 0);
  }
  
! static inline bool
! hard_reg_set_equal_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3];
  }
  
! static inline bool
! hard_reg_set_intersect_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   return ((x[0] & y[0]) != 0
! 	  || (x[1] & y[1]) != 0
! 	  || (x[2] & y[2]) != 0
! 	  || (x[3] & y[3]) != 0);
  }
  
! static inline bool
! hard_reg_set_empty_p (const HARD_REG_SET x)
  {
!   return x[0] == 0 && x[1] == 0 && x[2] == 0 && x[3] == 0;
  }
  
- #else /* FIRST_PSEUDO_REGISTER > 4*HOST_BITS_PER_WIDEST_FAST_INT */
- 
- #define CLEAR_HARD_REG_SET(TO)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ = 0; } while (0)
- 
- #define SET_HARD_REG_SET(TO)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ = -1; } while (0)
- 
- #define COPY_HARD_REG_SET(TO, FROM)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ = *scan_fp_++; } while (0)
- 
- #define COMPL_HARD_REG_SET(TO, FROM)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ = ~ *scan_fp_++; } while (0)
- 
- #define AND_HARD_REG_SET(TO, FROM)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ &= *scan_fp_++; } while (0)
- 
- #define AND_COMPL_HARD_REG_SET(TO, FROM)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ &= ~ *scan_fp_++; } while (0)
- 
- #define IOR_HARD_REG_SET(TO, FROM)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ |= *scan_fp_++; } while (0)
- 
- #define IOR_COMPL_HARD_REG_SET(TO, FROM)  \
- do { HARD_REG_ELT_TYPE *scan_tp_ = (TO);			\
-      const HARD_REG_ELT_TYPE *scan_fp_ = (FROM);		\
-      int i;							\
-      for (i = 0; i < HARD_REG_SET_LONGS; i++)			\
-        *scan_tp_++ |= ~ *scan_fp_++; } while (0)
- 
  static inline bool
! hard_reg_set_subset_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   int i;
! 
!   for (i = 0; i < HARD_REG_SET_LONGS; i++)
!     if ((x[i] & ~y[i]) != 0)
!       return false;
!   return true;
  }
  
  static inline bool
! hard_reg_set_equal_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   int i;
! 
!   for (i = 0; i < HARD_REG_SET_LONGS; i++)
!     if (x[i] != y[i])
!       return false;
!   return true;
  }
  
  static inline bool
! hard_reg_set_intersect_p (const HARD_REG_SET x, const HARD_REG_SET y)
  {
!   int i;
! 
!   for (i = 0; i < HARD_REG_SET_LONGS; i++)
!     if ((x[i] & y[i]) != 0)
!       return true;
!   return false;
  }
  
  static inline bool
! hard_reg_set_empty_p (const HARD_REG_SET x)
  {
!   int i;
! 
!   for (i = 0; i < HARD_REG_SET_LONGS; i++)
!     if (x[i] != 0)
!       return false;
!   return true;
  }
- 
- #endif
- #endif
- #endif
  #endif
  
  /* Iterator for hard register sets.  */
--- 125,266 ----
  #define AND_COMPL_HARD_REG_SET(TO, FROM) ((TO) &= ~ (FROM))
  
  static inline bool
! hard_reg_set_subset_p (const_hard_reg_set x, const_hard_reg_set y)
  {
    return (x & ~y) == HARD_CONST (0);
  }
  
  static inline bool
! hard_reg_set_equal_p (const_hard_reg_set x, const_hard_reg_set y)
  {
    return x == y;
  }
  
  static inline bool
! hard_reg_set_intersect_p (const_hard_reg_set x, const_hard_reg_set y)
  {
    return (x & y) != HARD_CONST (0);
  }
  
  static inline bool
! hard_reg_set_empty_p (const_hard_reg_set x)
  {
    return x == HARD_CONST (0);
  }
  
  #else
  
! inline void
! SET_HARD_REG_BIT (HARD_REG_SET &set, unsigned int bit)
  {
!   set.elts[bit / UHOST_BITS_PER_WIDE_INT]
!     |= HARD_CONST (1) << (bit % UHOST_BITS_PER_WIDE_INT);
  }
  
! inline void
! CLEAR_HARD_REG_BIT (HARD_REG_SET &set, unsigned int bit)
  {
!   set.elts[bit / UHOST_BITS_PER_WIDE_INT]
!     &= ~(HARD_CONST (1) << (bit % UHOST_BITS_PER_WIDE_INT));
  }
  
! inline bool
! TEST_HARD_REG_BIT (const_hard_reg_set set, unsigned int bit)
  {
!   return (set.elts[bit / UHOST_BITS_PER_WIDE_INT]
! 	  & (HARD_CONST (1) << (bit % UHOST_BITS_PER_WIDE_INT)));
  }
  
! inline void
! CLEAR_HARD_REG_SET (HARD_REG_SET &set)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (set.elts); ++i)
!     set.elts[i] = 0;
  }
  
! inline void
! SET_HARD_REG_SET (HARD_REG_SET &set)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (set.elts); ++i)
!     set.elts[i] = -1;
  }
  
! inline void
! COPY_HARD_REG_SET (HARD_REG_SET &to, const_hard_reg_set from)
  {
!   to = from;
  }
  
! inline void
! COMPL_HARD_REG_SET (HARD_REG_SET &to, const_hard_reg_set from)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (to.elts); ++i)
!     to.elts[i] = ~from.elts[i];
  }
  
! inline void
! AND_HARD_REG_SET (HARD_REG_SET &to, const_hard_reg_set from)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (to.elts); ++i)
!     to.elts[i] &= from.elts[i];
  }
  
! inline void
! AND_COMPL_HARD_REG_SET (HARD_REG_SET &to, const_hard_reg_set from)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (to.elts); ++i)
!     to.elts[i] &= ~from.elts[i];
  }
  
! inline void
! IOR_HARD_REG_SET (HARD_REG_SET &to, const_hard_reg_set from)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (to.elts); ++i)
!     to.elts[i] |= from.elts[i];
  }
  
! inline void
! IOR_COMPL_HARD_REG_SET (HARD_REG_SET &to, const_hard_reg_set from)
  {
!   for (unsigned int i = 0; i < ARRAY_SIZE (to.elts); ++i)
!     to.elts[i] |= ~from.elts[i];
  }
  
  static inline bool
! hard_reg_set_subset_p (const_hard_reg_set x, const_hard_reg_set y)
  {
!   HARD_REG_ELT_TYPE bad = 0;
!   for (unsigned int i = 0; i < ARRAY_SIZE (x.elts); ++i)
!     bad |= (x.elts[i] & ~y.elts[i]);
!   return bad == 0;
  }
  
  static inline bool
! hard_reg_set_equal_p (const_hard_reg_set x, const_hard_reg_set y)
  {
!   HARD_REG_ELT_TYPE bad = 0;
!   for (unsigned int i = 0; i < ARRAY_SIZE (x.elts); ++i)
!     bad |= (x.elts[i] ^ y.elts[i]);
!   return bad == 0;
  }
  
  static inline bool
! hard_reg_set_intersect_p (const_hard_reg_set x, const_hard_reg_set y)
  {
!   HARD_REG_ELT_TYPE good = 0;
!   for (unsigned int i = 0; i < ARRAY_SIZE (x.elts); ++i)
!     good |= (x.elts[i] & y.elts[i]);
!   return good != 0;
  }
  
  static inline bool
! hard_reg_set_empty_p (const_hard_reg_set x)
  {
!   HARD_REG_ELT_TYPE bad = 0;
!   for (unsigned int i = 0; i < ARRAY_SIZE (x.elts); ++i)
!     bad |= x.elts[i];
!   return bad == 0;
  }
  #endif
  
  /* Iterator for hard register sets.  */
*************** hard_reg_set_empty_p (const HARD_REG_SET
*** 515,521 ****
  struct hard_reg_set_iterator
  {
    /* Pointer to the current element.  */
!   HARD_REG_ELT_TYPE *pelt;
  
    /* The length of the set.  */
    unsigned short length;
--- 268,274 ----
  struct hard_reg_set_iterator
  {
    /* Pointer to the current element.  */
!   const HARD_REG_ELT_TYPE *pelt;
  
    /* The length of the set.  */
    unsigned short length;
*************** #define HARD_REG_ELT_BITS UHOST_BITS_PER
*** 534,544 ****
  /* The implementation of the iterator functions is fully analogous to
     the bitmap iterators.  */
  static inline void
! hard_reg_set_iter_init (hard_reg_set_iterator *iter, HARD_REG_SET set,
                          unsigned min, unsigned *regno)
  {
  #ifdef HARD_REG_SET_LONGS
!   iter->pelt = set;
    iter->length = HARD_REG_SET_LONGS;
  #else
    iter->pelt = &set;
--- 287,297 ----
  /* The implementation of the iterator functions is fully analogous to
     the bitmap iterators.  */
  static inline void
! hard_reg_set_iter_init (hard_reg_set_iterator *iter, const_hard_reg_set set,
                          unsigned min, unsigned *regno)
  {
  #ifdef HARD_REG_SET_LONGS
!   iter->pelt = set.elts;
    iter->length = HARD_REG_SET_LONGS;
  #else
    iter->pelt = &set;
Index: gcc/recog.h
===================================================================
*** gcc/recog.h	2019-09-09 16:53:27.837266363 +0100
--- gcc/recog.h	2019-09-09 16:53:27.829266420 +0100
*************** extern void preprocess_constraints (rtx_
*** 142,148 ****
  extern rtx_insn *peep2_next_insn (int);
  extern int peep2_regno_dead_p (int, int);
  extern int peep2_reg_dead_p (int, rtx);
! #ifdef CLEAR_HARD_REG_SET
  extern rtx peep2_find_free_register (int, int, const char *,
  				     machine_mode, HARD_REG_SET *);
  #endif
--- 142,148 ----
  extern rtx_insn *peep2_next_insn (int);
  extern int peep2_regno_dead_p (int, int);
  extern int peep2_reg_dead_p (int, rtx);
! #ifdef HARD_CONST
  extern rtx peep2_find_free_register (int, int, const char *,
  				     machine_mode, HARD_REG_SET *);
  #endif
Index: gcc/reload.h
===================================================================
*** gcc/reload.h	2019-09-09 16:53:27.837266363 +0100
--- gcc/reload.h	2019-09-09 16:53:27.829266420 +0100
*************** #define reg_equiv_init(ELT) \
*** 274,280 ****
  
  extern int num_not_at_initial_offset;
  
! #if defined SET_HARD_REG_BIT && defined CLEAR_REG_SET
  /* This structure describes instructions which are relevant for reload.
     Apart from all regular insns, this also includes CODE_LABELs, since they
     must be examined for register elimination.  */
--- 274,280 ----
  
  extern int num_not_at_initial_offset;
  
! #if defined HARD_CONST && defined CLEAR_REG_SET
  /* This structure describes instructions which are relevant for reload.
     Apart from all regular insns, this also includes CODE_LABELs, since they
     must be examined for register elimination.  */
*************** #define reg_equiv_init(ELT) \
*** 326,332 ****
  extern class insn_chain *new_insn_chain (void);
  #endif
  
! #if defined SET_HARD_REG_BIT
  extern void compute_use_by_pseudos (HARD_REG_SET *, bitmap);
  #endif
  
--- 326,332 ----
  extern class insn_chain *new_insn_chain (void);
  #endif
  
! #if defined HARD_CONST
  extern void compute_use_by_pseudos (HARD_REG_SET *, bitmap);
  #endif
  
Index: gcc/rtl.h
===================================================================
*** gcc/rtl.h	2019-09-09 16:53:27.837266363 +0100
--- gcc/rtl.h	2019-09-09 16:53:27.833266392 +0100
*************** extern bool val_signbit_known_clear_p (m
*** 3383,3390 ****
  				       unsigned HOST_WIDE_INT);
  
  /* In reginfo.c  */
! extern machine_mode choose_hard_reg_mode (unsigned int, unsigned int,
! 					       bool);
  extern const HARD_REG_SET &simplifiable_subregs (const subreg_shape &);
  
  /* In emit-rtl.c  */
--- 3383,3389 ----
  				       unsigned HOST_WIDE_INT);
  
  /* In reginfo.c  */
! extern machine_mode choose_hard_reg_mode (unsigned int, unsigned int, bool);
  extern const HARD_REG_SET &simplifiable_subregs (const subreg_shape &);
  
  /* In emit-rtl.c  */
Index: gcc/regs.h
===================================================================
*** gcc/regs.h	2019-09-09 16:53:27.837266363 +0100
--- gcc/regs.h	2019-09-09 16:53:27.829266420 +0100
*************** remove_from_hard_reg_set (HARD_REG_SET *
*** 298,304 ****
  /* Return true if REGS contains the whole of (reg:MODE REGNO).  */
  
  static inline bool
! in_hard_reg_set_p (const HARD_REG_SET regs, machine_mode mode,
  		   unsigned int regno)
  {
    unsigned int end_regno;
--- 298,304 ----
  /* Return true if REGS contains the whole of (reg:MODE REGNO).  */
  
  static inline bool
! in_hard_reg_set_p (const_hard_reg_set regs, machine_mode mode,
  		   unsigned int regno)
  {
    unsigned int end_regno;
*************** in_hard_reg_set_p (const HARD_REG_SET re
*** 323,329 ****
  /* Return true if (reg:MODE REGNO) includes an element of REGS.  */
  
  static inline bool
! overlaps_hard_reg_set_p (const HARD_REG_SET regs, machine_mode mode,
  			 unsigned int regno)
  {
    unsigned int end_regno;
--- 323,329 ----
  /* Return true if (reg:MODE REGNO) includes an element of REGS.  */
  
  static inline bool
! overlaps_hard_reg_set_p (const_hard_reg_set regs, machine_mode mode,
  			 unsigned int regno)
  {
    unsigned int end_regno;
*************** remove_range_from_hard_reg_set (HARD_REG
*** 363,369 ****
  /* Like overlaps_hard_reg_set_p, but use a REGNO/NREGS range instead of
     REGNO and MODE.  */
  static inline bool
! range_overlaps_hard_reg_set_p (const HARD_REG_SET set, unsigned regno,
  			       int nregs)
  {
    while (nregs-- > 0)
--- 363,369 ----
  /* Like overlaps_hard_reg_set_p, but use a REGNO/NREGS range instead of
     REGNO and MODE.  */
  static inline bool
! range_overlaps_hard_reg_set_p (const_hard_reg_set set, unsigned regno,
  			       int nregs)
  {
    while (nregs-- > 0)
*************** range_overlaps_hard_reg_set_p (const HAR
*** 375,381 ****
  /* Like in_hard_reg_set_p, but use a REGNO/NREGS range instead of
     REGNO and MODE.  */
  static inline bool
! range_in_hard_reg_set_p (const HARD_REG_SET set, unsigned regno, int nregs)
  {
    while (nregs-- > 0)
      if (!TEST_HARD_REG_BIT (set, regno + nregs))
--- 375,381 ----
  /* Like in_hard_reg_set_p, but use a REGNO/NREGS range instead of
     REGNO and MODE.  */
  static inline bool
! range_in_hard_reg_set_p (const_hard_reg_set set, unsigned regno, int nregs)
  {
    while (nregs-- > 0)
      if (!TEST_HARD_REG_BIT (set, regno + nregs))
Index: gcc/ira-costs.c
===================================================================
*** gcc/ira-costs.c	2019-09-09 16:53:27.837266363 +0100
--- gcc/ira-costs.c	2019-09-09 16:53:27.829266420 +0100
*************** setup_cost_classes (cost_classes_t from)
*** 237,243 ****
     allocated.  */
  static cost_classes_t
  restrict_cost_classes (cost_classes_t full, machine_mode mode,
! 		       const HARD_REG_SET &regs)
  {
    static struct cost_classes narrow;
    int map[N_REG_CLASSES];
--- 237,243 ----
     allocated.  */
  static cost_classes_t
  restrict_cost_classes (cost_classes_t full, machine_mode mode,
! 		       const_hard_reg_set regs)
  {
    static struct cost_classes narrow;
    int map[N_REG_CLASSES];
Index: gcc/shrink-wrap.c
===================================================================
*** gcc/shrink-wrap.c	2019-09-09 16:53:27.837266363 +0100
--- gcc/shrink-wrap.c	2019-09-09 16:53:27.833266392 +0100
*************** live_edge_for_reg (basic_block bb, int r
*** 151,158 ****
  
  static bool
  move_insn_for_shrink_wrap (basic_block bb, rtx_insn *insn,
! 			   const HARD_REG_SET uses,
! 			   const HARD_REG_SET defs,
  			   bool *split_p,
  			   struct dead_debug_local *debug)
  {
--- 151,158 ----
  
  static bool
  move_insn_for_shrink_wrap (basic_block bb, rtx_insn *insn,
! 			   const_hard_reg_set uses,
! 			   const_hard_reg_set defs,
  			   bool *split_p,
  			   struct dead_debug_local *debug)
  {
Index: gcc/config/epiphany/resolve-sw-modes.c
===================================================================
*** gcc/config/epiphany/resolve-sw-modes.c	2019-09-09 16:53:27.837266363 +0100
--- gcc/config/epiphany/resolve-sw-modes.c	2019-09-09 16:53:27.825266448 +0100
*************** pass_resolve_sw_modes::execute (function
*** 167,173 ****
  	    }
  	  start_sequence ();
  	  emit_set_fp_mode (EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN,
! 			    jilted_mode, FP_MODE_NONE, NULL);
  	  seq = get_insns ();
  	  end_sequence ();
  	  need_commit = true;
--- 167,174 ----
  	    }
  	  start_sequence ();
  	  emit_set_fp_mode (EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN,
! 			    jilted_mode, FP_MODE_NONE,
! 			    reg_class_contents[NO_REGS]);
  	  seq = get_insns ();
  	  end_sequence ();
  	  need_commit = true;
Index: gcc/config/ia64/ia64.c
===================================================================
*** gcc/config/ia64/ia64.c	2019-09-09 16:53:27.837266363 +0100
--- gcc/config/ia64/ia64.c	2019-09-09 16:53:27.829266420 +0100
*************** struct reg_write_state
*** 6230,6249 ****
  struct reg_write_state rws_sum[NUM_REGS];
  #if CHECKING_P
  /* Bitmap whether a register has been written in the current insn.  */
! HARD_REG_ELT_TYPE rws_insn[(NUM_REGS + HOST_BITS_PER_WIDEST_FAST_INT - 1)
! 			   / HOST_BITS_PER_WIDEST_FAST_INT];
  
  static inline void
! rws_insn_set (int regno)
  {
!   gcc_assert (!TEST_HARD_REG_BIT (rws_insn, regno));
!   SET_HARD_REG_BIT (rws_insn, regno);
  }
  
  static inline int
! rws_insn_test (int regno)
  {
!   return TEST_HARD_REG_BIT (rws_insn, regno);
  }
  #else
  /* When not checking, track just REG_AR_CFM and REG_VOLATILE.  */
--- 6230,6254 ----
  struct reg_write_state rws_sum[NUM_REGS];
  #if CHECKING_P
  /* Bitmap whether a register has been written in the current insn.  */
! unsigned HOST_WIDEST_FAST_INT rws_insn
!   [(NUM_REGS + HOST_BITS_PER_WIDEST_FAST_INT - 1)
!    / HOST_BITS_PER_WIDEST_FAST_INT];
  
  static inline void
! rws_insn_set (unsigned int regno)
  {
!   unsigned int elt = regno / HOST_BITS_PER_WIDEST_FAST_INT;
!   unsigned int bit = regno % HOST_BITS_PER_WIDEST_FAST_INT;
!   gcc_assert (!((rws_insn[elt] >> bit) & 1));
!   rws_insn[elt] |= (unsigned HOST_WIDEST_FAST_INT) 1 << bit;
  }
  
  static inline int
! rws_insn_test (unsigned int regno)
  {
!   unsigned int elt = regno / HOST_BITS_PER_WIDEST_FAST_INT;
!   unsigned int bit = regno % HOST_BITS_PER_WIDEST_FAST_INT;
!   return (rws_insn[elt] >> bit) & 1;
  }
  #else
  /* When not checking, track just REG_AR_CFM and REG_VOLATILE.  */
Jeff Law Sept. 9, 2019, 5:35 p.m. UTC | #2
On 9/9/19 9:53 AM, Richard Sandiford wrote:
> We have two styles of HARD_REG_SET: a single integer based on
> HOST_WIDEST_FAST_INT (used when FIRST_PSEUDO_REGISTER is small enough)
> or an array of integers.  One of the nice properties of this arrangement
> is that:
> 
>   void foo (const HARD_REG_SET);
> 
> is passed by value as an integer when the set is small enough and
> by reference otherwise.
True, but I suspect that few, if any, important targets are able to use
the simple integer version.


> 
> However, one of the disadvantages of using an array is that simple
> assignment doesn't work.  We need to use COPY_HARD_REG_SET instead.
RIght.  Clear historical wart.

> 
> This patch uses a structure wrapper around the array, and preserves
> the above "nice property" using a new const_hard_reg_set typedef.
> The patch also removes the manual unrolling for small array sizes;
> I think these days we can rely on the compiler to do that for us.
Certainly makes sense to me.  Most of this was designed in the "early
days" when most targets had small register files.

> 
> This meant fixing two port-specific quirks:
> 
> - epiphany passed NULL as a HARD_REG_SET whose value doesn't matter.
>   The patch passes the NO_REGS set instead.
> 
> - ia64 reused TEST_HARD_REG_BIT and SET_HARD_REG_BIT for arrays that
>   are bigger than HARD_REG_SET.  The patch just open-codes them.
> 
> The patch is probably being too conservative.  Very few places actually
> take advantage of the "nice property" above, and we could have a
> cleaner interface if we used a structure wrapper for all cases.
I wouldn't object to dropping the "nice property".  I doubt it matters
anymore and if the result is cleaner and easier to work with, then it's
a net win.

> 
> 
> 2019-09-09  Richard Sandiford  <richard.sandiford@arm.com>
> 
> gcc/
> 	* hard-reg-set.h (HARD_REG_SET): Define using a typedef rather
> 	than a #define.  Use a structure rather than an array as the
> 	fallback definition.  Remove special cases for low array sizes.
> 	(const_hard_reg_set): New typedef.
> 	(hard_reg_set_subset_p): Use it instead of "const HARD_REG_SET".
> 	(hard_reg_set_equal_p, hard_reg_set_intersect_p): Likewise.
> 	(hard_reg_set_empty_p): Likewise.
> 	(SET_HARD_REG_BIT): Use a function rather than a macro to
> 	handle the case in which HARD_REG_SET is a structure.
> 	(CLEAR_HARD_REG_BIT, TEST_HARD_REG_BIT, CLEAR_HARD_REG_SET)
> 	(SET_HARD_REG_SET, COPY_HARD_REG_SET, COMPL_HARD_REG_SET)
> 	(AND_HARD_REG_SET, AND_COMPL_HARD_REG_SET, IOR_HARD_REG_SET)
> 	(IOR_COMPL_HARD_REG_SET): Likewise.
> 	(hard_reg_set_iterator::pset): Constify the pointer target.
> 	(hard_reg_set_iter_init): Take a const_hard_reg_set instead
> 	of a "const HARD_REG_SET".  Update the handling of non-integer
> 	HARD_REG_SETs.
> 	* recog.h: Test HARD_CONST instead of CLEAR_HARD_REG_SET.
> 	* reload.h: Likewise.
> 	* rtl.h (choose_hard_reg_mode): Remove unnecessary line break.
> 	* regs.h (in_hard_reg_set_p): Take a const_hard_reg_set instead
> 	of a "const HARD_REG_SET".
> 	(overlaps_hard_reg_set_p, range_overlaps_hard_reg_set_p): Likewise.
> 	(range_in_hard_reg_set_p): Likewise.
> 	* ira-costs.c (restrict_cost_classes): Likewise.
> 	* shrink-wrap.c (move_insn_for_shrink_wrap): Likewise.
> 	* config/epiphany/resolve-sw-modes.c (pass_resolve_sw_modes::execute):
> 	Pass a NO_REGS HARD_REG_SET rather than NULL to emit_set_fp_mode.
> 	* config/ia64/ia64.c (rws_insn): In the CHECKING_P version,
> 	use unsigned HOST_WIDEST_FAST_INT rather than HARD_REG_ELT_TYPE.
> 	(rws_insn_set, rws_insn_test): In the CHECKING_P version,
> 	take an unsigned int and open-code the HARD_REG_SET operations.

OK

jeff
Richard Sandiford Sept. 9, 2019, 6:05 p.m. UTC | #3
Jeff Law <law@redhat.com> writes:
> On 9/9/19 9:53 AM, Richard Sandiford wrote:
>> We have two styles of HARD_REG_SET: a single integer based on
>> HOST_WIDEST_FAST_INT (used when FIRST_PSEUDO_REGISTER is small enough)
>> or an array of integers.  One of the nice properties of this arrangement
>> is that:
>> 
>>   void foo (const HARD_REG_SET);
>> 
>> is passed by value as an integer when the set is small enough and
>> by reference otherwise.
> True, but I suspect that few, if any, important targets are able to use
> the simple integer version.
>
>
>> 
>> However, one of the disadvantages of using an array is that simple
>> assignment doesn't work.  We need to use COPY_HARD_REG_SET instead.
> RIght.  Clear historical wart.
>
>> 
>> This patch uses a structure wrapper around the array, and preserves
>> the above "nice property" using a new const_hard_reg_set typedef.
>> The patch also removes the manual unrolling for small array sizes;
>> I think these days we can rely on the compiler to do that for us.
> Certainly makes sense to me.  Most of this was designed in the "early
> days" when most targets had small register files.
>
>> 
>> This meant fixing two port-specific quirks:
>> 
>> - epiphany passed NULL as a HARD_REG_SET whose value doesn't matter.
>>   The patch passes the NO_REGS set instead.
>> 
>> - ia64 reused TEST_HARD_REG_BIT and SET_HARD_REG_BIT for arrays that
>>   are bigger than HARD_REG_SET.  The patch just open-codes them.
>> 
>> The patch is probably being too conservative.  Very few places actually
>> take advantage of the "nice property" above, and we could have a
>> cleaner interface if we used a structure wrapper for all cases.
> I wouldn't object to dropping the "nice property".  I doubt it matters
> anymore and if the result is cleaner and easier to work with, then it's
> a net win.

Yeah.  I might come back to this later and look at a fuller transition
to C++ (or at least to try to get rid of CLEAR_HARD_REG_SET).

Thanks,
Richard
Oleg Endo Sept. 10, 2019, 9:16 a.m. UTC | #4
On Mon, 2019-09-09 at 19:05 +0100, Richard Sandiford wrote:
> 
> Yeah.  I might come back to this later and look at a fuller
> transition
> to C++ (or at least to try to get rid of CLEAR_HARD_REG_SET).
> 

Maybe you can just typedef it to std::bitset ;)

Cheers,
Oleg