The limit calculation is not valid for modes smaller than int since they
have extra casting/promotion operations.

gcc/ChangeLog:

        * gimple-ssa-store-merging.cc (struct bswap_stat):
        Delete and convert into an array.
        (find_bitperm_or_nop): Correct limit calculation for QI/HI.
        (bitperm_replace): Adjust stats counting.
        (pass_optimize_bswap::execute): Add bitreverseqi support and
        relax rotation restrictions.

gcc/testsuite/ChangeLog:

        * gcc.dg/optimize-bitrevhi-1.c: New test.
        * gcc.dg/optimize-bitrevqi-1.c: New test.
        * gcc.dg/optimize-bitrevsi-5.c: New test.
        * gcc.dg/optimize-bitrevsi-6.c: New test.

Signed-off-by: David Guillen Fandos <[email protected]>
---
 gcc/gimple-ssa-store-merging.cc            | 104 ++++++++-------------
 gcc/testsuite/gcc.dg/optimize-bitrevhi-1.c |  19 ++++
 gcc/testsuite/gcc.dg/optimize-bitrevqi-1.c |  18 ++++
 gcc/testsuite/gcc.dg/optimize-bitrevsi-5.c |  30 ++++++
 gcc/testsuite/gcc.dg/optimize-bitrevsi-6.c |  28 ++++++
 5 files changed, 136 insertions(+), 63 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevhi-1.c
 create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevqi-1.c
 create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevsi-5.c
 create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevsi-6.c

diff --git a/gcc/gimple-ssa-store-merging.cc b/gcc/gimple-ssa-store-merging.cc
index 53671f32b24..43b08ded1d8 100644
--- a/gcc/gimple-ssa-store-merging.cc
+++ b/gcc/gimple-ssa-store-merging.cc
@@ -194,17 +194,9 @@ static const char * const bitperm_names[] = {
   "bitreverse"
 };
 
-struct bswap_stat
-{
-  /* Number of hand-written 16-bit nop / bswaps / bitrev found.  */
-  int found_16bit;
-
-  /* Number of hand-written 32-bit nop / bswaps / bitrev found.  */
-  int found_32bit;
-
-  /* Number of hand-written 64-bit nop / bswaps / bitrev found.  */
-  int found_64bit;
-} op_stats[BITOP_LAST];
+/* Number of hand-written N-bit nop / bswaps / bitrev found, where N can be
+   8, 16, 32 or 64. (Uses log2(nbytes) to index the array).   */
+int op_stats[BITOP_LAST][4];
 
 /* A symbolic number structure is used to detect bit permutation and selection
    patterns of a source.  To achieve that, its array N contains a series of
@@ -1098,8 +1090,9 @@ find_bitperm_or_nop (gimple *stmt, struct symbolic_number 
*n,
      increase that number by 2 * (log2(n) + 1) here in order to also
      cover signed -> unsigned conversions of the src operand as can be seen
      in libgcc, and for initial shift/and operation of the src operand.  */
-  int numbits = tree_to_uhwi (type_size) * BITS_PER_UNIT;
-  int limit = 2 + 3 * (1 + (int) ceil_log2 ((unsigned HOST_WIDE_INT) numbits));
+  unsigned int numbits = tree_to_uhwi (type_size) * BITS_PER_UNIT;
+  unsigned int nops_level = (numbits < INT_TYPE_SIZE) ? 4 : 3;
+  int limit = 2 + nops_level * (1 + ceil_log2 (numbits));
   gimple *ins_stmt = find_bswap_or_nop_2 (stmt, n, limit);
   if (!ins_stmt)
     return NULL;
@@ -1360,15 +1353,8 @@ bitperm_replace (gimple_stmt_iterator gsi, gimple 
*ins_stmt, tree fndecl,
 
       if (op == BITOP_NOP)
        {
-         if (n->range == 16)
-           op_stats[BITOP_NOP].found_16bit++;
-         else if (n->range == 32)
-           op_stats[BITOP_NOP].found_32bit++;
-         else
-           {
-             gcc_assert (n->range == 64);
-             op_stats[BITOP_NOP].found_64bit++;
-           }
+         gcc_assert (n->range >= 8 && n->range <= 64);
+         op_stats[BITOP_NOP][floor_log2 (n->range / BITS_PER_UNIT)]++;
 
          /* Convert the result of load if necessary.  */
          if (tgt && !useless_type_conversion_p (TREE_TYPE (tgt), load_type))
@@ -1431,15 +1417,10 @@ bitperm_replace (gimple_stmt_iterator gsi, gimple 
*ins_stmt, tree fndecl,
        g = gimple_build_assign (tgt, src);
       else
        tgt = src;
-      if (n->range == 16)
-       op_stats[BITOP_NOP].found_16bit++;
-      else if (n->range == 32)
-       op_stats[BITOP_NOP].found_32bit++;
-      else
-       {
-         gcc_assert (n->range == 64);
-         op_stats[BITOP_NOP].found_64bit++;
-       }
+
+      gcc_assert (n->range >= 8 && n->range <= 64);
+      op_stats[BITOP_NOP][floor_log2 (n->range / BITS_PER_UNIT)]++;
+
       if (dump_file)
        {
          fprintf (dump_file,
@@ -1460,15 +1441,8 @@ bitperm_replace (gimple_stmt_iterator gsi, gimple 
*ins_stmt, tree fndecl,
   else if (TREE_CODE (src) == BIT_FIELD_REF)
     src = TREE_OPERAND (src, 0);
 
-  if (n->range == 16)
-    op_stats[op].found_16bit++;
-  else if (n->range == 32)
-    op_stats[op].found_32bit++;
-  else
-    {
-      gcc_assert (n->range == 64);
-      op_stats[op].found_64bit++;
-    }
+  gcc_assert (n->range >= 8 && n->range <= 64);
+  op_stats[op][floor_log2 (n->range / BITS_PER_UNIT)]++;
 
   tmp = src;
 
@@ -1656,10 +1630,10 @@ pass_optimize_bswap::execute (function *fun)
 {
   basic_block bb;
   bool bswap32_p, bswap64_p;
-  bool bitrev16_p, bitrev32_p, bitrev64_p;
+  bool bitrev8_p, bitrev16_p, bitrev32_p, bitrev64_p;
   bool changed = false;
   tree bswap32_type = NULL_TREE, bswap64_type = NULL_TREE;
-  tree bitrev16_type = NULL_TREE;
+  tree bitrev8_type = NULL_TREE, bitrev16_type = NULL_TREE;
   tree bitrev32_type = NULL_TREE, bitrev64_type = NULL_TREE;
 
   bswap32_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP32)
@@ -1667,6 +1641,8 @@ pass_optimize_bswap::execute (function *fun)
   bswap64_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP64)
               && can_open_code_p (bswap_optab, DImode));
 
+  bitrev8_p = (builtin_decl_explicit_p (BUILT_IN_BITREVERSE8)
+               && can_open_code_p (bitreverse_optab, QImode));
   bitrev16_p = (builtin_decl_explicit_p (BUILT_IN_BITREVERSE16)
                && can_open_code_p (bitreverse_optab, HImode));
   bitrev32_p = (builtin_decl_explicit_p (BUILT_IN_BITREVERSE32)
@@ -1688,6 +1664,10 @@ pass_optimize_bswap::execute (function *fun)
       bswap64_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
     }
 
+  if (bitrev8_p)
+    bitrev8_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (
+       builtin_decl_explicit (BUILT_IN_BITREVERSE8))));
+
   if (bitrev16_p)
     bitrev16_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (
        builtin_decl_explicit (BUILT_IN_BITREVERSE16))));
@@ -1735,9 +1715,7 @@ pass_optimize_bswap::execute (function *fun)
            {
            case LROTATE_EXPR:
            case RROTATE_EXPR:
-             if (!tree_fits_uhwi_p (gimple_assign_rhs2 (cur_stmt))
-                 || tree_to_uhwi (gimple_assign_rhs2 (cur_stmt))
-                    % BITS_PER_UNIT)
+             if (!tree_fits_uhwi_p (gimple_assign_rhs2 (cur_stmt)))
                continue;
              /* Fall through.  */
            case BIT_IOR_EXPR:
@@ -1768,6 +1746,16 @@ pass_optimize_bswap::execute (function *fun)
 
          switch (n.range)
            {
+           case 8:
+             load_type = bitperm_type = unsigned_char_type_node;
+             if (bitop == BITOP_BITREV && bitrev8_p)
+               {
+                 fndecl = builtin_decl_explicit (BUILT_IN_BITREVERSE8);
+                 bitperm_type = bitrev8_type;
+               }
+             else
+               continue;
+             break;
            case 16:
              load_type = bitperm_type = uint16_type_node;
              if (bitop == BITOP_BSWAP)
@@ -1825,24 +1813,14 @@ pass_optimize_bswap::execute (function *fun)
        }
     }
 
-  statistics_counter_event (fun, "16-bit nop implementations found",
-                           op_stats[BITOP_NOP].found_16bit);
-  statistics_counter_event (fun, "32-bit nop implementations found",
-                           op_stats[BITOP_NOP].found_32bit);
-  statistics_counter_event (fun, "64-bit nop implementations found",
-                           op_stats[BITOP_NOP].found_64bit);
-  statistics_counter_event (fun, "16-bit bswap implementations found",
-                           op_stats[BITOP_BSWAP].found_16bit);
-  statistics_counter_event (fun, "32-bit bswap implementations found",
-                           op_stats[BITOP_BSWAP].found_32bit);
-  statistics_counter_event (fun, "64-bit bswap implementations found",
-                           op_stats[BITOP_BSWAP].found_64bit);
-  statistics_counter_event (fun, "16-bit bitreverse implementations found",
-                           op_stats[BITOP_BITREV].found_16bit);
-  statistics_counter_event (fun, "32-bit bitreverse implementations found",
-                           op_stats[BITOP_BITREV].found_32bit);
-  statistics_counter_event (fun, "64-bit bitreverse implementations found",
-                           op_stats[BITOP_BITREV].found_64bit);
+  for (int p = BITOP_NOP; p < BITOP_LAST; p++)
+    for (int i = 8, j = 0; i <= 64; i*=2, j++)
+      {
+       char str[128];
+       snprintf (str, sizeof (str), "%d-bit %s implementation found", i,
+                 bitperm_names[p]);
+       statistics_counter_event (fun, str, op_stats[p][j]);
+      }
 
   return (changed ? TODO_update_ssa : 0);
 }
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevhi-1.c 
b/gcc/testsuite/gcc.dg/optimize-bitrevhi-1.c
new file mode 100644
index 00000000000..8d7ba2dd5d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevhi-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test a common implementation of bitreverse, from Hacker's Delight.  */
+
+#include <stdint.h>
+
+uint16_t
+bitrhi(uint16_t x) {
+  x = (x & 0x5555) <<  1 | (x & 0xAAAA) >>  1;
+  x = (x & 0x3333) <<  2 | (x & 0xCCCC) >>  2;
+  x = (x & 0x0F0F) <<  4 | (x & 0xF0F0) >>  4;
+  x = (x & 0x00FF) <<  8 | (x & 0xFF00) >>  8;
+  return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse16 \\\(" 1 
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevqi-1.c 
b/gcc/testsuite/gcc.dg/optimize-bitrevqi-1.c
new file mode 100644
index 00000000000..79f7db7180b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevqi-1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test a common implementation of bitreverse, from Hacker's Delight.  */
+
+#include <stdint.h>
+
+uint8_t
+bitrqi(uint8_t x) {
+  x = (x & 0x55) <<  1 | (x & 0xAA) >>  1;
+  x = (x & 0x33) <<  2 | (x & 0xCC) >>  2;
+  x = (x & 0x0F) <<  4 | (x & 0xF0) >>  4;
+  return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse8 \\\(" 1 
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevsi-5.c 
b/gcc/testsuite/gcc.dg/optimize-bitrevsi-5.c
new file mode 100644
index 00000000000..0b95c69d296
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevsi-5.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test memory path too, where byte offsets are properly mapped to bits.  */
+
+#include <stdint.h>
+
+static inline uint32_t
+rev8(uint8_t x) {
+  x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
+  x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
+  x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
+  return x;
+}
+
+uint32_t
+bitrev_mem (const uint8_t *s)
+{
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+  return rev8(s[3]) | (rev8(s[2]) << 8)
+        | (rev8(s[1]) << 16) | (rev8(s[0]) << 24);
+#else
+  return rev8(s[0]) | (rev8(s[1]) << 8)
+        | (rev8(s[2]) << 16) | (rev8(s[3]) << 24);
+#endif
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse32 \\\(" 1 
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevsi-6.c 
b/gcc/testsuite/gcc.dg/optimize-bitrevsi-6.c
new file mode 100644
index 00000000000..f98384c9b1a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevsi-6.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test memory path with masking, ie. reversing all bits in 3 bytes.  */
+
+#include <stdint.h>
+
+static inline uint32_t
+rev8(uint8_t x) {
+  x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
+  x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
+  x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
+  return x;
+}
+
+uint32_t
+bitrev_memmask (const uint8_t *s)
+{
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+  return rev8(s[3]) | (rev8(s[1]) << 16) | (rev8(s[0]) << 24);
+#else
+  return rev8(s[0]) | (rev8(s[2]) << 16) | (rev8(s[3]) << 24);
+#endif
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse32 \\\(" 1 
"optimized" } } */
-- 
2.55.0

Reply via email to