OSDN Git Service

gcc/ada/
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-math-opts.c
index 4d02894..543169a 100644 (file)
@@ -1,11 +1,11 @@
 /* Global, SSA-based optimizations using mathematical identities.
-   Copyright (C) 2005 Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007 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 2, or (at your option) any
+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
@@ -14,9 +14,8 @@ 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 COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 /* Currently, the only mini-pass in this file tries to CSE reciprocal
    operations.  These are common in sequences such as this one:
@@ -111,7 +110,7 @@ struct occurrence {
      inserted in BB.  */
   tree recip_def;
 
-  /* If non-NULL, the MODIFY_EXPR for a reciprocal computation that
+  /* If non-NULL, the GIMPLE_MODIFY_STMT for a reciprocal computation that
      was inserted in BB.  */
   tree recip_def_stmt;
 
@@ -151,7 +150,7 @@ occ_new (basic_block bb, struct occurrence *children)
 {
   struct occurrence *occ;
 
-  occ = bb->aux = pool_alloc (occ_pool);
+  bb->aux = occ = (struct occurrence *) pool_alloc (occ_pool);
   memset (occ, 0, sizeof (struct occurrence));
 
   occ->bb = bb;
@@ -274,38 +273,9 @@ compute_merit (struct occurrence *occ)
 static inline bool
 is_division_by (tree use_stmt, tree def)
 {
-  return TREE_CODE (use_stmt) == MODIFY_EXPR
-        && TREE_CODE (TREE_OPERAND (use_stmt, 1)) == RDIV_EXPR
-        && TREE_OPERAND (TREE_OPERAND (use_stmt, 1), 1) == def;
-}
-
-/* Return the LHS of a RDIV_EXPR that computes a reciprocal in type TYPE.  */
-static tree
-get_constant_one (tree type)
-{
-  tree scalar, cst;
-  int i;
-
-  gcc_assert (FLOAT_TYPE_P (type));
-  switch (TREE_CODE (type))
-    {
-    case REAL_TYPE:
-      return build_real (type, dconst1);
-
-    case VECTOR_TYPE:
-      scalar = build_real (TREE_TYPE (type), dconst1);
-
-      /* Create 'vect_cst_ = {cst,cst,...,cst}'  */
-      cst = NULL_TREE;
-      for (i = TYPE_VECTOR_SUBPARTS (type); --i >= 0; )
-        cst = tree_cons (NULL_TREE, scalar, cst);
-
-      return build_vector (type, cst);
-
-    default:
-      /* Complex operations have been split already.  */
-      gcc_unreachable ();
-    }
+  return TREE_CODE (use_stmt) == GIMPLE_MODIFY_STMT
+        && TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == RDIV_EXPR
+        && TREE_OPERAND (GIMPLE_STMT_OPERAND (use_stmt, 1), 1) == def;
 }
 
 /* Walk the subset of the dominator tree rooted at OCC, setting the
@@ -332,9 +302,10 @@ insert_reciprocals (block_stmt_iterator *def_bsi, struct occurrence *occ,
       /* Make a variable with the replacement and substitute it.  */
       type = TREE_TYPE (def);
       recip_def = make_rename_temp (type, "reciptmp");
-      new_stmt = build2 (MODIFY_EXPR, void_type_node, recip_def,
-                        fold_build2 (RDIV_EXPR, type, get_constant_one (type),
-                                     def));
+      new_stmt = build_gimple_modify_stmt (recip_def,
+                                          fold_build2 (RDIV_EXPR, type,
+                                                       build_one_cst (type),
+                                                       def));
   
   
       if (occ->bb_has_division)
@@ -382,7 +353,7 @@ replace_reciprocal (use_operand_p use_p)
 
   if (occ->recip_def && use_stmt != occ->recip_def_stmt)
     {
-      TREE_SET_CODE (TREE_OPERAND (use_stmt, 1), MULT_EXPR);
+      TREE_SET_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1), MULT_EXPR);
       SET_USE (use_p, occ->recip_def);
       fold_stmt_inplace (use_stmt);
       update_stmt (use_stmt);
@@ -446,17 +417,20 @@ execute_cse_reciprocals_1 (block_stmt_iterator *def_bsi, tree def)
   threshold = targetm.min_divisions_for_recip_mul (TYPE_MODE (TREE_TYPE (def)));
   if (count >= threshold)
     {
+      tree use_stmt;
       for (occ = occ_head; occ; occ = occ->next)
        {
          compute_merit (occ);
          insert_reciprocals (def_bsi, occ, def, NULL, threshold);
        }
 
-      FOR_EACH_IMM_USE_SAFE (use_p, use_iter, def)
+      FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, def)
        {
-         tree use_stmt = USE_STMT (use_p);
          if (is_division_by (use_stmt, def))
-           replace_reciprocal (use_p);
+           {
+             FOR_EACH_IMM_USE_ON_STMT (use_p, use_iter)
+               replace_reciprocal (use_p);
+           }
        }
     }
 
@@ -466,14 +440,12 @@ execute_cse_reciprocals_1 (block_stmt_iterator *def_bsi, tree def)
   occ_head = NULL;
 }
 
-
 static bool
 gate_cse_reciprocals (void)
 {
-  return optimize && !optimize_size && flag_unsafe_math_optimizations;
+  return optimize && !optimize_size && flag_reciprocal_math;
 }
 
-
 /* Go through all the floating-point SSA_NAMEs, and call
    execute_cse_reciprocals_1 on each of them.  */
 static unsigned int
@@ -486,7 +458,8 @@ execute_cse_reciprocals (void)
                                sizeof (struct occurrence),
                                n_basic_blocks / 3 + 1);
 
-  calculate_dominance_info (CDI_DOMINATORS | CDI_POST_DOMINATORS);
+  calculate_dominance_info (CDI_DOMINATORS);
+  calculate_dominance_info (CDI_POST_DOMINATORS);
 
 #ifdef ENABLE_CHECKING
   FOR_EACH_BB (bb)
@@ -494,10 +467,10 @@ execute_cse_reciprocals (void)
 #endif
 
   for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = TREE_CHAIN (arg))
-    if (default_def (arg)
+    if (gimple_default_def (cfun, arg)
        && FLOAT_TYPE_P (TREE_TYPE (arg))
        && is_gimple_reg (arg))
-      execute_cse_reciprocals_1 (NULL, default_def (arg));
+      execute_cse_reciprocals_1 (NULL, gimple_default_def (cfun, arg));
 
   FOR_EACH_BB (bb)
     {
@@ -515,15 +488,65 @@ execute_cse_reciprocals (void)
       for (bsi = bsi_after_labels (bb); !bsi_end_p (bsi); bsi_next (&bsi))
         {
          tree stmt = bsi_stmt (bsi);
-         if (TREE_CODE (stmt) == MODIFY_EXPR
+
+         if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
              && (def = SINGLE_SSA_TREE_OPERAND (stmt, SSA_OP_DEF)) != NULL
              && FLOAT_TYPE_P (TREE_TYPE (def))
              && TREE_CODE (def) == SSA_NAME)
            execute_cse_reciprocals_1 (&bsi, def);
        }
+
+      /* Scan for a/func(b) and convert it to reciprocal a*rfunc(b).  */
+      for (bsi = bsi_after_labels (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+        {
+         tree stmt = bsi_stmt (bsi);
+         tree fndecl;
+
+         if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+             && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == RDIV_EXPR)
+           {
+             tree arg1 = TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt, 1), 1);
+             tree stmt1;
+
+             if (TREE_CODE (arg1) != SSA_NAME)
+               continue;
+
+             stmt1 = SSA_NAME_DEF_STMT (arg1);
+
+             if (TREE_CODE (stmt1) == GIMPLE_MODIFY_STMT
+                 && TREE_CODE (GIMPLE_STMT_OPERAND (stmt1, 1)) == CALL_EXPR
+                 && (fndecl
+                     = get_callee_fndecl (GIMPLE_STMT_OPERAND (stmt1, 1)))
+                 && (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+                     || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD))
+               {
+                 enum built_in_function code;
+                 bool md_code;
+                 tree arg10;
+                 tree tmp;
+
+                 code = DECL_FUNCTION_CODE (fndecl);
+                 md_code = DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD;
+
+                 fndecl = targetm.builtin_reciprocal (code, md_code, false);
+                 if (!fndecl)
+                   continue;
+
+                 arg10 = CALL_EXPR_ARG (GIMPLE_STMT_OPERAND (stmt1, 1), 0);
+                 tmp = build_call_expr (fndecl, 1, arg10);
+                 GIMPLE_STMT_OPERAND (stmt1, 1) = tmp;
+                 update_stmt (stmt1);
+
+                 TREE_SET_CODE (GIMPLE_STMT_OPERAND (stmt, 1), MULT_EXPR);
+                 fold_stmt_inplace (stmt);
+                 update_stmt (stmt);
+               }
+           }
+       }
     }
 
-  free_dominance_info (CDI_DOMINATORS | CDI_POST_DOMINATORS);
+  free_dominance_info (CDI_DOMINATORS);
+  free_dominance_info (CDI_POST_DOMINATORS);
   free_alloc_pool (occ_pool);
   return 0;
 }
@@ -545,3 +568,300 @@ struct tree_opt_pass pass_cse_reciprocals =
     | TODO_verify_stmts,                /* todo_flags_finish */
   0                                    /* letter */
 };
+
+/* Records an occurrence at statement USE_STMT in the vector of trees
+   STMTS if it is dominated by *TOP_BB or dominates it or this basic block
+   is not yet initialized.  Returns true if the occurrence was pushed on
+   the vector.  Adjusts *TOP_BB to be the basic block dominating all
+   statements in the vector.  */
+
+static bool
+maybe_record_sincos (VEC(tree, heap) **stmts,
+                    basic_block *top_bb, tree use_stmt)
+{
+  basic_block use_bb = bb_for_stmt (use_stmt);
+  if (*top_bb
+      && (*top_bb == use_bb
+         || dominated_by_p (CDI_DOMINATORS, use_bb, *top_bb)))
+    VEC_safe_push (tree, heap, *stmts, use_stmt);
+  else if (!*top_bb
+          || dominated_by_p (CDI_DOMINATORS, *top_bb, use_bb))
+    {
+      VEC_safe_push (tree, heap, *stmts, use_stmt);
+      *top_bb = use_bb;
+    }
+  else
+    return false;
+
+  return true;
+}
+
+/* Look for sin, cos and cexpi calls with the same argument NAME and
+   create a single call to cexpi CSEing the result in this case.
+   We first walk over all immediate uses of the argument collecting
+   statements that we can CSE in a vector and in a second pass replace
+   the statement rhs with a REALPART or IMAGPART expression on the
+   result of the cexpi call we insert before the use statement that
+   dominates all other candidates.  */
+
+static void
+execute_cse_sincos_1 (tree name)
+{
+  block_stmt_iterator bsi;
+  imm_use_iterator use_iter;
+  tree def_stmt, use_stmt, fndecl, res, call, stmt, type;
+  int seen_cos = 0, seen_sin = 0, seen_cexpi = 0;
+  VEC(tree, heap) *stmts = NULL;
+  basic_block top_bb = NULL;
+  int i;
+
+  type = TREE_TYPE (name);
+  FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, name)
+    {
+      if (TREE_CODE (use_stmt) != GIMPLE_MODIFY_STMT
+         || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) != CALL_EXPR
+         || !(fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (use_stmt, 1)))
+         || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+       continue;
+
+      switch (DECL_FUNCTION_CODE (fndecl))
+       {
+       CASE_FLT_FN (BUILT_IN_COS):
+         seen_cos |= maybe_record_sincos (&stmts, &top_bb, use_stmt) ? 1 : 0;
+         break;
+
+       CASE_FLT_FN (BUILT_IN_SIN):
+         seen_sin |= maybe_record_sincos (&stmts, &top_bb, use_stmt) ? 1 : 0;
+         break;
+
+       CASE_FLT_FN (BUILT_IN_CEXPI):
+         seen_cexpi |= maybe_record_sincos (&stmts, &top_bb, use_stmt) ? 1 : 0;
+         break;
+
+       default:;
+       }
+    }
+
+  if (seen_cos + seen_sin + seen_cexpi <= 1)
+    {
+      VEC_free(tree, heap, stmts);
+      return;
+    }
+
+  /* Simply insert cexpi at the beginning of top_bb but not earlier than
+     the name def statement.  */
+  fndecl = mathfn_built_in (type, BUILT_IN_CEXPI);
+  if (!fndecl)
+    return;
+  res = make_rename_temp (TREE_TYPE (TREE_TYPE (fndecl)), "sincostmp");
+  call = build_call_expr (fndecl, 1, name);
+  stmt = build_gimple_modify_stmt (res, call);
+  def_stmt = SSA_NAME_DEF_STMT (name);
+  if (bb_for_stmt (def_stmt) == top_bb
+      && TREE_CODE (def_stmt) == GIMPLE_MODIFY_STMT)
+    {
+      bsi = bsi_for_stmt (def_stmt);
+      bsi_insert_after (&bsi, stmt, BSI_SAME_STMT);
+    }
+  else
+    {
+      bsi = bsi_after_labels (top_bb);
+      bsi_insert_before (&bsi, stmt, BSI_SAME_STMT);
+    }
+  update_stmt (stmt);
+
+  /* And adjust the recorded old call sites.  */
+  for (i = 0; VEC_iterate(tree, stmts, i, use_stmt); ++i)
+    {
+      fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (use_stmt, 1));
+      switch (DECL_FUNCTION_CODE (fndecl))
+       {
+       CASE_FLT_FN (BUILT_IN_COS):
+         GIMPLE_STMT_OPERAND (use_stmt, 1) = fold_build1 (REALPART_EXPR,
+                                                          type, res);
+         break;
+
+       CASE_FLT_FN (BUILT_IN_SIN):
+         GIMPLE_STMT_OPERAND (use_stmt, 1) = fold_build1 (IMAGPART_EXPR,
+                                                          type, res);
+         break;
+
+       CASE_FLT_FN (BUILT_IN_CEXPI):
+         GIMPLE_STMT_OPERAND (use_stmt, 1) = res;
+         break;
+
+       default:;
+         gcc_unreachable ();
+       }
+
+       update_stmt (use_stmt);
+    }
+
+  VEC_free(tree, heap, stmts);
+}
+
+/* Go through all calls to sin, cos and cexpi and call execute_cse_sincos_1
+   on the SSA_NAME argument of each of them.  */
+
+static unsigned int
+execute_cse_sincos (void)
+{
+  basic_block bb;
+
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator bsi;
+
+      for (bsi = bsi_after_labels (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+        {
+         tree stmt = bsi_stmt (bsi);
+         tree fndecl;
+
+         if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+             && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR
+             && (fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (stmt, 1)))
+             && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+           {
+             tree arg;
+
+             switch (DECL_FUNCTION_CODE (fndecl))
+               {
+               CASE_FLT_FN (BUILT_IN_COS):
+               CASE_FLT_FN (BUILT_IN_SIN):
+               CASE_FLT_FN (BUILT_IN_CEXPI):
+                 arg = GIMPLE_STMT_OPERAND (stmt, 1);
+                 arg = CALL_EXPR_ARG (arg, 0);
+                 if (TREE_CODE (arg) == SSA_NAME)
+                   execute_cse_sincos_1 (arg);
+                 break;
+
+               default:;
+               }
+           }
+       }
+    }
+
+  free_dominance_info (CDI_DOMINATORS);
+  return 0;
+}
+
+static bool
+gate_cse_sincos (void)
+{
+  /* Make sure we have either sincos or cexp.  */
+  return (TARGET_HAS_SINCOS
+         || TARGET_C99_FUNCTIONS)
+        && optimize;
+}
+
+struct tree_opt_pass pass_cse_sincos =
+{
+  "sincos",                            /* name */
+  gate_cse_sincos,                     /* gate */
+  execute_cse_sincos,                  /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  PROP_ssa,                            /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func | TODO_update_ssa | TODO_verify_ssa
+    | TODO_verify_stmts,                /* todo_flags_finish */
+  0                                    /* letter */
+};
+
+/* Find all expressions in the form of sqrt(a/b) and
+   convert them to rsqrt(b/a).  */
+
+static unsigned int
+execute_convert_to_rsqrt (void)
+{
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator bsi;
+
+      for (bsi = bsi_after_labels (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+        {
+         tree stmt = bsi_stmt (bsi);
+         tree fndecl;
+
+         if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+             && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR
+             && (fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (stmt, 1)))
+             && (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+                 || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD))
+           {
+             enum built_in_function code;
+             bool md_code;
+             tree arg1;
+             tree stmt1;
+
+             code = DECL_FUNCTION_CODE (fndecl);
+             md_code = DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD;
+
+             fndecl = targetm.builtin_reciprocal (code, md_code, true);
+             if (!fndecl)
+               continue;
+
+             arg1 = CALL_EXPR_ARG (GIMPLE_STMT_OPERAND (stmt, 1), 0);
+
+             if (TREE_CODE (arg1) != SSA_NAME)
+               continue;
+
+             stmt1 = SSA_NAME_DEF_STMT (arg1);
+
+             if (TREE_CODE (stmt1) == GIMPLE_MODIFY_STMT
+                 && TREE_CODE (GIMPLE_STMT_OPERAND (stmt1, 1)) == RDIV_EXPR)
+               {
+                 tree arg10, arg11;
+                 tree tmp;
+
+                 arg10 = TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt1, 1), 0);
+                 arg11 = TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt1, 1), 1);
+
+                 /* Swap operands of RDIV_EXPR.  */
+                 TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt1, 1), 0) = arg11;
+                 TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt1, 1), 1) = arg10;
+                 fold_stmt_inplace (stmt1);
+                 update_stmt (stmt1);
+
+                 tmp = build_call_expr (fndecl, 1, arg1);
+                 GIMPLE_STMT_OPERAND (stmt, 1) = tmp;
+                 update_stmt (stmt);
+               }
+           }
+       }
+    }
+
+  return 0;
+}
+
+static bool
+gate_convert_to_rsqrt (void)
+{
+  return flag_unsafe_math_optimizations && optimize;
+}
+
+struct tree_opt_pass pass_convert_to_rsqrt =
+{
+  "rsqrt",                             /* name */
+  gate_convert_to_rsqrt,               /* gate */
+  execute_convert_to_rsqrt,            /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  PROP_ssa,                            /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func | TODO_update_ssa | TODO_verify_ssa
+    | TODO_verify_stmts,                /* todo_flags_finish */
+  0                                    /* letter */
+};