Implement more two insn constants.

        PR 94393
        * config/rs6000/rs6000.c (rotate_and_mask_constant): New function.
        (num_insns_constant_multi, rs6000_emit_set_long_const): Use it here.

diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 86c90c4d756..1848cb57ef8 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -1112,6 +1112,8 @@ static tree rs6000_handle_altivec_attribute (tree *, 
tree, tree, int, bool *);
 static tree rs6000_handle_struct_attribute (tree *, tree, tree, int, bool *);
 static tree rs6000_builtin_vectorized_libmass (combined_fn, tree, tree);
 static void rs6000_emit_set_long_const (rtx, HOST_WIDE_INT);
+static bool rotate_and_mask_constant (unsigned HOST_WIDE_INT, HOST_WIDE_INT *,
+                                     int *, unsigned HOST_WIDE_INT *);
 static int rs6000_memory_move_cost (machine_mode, reg_class_t, bool);
 static bool rs6000_debug_rtx_costs (rtx, machine_mode, int, int, int *, bool);
 static int rs6000_debug_address_cost (rtx, machine_mode, addr_space_t,
@@ -5789,7 +5791,8 @@ num_insns_constant_multi (HOST_WIDE_INT value, 
machine_mode mode)
          /* 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))
+         && (rs6000_is_valid_and_mask (GEN_INT (low), DImode)
+             || rotate_and_mask_constant (low, NULL, NULL, NULL)))
        insns = 2;
       total += insns;
       /* If BITS_PER_WORD is the number of bits in HOST_WIDE_INT, doing
@@ -9420,6 +9423,82 @@ rs6000_emit_set_const (rtx dest, rtx source)
   return true;
 }
 
+/* Detect cases where a constant can be formed by li; rldicl, li; rldicr,
+   or lis; rldicl.  */
+
+static bool
+rotate_and_mask_constant (unsigned HOST_WIDE_INT c,
+                         HOST_WIDE_INT *val, int *shift,
+                         unsigned HOST_WIDE_INT *mask)
+{
+  /* We know C can't be formed by lis,addi so that puts constraints
+     on the max leading zeros.  lead_zeros won't be larger than
+     HOST_BITS_PER_WIDE_INT - 31.  */
+  int lead_zeros = wi::clz (c);
+  int non_zero = HOST_BITS_PER_WIDE_INT - lead_zeros;
+  /* 00...01xxxxxxxxxxxxxx0..00 (up to 14 x's, any number of leading
+     and trailing 0's) can be implemented as a li, rldicl.  */
+  if ((c & ~(HOST_WIDE_INT_UC (0x7fff) << (non_zero - 15))) == 0)
+    {
+      /* eg. c = 1100 0000 0000 ... 0000
+        -> val = 0x3000, shift = 49, mask = -1ull.  */
+      if (val)
+       {
+         *val = c >> (non_zero - 15);
+         *shift = non_zero - 15;
+         *mask = HOST_WIDE_INT_M1U;
+       }
+      return true;
+    }
+  /* 00...01xxxxxxxxxxxxxxx1..11 (up to 15 x's, any number of leading
+     0's and trailing 1's) can be implemented as a li, rldicl.  */
+  if ((c | (HOST_WIDE_INT_M1U << (non_zero - 16))) == HOST_WIDE_INT_M1U)
+    {
+      /* eg. c = 0000 1011 1111 1111 ... 1111
+        -> val = sext(0xbfff), shift = 44, mask = 0x0fffffffffffffff.  */
+      if (val)
+       {
+         *val = (((c >> (non_zero - 16)) & 0xffff) ^ 0x8000) - 0x8000;
+         *shift = non_zero - 16;
+         *mask = HOST_WIDE_INT_M1U >> lead_zeros;
+       }
+      return true;
+    }
+  /* 00...01xxxxxxxxxxxxxxx00..01..11 (up to 15 x's followed by 16 0's,
+     any number of leading 0's and trailing 1's) can be implemented as
+     lis, rldicl.  */
+  if (non_zero >= 32
+      && (c & ((HOST_WIDE_INT_1U << (non_zero - 16))
+              - (HOST_WIDE_INT_1U << (non_zero - 32)))) == 0
+      && (c | (HOST_WIDE_INT_M1U << (non_zero - 32))) == HOST_WIDE_INT_M1U)
+    {
+      if (val)
+       {
+         *val = (((c >> (non_zero - 32)) & 0xffffffff)
+                 ^ 0x80000000) - 0x80000000;
+         *shift = non_zero - 32;
+         *mask = HOST_WIDE_INT_M1U >> lead_zeros;
+       }
+      return true;
+    }
+  /* 11..1xxxxxxxxxxxxxxx0..0 (up to 15 x's, any number of leading 1's
+     and trailing 0's) can be implemented as a li, rldicr.  */
+  int trail_zeros = wi::ctz (c);
+  if (trail_zeros >= 48
+      || ((c | ((HOST_WIDE_INT_1U << (trail_zeros + 15)) - 1))
+         == HOST_WIDE_INT_M1U))
+    {
+      if (val)
+       {
+         *val = (((c >> trail_zeros) & 0xffff) ^ 0x8000) - 0x8000;
+         *shift = trail_zeros;
+         *mask = HOST_WIDE_INT_M1U << trail_zeros;
+       }
+      return true;
+    }
+  return false;
+}
+
 /* Subroutine of rs6000_emit_set_const, handling PowerPC64 DImode.
    Output insns to set DEST equal to the constant C as a series of
    lis, ori and shl instructions.  */
@@ -9429,6 +9508,9 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c)
 {
   rtx temp;
   HOST_WIDE_INT ud1, ud2, ud3, ud4;
+  HOST_WIDE_INT val = c;
+  int shift;
+  unsigned HOST_WIDE_INT mask;
 
   ud1 = c & 0xffff;
   c = c >> 16;
@@ -9454,6 +9536,15 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c)
                        gen_rtx_IOR (DImode, copy_rtx (temp),
                                     GEN_INT (ud1)));
     }
+  else if (rotate_and_mask_constant (val, &val, &shift, &mask))
+    {
+      temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);
+      emit_move_insn (temp, GEN_INT (val));
+      rtx x = gen_rtx_ROTATE (DImode, copy_rtx (temp), GEN_INT (shift));
+      if (mask != HOST_WIDE_INT_M1U)
+       x = gen_rtx_AND (DImode, x, GEN_INT (mask));
+      emit_move_insn (dest, x);
+    }
   else if (ud3 == 0 && ud4 == 0)
     {
       temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);

Reply via email to