gimple.h: Remove all includes.
[platform/upstream/gcc.git] / gcc / tree-ssa-math-opts.c
index 30f78bc..f77c016 100644 (file)
@@ -1,6 +1,5 @@
 /* Global, SSA-based optimizations using mathematical identities.
 /* Global, SSA-based optimizations using mathematical identities.
-   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   Copyright (C) 2005-2013 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 
 This file is part of GCC.
 
@@ -91,10 +90,27 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "flags.h"
 #include "tree.h"
 #include "tm.h"
 #include "flags.h"
 #include "tree.h"
-#include "tree-flow.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "stor-layout.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "expr.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
 #include "tree-pass.h"
 #include "alloc-pool.h"
 #include "tree-pass.h"
 #include "alloc-pool.h"
-#include "basic-block.h"
 #include "target.h"
 #include "gimple-pretty-print.h"
 
 #include "target.h"
 #include "gimple-pretty-print.h"
 
@@ -154,6 +170,9 @@ static struct
 
 static struct
 {
 
 static struct
 {
+  /* Number of hand-written 16-bit bswaps found.  */
+  int found_16bit;
+
   /* Number of hand-written 32-bit bswaps found.  */
   int found_32bit;
 
   /* Number of hand-written 32-bit bswaps found.  */
   int found_32bit;
 
@@ -274,7 +293,7 @@ register_division_in (basic_block bb)
   if (!occ)
     {
       occ = occ_new (bb, NULL);
   if (!occ)
     {
       occ = occ_new (bb, NULL);
-      insert_bb (occ, ENTRY_BLOCK_PTR, &occ_head);
+      insert_bb (occ, ENTRY_BLOCK_PTR_FOR_FN (cfun), &occ_head);
     }
 
   occ->bb_has_division = true;
     }
 
   occ->bb_has_division = true;
@@ -345,7 +364,7 @@ insert_reciprocals (gimple_stmt_iterator *def_gsi, struct occurrence *occ,
     {
       /* Make a variable with the replacement and substitute it.  */
       type = TREE_TYPE (def);
     {
       /* Make a variable with the replacement and substitute it.  */
       type = TREE_TYPE (def);
-      recip_def = make_rename_temp (type, "reciptmp");
+      recip_def = create_tmp_reg (type, "reciptmp");
       new_stmt = gimple_build_assign_with_ops (RDIV_EXPR, recip_def,
                                               build_one_cst (type), def);
 
       new_stmt = gimple_build_assign_with_ops (RDIV_EXPR, recip_def,
                                               build_one_cst (type), def);
 
@@ -501,7 +520,7 @@ execute_cse_reciprocals (void)
 
   occ_pool = create_alloc_pool ("dominators for recip",
                                sizeof (struct occurrence),
 
   occ_pool = create_alloc_pool ("dominators for recip",
                                sizeof (struct occurrence),
-                               n_basic_blocks / 3 + 1);
+                               n_basic_blocks_for_fn (cfun) / 3 + 1);
 
   memset (&reciprocal_stats, 0, sizeof (reciprocal_stats));
   calculate_dominance_info (CDI_DOMINATORS);
 
   memset (&reciprocal_stats, 0, sizeof (reciprocal_stats));
   calculate_dominance_info (CDI_DOMINATORS);
@@ -513,10 +532,13 @@ execute_cse_reciprocals (void)
 #endif
 
   for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
 #endif
 
   for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
-    if (gimple_default_def (cfun, arg)
-       && FLOAT_TYPE_P (TREE_TYPE (arg))
+    if (FLOAT_TYPE_P (TREE_TYPE (arg))
        && is_gimple_reg (arg))
        && is_gimple_reg (arg))
-      execute_cse_reciprocals_1 (NULL, gimple_default_def (cfun, arg));
+      {
+       tree name = ssa_default_def (cfun, arg);
+       if (name)
+         execute_cse_reciprocals_1 (NULL, name);
+      }
 
   FOR_EACH_BB (bb)
     {
 
   FOR_EACH_BB (bb)
     {
@@ -528,8 +550,8 @@ execute_cse_reciprocals (void)
        {
          phi = gsi_stmt (gsi);
          def = PHI_RESULT (phi);
        {
          phi = gsi_stmt (gsi);
          def = PHI_RESULT (phi);
-         if (FLOAT_TYPE_P (TREE_TYPE (def))
-             && is_gimple_reg (def))
+         if (! virtual_operand_p (def)
+             && FLOAT_TYPE_P (TREE_TYPE (def)))
            execute_cse_reciprocals_1 (NULL, def);
        }
 
            execute_cse_reciprocals_1 (NULL, def);
        }
 
@@ -603,7 +625,7 @@ execute_cse_reciprocals (void)
                  if (fail)
                    continue;
 
                  if (fail)
                    continue;
 
-                 gimple_replace_lhs (stmt1, arg1);
+                 gimple_replace_ssa_lhs (stmt1, arg1);
                  gimple_call_set_fndecl (stmt1, fndecl);
                  update_stmt (stmt1);
                  reciprocal_stats.rfuncs_inserted++;
                  gimple_call_set_fndecl (stmt1, fndecl);
                  update_stmt (stmt1);
                  reciprocal_stats.rfuncs_inserted++;
@@ -631,26 +653,45 @@ execute_cse_reciprocals (void)
   return 0;
 }
 
   return 0;
 }
 
-struct gimple_opt_pass pass_cse_reciprocals =
+namespace {
+
+const pass_data pass_data_cse_reciprocals =
 {
 {
- {
-  GIMPLE_PASS,
-  "recip",                             /* name */
-  gate_cse_reciprocals,                        /* gate */
-  execute_cse_reciprocals,             /* 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 */
-  TODO_update_ssa | TODO_verify_ssa
-    | TODO_verify_stmts                /* todo_flags_finish */
- }
+  GIMPLE_PASS, /* type */
+  "recip", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_update_ssa | TODO_verify_ssa
+    | TODO_verify_stmts ), /* todo_flags_finish */
 };
 
 };
 
+class pass_cse_reciprocals : public gimple_opt_pass
+{
+public:
+  pass_cse_reciprocals (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_cse_reciprocals, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_cse_reciprocals (); }
+  unsigned int execute () { return execute_cse_reciprocals (); }
+
+}; // class pass_cse_reciprocals
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_cse_reciprocals (gcc::context *ctxt)
+{
+  return new pass_cse_reciprocals (ctxt);
+}
+
 /* 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
 /* 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
@@ -658,18 +699,18 @@ struct gimple_opt_pass pass_cse_reciprocals =
    statements in the vector.  */
 
 static bool
    statements in the vector.  */
 
 static bool
-maybe_record_sincos (VEC(gimple, heap) **stmts,
+maybe_record_sincos (vec<gimple> *stmts,
                     basic_block *top_bb, gimple use_stmt)
 {
   basic_block use_bb = gimple_bb (use_stmt);
   if (*top_bb
       && (*top_bb == use_bb
          || dominated_by_p (CDI_DOMINATORS, use_bb, *top_bb)))
                     basic_block *top_bb, gimple use_stmt)
 {
   basic_block use_bb = gimple_bb (use_stmt);
   if (*top_bb
       && (*top_bb == use_bb
          || dominated_by_p (CDI_DOMINATORS, use_bb, *top_bb)))
-    VEC_safe_push (gimple, heap, *stmts, use_stmt);
+    stmts->safe_push (use_stmt);
   else if (!*top_bb
           || dominated_by_p (CDI_DOMINATORS, *top_bb, use_bb))
     {
   else if (!*top_bb
           || dominated_by_p (CDI_DOMINATORS, *top_bb, use_bb))
     {
-      VEC_safe_push (gimple, heap, *stmts, use_stmt);
+      stmts->safe_push (use_stmt);
       *top_bb = use_bb;
     }
   else
       *top_bb = use_bb;
     }
   else
@@ -694,7 +735,7 @@ execute_cse_sincos_1 (tree name)
   tree fndecl, res, type;
   gimple def_stmt, use_stmt, stmt;
   int seen_cos = 0, seen_sin = 0, seen_cexpi = 0;
   tree fndecl, res, type;
   gimple def_stmt, use_stmt, stmt;
   int seen_cos = 0, seen_sin = 0, seen_cexpi = 0;
-  VEC(gimple, heap) *stmts = NULL;
+  vec<gimple> stmts = vNULL;
   basic_block top_bb = NULL;
   int i;
   bool cfg_changed = false;
   basic_block top_bb = NULL;
   int i;
   bool cfg_changed = false;
@@ -728,7 +769,7 @@ execute_cse_sincos_1 (tree name)
 
   if (seen_cos + seen_sin + seen_cexpi <= 1)
     {
 
   if (seen_cos + seen_sin + seen_cexpi <= 1)
     {
-      VEC_free(gimple, heap, stmts);
+      stmts.release ();
       return false;
     }
 
       return false;
     }
 
@@ -737,9 +778,8 @@ execute_cse_sincos_1 (tree name)
   fndecl = mathfn_built_in (type, BUILT_IN_CEXPI);
   if (!fndecl)
     return false;
   fndecl = mathfn_built_in (type, BUILT_IN_CEXPI);
   if (!fndecl)
     return false;
-  res = create_tmp_reg (TREE_TYPE (TREE_TYPE (fndecl)), "sincostmp");
   stmt = gimple_build_call (fndecl, 1, name);
   stmt = gimple_build_call (fndecl, 1, name);
-  res = make_ssa_name (res, stmt);
+  res = make_temp_ssa_name (TREE_TYPE (TREE_TYPE (fndecl)), stmt, "sincostmp");
   gimple_call_set_lhs (stmt, res);
 
   def_stmt = SSA_NAME_DEF_STMT (name);
   gimple_call_set_lhs (stmt, res);
 
   def_stmt = SSA_NAME_DEF_STMT (name);
@@ -755,11 +795,10 @@ execute_cse_sincos_1 (tree name)
       gsi = gsi_after_labels (top_bb);
       gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
     }
       gsi = gsi_after_labels (top_bb);
       gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
     }
-  update_stmt (stmt);
   sincos_stats.inserted++;
 
   /* And adjust the recorded old call sites.  */
   sincos_stats.inserted++;
 
   /* And adjust the recorded old call sites.  */
-  for (i = 0; VEC_iterate(gimple, stmts, i, use_stmt); ++i)
+  for (i = 0; stmts.iterate (i, &use_stmt); ++i)
     {
       tree rhs = NULL;
       fndecl = gimple_call_fndecl (use_stmt);
     {
       tree rhs = NULL;
       fndecl = gimple_call_fndecl (use_stmt);
@@ -791,7 +830,7 @@ execute_cse_sincos_1 (tree name)
          cfg_changed = true;
     }
 
          cfg_changed = true;
     }
 
-  VEC_free(gimple, heap, stmts);
+  stmts.release ();
 
   return cfg_changed;
 }
 
   return cfg_changed;
 }
@@ -936,7 +975,7 @@ powi_cost (HOST_WIDE_INT n)
 
 static tree
 powi_as_mults_1 (gimple_stmt_iterator *gsi, location_t loc, tree type,
 
 static tree
 powi_as_mults_1 (gimple_stmt_iterator *gsi, location_t loc, tree type,
-                HOST_WIDE_INT n, tree *cache, tree target)
+                HOST_WIDE_INT n, tree *cache)
 {
   tree op0, op1, ssa_target;
   unsigned HOST_WIDE_INT digit;
 {
   tree op0, op1, ssa_target;
   unsigned HOST_WIDE_INT digit;
@@ -945,23 +984,23 @@ powi_as_mults_1 (gimple_stmt_iterator *gsi, location_t loc, tree type,
   if (n < POWI_TABLE_SIZE && cache[n])
     return cache[n];
 
   if (n < POWI_TABLE_SIZE && cache[n])
     return cache[n];
 
-  ssa_target = make_ssa_name (target, NULL);
+  ssa_target = make_temp_ssa_name (type, NULL, "powmult");
 
   if (n < POWI_TABLE_SIZE)
     {
       cache[n] = ssa_target;
 
   if (n < POWI_TABLE_SIZE)
     {
       cache[n] = ssa_target;
-      op0 = powi_as_mults_1 (gsi, loc, type, n - powi_table[n], cache, target);
-      op1 = powi_as_mults_1 (gsi, loc, type, powi_table[n], cache, target);
+      op0 = powi_as_mults_1 (gsi, loc, type, n - powi_table[n], cache);
+      op1 = powi_as_mults_1 (gsi, loc, type, powi_table[n], cache);
     }
   else if (n & 1)
     {
       digit = n & ((1 << POWI_WINDOW_SIZE) - 1);
     }
   else if (n & 1)
     {
       digit = n & ((1 << POWI_WINDOW_SIZE) - 1);
-      op0 = powi_as_mults_1 (gsi, loc, type, n - digit, cache, target);
-      op1 = powi_as_mults_1 (gsi, loc, type, digit, cache, target);
+      op0 = powi_as_mults_1 (gsi, loc, type, n - digit, cache);
+      op1 = powi_as_mults_1 (gsi, loc, type, digit, cache);
     }
   else
     {
     }
   else
     {
-      op0 = powi_as_mults_1 (gsi, loc, type, n >> 1, cache, target);
+      op0 = powi_as_mults_1 (gsi, loc, type, n >> 1, cache);
       op1 = op0;
     }
 
       op1 = op0;
     }
 
@@ -979,8 +1018,9 @@ static tree
 powi_as_mults (gimple_stmt_iterator *gsi, location_t loc,
               tree arg0, HOST_WIDE_INT n)
 {
 powi_as_mults (gimple_stmt_iterator *gsi, location_t loc,
               tree arg0, HOST_WIDE_INT n)
 {
-  tree cache[POWI_TABLE_SIZE], result, type = TREE_TYPE (arg0), target;
+  tree cache[POWI_TABLE_SIZE], result, type = TREE_TYPE (arg0);
   gimple div_stmt;
   gimple div_stmt;
+  tree target;
 
   if (n == 0)
     return build_real (type, dconst1);
 
   if (n == 0)
     return build_real (type, dconst1);
@@ -988,16 +1028,12 @@ powi_as_mults (gimple_stmt_iterator *gsi, location_t loc,
   memset (cache, 0,  sizeof (cache));
   cache[1] = arg0;
 
   memset (cache, 0,  sizeof (cache));
   cache[1] = arg0;
 
-  target = create_tmp_reg (type, "powmult");
-  add_referenced_var (target);
-
-  result = powi_as_mults_1 (gsi, loc, type, (n < 0) ? -n : n, cache, target);
-
+  result = powi_as_mults_1 (gsi, loc, type, (n < 0) ? -n : n, cache);
   if (n >= 0)
     return result;
 
   /* If the original exponent was negative, reciprocate the result.  */
   if (n >= 0)
     return result;
 
   /* If the original exponent was negative, reciprocate the result.  */
-  target = make_ssa_name (target, NULL);
+  target = make_temp_ssa_name (type, NULL, "powmult");
   div_stmt = gimple_build_assign_with_ops (RDIV_EXPR, target, 
                                           build_real (type, dconst1),
                                           result);
   div_stmt = gimple_build_assign_with_ops (RDIV_EXPR, target, 
                                           build_real (type, dconst1),
                                           result);
@@ -1028,26 +1064,19 @@ gimple_expand_builtin_powi (gimple_stmt_iterator *gsi, location_t loc,
 }
 
 /* Build a gimple call statement that calls FN with argument ARG.
 }
 
 /* Build a gimple call statement that calls FN with argument ARG.
-   Set the lhs of the call statement to a fresh SSA name for
-   variable VAR.  If VAR is NULL, first allocate it.  Insert the
+   Set the lhs of the call statement to a fresh SSA name.  Insert the
    statement prior to GSI's current position, and return the fresh
    SSA name.  */
 
 static tree
 build_and_insert_call (gimple_stmt_iterator *gsi, location_t loc,
    statement prior to GSI's current position, and return the fresh
    SSA name.  */
 
 static tree
 build_and_insert_call (gimple_stmt_iterator *gsi, location_t loc,
-                      tree *var, tree fn, tree arg)
+                      tree fn, tree arg)
 {
   gimple call_stmt;
   tree ssa_target;
 
 {
   gimple call_stmt;
   tree ssa_target;
 
-  if (!*var)
-    {
-      *var = create_tmp_reg (TREE_TYPE (arg), "powroot");
-      add_referenced_var (*var);
-    }
-
   call_stmt = gimple_build_call (fn, 1, arg);
   call_stmt = gimple_build_call (fn, 1, arg);
-  ssa_target = make_ssa_name (*var, NULL);
+  ssa_target = make_temp_ssa_name (TREE_TYPE (arg), NULL, "powroot");
   gimple_set_lhs (call_stmt, ssa_target);
   gimple_set_location (call_stmt, loc);
   gsi_insert_before (gsi, call_stmt, GSI_SAME_STMT);
   gimple_set_lhs (call_stmt, ssa_target);
   gimple_set_location (call_stmt, loc);
   gsi_insert_before (gsi, call_stmt, GSI_SAME_STMT);
@@ -1062,9 +1091,10 @@ build_and_insert_call (gimple_stmt_iterator *gsi, location_t loc,
 
 static tree
 build_and_insert_binop (gimple_stmt_iterator *gsi, location_t loc,
 
 static tree
 build_and_insert_binop (gimple_stmt_iterator *gsi, location_t loc,
-                       tree target, enum tree_code code, tree arg0, tree arg1)
+                       const char *name, enum tree_code code,
+                       tree arg0, tree arg1)
 {
 {
-  tree result = make_ssa_name (target, NULL);
+  tree result = make_temp_ssa_name (TREE_TYPE (arg0), NULL, name);
   gimple stmt = gimple_build_assign_with_ops (code, result, arg0, arg1);
   gimple_set_location (stmt, loc);
   gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
   gimple stmt = gimple_build_assign_with_ops (code, result, arg0, arg1);
   gimple_set_location (stmt, loc);
   gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
@@ -1072,29 +1102,33 @@ build_and_insert_binop (gimple_stmt_iterator *gsi, location_t loc,
 }
 
 /* Build a gimple reference operation with the given CODE and argument
 }
 
 /* Build a gimple reference operation with the given CODE and argument
-   ARG, assigning the result to a new SSA name for variable TARGET.  
+   ARG, assigning the result to a new SSA name of TYPE with NAME.
    Insert the statement prior to GSI's current position, and return
    the fresh SSA name.  */
 
 static inline tree
 build_and_insert_ref (gimple_stmt_iterator *gsi, location_t loc, tree type,
    Insert the statement prior to GSI's current position, and return
    the fresh SSA name.  */
 
 static inline tree
 build_and_insert_ref (gimple_stmt_iterator *gsi, location_t loc, tree type,
-                     tree target, enum tree_code code, tree arg0)
+                     const char *name, enum tree_code code, tree arg0)
 {
 {
-  tree result = make_ssa_name (target, NULL);
+  tree result = make_temp_ssa_name (type, NULL, name);
   gimple stmt = gimple_build_assign (result, build1 (code, type, arg0));
   gimple_set_location (stmt, loc);
   gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
   return result;
 }
 
   gimple stmt = gimple_build_assign (result, build1 (code, type, arg0));
   gimple_set_location (stmt, loc);
   gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
   return result;
 }
 
-/* Build a gimple assignment to cast VAL to TARGET.  Insert the statement
+/* Build a gimple assignment to cast VAL to TYPE.  Insert the statement
    prior to GSI's current position, and return the fresh SSA name.  */
 
 static tree
 build_and_insert_cast (gimple_stmt_iterator *gsi, location_t loc,
    prior to GSI's current position, and return the fresh SSA name.  */
 
 static tree
 build_and_insert_cast (gimple_stmt_iterator *gsi, location_t loc,
-                      tree target, tree val)
+                      tree type, tree val)
 {
 {
-  return build_and_insert_binop (gsi, loc, target, CONVERT_EXPR, val, NULL);
+  tree result = make_ssa_name (type, NULL);
+  gimple stmt = gimple_build_assign_with_ops (NOP_EXPR, result, val, NULL_TREE);
+  gimple_set_location (stmt, loc);
+  gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+  return result;
 }
 
 /* ARG0 and ARG1 are the two arguments to a pow builtin call in GSI
 }
 
 /* ARG0 and ARG1 are the two arguments to a pow builtin call in GSI
@@ -1110,9 +1144,8 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
   REAL_VALUE_TYPE c2, dconst3;
   HOST_WIDE_INT n;
   tree type, sqrtfn, cbrtfn, sqrt_arg0, sqrt_sqrt, result, cbrt_x, powi_cbrt_x;
   REAL_VALUE_TYPE c2, dconst3;
   HOST_WIDE_INT n;
   tree type, sqrtfn, cbrtfn, sqrt_arg0, sqrt_sqrt, result, cbrt_x, powi_cbrt_x;
-  tree target = NULL_TREE;
   enum machine_mode mode;
   enum machine_mode mode;
-  bool hw_sqrt_exists;
+  bool hw_sqrt_exists, c_is_int, c2_is_int;
 
   /* If the exponent isn't a constant, there's nothing of interest
      to be done.  */
 
   /* If the exponent isn't a constant, there's nothing of interest
      to be done.  */
@@ -1124,8 +1157,9 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
   c = TREE_REAL_CST (arg1);
   n = real_to_integer (&c);
   real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
   c = TREE_REAL_CST (arg1);
   n = real_to_integer (&c);
   real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
+  c_is_int = real_identical (&c, &cint);
 
 
-  if (real_identical (&c, &cint)
+  if (c_is_int
       && ((n >= -1 && n <= 2)
          || (flag_unsafe_math_optimizations
              && optimize_insn_for_speed_p ()
       && ((n >= -1 && n <= 2)
          || (flag_unsafe_math_optimizations
              && optimize_insn_for_speed_p ()
@@ -1143,7 +1177,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
   if (sqrtfn
       && REAL_VALUES_EQUAL (c, dconsthalf)
       && !HONOR_SIGNED_ZEROS (mode))
   if (sqrtfn
       && REAL_VALUES_EQUAL (c, dconsthalf)
       && !HONOR_SIGNED_ZEROS (mode))
-    return build_and_insert_call (gsi, loc, &target, sqrtfn, arg0);
+    return build_and_insert_call (gsi, loc, sqrtfn, arg0);
 
   /* Optimize pow(x,0.25) = sqrt(sqrt(x)).  Assume on most machines that
      a builtin sqrt instruction is smaller than a call to pow with 0.25,
 
   /* Optimize pow(x,0.25) = sqrt(sqrt(x)).  Assume on most machines that
      a builtin sqrt instruction is smaller than a call to pow with 0.25,
@@ -1159,10 +1193,10 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       && hw_sqrt_exists)
     {
       /* sqrt(x)  */
       && hw_sqrt_exists)
     {
       /* sqrt(x)  */
-      sqrt_arg0 = build_and_insert_call (gsi, loc, &target, sqrtfn, arg0);
+      sqrt_arg0 = build_and_insert_call (gsi, loc, sqrtfn, arg0);
 
       /* sqrt(sqrt(x))  */
 
       /* sqrt(sqrt(x))  */
-      return build_and_insert_call (gsi, loc, &target, sqrtfn, sqrt_arg0);
+      return build_and_insert_call (gsi, loc, sqrtfn, sqrt_arg0);
     }
       
   /* Optimize pow(x,0.75) = sqrt(x) * sqrt(sqrt(x)) unless we are
     }
       
   /* Optimize pow(x,0.75) = sqrt(x) * sqrt(sqrt(x)) unless we are
@@ -1178,13 +1212,13 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       && hw_sqrt_exists)
     {
       /* sqrt(x)  */
       && hw_sqrt_exists)
     {
       /* sqrt(x)  */
-      sqrt_arg0 = build_and_insert_call (gsi, loc, &target, sqrtfn, arg0);
+      sqrt_arg0 = build_and_insert_call (gsi, loc, sqrtfn, arg0);
 
       /* sqrt(sqrt(x))  */
 
       /* sqrt(sqrt(x))  */
-      sqrt_sqrt = build_and_insert_call (gsi, loc, &target, sqrtfn, sqrt_arg0);
+      sqrt_sqrt = build_and_insert_call (gsi, loc, sqrtfn, sqrt_arg0);
 
       /* sqrt(x) * sqrt(sqrt(x))  */
 
       /* sqrt(x) * sqrt(sqrt(x))  */
-      return build_and_insert_binop (gsi, loc, target, MULT_EXPR,
+      return build_and_insert_binop (gsi, loc, "powroot", MULT_EXPR,
                                     sqrt_arg0, sqrt_sqrt);
     }
 
                                     sqrt_arg0, sqrt_sqrt);
     }
 
@@ -1201,7 +1235,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       && cbrtfn
       && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
       && REAL_VALUES_EQUAL (c, dconst1_3))
       && cbrtfn
       && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
       && REAL_VALUES_EQUAL (c, dconst1_3))
-    return build_and_insert_call (gsi, loc, &target, cbrtfn, arg0);
+    return build_and_insert_call (gsi, loc, cbrtfn, arg0);
   
   /* Optimize pow(x,1./6.) = cbrt(sqrt(x)).  Don't do this optimization
      if we don't have a hardware sqrt insn.  */
   
   /* Optimize pow(x,1./6.) = cbrt(sqrt(x)).  Don't do this optimization
      if we don't have a hardware sqrt insn.  */
@@ -1217,13 +1251,14 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       && REAL_VALUES_EQUAL (c, dconst1_6))
     {
       /* sqrt(x)  */
       && REAL_VALUES_EQUAL (c, dconst1_6))
     {
       /* sqrt(x)  */
-      sqrt_arg0 = build_and_insert_call (gsi, loc, &target, sqrtfn, arg0);
+      sqrt_arg0 = build_and_insert_call (gsi, loc, sqrtfn, arg0);
 
       /* cbrt(sqrt(x))  */
 
       /* cbrt(sqrt(x))  */
-      return build_and_insert_call (gsi, loc, &target, cbrtfn, sqrt_arg0);
+      return build_and_insert_call (gsi, loc, cbrtfn, sqrt_arg0);
     }
 
     }
 
-  /* Optimize pow(x,c), where n = 2c for some nonzero integer n, into
+  /* Optimize pow(x,c), where n = 2c for some nonzero integer n
+     and c not an integer, into
 
        sqrt(x) * powi(x, n/2),                n > 0;
        1.0 / (sqrt(x) * powi(x, abs(n/2))),   n < 0.
 
        sqrt(x) * powi(x, n/2),                n > 0;
        1.0 / (sqrt(x) * powi(x, abs(n/2))),   n < 0.
@@ -1232,10 +1267,13 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
   real_arithmetic (&c2, MULT_EXPR, &c, &dconst2);
   n = real_to_integer (&c2);
   real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
   real_arithmetic (&c2, MULT_EXPR, &c, &dconst2);
   n = real_to_integer (&c2);
   real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
+  c2_is_int = real_identical (&c2, &cint);
 
   if (flag_unsafe_math_optimizations
       && sqrtfn
 
   if (flag_unsafe_math_optimizations
       && sqrtfn
-      && real_identical (&c2, &cint))
+      && c2_is_int
+      && !c_is_int
+      && optimize_function_for_speed_p (cfun))
     {
       tree powi_x_ndiv2 = NULL_TREE;
 
     {
       tree powi_x_ndiv2 = NULL_TREE;
 
@@ -1252,17 +1290,17 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
 
       /* Calculate sqrt(x).  When n is not 1 or -1, multiply it by the
         result of the optimal multiply sequence just calculated.  */
 
       /* Calculate sqrt(x).  When n is not 1 or -1, multiply it by the
         result of the optimal multiply sequence just calculated.  */
-      sqrt_arg0 = build_and_insert_call (gsi, loc, &target, sqrtfn, arg0);
+      sqrt_arg0 = build_and_insert_call (gsi, loc, sqrtfn, arg0);
 
       if (absu_hwi (n) == 1)
        result = sqrt_arg0;
       else
 
       if (absu_hwi (n) == 1)
        result = sqrt_arg0;
       else
-       result = build_and_insert_binop (gsi, loc, target, MULT_EXPR,
+       result = build_and_insert_binop (gsi, loc, "powroot", MULT_EXPR,
                                         sqrt_arg0, powi_x_ndiv2);
 
       /* If n is negative, reciprocate the result.  */
       if (n < 0)
                                         sqrt_arg0, powi_x_ndiv2);
 
       /* If n is negative, reciprocate the result.  */
       if (n < 0)
-       result = build_and_insert_binop (gsi, loc, target, RDIV_EXPR,
+       result = build_and_insert_binop (gsi, loc, "powroot", RDIV_EXPR,
                                         build_real (type, dconst1), result);
       return result;
     }
                                         build_real (type, dconst1), result);
       return result;
     }
@@ -1288,6 +1326,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       && cbrtfn
       && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
       && real_identical (&c2, &c)
       && cbrtfn
       && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
       && real_identical (&c2, &c)
+      && !c2_is_int
       && optimize_function_for_speed_p (cfun)
       && powi_cost (n / 3) <= POWI_MAX_MULTS)
     {
       && optimize_function_for_speed_p (cfun)
       && powi_cost (n / 3) <= POWI_MAX_MULTS)
     {
@@ -1307,24 +1346,24 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       /* Calculate powi(cbrt(x), n%3).  Don't use gimple_expand_builtin_powi
          as that creates an unnecessary variable.  Instead, just produce
          either cbrt(x) or cbrt(x) * cbrt(x).  */
       /* Calculate powi(cbrt(x), n%3).  Don't use gimple_expand_builtin_powi
          as that creates an unnecessary variable.  Instead, just produce
          either cbrt(x) or cbrt(x) * cbrt(x).  */
-      cbrt_x = build_and_insert_call (gsi, loc, &target, cbrtfn, arg0);
+      cbrt_x = build_and_insert_call (gsi, loc, cbrtfn, arg0);
 
       if (absu_hwi (n) % 3 == 1)
        powi_cbrt_x = cbrt_x;
       else
 
       if (absu_hwi (n) % 3 == 1)
        powi_cbrt_x = cbrt_x;
       else
-       powi_cbrt_x = build_and_insert_binop (gsi, loc, target, MULT_EXPR,
+       powi_cbrt_x = build_and_insert_binop (gsi, loc, "powroot", MULT_EXPR,
                                              cbrt_x, cbrt_x);
 
       /* Multiply the two subexpressions, unless powi(x,abs(n)/3) = 1.  */
       if (absu_hwi (n) < 3)
        result = powi_cbrt_x;
       else
                                              cbrt_x, cbrt_x);
 
       /* Multiply the two subexpressions, unless powi(x,abs(n)/3) = 1.  */
       if (absu_hwi (n) < 3)
        result = powi_cbrt_x;
       else
-       result = build_and_insert_binop (gsi, loc, target, MULT_EXPR,
+       result = build_and_insert_binop (gsi, loc, "powroot", MULT_EXPR,
                                         powi_x_ndiv3, powi_cbrt_x);
 
       /* If n is negative, reciprocate the result.  */
       if (n < 0)
                                         powi_x_ndiv3, powi_cbrt_x);
 
       /* If n is negative, reciprocate the result.  */
       if (n < 0)
-       result = build_and_insert_binop (gsi, loc, target, RDIV_EXPR, 
+       result = build_and_insert_binop (gsi, loc, "powroot", RDIV_EXPR,
                                         build_real (type, dconst1), result);
 
       return result;
                                         build_real (type, dconst1), result);
 
       return result;
@@ -1342,7 +1381,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
 static tree
 gimple_expand_builtin_cabs (gimple_stmt_iterator *gsi, location_t loc, tree arg)
 {
 static tree
 gimple_expand_builtin_cabs (gimple_stmt_iterator *gsi, location_t loc, tree arg)
 {
-  tree target, real_part, imag_part, addend1, addend2, sum, result;
+  tree real_part, imag_part, addend1, addend2, sum, result;
   tree type = TREE_TYPE (TREE_TYPE (arg));
   tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
   enum machine_mode mode = TYPE_MODE (type);
   tree type = TREE_TYPE (TREE_TYPE (arg));
   tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
   enum machine_mode mode = TYPE_MODE (type);
@@ -1353,19 +1392,16 @@ gimple_expand_builtin_cabs (gimple_stmt_iterator *gsi, location_t loc, tree arg)
       || optab_handler (sqrt_optab, mode) == CODE_FOR_nothing)
     return NULL_TREE;
 
       || optab_handler (sqrt_optab, mode) == CODE_FOR_nothing)
     return NULL_TREE;
 
-  target = create_tmp_reg (type, "cabs");
-  add_referenced_var (target);
-
-  real_part = build_and_insert_ref (gsi, loc, type, target,
+  real_part = build_and_insert_ref (gsi, loc, type, "cabs",
                                    REALPART_EXPR, arg);
                                    REALPART_EXPR, arg);
-  addend1 = build_and_insert_binop (gsi, loc, target, MULT_EXPR,
+  addend1 = build_and_insert_binop (gsi, loc, "cabs", MULT_EXPR,
                                    real_part, real_part);
                                    real_part, real_part);
-  imag_part = build_and_insert_ref (gsi, loc, type, target, 
+  imag_part = build_and_insert_ref (gsi, loc, type, "cabs",
                                    IMAGPART_EXPR, arg);
                                    IMAGPART_EXPR, arg);
-  addend2 = build_and_insert_binop (gsi, loc, target, MULT_EXPR,
+  addend2 = build_and_insert_binop (gsi, loc, "cabs", MULT_EXPR,
                                    imag_part, imag_part);
                                    imag_part, imag_part);
-  sum = build_and_insert_binop (gsi, loc, target, PLUS_EXPR, addend1, addend2);
-  result = build_and_insert_call (gsi, loc, &target, sqrtfn, sum);
+  sum = build_and_insert_binop (gsi, loc, "cabs", PLUS_EXPR, addend1, addend2);
+  result = build_and_insert_call (gsi, loc, sqrtfn, sum);
 
   return result;
 }
 
   return result;
 }
@@ -1386,12 +1422,18 @@ execute_cse_sincos (void)
   FOR_EACH_BB (bb)
     {
       gimple_stmt_iterator gsi;
   FOR_EACH_BB (bb)
     {
       gimple_stmt_iterator gsi;
+      bool cleanup_eh = false;
 
       for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi))
         {
          gimple stmt = gsi_stmt (gsi);
          tree fndecl;
 
 
       for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi))
         {
          gimple stmt = gsi_stmt (gsi);
          tree fndecl;
 
+         /* Only the last stmt in a bb could throw, no need to call
+            gimple_purge_dead_eh_edges if we change something in the middle
+            of a basic block.  */
+         cleanup_eh = false;
+
          if (is_gimple_call (stmt)
              && gimple_call_lhs (stmt)
              && (fndecl = gimple_call_fndecl (stmt))
          if (is_gimple_call (stmt)
              && gimple_call_lhs (stmt)
              && (fndecl = gimple_call_fndecl (stmt))
@@ -1407,7 +1449,8 @@ execute_cse_sincos (void)
                CASE_FLT_FN (BUILT_IN_SIN):
                CASE_FLT_FN (BUILT_IN_CEXPI):
                  /* Make sure we have either sincos or cexp.  */
                CASE_FLT_FN (BUILT_IN_SIN):
                CASE_FLT_FN (BUILT_IN_CEXPI):
                  /* Make sure we have either sincos or cexp.  */
-                 if (!TARGET_HAS_SINCOS && !TARGET_C99_FUNCTIONS)
+                 if (!targetm.libc_has_function (function_c99_math_complex)
+                     && !targetm.libc_has_function (function_sincos))
                    break;
 
                  arg = gimple_call_arg (stmt, 0);
                    break;
 
                  arg = gimple_call_arg (stmt, 0);
@@ -1429,6 +1472,7 @@ execute_cse_sincos (void)
                      gimple_set_location (new_stmt, loc);
                      unlink_stmt_vdef (stmt);
                      gsi_replace (&gsi, new_stmt, true);
                      gimple_set_location (new_stmt, loc);
                      unlink_stmt_vdef (stmt);
                      gsi_replace (&gsi, new_stmt, true);
+                     cleanup_eh = true;
                      if (gimple_vdef (stmt))
                        release_ssa_name (gimple_vdef (stmt));
                    }
                      if (gimple_vdef (stmt))
                        release_ssa_name (gimple_vdef (stmt));
                    }
@@ -1437,12 +1481,41 @@ execute_cse_sincos (void)
                CASE_FLT_FN (BUILT_IN_POWI):
                  arg0 = gimple_call_arg (stmt, 0);
                  arg1 = gimple_call_arg (stmt, 1);
                CASE_FLT_FN (BUILT_IN_POWI):
                  arg0 = gimple_call_arg (stmt, 0);
                  arg1 = gimple_call_arg (stmt, 1);
-                 if (!host_integerp (arg1, 0))
-                   break;
-
-                 n = TREE_INT_CST_LOW (arg1);
                  loc = gimple_location (stmt);
                  loc = gimple_location (stmt);
-                 result = gimple_expand_builtin_powi (&gsi, loc, arg0, n);
+
+                 if (real_minus_onep (arg0))
+                   {
+                      tree t0, t1, cond, one, minus_one;
+                     gimple stmt;
+
+                     t0 = TREE_TYPE (arg0);
+                     t1 = TREE_TYPE (arg1);
+                     one = build_real (t0, dconst1);
+                     minus_one = build_real (t0, dconstm1);
+
+                     cond = make_temp_ssa_name (t1, NULL, "powi_cond");
+                     stmt = gimple_build_assign_with_ops (BIT_AND_EXPR, cond,
+                                                          arg1,
+                                                          build_int_cst (t1,
+                                                                         1));
+                     gimple_set_location (stmt, loc);
+                     gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
+
+                     result = make_temp_ssa_name (t0, NULL, "powi");
+                     stmt = gimple_build_assign_with_ops (COND_EXPR, result,
+                                                          cond,
+                                                          minus_one, one);
+                     gimple_set_location (stmt, loc);
+                     gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
+                   }
+                 else
+                   {
+                     if (!tree_fits_shwi_p (arg1))
+                       break;
+
+                     n = tree_to_shwi (arg1);
+                     result = gimple_expand_builtin_powi (&gsi, loc, arg0, n);
+                   }
 
                  if (result)
                    {
 
                  if (result)
                    {
@@ -1451,6 +1524,7 @@ execute_cse_sincos (void)
                      gimple_set_location (new_stmt, loc);
                      unlink_stmt_vdef (stmt);
                      gsi_replace (&gsi, new_stmt, true);
                      gimple_set_location (new_stmt, loc);
                      unlink_stmt_vdef (stmt);
                      gsi_replace (&gsi, new_stmt, true);
+                     cleanup_eh = true;
                      if (gimple_vdef (stmt))
                        release_ssa_name (gimple_vdef (stmt));
                    }
                      if (gimple_vdef (stmt))
                        release_ssa_name (gimple_vdef (stmt));
                    }
@@ -1468,6 +1542,7 @@ execute_cse_sincos (void)
                      gimple_set_location (new_stmt, loc);
                      unlink_stmt_vdef (stmt);
                      gsi_replace (&gsi, new_stmt, true);
                      gimple_set_location (new_stmt, loc);
                      unlink_stmt_vdef (stmt);
                      gsi_replace (&gsi, new_stmt, true);
+                     cleanup_eh = true;
                      if (gimple_vdef (stmt))
                        release_ssa_name (gimple_vdef (stmt));
                    }
                      if (gimple_vdef (stmt))
                        release_ssa_name (gimple_vdef (stmt));
                    }
@@ -1477,6 +1552,8 @@ execute_cse_sincos (void)
                }
            }
        }
                }
            }
        }
+      if (cleanup_eh)
+       cfg_changed |= gimple_purge_dead_eh_edges (bb);
     }
 
   statistics_counter_event (cfun, "sincos statements inserted",
     }
 
   statistics_counter_event (cfun, "sincos statements inserted",
@@ -1494,26 +1571,45 @@ gate_cse_sincos (void)
   return optimize;
 }
 
   return optimize;
 }
 
-struct gimple_opt_pass pass_cse_sincos =
+namespace {
+
+const pass_data pass_data_cse_sincos =
 {
 {
- {
-  GIMPLE_PASS,
-  "sincos",                            /* name */
-  gate_cse_sincos,                     /* gate */
-  execute_cse_sincos,                  /* 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 */
-  TODO_update_ssa | TODO_verify_ssa
-    | TODO_verify_stmts                 /* todo_flags_finish */
- }
+  GIMPLE_PASS, /* type */
+  "sincos", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_update_ssa | TODO_verify_ssa
+    | TODO_verify_stmts ), /* todo_flags_finish */
 };
 
 };
 
+class pass_cse_sincos : public gimple_opt_pass
+{
+public:
+  pass_cse_sincos (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_cse_sincos, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_cse_sincos (); }
+  unsigned int execute () { return execute_cse_sincos (); }
+
+}; // class pass_cse_sincos
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_cse_sincos (gcc::context *ctxt)
+{
+  return new pass_cse_sincos (ctxt);
+}
+
 /* 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:
 /* 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:
@@ -1800,9 +1896,9 @@ static unsigned int
 execute_optimize_bswap (void)
 {
   basic_block bb;
 execute_optimize_bswap (void)
 {
   basic_block bb;
-  bool bswap32_p, bswap64_p;
+  bool bswap16_p, bswap32_p, bswap64_p;
   bool changed = false;
   bool changed = false;
-  tree bswap32_type = NULL_TREE, bswap64_type = NULL_TREE;
+  tree bswap16_type = NULL_TREE, bswap32_type = NULL_TREE, bswap64_type = NULL_TREE;
 
   if (BITS_PER_UNIT != 8)
     return 0;
 
   if (BITS_PER_UNIT != 8)
     return 0;
@@ -1810,17 +1906,25 @@ execute_optimize_bswap (void)
   if (sizeof (HOST_WIDEST_INT) < 8)
     return 0;
 
   if (sizeof (HOST_WIDEST_INT) < 8)
     return 0;
 
+  bswap16_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP16)
+              && optab_handler (bswap_optab, HImode) != CODE_FOR_nothing);
   bswap32_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP32)
               && optab_handler (bswap_optab, SImode) != CODE_FOR_nothing);
   bswap64_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP64)
               && (optab_handler (bswap_optab, DImode) != CODE_FOR_nothing
                   || (bswap32_p && word_mode == SImode)));
 
   bswap32_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP32)
               && optab_handler (bswap_optab, SImode) != CODE_FOR_nothing);
   bswap64_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP64)
               && (optab_handler (bswap_optab, DImode) != CODE_FOR_nothing
                   || (bswap32_p && word_mode == SImode)));
 
-  if (!bswap32_p && !bswap64_p)
+  if (!bswap16_p && !bswap32_p && !bswap64_p)
     return 0;
 
   /* Determine the argument type of the builtins.  The code later on
      assumes that the return and argument type are the same.  */
     return 0;
 
   /* Determine the argument type of the builtins.  The code later on
      assumes that the return and argument type are the same.  */
+  if (bswap16_p)
+    {
+      tree fndecl = builtin_decl_explicit (BUILT_IN_BSWAP16);
+      bswap16_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
+    }
+
   if (bswap32_p)
     {
       tree fndecl = builtin_decl_explicit (BUILT_IN_BSWAP32);
   if (bswap32_p)
     {
       tree fndecl = builtin_decl_explicit (BUILT_IN_BSWAP32);
@@ -1860,6 +1964,13 @@ execute_optimize_bswap (void)
 
          switch (type_size)
            {
 
          switch (type_size)
            {
+           case 16:
+             if (bswap16_p)
+               {
+                 fndecl = builtin_decl_explicit (BUILT_IN_BSWAP16);
+                 bswap_type = bswap16_type;
+               }
+             break;
            case 32:
              if (bswap32_p)
                {
            case 32:
              if (bswap32_p)
                {
@@ -1887,7 +1998,9 @@ execute_optimize_bswap (void)
            continue;
 
          changed = true;
            continue;
 
          changed = true;
-         if (type_size == 32)
+         if (type_size == 16)
+           bswap_stats.found_16bit++;
+         else if (type_size == 32)
            bswap_stats.found_32bit++;
          else
            bswap_stats.found_64bit++;
            bswap_stats.found_32bit++;
          else
            bswap_stats.found_64bit++;
@@ -1898,13 +2011,9 @@ execute_optimize_bswap (void)
          if (!useless_type_conversion_p (TREE_TYPE (bswap_tmp), bswap_type))
            {
              gimple convert_stmt;
          if (!useless_type_conversion_p (TREE_TYPE (bswap_tmp), bswap_type))
            {
              gimple convert_stmt;
-
-             bswap_tmp = create_tmp_var (bswap_type, "bswapsrc");
-             add_referenced_var (bswap_tmp);
-             bswap_tmp = make_ssa_name (bswap_tmp, NULL);
-
-             convert_stmt = gimple_build_assign_with_ops (
-                              CONVERT_EXPR, bswap_tmp, bswap_src, NULL);
+             bswap_tmp = make_temp_ssa_name (bswap_type, NULL, "bswapsrc");
+             convert_stmt = gimple_build_assign_with_ops
+                               (NOP_EXPR, bswap_tmp, bswap_src, NULL);
              gsi_insert_before (&gsi, convert_stmt, GSI_SAME_STMT);
            }
 
              gsi_insert_before (&gsi, convert_stmt, GSI_SAME_STMT);
            }
 
@@ -1916,12 +2025,9 @@ execute_optimize_bswap (void)
          if (!useless_type_conversion_p (TREE_TYPE (bswap_tmp), bswap_type))
            {
              gimple convert_stmt;
          if (!useless_type_conversion_p (TREE_TYPE (bswap_tmp), bswap_type))
            {
              gimple convert_stmt;
-
-             bswap_tmp = create_tmp_var (bswap_type, "bswapdst");
-             add_referenced_var (bswap_tmp);
-             bswap_tmp = make_ssa_name (bswap_tmp, NULL);
-             convert_stmt = gimple_build_assign_with_ops (
-                              CONVERT_EXPR, gimple_assign_lhs (stmt), bswap_tmp, NULL);
+             bswap_tmp = make_temp_ssa_name (bswap_type, NULL, "bswapdst");
+             convert_stmt = gimple_build_assign_with_ops
+                       (NOP_EXPR, gimple_assign_lhs (stmt), bswap_tmp, NULL);
              gsi_insert_after (&gsi, convert_stmt, GSI_SAME_STMT);
            }
 
              gsi_insert_after (&gsi, convert_stmt, GSI_SAME_STMT);
            }
 
@@ -1939,6 +2045,8 @@ execute_optimize_bswap (void)
        }
     }
 
        }
     }
 
+  statistics_counter_event (cfun, "16-bit bswap implementations found",
+                           bswap_stats.found_16bit);
   statistics_counter_event (cfun, "32-bit bswap implementations found",
                            bswap_stats.found_32bit);
   statistics_counter_event (cfun, "64-bit bswap implementations found",
   statistics_counter_event (cfun, "32-bit bswap implementations found",
                            bswap_stats.found_32bit);
   statistics_counter_event (cfun, "64-bit bswap implementations found",
@@ -1954,25 +2062,85 @@ gate_optimize_bswap (void)
   return flag_expensive_optimizations && optimize;
 }
 
   return flag_expensive_optimizations && optimize;
 }
 
-struct gimple_opt_pass pass_optimize_bswap =
+namespace {
+
+const pass_data pass_data_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 */
- }
+  GIMPLE_PASS, /* type */
+  "bswap", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
 
 };
 
+class pass_optimize_bswap : public gimple_opt_pass
+{
+public:
+  pass_optimize_bswap (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_optimize_bswap, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_optimize_bswap (); }
+  unsigned int execute () { return execute_optimize_bswap (); }
+
+}; // class pass_optimize_bswap
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_optimize_bswap (gcc::context *ctxt)
+{
+  return new pass_optimize_bswap (ctxt);
+}
+
+/* Return true if stmt is a type conversion operation that can be stripped
+   when used in a widening multiply operation.  */
+static bool
+widening_mult_conversion_strippable_p (tree result_type, gimple stmt)
+{
+  enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
+
+  if (TREE_CODE (result_type) == INTEGER_TYPE)
+    {
+      tree op_type;
+      tree inner_op_type;
+
+      if (!CONVERT_EXPR_CODE_P (rhs_code))
+       return false;
+
+      op_type = TREE_TYPE (gimple_assign_lhs (stmt));
+
+      /* If the type of OP has the same precision as the result, then
+        we can strip this conversion.  The multiply operation will be
+        selected to create the correct extension as a by-product.  */
+      if (TYPE_PRECISION (result_type) == TYPE_PRECISION (op_type))
+       return true;
+
+      /* We can also strip a conversion if it preserves the signed-ness of
+        the operation and doesn't narrow the range.  */
+      inner_op_type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+
+      /* If the inner-most type is unsigned, then we can strip any
+        intermediate widening operation.  If it's signed, then the
+        intermediate widening operation must also be signed.  */
+      if ((TYPE_UNSIGNED (inner_op_type)
+          || TYPE_UNSIGNED (op_type) == TYPE_UNSIGNED (inner_op_type))
+         && TYPE_PRECISION (op_type) > TYPE_PRECISION (inner_op_type))
+       return true;
+
+      return false;
+    }
+
+  return rhs_code == FIXED_CONVERT_EXPR;
+}
+
 /* Return true if RHS is a suitable operand for a widening multiplication,
    assuming a target type of TYPE.
    There are two cases:
 /* Return true if RHS is a suitable operand for a widening multiplication,
    assuming a target type of TYPE.
    There are two cases:
@@ -1989,17 +2157,13 @@ is_widening_mult_rhs_p (tree type, tree rhs, tree *type_out,
 {
   gimple stmt;
   tree type1, rhs1;
 {
   gimple stmt;
   tree type1, rhs1;
-  enum tree_code rhs_code;
 
   if (TREE_CODE (rhs) == SSA_NAME)
     {
       stmt = SSA_NAME_DEF_STMT (rhs);
       if (is_gimple_assign (stmt))
        {
 
   if (TREE_CODE (rhs) == SSA_NAME)
     {
       stmt = SSA_NAME_DEF_STMT (rhs);
       if (is_gimple_assign (stmt))
        {
-         rhs_code = gimple_assign_rhs_code (stmt);
-         if (TREE_CODE (type) == INTEGER_TYPE
-             ? !CONVERT_EXPR_CODE_P (rhs_code)
-             : rhs_code != FIXED_CONVERT_EXPR)
+         if (! widening_mult_conversion_strippable_p (type, stmt))
            rhs1 = rhs;
          else
            {
            rhs1 = rhs;
          else
            {
@@ -2098,7 +2262,7 @@ is_widening_mult_p (gimple stmt,
 static bool
 convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
 {
 static bool
 convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
 {
-  tree lhs, rhs1, rhs2, type, type1, type2, tmp = NULL;
+  tree lhs, rhs1, rhs2, type, type1, type2;
   enum insn_code handler;
   enum machine_mode to_mode, from_mode, actual_mode;
   optab op;
   enum insn_code handler;
   enum machine_mode to_mode, from_mode, actual_mode;
   optab op;
@@ -2167,22 +2331,14 @@ convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
     return false;
   if (actual_precision != TYPE_PRECISION (type1)
       || from_unsigned1 != TYPE_UNSIGNED (type1))
     return false;
   if (actual_precision != TYPE_PRECISION (type1)
       || from_unsigned1 != TYPE_UNSIGNED (type1))
-    {
-      tmp = create_tmp_var (build_nonstandard_integer_type
-                               (actual_precision, from_unsigned1),
-                           NULL);
-      rhs1 = build_and_insert_cast (gsi, loc, tmp, rhs1);
-    }
+    rhs1 = build_and_insert_cast (gsi, loc,
+                                 build_nonstandard_integer_type
+                                   (actual_precision, from_unsigned1), rhs1);
   if (actual_precision != TYPE_PRECISION (type2)
       || from_unsigned2 != TYPE_UNSIGNED (type2))
   if (actual_precision != TYPE_PRECISION (type2)
       || from_unsigned2 != TYPE_UNSIGNED (type2))
-    {
-      /* Reuse the same type info, if possible.  */
-      if (!tmp || from_unsigned1 != from_unsigned2)
-       tmp = create_tmp_var (build_nonstandard_integer_type
-                               (actual_precision, from_unsigned2),
-                             NULL);
-      rhs2 = build_and_insert_cast (gsi, loc, tmp, rhs2);
-    }
+    rhs2 = build_and_insert_cast (gsi, loc,
+                                 build_nonstandard_integer_type
+                                   (actual_precision, from_unsigned2), rhs2);
 
   /* Handle constants.  */
   if (TREE_CODE (rhs1) == INTEGER_CST)
 
   /* Handle constants.  */
   if (TREE_CODE (rhs1) == INTEGER_CST)
@@ -2210,7 +2366,7 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
 {
   gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
   gimple conv1_stmt = NULL, conv2_stmt = NULL, conv_stmt;
 {
   gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
   gimple conv1_stmt = NULL, conv2_stmt = NULL, conv_stmt;
-  tree type, type1, type2, optype, tmp = NULL;
+  tree type, type1, type2, optype;
   tree lhs, rhs1, rhs2, mult_rhs1, mult_rhs2, add_rhs;
   enum tree_code rhs1_code = ERROR_MARK, rhs2_code = ERROR_MARK;
   optab this_optab;
   tree lhs, rhs1, rhs2, mult_rhs1, mult_rhs2, add_rhs;
   enum tree_code rhs1_code = ERROR_MARK, rhs2_code = ERROR_MARK;
   optab this_optab;
@@ -2286,20 +2442,25 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
 
      It might also appear that it would be sufficient to use the existing
      operands of the widening multiply, but that would limit the choice of
 
      It might also appear that it would be sufficient to use the existing
      operands of the widening multiply, but that would limit the choice of
-     multiply-and-accumulate instructions.  */
+     multiply-and-accumulate instructions.
+
+     If the widened-multiplication result has more than one uses, it is
+     probably wiser not to do the conversion.  */
   if (code == PLUS_EXPR
       && (rhs1_code == MULT_EXPR || rhs1_code == WIDEN_MULT_EXPR))
     {
   if (code == PLUS_EXPR
       && (rhs1_code == MULT_EXPR || rhs1_code == WIDEN_MULT_EXPR))
     {
-      if (!is_widening_mult_p (rhs1_stmt, &type1, &mult_rhs1,
-                              &type2, &mult_rhs2))
+      if (!has_single_use (rhs1)
+         || !is_widening_mult_p (rhs1_stmt, &type1, &mult_rhs1,
+                                 &type2, &mult_rhs2))
        return false;
       add_rhs = rhs2;
       conv_stmt = conv1_stmt;
     }
   else if (rhs2_code == MULT_EXPR || rhs2_code == WIDEN_MULT_EXPR)
     {
        return false;
       add_rhs = rhs2;
       conv_stmt = conv1_stmt;
     }
   else if (rhs2_code == MULT_EXPR || rhs2_code == WIDEN_MULT_EXPR)
     {
-      if (!is_widening_mult_p (rhs2_stmt, &type1, &mult_rhs1,
-                              &type2, &mult_rhs2))
+      if (!has_single_use (rhs2)
+         || !is_widening_mult_p (rhs2_stmt, &type1, &mult_rhs1,
+                                 &type2, &mult_rhs2))
        return false;
       add_rhs = rhs1;
       conv_stmt = conv2_stmt;
        return false;
       add_rhs = rhs1;
       conv_stmt = conv2_stmt;
@@ -2379,25 +2540,19 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
   actual_precision = GET_MODE_PRECISION (actual_mode);
   if (actual_precision != TYPE_PRECISION (type1)
       || from_unsigned1 != TYPE_UNSIGNED (type1))
   actual_precision = GET_MODE_PRECISION (actual_mode);
   if (actual_precision != TYPE_PRECISION (type1)
       || from_unsigned1 != TYPE_UNSIGNED (type1))
-    {
-      tmp = create_tmp_var (build_nonstandard_integer_type
-                               (actual_precision, from_unsigned1),
-                           NULL);
-      mult_rhs1 = build_and_insert_cast (gsi, loc, tmp, mult_rhs1);
-    }
+    mult_rhs1 = build_and_insert_cast (gsi, loc,
+                                      build_nonstandard_integer_type
+                                        (actual_precision, from_unsigned1),
+                                      mult_rhs1);
   if (actual_precision != TYPE_PRECISION (type2)
       || from_unsigned2 != TYPE_UNSIGNED (type2))
   if (actual_precision != TYPE_PRECISION (type2)
       || from_unsigned2 != TYPE_UNSIGNED (type2))
-    {
-      if (!tmp || from_unsigned1 != from_unsigned2)
-       tmp = create_tmp_var (build_nonstandard_integer_type
-                               (actual_precision, from_unsigned2),
-                             NULL);
-      mult_rhs2 = build_and_insert_cast (gsi, loc, tmp, mult_rhs2);
-    }
+    mult_rhs2 = build_and_insert_cast (gsi, loc,
+                                      build_nonstandard_integer_type
+                                        (actual_precision, from_unsigned2),
+                                      mult_rhs2);
 
   if (!useless_type_conversion_p (type, TREE_TYPE (add_rhs)))
 
   if (!useless_type_conversion_p (type, TREE_TYPE (add_rhs)))
-    add_rhs = build_and_insert_cast (gsi, loc, create_tmp_var (type, NULL),
-                                    add_rhs);
+    add_rhs = build_and_insert_cast (gsi, loc, type, add_rhs);
 
   /* Handle constants.  */
   if (TREE_CODE (mult_rhs1) == INTEGER_CST)
 
   /* Handle constants.  */
   if (TREE_CODE (mult_rhs1) == INTEGER_CST)
@@ -2521,6 +2676,28 @@ convert_mult_to_fma (gimple mul_stmt, tree op1, tree op2)
          return false;
        }
 
          return false;
        }
 
+      /* If the subtrahend (gimple_assign_rhs2 (use_stmt)) is computed
+        by a MULT_EXPR that we'll visit later, we might be able to
+        get a more profitable match with fnma.
+        OTOH, if we don't, a negate / fma pair has likely lower latency
+        that a mult / subtract pair.  */
+      if (use_code == MINUS_EXPR && !negate_p
+         && gimple_assign_rhs1 (use_stmt) == result
+         && optab_handler (fms_optab, TYPE_MODE (type)) == CODE_FOR_nothing
+         && optab_handler (fnma_optab, TYPE_MODE (type)) != CODE_FOR_nothing)
+       {
+         tree rhs2 = gimple_assign_rhs2 (use_stmt);
+
+         if (TREE_CODE (rhs2) == SSA_NAME)
+           {
+             gimple stmt2 = SSA_NAME_DEF_STMT (rhs2);
+             if (has_single_use (rhs2)
+                 && is_gimple_assign (stmt2)
+                 && gimple_assign_rhs_code (stmt2) == MULT_EXPR)
+             return false;
+           }
+       }
+
       /* We can't handle a * b + a * b.  */
       if (gimple_assign_rhs1 (use_stmt) == gimple_assign_rhs2 (use_stmt))
        return false;
       /* We can't handle a * b + a * b.  */
       if (gimple_assign_rhs1 (use_stmt) == gimple_assign_rhs2 (use_stmt))
        return false;
@@ -2585,10 +2762,10 @@ convert_mult_to_fma (gimple mul_stmt, tree op1, tree op2)
                                           true, NULL_TREE, true,
                                           GSI_SAME_STMT);
 
                                           true, NULL_TREE, true,
                                           GSI_SAME_STMT);
 
-      fma_stmt = gimple_build_assign_with_ops3 (FMA_EXPR,
-                                               gimple_assign_lhs (use_stmt),
-                                               mulop1, op2,
-                                               addop);
+      fma_stmt = gimple_build_assign_with_ops (FMA_EXPR,
+                                              gimple_assign_lhs (use_stmt),
+                                              mulop1, op2,
+                                              addop);
       gsi_replace (&gsi, fma_stmt, true);
       widen_mul_stats.fmas_inserted++;
     }
       gsi_replace (&gsi, fma_stmt, true);
       widen_mul_stats.fmas_inserted++;
     }
@@ -2695,23 +2872,41 @@ gate_optimize_widening_mul (void)
   return flag_expensive_optimizations && optimize;
 }
 
   return flag_expensive_optimizations && optimize;
 }
 
-struct gimple_opt_pass pass_optimize_widening_mul =
+namespace {
+
+const pass_data pass_data_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 */
-  TODO_verify_ssa
-  | TODO_verify_stmts
-  | TODO_update_ssa                     /* todo_flags_finish */
- }
+  GIMPLE_PASS, /* type */
+  "widening_mul", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_verify_ssa | TODO_verify_stmts
+    | TODO_update_ssa ), /* todo_flags_finish */
 };
 };
+
+class pass_optimize_widening_mul : public gimple_opt_pass
+{
+public:
+  pass_optimize_widening_mul (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_optimize_widening_mul, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_optimize_widening_mul (); }
+  unsigned int execute () { return execute_optimize_widening_mul (); }
+
+}; // class pass_optimize_widening_mul
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_optimize_widening_mul (gcc::context *ctxt)
+{
+  return new pass_optimize_widening_mul (ctxt);
+}