gimple.h: Remove all includes.
[platform/upstream/gcc.git] / gcc / tree-ssa-math-opts.c
index 748bf2f..f77c016 100644 (file)
@@ -1,6 +1,5 @@
 /* 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.
 
@@ -91,10 +90,27 @@ along with GCC; see the file COPYING3.  If not see
 #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 "basic-block.h"
 #include "target.h"
 #include "gimple-pretty-print.h"
 
@@ -154,6 +170,9 @@ 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;
 
@@ -274,7 +293,7 @@ register_division_in (basic_block bb)
   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;
@@ -501,7 +520,7 @@ execute_cse_reciprocals (void)
 
   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);
@@ -606,7 +625,7 @@ execute_cse_reciprocals (void)
                  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++;
@@ -634,26 +653,45 @@ execute_cse_reciprocals (void)
   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
@@ -661,18 +699,18 @@ struct gimple_opt_pass pass_cse_reciprocals =
    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)))
-    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))
     {
-      VEC_safe_push (gimple, heap, *stmts, use_stmt);
+      stmts->safe_push (use_stmt);
       *top_bb = use_bb;
     }
   else
@@ -697,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;
-  VEC(gimple, heap) *stmts = NULL;
+  vec<gimple> stmts = vNULL;
   basic_block top_bb = NULL;
   int i;
   bool cfg_changed = false;
@@ -731,7 +769,7 @@ execute_cse_sincos_1 (tree name)
 
   if (seen_cos + seen_sin + seen_cexpi <= 1)
     {
-      VEC_free(gimple, heap, stmts);
+      stmts.release ();
       return false;
     }
 
@@ -760,7 +798,7 @@ execute_cse_sincos_1 (tree name)
   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);
@@ -792,7 +830,7 @@ execute_cse_sincos_1 (tree name)
          cfg_changed = true;
     }
 
-  VEC_free(gimple, heap, stmts);
+  stmts.release ();
 
   return cfg_changed;
 }
@@ -1107,7 +1145,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
   HOST_WIDE_INT n;
   tree type, sqrtfn, cbrtfn, sqrt_arg0, sqrt_sqrt, result, cbrt_x, powi_cbrt_x;
   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.  */
@@ -1119,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_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 ()
@@ -1218,7 +1257,8 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
       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.
@@ -1227,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);
+  c2_is_int = real_identical (&c2, &cint);
 
   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;
 
@@ -1283,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)
+      && !c2_is_int
       && optimize_function_for_speed_p (cfun)
       && powi_cost (n / 3) <= POWI_MAX_MULTS)
     {
@@ -1378,12 +1422,18 @@ execute_cse_sincos (void)
   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;
 
+         /* 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))
@@ -1399,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.  */
-                 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);
@@ -1421,6 +1472,7 @@ execute_cse_sincos (void)
                      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));
                    }
@@ -1429,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);
-                 if (!host_integerp (arg1, 0))
-                   break;
-
-                 n = TREE_INT_CST_LOW (arg1);
                  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)
                    {
@@ -1443,6 +1524,7 @@ execute_cse_sincos (void)
                      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));
                    }
@@ -1460,6 +1542,7 @@ execute_cse_sincos (void)
                      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));
                    }
@@ -1469,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",
@@ -1486,26 +1571,45 @@ gate_cse_sincos (void)
   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:
@@ -1792,9 +1896,9 @@ static unsigned int
 execute_optimize_bswap (void)
 {
   basic_block bb;
-  bool bswap32_p, bswap64_p;
+  bool bswap16_p, bswap32_p, bswap64_p;
   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;
@@ -1802,17 +1906,25 @@ execute_optimize_bswap (void)
   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)));
 
-  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.  */
+  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);
@@ -1852,6 +1964,13 @@ execute_optimize_bswap (void)
 
          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)
                {
@@ -1879,7 +1998,9 @@ execute_optimize_bswap (void)
            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++;
@@ -1924,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",
@@ -1939,25 +2062,44 @@ gate_optimize_bswap (void)
   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
@@ -1985,7 +2127,11 @@ widening_mult_conversion_strippable_p (tree result_type, gimple stmt)
         the operation and doesn't narrow the range.  */
       inner_op_type = TREE_TYPE (gimple_assign_rhs1 (stmt));
 
-      if (TYPE_UNSIGNED (op_type) == TYPE_UNSIGNED (inner_op_type)
+      /* 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;
 
@@ -2011,14 +2157,12 @@ is_widening_mult_rhs_p (tree type, tree rhs, tree *type_out,
 {
   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))
        {
-         rhs_code = gimple_assign_rhs_code (stmt);
          if (! widening_mult_conversion_strippable_p (type, stmt))
            rhs1 = rhs;
          else
@@ -2298,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
-     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 (!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)
     {
-      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;
@@ -2527,6 +2676,28 @@ convert_mult_to_fma (gimple mul_stmt, tree op1, tree op2)
          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;
@@ -2591,10 +2762,10 @@ convert_mult_to_fma (gimple mul_stmt, tree op1, tree op2)
                                           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++;
     }
@@ -2701,23 +2872,41 @@ gate_optimize_widening_mul (void)
   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);
+}