diff gcc/tree-ssa-math-opts.c @ 67:f6334be47118

update gcc from gcc-4.6-20100522 to gcc-4.6-20110318
author nobuyasu <dimolto@cr.ie.u-ryukyu.ac.jp>
date Tue, 22 Mar 2011 17:18:12 +0900
parents b7f97abdc517
children 04ced10e8804
line wrap: on
line diff
--- a/gcc/tree-ssa-math-opts.c	Tue May 25 18:58:51 2010 +0900
+++ b/gcc/tree-ssa-math-opts.c	Tue Mar 22 17:18:12 2011 +0900
@@ -97,7 +97,6 @@
 #include "alloc-pool.h"
 #include "basic-block.h"
 #include "target.h"
-#include "diagnostic.h"
 #include "gimple-pretty-print.h"
 
 /* FIXME: RTL headers have to be included here for optabs.  */
@@ -475,7 +474,7 @@
     gcc_assert (!bb->aux);
 #endif
 
-  for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = TREE_CHAIN (arg))
+  for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
     if (gimple_default_def (cfun, arg)
 	&& FLOAT_TYPE_P (TREE_TYPE (arg))
 	&& is_gimple_reg (arg))
@@ -642,7 +641,7 @@
    result of the cexpi call we insert before the use statement that
    dominates all other candidates.  */
 
-static void
+static bool
 execute_cse_sincos_1 (tree name)
 {
   gimple_stmt_iterator gsi;
@@ -653,6 +652,7 @@
   VEC(gimple, heap) *stmts = NULL;
   basic_block top_bb = NULL;
   int i;
+  bool cfg_changed = false;
 
   type = TREE_TYPE (name);
   FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, name)
@@ -684,16 +684,17 @@
   if (seen_cos + seen_sin + seen_cexpi <= 1)
     {
       VEC_free(gimple, heap, stmts);
-      return;
+      return false;
     }
 
   /* 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");
+    return false;
+  res = create_tmp_reg (TREE_TYPE (TREE_TYPE (fndecl)), "sincostmp");
   stmt = gimple_build_call (fndecl, 1, name);
+  res = make_ssa_name (res, stmt);
   gimple_call_set_lhs (stmt, res);
 
   def_stmt = SSA_NAME_DEF_STMT (name);
@@ -739,11 +740,14 @@
 	stmt = gimple_build_assign (gimple_call_lhs (use_stmt), rhs);
 
 	gsi = gsi_for_stmt (use_stmt);
-	gsi_insert_after (&gsi, stmt, GSI_SAME_STMT);
-	gsi_remove (&gsi, true);
+	gsi_replace (&gsi, stmt, true);
+	if (gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+	  cfg_changed = true;
     }
 
   VEC_free(gimple, heap, stmts);
+
+  return cfg_changed;
 }
 
 /* Go through all calls to sin, cos and cexpi and call execute_cse_sincos_1
@@ -753,6 +757,7 @@
 execute_cse_sincos (void)
 {
   basic_block bb;
+  bool cfg_changed = false;
 
   calculate_dominance_info (CDI_DOMINATORS);
 
@@ -779,7 +784,7 @@
 		CASE_FLT_FN (BUILT_IN_CEXPI):
 		  arg = gimple_call_arg (stmt, 0);
 		  if (TREE_CODE (arg) == SSA_NAME)
-		    execute_cse_sincos_1 (arg);
+		    cfg_changed |= execute_cse_sincos_1 (arg);
 		  break;
 
 		default:;
@@ -789,7 +794,7 @@
     }
 
   free_dominance_info (CDI_DOMINATORS);
-  return 0;
+  return cfg_changed ? TODO_cleanup_cfg : 0;
 }
 
 static bool
@@ -1114,11 +1119,9 @@
     return 0;
 
   bswap32_p = (built_in_decls[BUILT_IN_BSWAP32]
-	       && optab_handler (bswap_optab, SImode)->insn_code !=
-	       CODE_FOR_nothing);
+	       && optab_handler (bswap_optab, SImode) != CODE_FOR_nothing);
   bswap64_p = (built_in_decls[BUILT_IN_BSWAP64]
-	       && (optab_handler (bswap_optab, DImode)->insn_code !=
-		   CODE_FOR_nothing
+	       && (optab_handler (bswap_optab, DImode) != CODE_FOR_nothing
 		   || (bswap32_p && word_mode == SImode)));
 
   if (!bswap32_p && !bswap64_p)
@@ -1263,6 +1266,411 @@
  }
 };
 
+/* Return true if RHS is a suitable operand for a widening multiplication.
+   There are two cases:
+
+     - RHS makes some value twice as wide.  Store that value in *NEW_RHS_OUT
+       if so, and store its type in *TYPE_OUT.
+
+     - RHS is an integer constant.  Store that value in *NEW_RHS_OUT if so,
+       but leave *TYPE_OUT untouched.  */
+
+static bool
+is_widening_mult_rhs_p (tree rhs, tree *type_out, tree *new_rhs_out)
+{
+  gimple stmt;
+  tree type, type1, rhs1;
+  enum tree_code rhs_code;
+
+  if (TREE_CODE (rhs) == SSA_NAME)
+    {
+      type = TREE_TYPE (rhs);
+      stmt = SSA_NAME_DEF_STMT (rhs);
+      if (!is_gimple_assign (stmt))
+	return false;
+
+      rhs_code = gimple_assign_rhs_code (stmt);
+      if (TREE_CODE (type) == INTEGER_TYPE
+	  ? !CONVERT_EXPR_CODE_P (rhs_code)
+	  : rhs_code != FIXED_CONVERT_EXPR)
+	return false;
+
+      rhs1 = gimple_assign_rhs1 (stmt);
+      type1 = TREE_TYPE (rhs1);
+      if (TREE_CODE (type1) != TREE_CODE (type)
+	  || TYPE_PRECISION (type1) * 2 != TYPE_PRECISION (type))
+	return false;
+
+      *new_rhs_out = rhs1;
+      *type_out = type1;
+      return true;
+    }
+
+  if (TREE_CODE (rhs) == INTEGER_CST)
+    {
+      *new_rhs_out = rhs;
+      *type_out = NULL;
+      return true;
+    }
+
+  return false;
+}
+
+/* Return true if STMT performs a widening multiplication.  If so,
+   store the unwidened types of the operands in *TYPE1_OUT and *TYPE2_OUT
+   respectively.  Also fill *RHS1_OUT and *RHS2_OUT such that converting
+   those operands to types *TYPE1_OUT and *TYPE2_OUT would give the
+   operands of the multiplication.  */
+
+static bool
+is_widening_mult_p (gimple stmt,
+		    tree *type1_out, tree *rhs1_out,
+		    tree *type2_out, tree *rhs2_out)
+{
+  tree type;
+
+  type = TREE_TYPE (gimple_assign_lhs (stmt));
+  if (TREE_CODE (type) != INTEGER_TYPE
+      && TREE_CODE (type) != FIXED_POINT_TYPE)
+    return false;
+
+  if (!is_widening_mult_rhs_p (gimple_assign_rhs1 (stmt), type1_out, rhs1_out))
+    return false;
+
+  if (!is_widening_mult_rhs_p (gimple_assign_rhs2 (stmt), type2_out, rhs2_out))
+    return false;
+
+  if (*type1_out == NULL)
+    {
+      if (*type2_out == NULL || !int_fits_type_p (*rhs1_out, *type2_out))
+	return false;
+      *type1_out = *type2_out;
+    }
+
+  if (*type2_out == NULL)
+    {
+      if (!int_fits_type_p (*rhs2_out, *type1_out))
+	return false;
+      *type2_out = *type1_out;
+    }
+
+  return true;
+}
+
+/* Process a single gimple statement STMT, which has a MULT_EXPR as
+   its rhs, and try to convert it into a WIDEN_MULT_EXPR.  The return
+   value is true iff we converted the statement.  */
+
+static bool
+convert_mult_to_widen (gimple stmt)
+{
+  tree lhs, rhs1, rhs2, type, type1, type2;
+  enum insn_code handler;
+
+  lhs = gimple_assign_lhs (stmt);
+  type = TREE_TYPE (lhs);
+  if (TREE_CODE (type) != INTEGER_TYPE)
+    return false;
+
+  if (!is_widening_mult_p (stmt, &type1, &rhs1, &type2, &rhs2))
+    return false;
+
+  if (TYPE_UNSIGNED (type1) && TYPE_UNSIGNED (type2))
+    handler = optab_handler (umul_widen_optab, TYPE_MODE (type));
+  else if (!TYPE_UNSIGNED (type1) && !TYPE_UNSIGNED (type2))
+    handler = optab_handler (smul_widen_optab, TYPE_MODE (type));
+  else
+    handler = optab_handler (usmul_widen_optab, TYPE_MODE (type));
+
+  if (handler == CODE_FOR_nothing)
+    return false;
+
+  gimple_assign_set_rhs1 (stmt, fold_convert (type1, rhs1));
+  gimple_assign_set_rhs2 (stmt, fold_convert (type2, rhs2));
+  gimple_assign_set_rhs_code (stmt, WIDEN_MULT_EXPR);
+  update_stmt (stmt);
+  return true;
+}
+
+/* Process a single gimple statement STMT, which is found at the
+   iterator GSI and has a either a PLUS_EXPR or a MINUS_EXPR as its
+   rhs (given by CODE), and try to convert it into a
+   WIDEN_MULT_PLUS_EXPR or a WIDEN_MULT_MINUS_EXPR.  The return value
+   is true iff we converted the statement.  */
+
+static bool
+convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
+			    enum tree_code code)
+{
+  gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
+  tree type, type1, type2;
+  tree lhs, rhs1, rhs2, mult_rhs1, mult_rhs2, add_rhs;
+  enum tree_code rhs1_code = ERROR_MARK, rhs2_code = ERROR_MARK;
+  optab this_optab;
+  enum tree_code wmult_code;
+
+  lhs = gimple_assign_lhs (stmt);
+  type = TREE_TYPE (lhs);
+  if (TREE_CODE (type) != INTEGER_TYPE
+      && TREE_CODE (type) != FIXED_POINT_TYPE)
+    return false;
+
+  if (code == MINUS_EXPR)
+    wmult_code = WIDEN_MULT_MINUS_EXPR;
+  else
+    wmult_code = WIDEN_MULT_PLUS_EXPR;
+
+  rhs1 = gimple_assign_rhs1 (stmt);
+  rhs2 = gimple_assign_rhs2 (stmt);
+
+  if (TREE_CODE (rhs1) == SSA_NAME)
+    {
+      rhs1_stmt = SSA_NAME_DEF_STMT (rhs1);
+      if (is_gimple_assign (rhs1_stmt))
+	rhs1_code = gimple_assign_rhs_code (rhs1_stmt);
+    }
+  else
+    return false;
+
+  if (TREE_CODE (rhs2) == SSA_NAME)
+    {
+      rhs2_stmt = SSA_NAME_DEF_STMT (rhs2);
+      if (is_gimple_assign (rhs2_stmt))
+	rhs2_code = gimple_assign_rhs_code (rhs2_stmt);
+    }
+  else
+    return false;
+
+  if (code == PLUS_EXPR && rhs1_code == MULT_EXPR)
+    {
+      if (!is_widening_mult_p (rhs1_stmt, &type1, &mult_rhs1,
+			       &type2, &mult_rhs2))
+	return false;
+      add_rhs = rhs2;
+    }
+  else if (rhs2_code == MULT_EXPR)
+    {
+      if (!is_widening_mult_p (rhs2_stmt, &type1, &mult_rhs1,
+			       &type2, &mult_rhs2))
+	return false;
+      add_rhs = rhs1;
+    }
+  else if (code == PLUS_EXPR && rhs1_code == WIDEN_MULT_EXPR)
+    {
+      mult_rhs1 = gimple_assign_rhs1 (rhs1_stmt);
+      mult_rhs2 = gimple_assign_rhs2 (rhs1_stmt);
+      type1 = TREE_TYPE (mult_rhs1);
+      type2 = TREE_TYPE (mult_rhs2);
+      add_rhs = rhs2;
+    }
+  else if (rhs2_code == WIDEN_MULT_EXPR)
+    {
+      mult_rhs1 = gimple_assign_rhs1 (rhs2_stmt);
+      mult_rhs2 = gimple_assign_rhs2 (rhs2_stmt);
+      type1 = TREE_TYPE (mult_rhs1);
+      type2 = TREE_TYPE (mult_rhs2);
+      add_rhs = rhs1;
+    }
+  else
+    return false;
+
+  if (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
+    return false;
+
+  /* Verify that the machine can perform a widening multiply
+     accumulate in this mode/signedness combination, otherwise
+     this transformation is likely to pessimize code.  */
+  this_optab = optab_for_tree_code (wmult_code, type1, optab_default);
+  if (optab_handler (this_optab, TYPE_MODE (type)) == CODE_FOR_nothing)
+    return false;
+
+  /* ??? May need some type verification here?  */
+
+  gimple_assign_set_rhs_with_ops_1 (gsi, wmult_code,
+				    fold_convert (type1, mult_rhs1),
+				    fold_convert (type2, mult_rhs2),
+				    add_rhs);
+  update_stmt (gsi_stmt (*gsi));
+  return true;
+}
+
+/* Combine the multiplication at MUL_STMT with operands MULOP1 and MULOP2
+   with uses in additions and subtractions to form fused multiply-add
+   operations.  Returns true if successful and MUL_STMT should be removed.  */
+
+static bool
+convert_mult_to_fma (gimple mul_stmt, tree op1, tree op2)
+{
+  tree mul_result = gimple_get_lhs (mul_stmt);
+  tree type = TREE_TYPE (mul_result);
+  gimple use_stmt, neguse_stmt, fma_stmt;
+  use_operand_p use_p;
+  imm_use_iterator imm_iter;
+
+  if (FLOAT_TYPE_P (type)
+      && flag_fp_contract_mode == FP_CONTRACT_OFF)
+    return false;
+
+  /* We don't want to do bitfield reduction ops.  */
+  if (INTEGRAL_TYPE_P (type)
+      && (TYPE_PRECISION (type)
+	  != GET_MODE_PRECISION (TYPE_MODE (type))))
+    return false;
+
+  /* If the target doesn't support it, don't generate it.  We assume that
+     if fma isn't available then fms, fnma or fnms are not either.  */
+  if (optab_handler (fma_optab, TYPE_MODE (type)) == CODE_FOR_nothing)
+    return false;
+
+  /* Make sure that the multiplication statement becomes dead after
+     the transformation, thus that all uses are transformed to FMAs.
+     This means we assume that an FMA operation has the same cost
+     as an addition.  */
+  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, mul_result)
+    {
+      enum tree_code use_code;
+      tree result = mul_result;
+      bool negate_p = false;
+
+      use_stmt = USE_STMT (use_p);
+
+      if (is_gimple_debug (use_stmt))
+	continue;
+
+      /* For now restrict this operations to single basic blocks.  In theory
+	 we would want to support sinking the multiplication in
+	 m = a*b;
+	 if ()
+	   ma = m + c;
+	 else
+	   d = m;
+	 to form a fma in the then block and sink the multiplication to the
+	 else block.  */
+      if (gimple_bb (use_stmt) != gimple_bb (mul_stmt))
+	return false;
+
+      if (!is_gimple_assign (use_stmt))
+	return false;
+
+      use_code = gimple_assign_rhs_code (use_stmt);
+
+      /* A negate on the multiplication leads to FNMA.  */
+      if (use_code == NEGATE_EXPR)
+	{
+	  ssa_op_iter iter;
+	  tree use;
+
+	  result = gimple_assign_lhs (use_stmt);
+
+	  /* Make sure the negate statement becomes dead with this
+	     single transformation.  */
+	  if (!single_imm_use (gimple_assign_lhs (use_stmt),
+			       &use_p, &neguse_stmt))
+	    return false;
+
+	  /* Make sure the multiplication isn't also used on that stmt.  */
+	  FOR_EACH_SSA_TREE_OPERAND (use, neguse_stmt, iter, SSA_OP_USE)
+	    if (use == mul_result)
+	      return false;
+
+	  /* Re-validate.  */
+	  use_stmt = neguse_stmt;
+	  if (gimple_bb (use_stmt) != gimple_bb (mul_stmt))
+	    return false;
+	  if (!is_gimple_assign (use_stmt))
+	    return false;
+
+	  use_code = gimple_assign_rhs_code (use_stmt);
+	  negate_p = true;
+	}
+
+      switch (use_code)
+	{
+	case MINUS_EXPR:
+	  if (gimple_assign_rhs2 (use_stmt) == result)
+	    negate_p = !negate_p;
+	  break;
+	case PLUS_EXPR:
+	  break;
+	default:
+	  /* FMA can only be formed from PLUS and MINUS.  */
+	  return false;
+	}
+
+      /* We can't handle a * b + a * b.  */
+      if (gimple_assign_rhs1 (use_stmt) == gimple_assign_rhs2 (use_stmt))
+	return false;
+
+      /* While it is possible to validate whether or not the exact form
+	 that we've recognized is available in the backend, the assumption
+	 is that the transformation is never a loss.  For instance, suppose
+	 the target only has the plain FMA pattern available.  Consider
+	 a*b-c -> fma(a,b,-c): we've exchanged MUL+SUB for FMA+NEG, which
+	 is still two operations.  Consider -(a*b)-c -> fma(-a,b,-c): we
+	 still have 3 operations, but in the FMA form the two NEGs are
+	 independant and could be run in parallel.  */
+    }
+
+  FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, mul_result)
+    {
+      gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
+      enum tree_code use_code;
+      tree addop, mulop1 = op1, result = mul_result;
+      bool negate_p = false;
+
+      if (is_gimple_debug (use_stmt))
+	continue;
+
+      use_code = gimple_assign_rhs_code (use_stmt);
+      if (use_code == NEGATE_EXPR)
+	{
+	  result = gimple_assign_lhs (use_stmt);
+	  single_imm_use (gimple_assign_lhs (use_stmt), &use_p, &neguse_stmt);
+	  gsi_remove (&gsi, true);
+	  release_defs (use_stmt);
+
+	  use_stmt = neguse_stmt;
+	  gsi = gsi_for_stmt (use_stmt);
+	  use_code = gimple_assign_rhs_code (use_stmt);
+	  negate_p = true;
+	}
+
+      if (gimple_assign_rhs1 (use_stmt) == result)
+	{
+	  addop = gimple_assign_rhs2 (use_stmt);
+	  /* a * b - c -> a * b + (-c)  */
+	  if (gimple_assign_rhs_code (use_stmt) == MINUS_EXPR)
+	    addop = force_gimple_operand_gsi (&gsi,
+					      build1 (NEGATE_EXPR,
+						      type, addop),
+					      true, NULL_TREE, true,
+					      GSI_SAME_STMT);
+	}
+      else
+	{
+	  addop = gimple_assign_rhs1 (use_stmt);
+	  /* a - b * c -> (-b) * c + a */
+	  if (gimple_assign_rhs_code (use_stmt) == MINUS_EXPR)
+	    negate_p = !negate_p;
+	}
+
+      if (negate_p)
+	mulop1 = force_gimple_operand_gsi (&gsi,
+					   build1 (NEGATE_EXPR,
+						   type, mulop1),
+					   true, NULL_TREE, true,
+					   GSI_SAME_STMT);
+
+      fma_stmt = gimple_build_assign_with_ops3 (FMA_EXPR,
+						gimple_assign_lhs (use_stmt),
+						mulop1, op2,
+						addop);
+      gsi_replace (&gsi, fma_stmt, true);
+    }
+
+  return true;
+}
+
 /* Find integer multiplications where the operands are extended from
    smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
    where appropriate.  */
@@ -1270,106 +1678,81 @@
 static unsigned int
 execute_optimize_widening_mul (void)
 {
-  bool changed = false;
   basic_block bb;
+  bool cfg_changed = false;
 
   FOR_EACH_BB (bb)
     {
       gimple_stmt_iterator gsi;
 
-      for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+      for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi);)
         {
 	  gimple stmt = gsi_stmt (gsi);
-	  gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
-	  tree type, type1 = NULL, type2 = NULL;
-	  tree rhs1, rhs2, rhs1_convop = NULL, rhs2_convop = NULL;
-	  enum tree_code rhs1_code, rhs2_code;
-
-	  if (!is_gimple_assign (stmt)
-	      || gimple_assign_rhs_code (stmt) != MULT_EXPR)
-	    continue;
-
-	  type = TREE_TYPE (gimple_assign_lhs (stmt));
-
-	  if (TREE_CODE (type) != INTEGER_TYPE)
-	    continue;
-
-	  rhs1 = gimple_assign_rhs1 (stmt);
-	  rhs2 = gimple_assign_rhs2 (stmt);
+	  enum tree_code code;
 
-	  if (TREE_CODE (rhs1) == SSA_NAME)
+	  if (is_gimple_assign (stmt))
 	    {
-	      rhs1_stmt = SSA_NAME_DEF_STMT (rhs1);
-	      if (!is_gimple_assign (rhs1_stmt))
-		continue;
-	      rhs1_code = gimple_assign_rhs_code (rhs1_stmt);
-	      if (!CONVERT_EXPR_CODE_P (rhs1_code))
-		continue;
-	      rhs1_convop = gimple_assign_rhs1 (rhs1_stmt);
-	      type1 = TREE_TYPE (rhs1_convop);
-	      if (TYPE_PRECISION (type1) * 2 != TYPE_PRECISION (type))
-		continue;
+	      code = gimple_assign_rhs_code (stmt);
+	      switch (code)
+		{
+		case MULT_EXPR:
+		  if (!convert_mult_to_widen (stmt)
+		      && convert_mult_to_fma (stmt,
+					      gimple_assign_rhs1 (stmt),
+					      gimple_assign_rhs2 (stmt)))
+		    {
+		      gsi_remove (&gsi, true);
+		      release_defs (stmt);
+		      continue;
+		    }
+		  break;
+
+		case PLUS_EXPR:
+		case MINUS_EXPR:
+		  convert_plusminus_to_widen (&gsi, stmt, code);
+		  break;
+
+		default:;
+		}
 	    }
-	  else if (TREE_CODE (rhs1) != INTEGER_CST)
-	    continue;
-
-	  if (TREE_CODE (rhs2) == SSA_NAME)
+	  else if (is_gimple_call (stmt)
+		   && gimple_call_lhs (stmt))
 	    {
-	      rhs2_stmt = SSA_NAME_DEF_STMT (rhs2);
-	      if (!is_gimple_assign (rhs2_stmt))
-		continue;
-	      rhs2_code = gimple_assign_rhs_code (rhs2_stmt);
-	      if (!CONVERT_EXPR_CODE_P (rhs2_code))
-		continue;
-	      rhs2_convop = gimple_assign_rhs1 (rhs2_stmt);
-	      type2 = TREE_TYPE (rhs2_convop);
-	      if (TYPE_PRECISION (type2) * 2 != TYPE_PRECISION (type))
-		continue;
-	    }
-	  else if (TREE_CODE (rhs2) != INTEGER_CST)
-	    continue;
-
-	  if (rhs1_stmt == NULL && rhs2_stmt == NULL)
-	    continue;
+	      tree fndecl = gimple_call_fndecl (stmt);
+	      if (fndecl
+		  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+		{
+		  switch (DECL_FUNCTION_CODE (fndecl))
+		    {
+		      case BUILT_IN_POWF:
+		      case BUILT_IN_POW:
+		      case BUILT_IN_POWL:
+			if (TREE_CODE (gimple_call_arg (stmt, 1)) == REAL_CST
+			    && REAL_VALUES_EQUAL
+			         (TREE_REAL_CST (gimple_call_arg (stmt, 1)),
+				  dconst2)
+			    && convert_mult_to_fma (stmt,
+						    gimple_call_arg (stmt, 0),
+						    gimple_call_arg (stmt, 0)))
+			  {
+			    unlink_stmt_vdef (stmt);
+			    gsi_remove (&gsi, true);
+			    release_defs (stmt);
+			    if (gimple_purge_dead_eh_edges (bb))
+			      cfg_changed = true;
+			    continue;
+			  }
+			  break;
 
-	  /* Verify that the machine can perform a widening multiply in this
-	     mode/signedness combination, otherwise this transformation is
-	     likely to pessimize code.  */
-	  if ((rhs1_stmt == NULL || TYPE_UNSIGNED (type1))
-	      && (rhs2_stmt == NULL || TYPE_UNSIGNED (type2))
-	      && (optab_handler (umul_widen_optab, TYPE_MODE (type))
-		  ->insn_code == CODE_FOR_nothing))
-	    continue;
-	  else if ((rhs1_stmt == NULL || !TYPE_UNSIGNED (type1))
-		   && (rhs2_stmt == NULL || !TYPE_UNSIGNED (type2))
-		   && (optab_handler (smul_widen_optab, TYPE_MODE (type))
-		       ->insn_code == CODE_FOR_nothing))
-	    continue;
-	  else if (rhs1_stmt != NULL && rhs2_stmt != 0
-		   && (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
-		   && (optab_handler (usmul_widen_optab, TYPE_MODE (type))
-		       ->insn_code == CODE_FOR_nothing))
-	    continue;
-
-	  if ((rhs1_stmt == NULL && !int_fits_type_p (rhs1, type2))
-	      || (rhs2_stmt == NULL && !int_fits_type_p (rhs2, type1)))
-	    continue;
-
-	  if (rhs1_stmt == NULL)
-	    gimple_assign_set_rhs1 (stmt, fold_convert (type2, rhs1));
-	  else
-	    gimple_assign_set_rhs1 (stmt, rhs1_convop);
-	  if (rhs2_stmt == NULL)
-	    gimple_assign_set_rhs2 (stmt, fold_convert (type1, rhs2));
-	  else
-	    gimple_assign_set_rhs2 (stmt, rhs2_convop);
-	  gimple_assign_set_rhs_code (stmt, WIDEN_MULT_EXPR);
-	  update_stmt (stmt);
-	  changed = true;
+		      default:;
+		    }
+		}
+	    }
+	  gsi_next (&gsi);
 	}
     }
-  return (changed ? TODO_dump_func | TODO_update_ssa | TODO_verify_ssa
-	  | TODO_verify_stmts : 0);
+
+  return cfg_changed ? TODO_cleanup_cfg : 0;
 }
 
 static bool
@@ -1393,6 +1776,9 @@
   0,					/* properties_provided */
   0,					/* properties_destroyed */
   0,					/* todo_flags_start */
-  0                                     /* todo_flags_finish */
+  TODO_verify_ssa
+  | TODO_verify_stmts
+  | TODO_dump_func
+  | TODO_update_ssa                     /* todo_flags_finish */
  }
 };