Base subreg rules on REGMODE_NATURAL_SIZE rather than UNITS_PER_WORD
authorRichard Sandiford <richard.sandiford@linaro.org>
Thu, 9 Nov 2017 14:22:39 +0000 (14:22 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Thu, 9 Nov 2017 14:22:39 +0000 (14:22 +0000)
Originally subregs operated at the word level and subreg offsets
were measured in words.  The offset units were later changed from
words to bytes (SUBREG_WORD became SUBREG_BYTE), but the fundamental
assumption that subregs should operate at the word level remained.
Whether (subreg:M1 (reg:M2 R2) N) is well-formed depended on the
way that M1 and M2 partitioned into words and whether the subword
part of N represented a lowpart.  However, some questions depended
instead on the macro REGMODE_NATURAL_SIZE, which was introduced
as part of the patch that moved from SUBREG_WORD to SUBREG_BYTE.
It is used to decide whether setting (subreg:M1 (reg:M2 R2) N)
clobbers all of R2 or just part of it (df_read_modify_subreg).

Using words doesn't really make sense for modern vector
architectures.  Vector registers are usually bigger than
a word and:

(a) setting the scalar lowpart of them usually clobbers the
    rest of the register (contrary to the subreg rules,
    where only the containing words should be clobbered).

(b) high words of vector registers are often not independently
    addressable, even though that's what the subreg rules expect.

This patch therefore uses REGMODE_NATURAL_SIZE instead of
UNITS_PER_WORD to determine the size of the independently
addressable blocks in an inner register.

This is needed for SVE because the number of words in a vector
mode isn't known at compile time, so isn't a sensible basis
for calculating the number of registers.

The only existing port to define REGMODE_NATURAL_SIZE is
64-bit SPARC, where FP registers are 32 bits.  (This is the
opposite of the use case for SVE, since the natural division
is smaller than a word.)  I compiled the testsuite before and
after the patch for sparc64-linux-gnu and the only test whose
assembly changed was g++.dg/debug/pr65678.C, where the order
of two independent stores was reversed and where a different
register was picked for one pseudo.  The new code was
otherwise equivalent to the old code.

2017-11-09  Richard Sandiford  <richard.sandiford@linaro.org>
    Alan Hayward  <alan.hayward@arm.com>
    David Sherwood  <david.sherwood@arm.com>

gcc/
* doc/rtl.texi: Rewrite the subreg rules so that they partition
the inner register into REGMODE_NATURAL_SIZE bytes rather than
UNITS_PER_WORD bytes.
* emit-rtl.c (validate_subreg): Divide subregs into blocks
based on REGMODE_NATURAL_SIZE of the inner mode.
(gen_lowpart_common): Split the SCALAR_FLOAT_MODE_P and
!SCALAR_FLOAT_MODE_P cases.  Use REGMODE_NATURAL_SIZE for the latter.
* expmed.c (lowpart_bit_field_p): Divide the value up into
chunks of REGMODE_NATURAL_SIZE rather than UNITS_PER_WORD.
* expr.c (store_constructor): Use REGMODE_NATURAL_SIZE to test
whether something is likely to occupy more than one register.

Co-Authored-By: Alan Hayward <alan.hayward@arm.com>
Co-Authored-By: David Sherwood <david.sherwood@arm.com>
From-SVN: r254583

gcc/ChangeLog
gcc/doc/rtl.texi
gcc/emit-rtl.c
gcc/expmed.c
gcc/expr.c

index 0953b4a36810c49446e3e9f468dfce6ecd62b698..c4e9444b51024b79699e3d7c362029abacaf686a 100644 (file)
@@ -1,3 +1,19 @@
+2017-11-09  Richard Sandiford  <richard.sandiford@linaro.org>
+           Alan Hayward  <alan.hayward@arm.com>
+           David Sherwood  <david.sherwood@arm.com>
+
+       * doc/rtl.texi: Rewrite the subreg rules so that they partition
+       the inner register into REGMODE_NATURAL_SIZE bytes rather than
+       UNITS_PER_WORD bytes.
+       * emit-rtl.c (validate_subreg): Divide subregs into blocks
+       based on REGMODE_NATURAL_SIZE of the inner mode.
+       (gen_lowpart_common): Split the SCALAR_FLOAT_MODE_P and
+       !SCALAR_FLOAT_MODE_P cases.  Use REGMODE_NATURAL_SIZE for the latter.
+       * expmed.c (lowpart_bit_field_p): Divide the value up into
+       chunks of REGMODE_NATURAL_SIZE rather than UNITS_PER_WORD.
+       * expr.c (store_constructor): Use REGMODE_NATURAL_SIZE to test
+       whether something is likely to occupy more than one register.
+
 2017-11-09  Jan Hubicka  <hubicka@ucw.cz>
 
        PR ipa/82879
index 30fc68830ec699896307c7eaf2e4865b7215f9f2..7e2925ad92a9f1f5c61839081859d8114b8b80de 100644 (file)
@@ -1925,19 +1925,32 @@ false.
 When @var{m1} is at least as narrow as @var{m2} the @code{subreg}
 expression is called @dfn{normal}.
 
+@findex REGMODE_NATURAL_SIZE
 Normal @code{subreg}s restrict consideration to certain bits of
-@var{reg}.  There are two cases.  If @var{m1} is smaller than a word,
-the @code{subreg} refers to the least-significant part (or
-@dfn{lowpart}) of one word of @var{reg}.  If @var{m1} is word-sized or
-greater, the @code{subreg} refers to one or more complete words.
-
-When used as an lvalue, @code{subreg} is a word-based accessor.
-Storing to a @code{subreg} modifies all the words of @var{reg} that
-overlap the @code{subreg}, but it leaves the other words of @var{reg}
+@var{reg}.  For this purpose, @var{reg} is divided into
+individually-addressable blocks in which each block has:
+
+@smallexample
+REGMODE_NATURAL_SIZE (@var{m2})
+@end smallexample
+
+bytes.  Usually the value is @code{UNITS_PER_WORD}; that is,
+most targets usually treat each word of a register as being
+independently addressable.
+
+There are two types of normal @code{subreg}.  If @var{m1} is known
+to be no bigger than a block, the @code{subreg} refers to the
+least-significant part (or @dfn{lowpart}) of one block of @var{reg}.
+If @var{m1} is known to be larger than a block, the @code{subreg} refers
+to two or more complete blocks.
+
+When used as an lvalue, @code{subreg} is a block-based accessor.
+Storing to a @code{subreg} modifies all the blocks of @var{reg} that
+overlap the @code{subreg}, but it leaves the other blocks of @var{reg}
 alone.
 
-When storing to a normal @code{subreg} that is smaller than a word,
-the other bits of the referenced word are usually left in an undefined
+When storing to a normal @code{subreg} that is smaller than a block,
+the other bits of the referenced block are usually left in an undefined
 state.  This laxity makes it easier to generate efficient code for
 such instructions.  To represent an instruction that preserves all the
 bits outside of those in the @code{subreg}, use @code{strict_low_part}
@@ -1995,10 +2008,11 @@ number of undefined bits.  For example:
 (subreg:PSI (reg:SI 0) 0)
 @end smallexample
 
+@findex REGMODE_NATURAL_SIZE
 accesses the whole of @samp{(reg:SI 0)}, but the exact relationship
 between the @code{PSImode} value and the @code{SImode} value is not
-defined.  If we assume @samp{UNITS_PER_WORD <= 4}, then the following
-two @code{subreg}s:
+defined.  If we assume @samp{REGMODE_NATURAL_SIZE (DImode) <= 4},
+then the following two @code{subreg}s:
 
 @smallexample
 (subreg:PSI (reg:DI 0) 0)
@@ -2009,7 +2023,7 @@ represent independent 4-byte accesses to the two halves of
 @samp{(reg:DI 0)}.  Both @code{subreg}s have an unknown number
 of undefined bits.
 
-If @samp{UNITS_PER_WORD <= 2} then these two @code{subreg}s:
+If @samp{REGMODE_NATURAL_SIZE (PSImode) <= 2} then these two @code{subreg}s:
 
 @smallexample
 (subreg:HI (reg:PSI 0) 0)
@@ -2882,7 +2896,7 @@ The presence of @code{strict_low_part} says that the part of the
 register which is meaningful in mode @var{n}, but is not part of
 mode @var{m}, is not to be altered.  Normally, an assignment to such
 a subreg is allowed to have undefined effects on the rest of the
-register when @var{m} is less than a word.
+register when @var{m} is smaller than @samp{REGMODE_NATURAL_SIZE (@var{n})}.
 @end table
 
 @node Side Effects
index da4f53315a6cb2f51cb1b22c78dbf33f7c84aabd..ac6fd6a98ad2de89d4b831286bd72fbcf9e81598 100644 (file)
@@ -816,6 +816,8 @@ validate_subreg (machine_mode omode, machine_mode imode,
   if (offset >= isize)
     return false;
 
+  unsigned int regsize = REGMODE_NATURAL_SIZE (imode);
+
   /* ??? This should not be here.  Temporarily continue to allow word_mode
      subregs of anything.  The most common offender is (subreg:SI (reg:DF)).
      Generally, backends are doing something sketchy but it'll take time to
@@ -824,7 +826,7 @@ validate_subreg (machine_mode omode, machine_mode imode,
     ;
   /* ??? Similarly, e.g. with (subreg:DF (reg:TI)).  Though store_bit_field
      is the culprit here, and not the backends.  */
-  else if (osize >= UNITS_PER_WORD && isize >= osize)
+  else if (osize >= regsize && isize >= osize)
     ;
   /* Allow component subregs of complex and vector.  Though given the below
      extraction rules, it's not always clear what that means.  */
@@ -876,17 +878,23 @@ validate_subreg (machine_mode omode, machine_mode imode,
     }
 
   /* For pseudo registers, we want most of the same checks.  Namely:
-     If the register no larger than a word, the subreg must be lowpart.
-     If the register is larger than a word, the subreg must be the lowpart
-     of a subword.  A subreg does *not* perform arbitrary bit extraction.
-     Given that we've already checked mode/offset alignment, we only have
-     to check subword subregs here.  */
-  if (osize < UNITS_PER_WORD
+
+     Assume that the pseudo register will be allocated to hard registers
+     that can hold REGSIZE bytes each.  If OSIZE is not a multiple of REGSIZE,
+     the remainder must correspond to the lowpart of the containing hard
+     register.  If BYTES_BIG_ENDIAN, the lowpart is at the highest offset,
+     otherwise it is at the lowest offset.
+
+     Given that we've already checked the mode and offset alignment,
+     we only have to check subblock subregs here.  */
+  if (osize < regsize
       && ! (lra_in_progress && (FLOAT_MODE_P (imode) || FLOAT_MODE_P (omode))))
     {
-      machine_mode wmode = isize > UNITS_PER_WORD ? word_mode : imode;
-      unsigned int low_off = subreg_lowpart_offset (omode, wmode);
-      if (offset % UNITS_PER_WORD != low_off)
+      unsigned int block_size = MIN (isize, regsize);
+      unsigned int offset_within_block = offset % block_size;
+      if (BYTES_BIG_ENDIAN
+         ? offset_within_block != block_size - osize
+         : offset_within_block != 0)
        return false;
     }
   return true;
@@ -1439,14 +1447,21 @@ gen_lowpart_common (machine_mode mode, rtx x)
   if (innermode == mode)
     return x;
 
-  /* MODE must occupy no more words than the mode of X.  */
-  if ((msize + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD
-      > ((xsize + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
-    return 0;
-
-  /* Don't allow generating paradoxical FLOAT_MODE subregs.  */
-  if (SCALAR_FLOAT_MODE_P (mode) && msize > xsize)
-    return 0;
+  if (SCALAR_FLOAT_MODE_P (mode))
+    {
+      /* Don't allow paradoxical FLOAT_MODE subregs.  */
+      if (msize > xsize)
+       return 0;
+    }
+  else
+    {
+      /* MODE must occupy no more of the underlying registers than X.  */
+      unsigned int regsize = REGMODE_NATURAL_SIZE (innermode);
+      unsigned int mregs = CEIL (msize, regsize);
+      unsigned int xregs = CEIL (xsize, regsize);
+      if (mregs > xregs)
+       return 0;
+    }
 
   scalar_int_mode int_mode, int_innermode, from_mode;
   if ((GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
index 00d2f1102bcfcf8e4f294dab92a2fe18912d48a9..8e9f15d6a65fb06acd317da5b9ad9e3e42e9aeaa 100644 (file)
@@ -506,12 +506,13 @@ lowpart_bit_field_p (unsigned HOST_WIDE_INT bitnum,
                     unsigned HOST_WIDE_INT bitsize,
                     machine_mode struct_mode)
 {
+  unsigned HOST_WIDE_INT regsize = REGMODE_NATURAL_SIZE (struct_mode);
   if (BYTES_BIG_ENDIAN)
     return (bitnum % BITS_PER_UNIT == 0
            && (bitnum + bitsize == GET_MODE_BITSIZE (struct_mode)
-               || (bitnum + bitsize) % BITS_PER_WORD == 0));
+               || (bitnum + bitsize) % (regsize * BITS_PER_UNIT) == 0));
   else
-    return bitnum % BITS_PER_WORD == 0;
+    return bitnum % (regsize * BITS_PER_UNIT) == 0;
 }
 
 /* Return true if -fstrict-volatile-bitfields applies to an access of OP0
index 649a057e43ddb67d942e7795ecdda4883603a220..76684c11cc3ee2c98f4ae73d11565c0a899bd6cf 100644 (file)
@@ -6186,7 +6186,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size,
           a constant.  But if more than one register is involved,
           this probably loses.  */
        else if (REG_P (target) && TREE_STATIC (exp)
-                && GET_MODE_SIZE (GET_MODE (target)) <= UNITS_PER_WORD)
+                && (GET_MODE_SIZE (GET_MODE (target))
+                    <= REGMODE_NATURAL_SIZE (GET_MODE (target))))
          {
            emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
            cleared = 1;