[RS6000] num_insns_constant ICE
authorAlan Modra <amodra@gmail.com>
Fri, 30 Nov 2018 13:33:18 +0000 (00:03 +1030)
committerAlan Modra <amodra@gcc.gnu.org>
Fri, 30 Nov 2018 13:33:18 +0000 (00:03 +1030)
This patch came about from investigating an ICE that appeared when I
was retesting an old half-baked patch of mine to rs6000_rtx_costs.
If a const_double is fed to rs6000_is_valid_and_mask and from there to
rs6000_is_valid_mask where INTVAL is used, gcc will ICE.

The num_insns_constant ICE was introduced with git commit f337168d97.
However, the code was buggy before that.  There was no point in
testing for a mask since the mask predicates only handle const_int.
In fact, I don't think the function ever handled floating point
constants that might match a load of minus one and mask.  It does now.
I've added a few comments regarding splitters so the next person
looking at this code can see how this works.

The patch also extracts code out of num_insns_constant that needed to
handle multiple gprs for DFmode constants in 32-bit mode, to a
function that handles multiple gprs a little more generally.  I don't
think there is any need for anything but the 32-bit DFmode case
currently, but this allows for possible future uses.  The
CONST_WIDE_INT case is also not used currently, and needed fixing.
Adding CONST_WIDE_INT_NUNITS - 1 only makes sense if the elements of
the array were being shifted into a register of size larger than the
element size (which is 64-bits).

* config/rs6000/rs6000.c (num_insns_constant_gpr): Renamed from
num_insns_constant_wide.  Make static.  Revise comment.
(num_insns_constant_multi): New function.
(num_insns_constant): Formatting.  Correct CONST_WIDE_INT
calculation.  Simplify and extract code common to both
CONST_INT and CONST_DOUBLE.  Add gcc_unreachable for unhandled
const_double modes.
* config/rs6000/rs6000-protos.h (num_insns_const_wide): Delete.

From-SVN: r266662

gcc/ChangeLog
gcc/config/rs6000/rs6000-protos.h
gcc/config/rs6000/rs6000.c

index de0acb9d50dd898962475bdd4541e6181ea44dfa..617a3d64d2ae0292f8fe21ee8b84ea2ab91d4e21 100644 (file)
@@ -1,3 +1,14 @@
+2018-11-30  Alan Modra  <amodra@gmail.com>
+
+       * config/rs6000/rs6000.c (num_insns_constant_gpr): Renamed from
+       num_insns_constant_wide.  Make static.  Revise comment.
+       (num_insns_constant_multi): New function.
+       (num_insns_constant): Formatting.  Correct CONST_WIDE_INT
+       calculation.  Simplify and extract code common to both
+       CONST_INT and CONST_DOUBLE.  Add gcc_unreachable for unhandled
+       const_double modes.
+       * config/rs6000/rs6000-protos.h (num_insns_const_wide): Delete.
+
 2018-11-30  Richard Biener  <rguenther@suse.de>
 
        PR tree-optimization/88274
index 1dfe7995ff15eeb023a991b47fb203d0af2739f4..dfee1f28aa9ed3d4907999ee21c576fb6342be3e 100644 (file)
@@ -36,7 +36,6 @@ extern int vspltis_shifted (rtx);
 extern HOST_WIDE_INT const_vector_elt_as_int (rtx, unsigned int);
 extern bool macho_lo_sum_memory_operand (rtx, machine_mode);
 extern int num_insns_constant (rtx, machine_mode);
-extern int num_insns_constant_wide (HOST_WIDE_INT);
 extern int small_data_operand (rtx, machine_mode);
 extern bool mem_operand_gpr (rtx, machine_mode);
 extern bool mem_operand_ds_form (rtx, machine_mode);
index 02e69c103ec60cb06af1676e5c7ccf0e107812f1..fe58bc1a3dddc96465281d8e689d07ee27623668 100644 (file)
@@ -5821,11 +5821,12 @@ direct_return (void)
   return 0;
 }
 
-/* Return the number of instructions it takes to form a constant in an
-   integer register.  */
+/* Helper for num_insns_constant.  Calculate number of instructions to
+   load VALUE to a single gpr using combinations of addi, addis, ori,
+   oris and sldi instructions.  */
 
-int
-num_insns_constant_wide (HOST_WIDE_INT value)
+static int
+num_insns_constant_gpr (HOST_WIDE_INT value)
 {
   /* signed constant loadable with addi */
   if (((unsigned HOST_WIDE_INT) value + 0x8000) < 0x10000)
@@ -5847,85 +5848,108 @@ num_insns_constant_wide (HOST_WIDE_INT value)
       high >>= 1;
 
       if (low == 0)
-       return num_insns_constant_wide (high) + 1;
+       return num_insns_constant_gpr (high) + 1;
       else if (high == 0)
-       return num_insns_constant_wide (low) + 1;
+       return num_insns_constant_gpr (low) + 1;
       else
-       return (num_insns_constant_wide (high)
-               + num_insns_constant_wide (low) + 1);
+       return (num_insns_constant_gpr (high)
+               + num_insns_constant_gpr (low) + 1);
     }
 
   else
     return 2;
 }
 
+/* Helper for num_insns_constant.  Allow constants formed by the
+   num_insns_constant_gpr sequences, plus li -1, rldicl/rldicr/rlwinm,
+   and handle modes that require multiple gprs.  */
+
+static int
+num_insns_constant_multi (HOST_WIDE_INT value, machine_mode mode)
+{
+  int nregs = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  int total = 0;
+  while (nregs-- > 0)
+    {
+      HOST_WIDE_INT low = sext_hwi (value, BITS_PER_WORD);
+      int insns = num_insns_constant_gpr (low);
+      if (insns > 2
+         /* We won't get more than 2 from num_insns_constant_gpr
+            except when TARGET_POWERPC64 and mode is DImode or
+            wider, so the register mode must be DImode.  */
+         && rs6000_is_valid_and_mask (GEN_INT (low), DImode))
+       insns = 2;
+      total += insns;
+      value >>= BITS_PER_WORD;
+    }
+  return total;
+}
+
+/* Return the number of instructions it takes to form a constant in as
+   many gprs are needed for MODE.  */
+
 int
 num_insns_constant (rtx op, machine_mode mode)
 {
-  HOST_WIDE_INT low, high;
+  HOST_WIDE_INT val;
 
   switch (GET_CODE (op))
     {
     case CONST_INT:
-      if ((INTVAL (op) >> 31) != 0 && (INTVAL (op) >> 31) != -1
-         && rs6000_is_valid_and_mask (op, mode))
-       return 2;
-      else
-       return num_insns_constant_wide (INTVAL (op));
+      val = INTVAL (op);
+      break;
 
     case CONST_WIDE_INT:
       {
-       int i;
-       int ins = CONST_WIDE_INT_NUNITS (op) - 1;
-       for (i = 0; i < CONST_WIDE_INT_NUNITS (op); i++)
-         ins += num_insns_constant_wide (CONST_WIDE_INT_ELT (op, i));
-       return ins;
+       int insns = 0;
+       for (int i = 0; i < CONST_WIDE_INT_NUNITS (op); i++)
+         insns += num_insns_constant_multi (CONST_WIDE_INT_ELT (op, i),
+                                            DImode);
+       return insns;
       }
 
-      case CONST_DOUBLE:
+    case CONST_DOUBLE:
+      {
+       const struct real_value *rv = CONST_DOUBLE_REAL_VALUE (op);
+
        if (mode == SFmode || mode == SDmode)
          {
            long l;
 
-           if (DECIMAL_FLOAT_MODE_P (mode))
-             REAL_VALUE_TO_TARGET_DECIMAL32
-               (*CONST_DOUBLE_REAL_VALUE (op), l);
+           if (mode == SDmode)
+             REAL_VALUE_TO_TARGET_DECIMAL32 (*rv, l);
            else
-             REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (op), l);
-           return num_insns_constant_wide ((HOST_WIDE_INT) l);
+             REAL_VALUE_TO_TARGET_SINGLE (*rv, l);
+           /* See the first define_split in rs6000.md handling a
+              const_double_operand.  */
+           val = l;
+           mode = SImode;
          }
-
-       long l[2];
-       if (DECIMAL_FLOAT_MODE_P (mode))
-         REAL_VALUE_TO_TARGET_DECIMAL64 (*CONST_DOUBLE_REAL_VALUE (op), l);
-       else
-         REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (op), l);
-       high = l[WORDS_BIG_ENDIAN == 0];
-       low  = l[WORDS_BIG_ENDIAN != 0];
-
-       if (TARGET_32BIT)
-         return (num_insns_constant_wide (low)
-                 + num_insns_constant_wide (high));
-       else
+       else if (mode == DFmode || mode == DDmode)
          {
-           if ((high == 0 && low >= 0)
-               || (high == -1 && low < 0))
-             return num_insns_constant_wide (low);
-
-           else if (rs6000_is_valid_and_mask (op, mode))
-             return 2;
-
-           else if (low == 0)
-             return num_insns_constant_wide (high) + 1;
+           long l[2];
 
+           if (mode == DDmode)
+             REAL_VALUE_TO_TARGET_DECIMAL64 (*rv, l);
            else
-             return (num_insns_constant_wide (high)
-                     + num_insns_constant_wide (low) + 1);
+             REAL_VALUE_TO_TARGET_DOUBLE (*rv, l);
+
+           /* See the second (32-bit) and third (64-bit) define_split
+              in rs6000.md handling a const_double_operand.  */
+           val = (unsigned HOST_WIDE_INT) l[WORDS_BIG_ENDIAN ? 0 : 1] << 32;
+           val |= l[WORDS_BIG_ENDIAN ? 1 : 0] & 0xffffffffUL;
+           mode = DImode;
          }
+       else
+         gcc_unreachable ();
+      }
+      break;
 
     default:
       gcc_unreachable ();
     }
+
+  return num_insns_constant_multi (val, mode);
 }
 
 /* Interpret element ELT of the CONST_VECTOR OP as an integer value.