On Thu, Nov 29, 2018 at 01:52:34PM -0600, Segher Boessenkool wrote:
> On Sun, Nov 25, 2018 at 10:49:12PM +1030, Alan Modra wrote:
> > +  while (nregs-- > 0)
> > +    {
> > +      int ins = num_insns_constant_gpr (value);
> 
> You probably should mask "value" here so that it is only one GPR.
> Alternatively, make num_insns_constant_gpr handle that.  Consider what
> happens for a 64-bit number with 32-bit registers here?

Oh, right, the low part will always give an answer of 2 if the high
parts isn't merely a sign extension.

> > +       val = l[WORDS_BIG_ENDIAN ? 0 : 1] << 32;
> 
> This doesn't work, as in the other patch: long can be 32 bit.

Huh, I originally had "high" and "low" HOST_WIDE_INTs which avoided
this sort of problem on 32-bit hosts.

Revised patch follows.  Bootstrapped and regression tested
powerpc64le-linux.  Hopefully this has removed all the stupidity.

        * 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.

diff --git a/gcc/config/rs6000/rs6000-protos.h 
b/gcc/config/rs6000/rs6000-protos.h
index 1dfe7995ff1..dfee1f28aa9 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -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);
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 02e69c103ec..c438fdc64fe 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -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.


-- 
Alan Modra
Australia Development Lab, IBM

Reply via email to