OSDN Git Service

2009-06-14 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
authorkrebbel <krebbel@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 14 Jun 2009 14:45:32 +0000 (14:45 +0000)
committerkrebbel <krebbel@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 14 Jun 2009 14:45:32 +0000 (14:45 +0000)
* passes.c: Add bswap pass.
* tree-pass.h: Add pass_optimize_bswap declaration.
* tree-ssa-math-opts.c: Include diagnostics.h for print_gimple_stmt.
Include rtl.h, expr.h and optabs.h for optab_handler check.
(struct symbolic_number, pass_optimize_bswap): New definition.
(do_shift_rotate, verify_symbolic_number_p): New functions.
(find_bswap_1, find_bswap, execute_optimize_bswap): New functions.
(gate_optimize_bswap): New function.
* tree.c (widest_int_cst_value): New function.
* tree.h (widest_int_cst_value): Prototype added.

2009-06-14  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

* gcc.dg/optimize-bswap-1.c: New testcase.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@148471 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/passes.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/optimize-bswap-1.c [new file with mode: 0644]
gcc/tree-pass.h
gcc/tree-ssa-math-opts.c
gcc/tree.c
gcc/tree.h

index 99a578f..7a92447 100644 (file)
@@ -1,3 +1,16 @@
+2009-06-14  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>
+
+       * passes.c: Add bswap pass.
+       * tree-pass.h: Add pass_optimize_bswap declaration.
+       * tree-ssa-math-opts.c: Include diagnostics.h for print_gimple_stmt.
+       Include rtl.h, expr.h and optabs.h for optab_handler check.
+       (struct symbolic_number, pass_optimize_bswap): New definition.
+       (do_shift_rotate, verify_symbolic_number_p): New functions.
+       (find_bswap_1, find_bswap, execute_optimize_bswap): New functions.
+       (gate_optimize_bswap): New function.
+       * tree.c (widest_int_cst_value): New function.
+       * tree.h (widest_int_cst_value): Prototype added.
+
 2009-06-14  Steven Bosscher  <steven@gcc.gnu.org>
 
        * cfgcleanup.c (old_insns_match_p): Remove code to substitute
index adf0ed0..6870973 100644 (file)
@@ -638,6 +638,7 @@ init_optimization_passes (void)
       NEXT_PASS (pass_copy_prop);
       NEXT_PASS (pass_fold_builtins);
       NEXT_PASS (pass_cse_sincos);
+      NEXT_PASS (pass_optimize_bswap);
       NEXT_PASS (pass_split_crit_edges);
       NEXT_PASS (pass_pre);
       NEXT_PASS (pass_sink_code);
index 4b7d929..25ae320 100644 (file)
@@ -1,3 +1,7 @@
+2009-06-14  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>
+
+       * gcc.dg/optimize-bswap-1.c: New testcase.
+
 2009-06-14  Richard Guenther  <rguenther@suse.de>
 
        PR middle-end/40389
diff --git a/gcc/testsuite/gcc.dg/optimize-bswap-1.c b/gcc/testsuite/gcc.dg/optimize-bswap-1.c
new file mode 100644 (file)
index 0000000..a603f6d
--- /dev/null
@@ -0,0 +1,52 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-bswap" } */
+
+#include <stdint.h>
+
+#define __const_swab32(x) ((uint32_t)(                               \
+        (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) |            \
+        (((uint32_t)(x) & (uint32_t)0x0000ff00UL) <<  8) |            \
+        (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >>  8) |            \
+        (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
+
+#define __const_swab64(x) ((uint64_t)(                                         \
+       (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) |             \
+       (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) |             \
+       (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) |             \
+       (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) <<  8) |             \
+       (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >>  8) |             \
+       (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) |             \
+       (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) |             \
+       (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56)))
+
+/* This byte swap implementation is used by the Linux kernel and the
+   GNU C library.  */
+
+uint32_t
+swap32_a (uint32_t in)
+{
+  return __const_swab32 (in);
+}
+
+uint64_t
+swap64 (uint64_t in)
+{
+  return __const_swab64 (in);
+}
+
+/* The OpenSSH byte swap implementation.  */
+uint32_t
+swap32_b (uint32_t in)
+{
+  uint32_t a;
+
+  a = (in << 16) | (in >> 16);
+  a = ((a & 0x00ff00ff) << 8) | ((a & 0xff00ff00) >> 8);
+
+  return a;
+}
+
+/* { dg-final { scan-tree-dump-times "32 bit bswap implementation found at" 2 "bswap" } } */
+/* { dg-final { scan-tree-dump-times "64 bit bswap implementation found at" 1 "bswap" } } */
+/* { dg-final { cleanup-tree-dump "bswap" } } */
index c2bf494..1268e35 100644 (file)
@@ -377,6 +377,7 @@ extern struct gimple_opt_pass pass_late_warn_uninitialized;
 extern struct gimple_opt_pass pass_cse_reciprocals;
 extern struct gimple_opt_pass pass_cse_sincos;
 extern struct gimple_opt_pass pass_convert_to_rsqrt;
+extern struct gimple_opt_pass pass_optimize_bswap;
 extern struct gimple_opt_pass pass_warn_function_return;
 extern struct gimple_opt_pass pass_warn_function_noreturn;
 extern struct gimple_opt_pass pass_cselim;
index 8d08aa7..d864e20 100644 (file)
@@ -97,7 +97,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "alloc-pool.h"
 #include "basic-block.h"
 #include "target.h"
-
+#include "diagnostic.h"
+#include "rtl.h"
+#include "expr.h"
+#include "optabs.h"
 
 /* This structure represents one basic block that either computes a
    division, or is a common dominator for basic block that compute a
@@ -879,3 +882,383 @@ struct gimple_opt_pass pass_convert_to_rsqrt =
     | TODO_verify_stmts                 /* todo_flags_finish */
  }
 };
+
+/* A symbolic number is used to detect byte permutation and selection
+   patterns.  Therefore the field N contains an artificial number
+   consisting of byte size markers:
+
+   0    - byte has the value 0
+   1..size - byte contains the content of the byte
+   number indexed with that value minus one  */
+
+struct symbolic_number {
+  unsigned HOST_WIDEST_INT n;
+  int size;
+};
+
+/* Perform a SHIFT or ROTATE operation by COUNT bits on symbolic
+   number N.  Return false if the requested operation is not permitted
+   on a symbolic number.  */
+
+static inline bool
+do_shift_rotate (enum tree_code code,
+                struct symbolic_number *n,
+                int count)
+{
+  if (count % 8 != 0)
+    return false;
+
+  /* Zero out the extra bits of N in order to avoid them being shifted
+     into the significant bits.  */
+  if (n->size < (int)sizeof (HOST_WIDEST_INT))
+    n->n &= ((unsigned HOST_WIDEST_INT)1 << (n->size * BITS_PER_UNIT)) - 1;
+
+  switch (code)
+    {
+    case LSHIFT_EXPR:
+      n->n <<= count;
+      break;
+    case RSHIFT_EXPR:
+      n->n >>= count;
+      break;
+    case LROTATE_EXPR:
+      n->n = (n->n << count) | (n->n >> ((n->size * BITS_PER_UNIT) - count));
+      break;
+    case RROTATE_EXPR:
+      n->n = (n->n >> count) | (n->n << ((n->size * BITS_PER_UNIT) - count));
+      break;
+    default:
+      return false;
+    }
+  return true;
+}
+
+/* Perform sanity checking for the symbolic number N and the gimple
+   statement STMT.  */
+
+static inline bool
+verify_symbolic_number_p (struct symbolic_number *n, gimple stmt)
+{
+  tree lhs_type;
+
+  lhs_type = gimple_expr_type (stmt);
+
+  if (TREE_CODE (lhs_type) != INTEGER_TYPE)
+    return false;
+
+  if (TYPE_PRECISION (lhs_type) != n->size * BITS_PER_UNIT)
+    return false;
+
+  return true;
+}
+
+/* find_bswap_1 invokes itself recursively with N and tries to perform
+   the operation given by the rhs of STMT on the result.  If the
+   operation could successfully be executed the function returns the
+   tree expression of the source operand and NULL otherwise.  */
+
+static tree
+find_bswap_1 (gimple stmt, struct symbolic_number *n, int limit)
+{
+  enum tree_code code;
+  tree rhs1, rhs2 = NULL;
+  gimple rhs1_stmt, rhs2_stmt;
+  tree source_expr1;
+  enum gimple_rhs_class rhs_class;
+
+  if (!limit || !is_gimple_assign (stmt))
+    return NULL_TREE;
+
+  rhs1 = gimple_assign_rhs1 (stmt);
+
+  if (TREE_CODE (rhs1) != SSA_NAME)
+    return NULL_TREE;
+
+  code = gimple_assign_rhs_code (stmt);
+  rhs_class = gimple_assign_rhs_class (stmt);
+  rhs1_stmt = SSA_NAME_DEF_STMT (rhs1);
+
+  if (rhs_class == GIMPLE_BINARY_RHS)
+    rhs2 = gimple_assign_rhs2 (stmt);
+
+  /* Handle unary rhs and binary rhs with integer constants as second
+     operand.  */
+
+  if (rhs_class == GIMPLE_UNARY_RHS
+      || (rhs_class == GIMPLE_BINARY_RHS
+         && TREE_CODE (rhs2) == INTEGER_CST))
+    {
+      if (code != BIT_AND_EXPR
+         && code != LSHIFT_EXPR
+         && code != RSHIFT_EXPR
+         && code != LROTATE_EXPR
+         && code != RROTATE_EXPR
+         && code != NOP_EXPR
+         && code != CONVERT_EXPR)
+       return NULL_TREE;
+
+      source_expr1 = find_bswap_1 (rhs1_stmt, n, limit - 1);
+
+      /* If find_bswap_1 returned NULL STMT is a leaf node and we have
+        to initialize the symbolic number.  */
+      if (!source_expr1)
+       {
+         /* Set up the symbolic number N by setting each byte to a
+            value between 1 and the byte size of rhs1.  The highest
+            order byte is set to 1 and the lowest order byte to
+            n.size.  */
+         n->size = TYPE_PRECISION (TREE_TYPE (rhs1));
+         if (n->size % BITS_PER_UNIT != 0)
+           return NULL_TREE;
+         n->size /= BITS_PER_UNIT;
+         n->n = (sizeof (HOST_WIDEST_INT) < 8 ? 0 :
+                 (unsigned HOST_WIDEST_INT)0x01020304 << 32 | 0x05060708);
+         n->n >>= (sizeof (HOST_WIDEST_INT) - n->size) * BITS_PER_UNIT;
+
+         source_expr1 = rhs1;
+       }
+
+      switch (code)
+       {
+       case BIT_AND_EXPR:
+         {
+           int i;
+           unsigned HOST_WIDEST_INT val = widest_int_cst_value (rhs2);
+           unsigned HOST_WIDEST_INT tmp = val;
+
+           /* Only constants masking full bytes are allowed.  */
+           for (i = 0; i < n->size; i++, tmp >>= BITS_PER_UNIT)
+             if ((tmp & 0xff) != 0 && (tmp & 0xff) != 0xff)
+               return NULL_TREE;
+
+           n->n &= val;
+         }
+         break;
+       case LSHIFT_EXPR:
+       case RSHIFT_EXPR:
+       case LROTATE_EXPR:
+       case RROTATE_EXPR:
+         if (!do_shift_rotate (code, n, (int)TREE_INT_CST_LOW (rhs2)))
+           return NULL_TREE;
+         break;
+       CASE_CONVERT:
+         {
+           int type_size;
+
+           type_size = TYPE_PRECISION (gimple_expr_type (stmt));
+           if (type_size % BITS_PER_UNIT != 0)
+             return NULL_TREE;
+
+           type_size /= BITS_PER_UNIT;
+
+           if (type_size / BITS_PER_UNIT < (int)(sizeof (HOST_WIDEST_INT)))
+             {
+               /* If STMT casts to a smaller type mask out the bits not
+                  belonging to the target type.  */
+               n->size = type_size / BITS_PER_UNIT;
+               n->n &= ((unsigned HOST_WIDEST_INT)1 << type_size) - 1;
+             }
+         }
+         break;
+       default:
+         return NULL_TREE;
+       };
+      return verify_symbolic_number_p (n, stmt) ? source_expr1 : NULL;
+    }
+
+  /* Handle binary rhs.  */
+
+  if (rhs_class == GIMPLE_BINARY_RHS)
+    {
+      struct symbolic_number n1, n2;
+      tree source_expr2;
+
+      if (code != BIT_IOR_EXPR)
+       return NULL_TREE;
+
+      if (TREE_CODE (rhs2) != SSA_NAME)
+       return NULL_TREE;
+
+      rhs2_stmt = SSA_NAME_DEF_STMT (rhs2);
+
+      switch (code)
+       {
+       case BIT_IOR_EXPR:
+         source_expr1 = find_bswap_1 (rhs1_stmt, &n1, limit - 1);
+
+         if (!source_expr1)
+           return NULL_TREE;
+
+         source_expr2 = find_bswap_1 (rhs2_stmt, &n2, limit - 1);
+
+         if (source_expr1 != source_expr2
+             || n1.size != n2.size)
+           return NULL_TREE;
+
+         n->size = n1.size;
+         n->n = n1.n | n2.n;
+
+         if (!verify_symbolic_number_p (n, stmt))
+           return NULL_TREE;
+
+         break;
+       default:
+         return NULL_TREE;
+       }
+      return source_expr1;
+    }
+  return NULL_TREE;
+}
+
+/* Check if STMT completes a bswap implementation consisting of ORs,
+   SHIFTs and ANDs.  Return the source tree expression on which the
+   byte swap is performed and NULL if no bswap was found.  */
+
+static tree
+find_bswap (gimple stmt)
+{
+/* The number which the find_bswap result should match in order to
+   have a full byte swap.  The insignificant bytes are masked out
+   before using it.  */
+  unsigned HOST_WIDEST_INT cmp =
+    sizeof (HOST_WIDEST_INT) < 8 ? 0 :
+    (unsigned HOST_WIDEST_INT)0x08070605 << 32 | 0x04030201;
+
+  struct symbolic_number n;
+  tree source_expr;
+
+  source_expr =  find_bswap_1 (stmt, &n,
+                              TREE_INT_CST_LOW (
+                                TYPE_SIZE_UNIT (gimple_expr_type (stmt))));
+
+  if (!source_expr)
+    return NULL_TREE;
+
+  /* Zero out the extra bits of N and CMP.  */
+  if (n.size < (int)sizeof (HOST_WIDEST_INT))
+    {
+      unsigned HOST_WIDEST_INT mask =
+       ((unsigned HOST_WIDEST_INT)1 << (n.size * BITS_PER_UNIT)) - 1;
+
+      n.n &= mask;
+      cmp &= mask;
+    }
+
+  /* A complete byte swap should make the symbolic number to start
+     with the largest digit in the highest order byte.  */
+  if (cmp != n.n)
+    return NULL_TREE;
+
+  return source_expr;
+}
+
+/* Find manual byte swap implementations and turn them into a bswap
+   builtin invokation.  */
+
+static unsigned int
+execute_optimize_bswap (void)
+{
+  basic_block bb;
+  bool bswap32_p, bswap64_p;
+  bool changed = false;
+
+  if (BITS_PER_UNIT != 8)
+    return 0;
+
+  if (sizeof (HOST_WIDEST_INT) < 8)
+    return 0;
+
+  bswap32_p = (built_in_decls[BUILT_IN_BSWAP32]
+              && optab_handler (bswap_optab, SImode)->insn_code !=
+              CODE_FOR_nothing);
+  bswap64_p = (built_in_decls[BUILT_IN_BSWAP64]
+              && optab_handler (bswap_optab, DImode)->insn_code !=
+              CODE_FOR_nothing);
+
+  if (!bswap32_p && !bswap64_p)
+    return 0;
+
+  FOR_EACH_BB (bb)
+    {
+      gimple_stmt_iterator gsi;
+
+      for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+        {
+         gimple stmt = gsi_stmt (gsi);
+         tree bswap_src;
+         tree fndecl = NULL_TREE;
+         int type_size;
+         gimple call;
+
+         if (!is_gimple_assign (stmt)
+             || gimple_assign_rhs_code (stmt) != BIT_IOR_EXPR)
+           continue;
+
+         type_size = TYPE_PRECISION (gimple_expr_type (stmt));
+
+         switch (type_size)
+           {
+           case 32:
+             if (bswap32_p)
+               fndecl = built_in_decls[BUILT_IN_BSWAP32];
+             break;
+           case 64:
+             if (bswap64_p)
+               fndecl = built_in_decls[BUILT_IN_BSWAP64];
+             break;
+           default:
+             continue;
+           }
+
+         if (!fndecl)
+           continue;
+
+         bswap_src = find_bswap (stmt);
+
+         if (!bswap_src)
+           continue;
+
+         changed = true;
+         call = gimple_build_call (fndecl, 1, bswap_src);
+         gimple_call_set_lhs (call, gimple_assign_lhs (stmt));
+
+         if (dump_file)
+           {
+             fprintf (dump_file, "%d bit bswap implementation found at: ",
+                      (int)type_size);
+             print_gimple_stmt (dump_file, stmt, 0, 0);
+           }
+
+         gsi_insert_after (&gsi, call, GSI_SAME_STMT);
+         gsi_remove (&gsi, true);
+       }
+    }
+
+  return (changed ? TODO_dump_func | TODO_update_ssa | TODO_verify_ssa
+         | TODO_verify_stmts : 0);
+}
+
+static bool
+gate_optimize_bswap (void)
+{
+  return flag_expensive_optimizations && optimize;
+}
+
+struct gimple_opt_pass pass_optimize_bswap =
+{
+ {
+  GIMPLE_PASS,
+  "bswap",                             /* name */
+  gate_optimize_bswap,                  /* gate */
+  execute_optimize_bswap,              /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  TV_NONE,                             /* tv_id */
+  PROP_ssa,                            /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0                                     /* todo_flags_finish */
+ }
+};
index eb1ad15..6ed29ca 100644 (file)
@@ -8489,6 +8489,35 @@ int_cst_value (const_tree x)
   return val;
 }
 
+/* Return value of a constant X and sign-extend it.  */
+
+HOST_WIDEST_INT
+widest_int_cst_value (const_tree x)
+{
+  unsigned bits = TYPE_PRECISION (TREE_TYPE (x));
+  unsigned HOST_WIDEST_INT val = TREE_INT_CST_LOW (x);
+
+#if HOST_BITS_PER_WIDEST_INT > HOST_BITS_PER_WIDE_INT
+  gcc_assert (HOST_BITS_PER_WIDEST_INT >= 2 * HOST_BITS_PER_WIDE_INT);
+  val |= TREE_INT_CST_HIGH (x) << HOST_BITS_PER_WIDE_INT;
+#else
+  /* Make sure the sign-extended value will fit in a HOST_WIDE_INT.  */
+  gcc_assert (TREE_INT_CST_HIGH (x) == 0
+             || TREE_INT_CST_HIGH (x) == -1);
+#endif
+
+  if (bits < HOST_BITS_PER_WIDEST_INT)
+    {
+      bool negative = ((val >> (bits - 1)) & 1) != 0;
+      if (negative)
+       val |= (~(unsigned HOST_WIDEST_INT) 0) << (bits - 1) << 1;
+      else
+       val &= ~((~(unsigned HOST_WIDEST_INT) 0) << (bits - 1) << 1);
+    }
+
+  return val;
+}
+
 /* If TYPE is an integral type, return an equivalent type which is
     unsigned iff UNSIGNEDP is true.  If TYPE is not an integral type,
     return TYPE itself.  */
index f490af4..9c1a1aa 100644 (file)
@@ -4868,6 +4868,7 @@ extern tree build_nonstandard_integer_type (unsigned HOST_WIDE_INT, int);
 extern tree build_range_type (tree, tree, tree);
 extern bool subrange_type_for_debug_p (const_tree, tree *, tree *);
 extern HOST_WIDE_INT int_cst_value (const_tree);
+extern HOST_WIDEST_INT widest_int_cst_value (const_tree);
 
 extern bool fields_compatible_p (const_tree, const_tree);
 extern tree find_compatible_field (tree, tree);