Extra subreg fold for variable-length CONST_VECTORs
authorRichard Sandiford <richard.sandiford@linaro.org>
Sat, 13 Jan 2018 17:50:13 +0000 (17:50 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 13 Jan 2018 17:50:13 +0000 (17:50 +0000)
The SVE support for the new CONST_VECTOR encoding needs to be able
to extract the first N bits of the vector and duplicate it.  This patch
adds a simplify_subreg rule for that.

The code is covered by the gcc.target/aarch64/sve_slp_*.c tests.

2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>

gcc/
* simplify-rtx.c (simplify_immed_subreg): Add an inner_bytes
parameter and use it instead of GET_MODE_SIZE (innermode).  Use
inner_bytes * BITS_PER_UNIT instead of GET_MODE_BITSIZE (innermode).
Use CEIL (inner_bytes, GET_MODE_UNIT_SIZE (innermode)) instead of
GET_MODE_NUNITS (innermode).  Also add a first_elem parameter.
Change innermode from fixed_mode_size to machine_mode.
(simplify_subreg): Update call accordingly.  Handle a constant-sized
subreg of a variable-length CONST_VECTOR.

From-SVN: r256610

gcc/ChangeLog
gcc/simplify-rtx.c

index 561fd708dae5ab25fad58b67daee80de347d59c2..3f1919e774f78e8c82ee32af6176bbca2dd5f70d 100644 (file)
@@ -1,3 +1,14 @@
+2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       * simplify-rtx.c (simplify_immed_subreg): Add an inner_bytes
+       parameter and use it instead of GET_MODE_SIZE (innermode).  Use
+       inner_bytes * BITS_PER_UNIT instead of GET_MODE_BITSIZE (innermode).
+       Use CEIL (inner_bytes, GET_MODE_UNIT_SIZE (innermode)) instead of
+       GET_MODE_NUNITS (innermode).  Also add a first_elem parameter.
+       Change innermode from fixed_mode_size to machine_mode.
+       (simplify_subreg): Update call accordingly.  Handle a constant-sized
+       subreg of a variable-length CONST_VECTOR.
+
 2018-01-13  Richard Sandiford  <richard.sandiford@linaro.org>
            Alan Hayward  <alan.hayward@arm.com>
            David Sherwood  <david.sherwood@arm.com>
index b052fbbe94952d59739961043c34c2fa1ee82b18..b0a0178310141eb67ca431e67cc914eae2d12893 100644 (file)
@@ -5924,13 +5924,16 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
    or CONST_FIXED or CONST_VECTOR, returning another CONST_INT or
    CONST_WIDE_INT or CONST_DOUBLE or CONST_FIXED or CONST_VECTOR.
 
-   Works by unpacking OP into a collection of 8-bit values
+   Works by unpacking INNER_BYTES bytes of OP into a collection of 8-bit values
    represented as a little-endian array of 'unsigned char', selecting by BYTE,
-   and then repacking them again for OUTERMODE.  */
+   and then repacking them again for OUTERMODE.  If OP is a CONST_VECTOR,
+   FIRST_ELEM is the number of the first element to extract, otherwise
+   FIRST_ELEM is ignored.  */
 
 static rtx
 simplify_immed_subreg (fixed_size_mode outermode, rtx op,
-                      fixed_size_mode innermode, unsigned int byte)
+                      machine_mode innermode, unsigned int byte,
+                      unsigned int first_elem, unsigned int inner_bytes)
 {
   enum {
     value_bit = 8,
@@ -5960,13 +5963,13 @@ simplify_immed_subreg (fixed_size_mode outermode, rtx op,
 
   /* We support any size mode.  */
   max_bitsize = MAX (GET_MODE_BITSIZE (outermode),
-                    GET_MODE_BITSIZE (innermode));
+                    inner_bytes * BITS_PER_UNIT);
 
   /* Unpack the value.  */
 
   if (GET_CODE (op) == CONST_VECTOR)
     {
-      num_elem = GET_MODE_NUNITS (innermode);
+      num_elem = CEIL (inner_bytes, GET_MODE_UNIT_SIZE (innermode));
       elem_bitsize = GET_MODE_UNIT_BITSIZE (innermode);
     }
   else
@@ -5983,7 +5986,7 @@ simplify_immed_subreg (fixed_size_mode outermode, rtx op,
     {
       unsigned char * vp;
       rtx el = (GET_CODE (op) == CONST_VECTOR
-               ? CONST_VECTOR_ELT (op, elem)
+               ? CONST_VECTOR_ELT (op, first_elem + elem)
                : op);
 
       /* Vectors are kept in target memory order.  (This is probably
@@ -6110,10 +6113,9 @@ simplify_immed_subreg (fixed_size_mode outermode, rtx op,
   /* Renumber BYTE so that the least-significant byte is byte 0.  A special
      case is paradoxical SUBREGs, which shouldn't be adjusted since they
      will already have offset 0.  */
-  if (GET_MODE_SIZE (innermode) >= GET_MODE_SIZE (outermode))
+  if (inner_bytes >= GET_MODE_SIZE (outermode))
     {
-      unsigned ibyte = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode)
-                       - byte);
+      unsigned ibyte = inner_bytes - GET_MODE_SIZE (outermode) - byte;
       unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
       unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
       byte = (subword_byte % UNITS_PER_WORD
@@ -6122,7 +6124,7 @@ simplify_immed_subreg (fixed_size_mode outermode, rtx op,
 
   /* BYTE should still be inside OP.  (Note that BYTE is unsigned,
      so if it's become negative it will instead be very large.)  */
-  gcc_assert (byte < GET_MODE_SIZE (innermode));
+  gcc_assert (byte < inner_bytes);
 
   /* Convert from bytes to chunks of size value_bit.  */
   value_start = byte * (BITS_PER_UNIT / value_bit);
@@ -6311,7 +6313,18 @@ simplify_subreg (machine_mode outermode, rtx op,
       if (is_a <fixed_size_mode> (outermode, &fs_outermode)
          && is_a <fixed_size_mode> (innermode, &fs_innermode)
          && byte.is_constant (&cbyte))
-       return simplify_immed_subreg (fs_outermode, op, fs_innermode, cbyte);
+       return simplify_immed_subreg (fs_outermode, op, fs_innermode, cbyte,
+                                     0, GET_MODE_SIZE (fs_innermode));
+
+      /* Handle constant-sized outer modes and variable-sized inner modes.  */
+      unsigned HOST_WIDE_INT first_elem;
+      if (GET_CODE (op) == CONST_VECTOR
+         && is_a <fixed_size_mode> (outermode, &fs_outermode)
+         && constant_multiple_p (byte, GET_MODE_UNIT_SIZE (innermode),
+                                 &first_elem))
+       return simplify_immed_subreg (fs_outermode, op, innermode, 0,
+                                     first_elem,
+                                     GET_MODE_SIZE (fs_outermode));
 
       return NULL_RTX;
     }