OSDN Git Service

2010-04-06 Tobias Burnus <burnus@net-b.de>
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-math-opts.c
index c0ddc8a..f89910b 100644 (file)
@@ -1,18 +1,19 @@
 /* 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.
-   
+
 GCC is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
 Free Software Foundation; either version 3, or (at your option) any
 later version.
-   
+
 GCC is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
-   
+
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
@@ -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
@@ -312,7 +315,7 @@ insert_reciprocals (gimple_stmt_iterator *def_gsi, struct occurrence *occ,
       recip_def = make_rename_temp (type, "reciptmp");
       new_stmt = gimple_build_assign_with_ops (RDIV_EXPR, recip_def,
                                               build_one_cst (type), def);
-  
+
       if (occ->bb_has_division)
         {
           /* Case 1: insert before an existing division.  */
@@ -418,7 +421,7 @@ execute_cse_reciprocals_1 (gimple_stmt_iterator *def_gsi, tree def)
          count++;
        }
     }
-  
+
   /* Do the expensive part only if we can hope to optimize something.  */
   threshold = targetm.min_divisions_for_recip_mul (TYPE_MODE (TREE_TYPE (def)));
   if (count >= threshold)
@@ -563,6 +566,7 @@ execute_cse_reciprocals (void)
                  if (fail)
                    continue;
 
+                 gimple_replace_lhs (stmt1, arg1);
                  gimple_call_set_fndecl (stmt1, fndecl);
                  update_stmt (stmt1);
 
@@ -736,7 +740,7 @@ execute_cse_sincos_1 (tree name)
 
        gsi = gsi_for_stmt (use_stmt);
        gsi_insert_after (&gsi, stmt, GSI_SAME_STMT);
-       gsi_remove (&gsi, true); 
+       gsi_remove (&gsi, true);
     }
 
   VEC_free(gimple, heap, stmts);
@@ -939,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;
        }
@@ -987,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:
@@ -1050,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;
@@ -1078,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
@@ -1110,8 +1117,9 @@ execute_optimize_bswap (void)
               && 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);
+              && (optab_handler (bswap_optab, DImode)->insn_code !=
+                  CODE_FOR_nothing
+                  || (bswap32_p && word_mode == SImode)));
 
   if (!bswap32_p && !bswap64_p)
     return 0;
@@ -1254,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 */
+ }
+};