match.pd: Canonicalize (x + (x << cst)) into (x * cst2) [PR94800]
authorJakub Jelinek <jakub@redhat.com>
Tue, 5 May 2020 09:36:47 +0000 (11:36 +0200)
committerJakub Jelinek <jakub@redhat.com>
Tue, 5 May 2020 09:36:47 +0000 (11:36 +0200)
The popcount* testcases show yet another creative way to write popcount,
but rather than adjusting the popcount matcher to deal with it, I think
we just should canonicalize those (X + (X << C) to X * (1 + (1 << C))
and (X << C1) + (X << C2) to X * ((1 << C1) + (1 << C2)), because for
multiplication we already have simplification rules that can handle nested
multiplication (X * CST1 * CST2), while the the shifts and adds we have
nothing like that.  And user could have written the multiplication anyway,
so if we don't emit the fastest or smallest code for the multiplication by
constant, we should improve that.  At least on the testcases seems the
emitted code is reasonable according to cost, except that perhaps we could
in some cases try to improve expansion of vector multiplication by
uniform constant.

2020-05-05  Jakub Jelinek  <jakub@redhat.com>

PR tree-optimization/94800
* match.pd (X + (X << C) to X * (1 + (1 << C)),
(X << C1) + (X << C2) to X * ((1 << C1) + (1 << C2))): New
canonicalizations.

* gcc.dg/tree-ssa/pr94800.c: New test.
* gcc.dg/tree-ssa/popcount5.c: New test.
* gcc.dg/tree-ssa/popcount5l.c: New test.
* gcc.dg/tree-ssa/popcount5ll.c: New test.

gcc/ChangeLog
gcc/match.pd
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/popcount5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/popcount5l.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/popcount5ll.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/pr94800.c [new file with mode: 0644]

index b796203aed6bfa2c3e6c2c0af61882822fa1e6e0..cc076c8ea42cb6f75547883937dd6d60024d88ee 100644 (file)
@@ -1,5 +1,10 @@
 2020-05-05  Jakub Jelinek  <jakub@redhat.com>
 
+       PR tree-optimization/94800
+       * match.pd (X + (X << C) to X * (1 + (1 << C)),
+       (X << C1) + (X << C2) to X * ((1 << C1) + (1 << C2))): New
+       canonicalizations.
+
        PR target/94942
        * config/i386/mmx.md (*vec_dupv4hi): Use xYw constraints instead of Yv.
 
index c45e94c0c6a99b59cdbea41cd02ada430e74f5f0..d9575179655111a6112221951a9583b4919f762c 100644 (file)
@@ -2570,6 +2570,41 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
         && single_use (@3))
      (mult (plusminus @2 { build_one_cst (type); }) @0))))))
 
+#if GIMPLE
+/* Canonicalize X + (X << C) into X * (1 + (1 << C)) and
+   (X << C1) + (X << C2) into X * ((1 << C1) + (1 << C2)).  */
+(simplify
+ (plus:c @0 (lshift:s @0 INTEGER_CST@1))
+  (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && tree_fits_uhwi_p (@1)
+       && tree_to_uhwi (@1) < element_precision (type))
+   (with { tree t = type;
+          if (!TYPE_OVERFLOW_WRAPS (t)) t = unsigned_type_for (t);
+          wide_int w = wi::set_bit_in_zero (tree_to_uhwi (@1),
+                                            element_precision (type));
+          w += 1;
+          tree cst = wide_int_to_tree (VECTOR_TYPE_P (t) ? TREE_TYPE (t)
+                                       : t, w);
+          cst = build_uniform_cst (t, cst); }
+    (convert (mult (convert:t @0) { cst; })))))
+(simplify
+ (plus (lshift:s @0 INTEGER_CST@1) (lshift:s @0 INTEGER_CST@2))
+  (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && tree_fits_uhwi_p (@1)
+       && tree_to_uhwi (@1) < element_precision (type)
+       && tree_fits_uhwi_p (@2)
+       && tree_to_uhwi (@2) < element_precision (type))
+   (with { tree t = type;
+          if (!TYPE_OVERFLOW_WRAPS (t)) t = unsigned_type_for (t);
+          unsigned int prec = element_precision (type);
+          wide_int w = wi::set_bit_in_zero (tree_to_uhwi (@1), prec);
+          w += wi::set_bit_in_zero (tree_to_uhwi (@2), prec);
+          tree cst = wide_int_to_tree (VECTOR_TYPE_P (t) ? TREE_TYPE (t)
+                                       : t, w);
+          cst = build_uniform_cst (t, cst); }
+    (convert (mult (convert:t @0) { cst; })))))
+#endif
+
 /* Simplifications of MIN_EXPR, MAX_EXPR, fmin() and fmax().  */
 
 (for minmax (min max FMIN_ALL FMAX_ALL)
index 6475b7d6ff46224e8520606dc8aa1b0d4632ed39..2b48741584619a461e99b92b72c81cb9b051e030 100644 (file)
@@ -1,5 +1,11 @@
 2020-05-05  Jakub Jelinek  <jakub@redhat.com>
 
+       PR tree-optimization/94800
+       * gcc.dg/tree-ssa/pr94800.c: New test.
+       * gcc.dg/tree-ssa/popcount5.c: New test.
+       * gcc.dg/tree-ssa/popcount5l.c: New test.
+       * gcc.dg/tree-ssa/popcount5ll.c: New test.
+
        PR target/94942
        * gcc.target/i386/pr94942.c: New test.
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/popcount5.c b/gcc/testsuite/gcc.dg/tree-ssa/popcount5.c
new file mode 100644 (file)
index 0000000..6358d4f
--- /dev/null
@@ -0,0 +1,22 @@
+/* PR tree-optimization/94800 */
+/* { dg-do compile } */
+/* { dg-require-effective-target popcount } */
+/* { dg-require-effective-target int32plus } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+const unsigned m1  = 0x55555555UL;
+const unsigned m2  = 0x33333333UL;
+const unsigned m4  = 0x0F0F0F0FUL;
+const int shift = 24;
+
+int popcount64c(unsigned x)
+{
+    x -= (x >> 1) & m1;
+    x = (x & m2) + ((x >> 2) & m2);
+    x = (x + (x >> 4)) & m4;
+    x += (x << 8);
+    x += (x << 16);
+    return x >> shift;
+}
+
+/* { dg-final { scan-tree-dump-times "\.POPCOUNT" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/popcount5l.c b/gcc/testsuite/gcc.dg/tree-ssa/popcount5l.c
new file mode 100644 (file)
index 0000000..b761667
--- /dev/null
@@ -0,0 +1,32 @@
+/* PR tree-optimization/94800 */
+/* { dg-do compile { target int32plus } } */
+/* { dg-require-effective-target popcountl } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#if __SIZEOF_LONG__ == 4
+const unsigned long m1  = 0x55555555UL;
+const unsigned long m2  = 0x33333333UL;
+const unsigned long m4  = 0x0F0F0F0FUL;
+const int shift = 24;
+#else
+const unsigned long m1  = 0x5555555555555555UL;
+const unsigned long m2  = 0x3333333333333333UL;
+const unsigned long m4  = 0x0f0f0f0f0f0f0f0fUL;
+const int shift = 56;
+#endif
+
+
+int popcount64c(unsigned long x)
+{
+    x -= (x >> 1) & m1;
+    x = (x & m2) + ((x >> 2) & m2);
+    x = (x + (x >> 4)) & m4;
+    x += (x << 8);
+    x += (x << 16);
+#if __SIZEOF_LONG__ != 4
+    x += (x << 32);
+#endif
+    return x >> shift;
+}
+
+/* { dg-final { scan-tree-dump-times "\.POPCOUNT" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/popcount5ll.c b/gcc/testsuite/gcc.dg/tree-ssa/popcount5ll.c
new file mode 100644 (file)
index 0000000..831d5e1
--- /dev/null
@@ -0,0 +1,22 @@
+/* PR tree-optimization/94800 */
+/* { dg-do compile } */
+/* { dg-require-effective-target popcountll } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+const unsigned long long m1  = 0x5555555555555555ULL;
+const unsigned long long m2  = 0x3333333333333333ULL;
+const unsigned long long m4  = 0x0F0F0F0F0F0F0F0FULL;
+const int shift = 56;
+
+int popcount64c(unsigned long long x)
+{
+    x -= (x >> 1) & m1;
+    x = (x & m2) + ((x >> 2) & m2);
+    x = (x + (x >> 4)) & m4;
+    x += (x << 8);
+    x += (x << 16);
+    x += (x << 32);
+    return x >> shift;
+}
+
+/* { dg-final { scan-tree-dump-times "\.POPCOUNT" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr94800.c b/gcc/testsuite/gcc.dg/tree-ssa/pr94800.c
new file mode 100644 (file)
index 0000000..4f92df3
--- /dev/null
@@ -0,0 +1,80 @@
+/* PR tree-optimization/94800 */
+/* { dg-do compile { target { ilp32 || lp64 } } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " \* 72340172838076673" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \* 16843009" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \* 289360691352306692" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \* 1229782938247303441" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "<<" "optimized" } } */
+
+unsigned long long int
+foo (unsigned long long int x)
+{
+  unsigned long long int a = x + (x << 8);
+  unsigned long long int b = a + (a << 16);
+  unsigned long long int c = b + (b << 32);
+  return c;
+}
+
+unsigned int
+bar (unsigned int x)
+{
+  unsigned int a = x + (x << 8);
+  unsigned int b = a + (a << 16);
+  return b;
+}
+
+unsigned long long int
+baz (unsigned long long int x)
+{
+  unsigned long long int a = (x << 2) + (x << 10);
+  unsigned long long int b = a + (a << 16);
+  unsigned long long int c = b + (b << 32);
+  return c;
+}
+
+unsigned long long int
+qux (unsigned long long int x)
+{
+  unsigned long long int a = x + (x << 4);
+  unsigned long long int b = a + (a << 8);
+  unsigned long long int c = b + (b << 16);
+  unsigned long long int d = c + (c << 32);
+  return d;
+}
+
+long long int
+quux (long long int x)
+{
+  long long int a = x + (x << 8);
+  long long int b = a + (a << 16);
+  long long int c = b + (b << 32);
+  return c;
+}
+
+int
+corge (int x)
+{
+  int a = x + (x << 8);
+  int b = a + (a << 16);
+  return b;
+}
+
+long long int
+garply (long long int x)
+{
+  long long int a = (x << 2) + (x << 10);
+  long long int b = a + (a << 16);
+  long long int c = b + (b << 32);
+  return c;
+}
+
+long long int
+waldo (long long int x)
+{
+  long long int a = x + (x << 4);
+  long long int b = a + (a << 8);
+  long long int c = b + (b << 16);
+  long long int d = c + (c << 32);
+  return d;
+}