OSDN Git Service

2010-04-27 Martin Jambor <mjambor@suse.cz>
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-math-opts.c
index c46a57f..f89910b 100644 (file)
@@ -1,5 +1,6 @@
 /* Global, SSA-based optimizations using mathematical identities.
-   Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -91,15 +92,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "tree.h"
 #include "tree-flow.h"
-#include "real.h"
 #include "timevar.h"
 #include "tree-pass.h"
 #include "alloc-pool.h"
 #include "basic-block.h"
 #include "target.h"
 #include "diagnostic.h"
-#include "rtl.h"
-#include "expr.h"
+#include "gimple-pretty-print.h"
+
+/* FIXME: RTL headers have to be included here for optabs.  */
+#include "rtl.h"               /* Because optabs.h wants enum rtx_code.  */
+#include "expr.h"              /* Because optabs.h wants sepops.  */
 #include "optabs.h"
 
 /* This structure represents one basic block that either computes a
@@ -940,15 +943,18 @@ find_bswap_1 (gimple stmt, struct symbolic_number *n, int limit)
        {
          /* 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.  */
+            order byte is set to n->size and the lowest order
+            byte to 1.  */
          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;
+                 (unsigned HOST_WIDEST_INT)0x08070605 << 32 | 0x04030201);
+
+         if (n->size < (int)sizeof (HOST_WIDEST_INT))
+           n->n &= ((unsigned HOST_WIDEST_INT)1 <<
+                    (n->size * BITS_PER_UNIT)) - 1;
 
          source_expr1 = rhs1;
        }
@@ -988,9 +994,9 @@ find_bswap_1 (gimple stmt, struct symbolic_number *n, int limit)
              {
                /* 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;
              }
+           n->size = type_size / BITS_PER_UNIT;
          }
          break;
        default:
@@ -1051,11 +1057,11 @@ 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.  */
+   have a full byte swap.  The number is shifted to the left according
+   to the size of the symbolic number before using it.  */
   unsigned HOST_WIDEST_INT cmp =
     sizeof (HOST_WIDEST_INT) < 8 ? 0 :
-    (unsigned HOST_WIDEST_INT)0x08070605 << 32 | 0x04030201;
+    (unsigned HOST_WIDEST_INT)0x01020304 << 32 | 0x05060708;
 
   struct symbolic_number n;
   tree source_expr;
@@ -1079,7 +1085,7 @@ find_bswap (gimple stmt)
        ((unsigned HOST_WIDEST_INT)1 << (n.size * BITS_PER_UNIT)) - 1;
 
       n.n &= mask;
-      cmp &= mask;
+      cmp >>= (sizeof (HOST_WIDEST_INT) - n.size) * BITS_PER_UNIT;
     }
 
   /* A complete byte swap should make the symbolic number to start
@@ -1256,3 +1262,137 @@ struct gimple_opt_pass pass_optimize_bswap =
   0                                     /* todo_flags_finish */
  }
 };
+
+/* Find integer multiplications where the operands are extended from
+   smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
+   where appropriate.  */
+
+static unsigned int
+execute_optimize_widening_mul (void)
+{
+  bool changed = false;
+  basic_block bb;
+
+  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);
+         gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
+         tree type, type1 = NULL, type2 = NULL;
+         tree rhs1, rhs2, rhs1_convop = NULL, rhs2_convop = NULL;
+         enum tree_code rhs1_code, rhs2_code;
+
+         if (!is_gimple_assign (stmt)
+             || gimple_assign_rhs_code (stmt) != MULT_EXPR)
+           continue;
+
+         type = TREE_TYPE (gimple_assign_lhs (stmt));
+
+         if (TREE_CODE (type) != INTEGER_TYPE)
+           continue;
+
+         rhs1 = gimple_assign_rhs1 (stmt);
+         rhs2 = gimple_assign_rhs2 (stmt);
+
+         if (TREE_CODE (rhs1) == SSA_NAME)
+           {
+             rhs1_stmt = SSA_NAME_DEF_STMT (rhs1);
+             if (!is_gimple_assign (rhs1_stmt))
+               continue;
+             rhs1_code = gimple_assign_rhs_code (rhs1_stmt);
+             if (!CONVERT_EXPR_CODE_P (rhs1_code))
+               continue;
+             rhs1_convop = gimple_assign_rhs1 (rhs1_stmt);
+             type1 = TREE_TYPE (rhs1_convop);
+             if (TYPE_PRECISION (type1) * 2 != TYPE_PRECISION (type))
+               continue;
+           }
+         else if (TREE_CODE (rhs1) != INTEGER_CST)
+           continue;
+
+         if (TREE_CODE (rhs2) == SSA_NAME)
+           {
+             rhs2_stmt = SSA_NAME_DEF_STMT (rhs2);
+             if (!is_gimple_assign (rhs2_stmt))
+               continue;
+             rhs2_code = gimple_assign_rhs_code (rhs2_stmt);
+             if (!CONVERT_EXPR_CODE_P (rhs2_code))
+               continue;
+             rhs2_convop = gimple_assign_rhs1 (rhs2_stmt);
+             type2 = TREE_TYPE (rhs2_convop);
+             if (TYPE_PRECISION (type2) * 2 != TYPE_PRECISION (type))
+               continue;
+           }
+         else if (TREE_CODE (rhs2) != INTEGER_CST)
+           continue;
+
+         if (rhs1_stmt == NULL && rhs2_stmt == NULL)
+           continue;
+
+         /* Verify that the machine can perform a widening multiply in this
+            mode/signedness combination, otherwise this transformation is
+            likely to pessimize code.  */
+         if ((rhs1_stmt == NULL || TYPE_UNSIGNED (type1))
+             && (rhs2_stmt == NULL || TYPE_UNSIGNED (type2))
+             && (optab_handler (umul_widen_optab, TYPE_MODE (type))
+                 ->insn_code == CODE_FOR_nothing))
+           continue;
+         else if ((rhs1_stmt == NULL || !TYPE_UNSIGNED (type1))
+                  && (rhs2_stmt == NULL || !TYPE_UNSIGNED (type2))
+                  && (optab_handler (smul_widen_optab, TYPE_MODE (type))
+                      ->insn_code == CODE_FOR_nothing))
+           continue;
+         else if (rhs1_stmt != NULL && rhs2_stmt != 0
+                  && (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
+                  && (optab_handler (usmul_widen_optab, TYPE_MODE (type))
+                      ->insn_code == CODE_FOR_nothing))
+           continue;
+
+         if ((rhs1_stmt == NULL && !int_fits_type_p (rhs1, type2))
+             || (rhs2_stmt == NULL && !int_fits_type_p (rhs2, type1)))
+           continue;
+
+         if (rhs1_stmt == NULL)
+           gimple_assign_set_rhs1 (stmt, fold_convert (type2, rhs1));
+         else
+           gimple_assign_set_rhs1 (stmt, rhs1_convop);
+         if (rhs2_stmt == NULL)
+           gimple_assign_set_rhs2 (stmt, fold_convert (type1, rhs2));
+         else
+           gimple_assign_set_rhs2 (stmt, rhs2_convop);
+         gimple_assign_set_rhs_code (stmt, WIDEN_MULT_EXPR);
+         update_stmt (stmt);
+         changed = true;
+       }
+    }
+  return (changed ? TODO_dump_func | TODO_update_ssa | TODO_verify_ssa
+         | TODO_verify_stmts : 0);
+}
+
+static bool
+gate_optimize_widening_mul (void)
+{
+  return flag_expensive_optimizations && optimize;
+}
+
+struct gimple_opt_pass pass_optimize_widening_mul =
+{
+ {
+  GIMPLE_PASS,
+  "widening_mul",                      /* name */
+  gate_optimize_widening_mul,          /* gate */
+  execute_optimize_widening_mul,       /* 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 */
+ }
+};