Index: gcc/tree-ssa-math-opts.c
===================================================================
--- gcc/tree-ssa-math-opts.c	2010-07-18 08:36:48.000000000 +0100
+++ gcc/tree-ssa-math-opts.c	2010-07-18 12:35:42.000000000 +0100
@@ -1260,22 +1260,30 @@ struct gimple_opt_pass pass_optimize_bsw
  }
 };
 
-/* 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.  */
+/* Process a single gimple statement STMT, which has a MULT_EXPR as its
+   rhs, and see if it is a widening multiplication.  If so:
+
+     - store the two unextended operands in RHS_OUT[0] and RHS_OUT[1],
+       if RHS_OUT is nonnull; and
+     - try to convert the statement into a WIDEN_MULT_EXPR.
+
+   Return true if the statement could be converted or if operands
+   were stored in RHS_OUT.  */
 
 static bool
-convert_mult_to_widen (gimple stmt)
+convert_mult_to_widen (gimple stmt, tree *rhs_out)
 {
   gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
   tree type1 = NULL, type2 = NULL;
-  tree rhs1, rhs2, rhs1_convop = NULL, rhs2_convop = NULL;
+  tree rhs1, rhs2, unwidened_rhs1 = NULL, unwidened_rhs2 = NULL;
   enum tree_code rhs1_code, rhs2_code;
   tree type;
+  bool use_widen_mult_p;
 
   type = TREE_TYPE (gimple_assign_lhs (stmt));
 
-  if (TREE_CODE (type) != INTEGER_TYPE)
+  if (TREE_CODE (type) != INTEGER_TYPE
+      && TREE_CODE (type) != FIXED_POINT_TYPE)
     return false;
 
   rhs1 = gimple_assign_rhs1 (stmt);
@@ -1287,14 +1295,17 @@ convert_mult_to_widen (gimple stmt)
       if (!is_gimple_assign (rhs1_stmt))
 	return false;
       rhs1_code = gimple_assign_rhs_code (rhs1_stmt);
-      if (!CONVERT_EXPR_CODE_P (rhs1_code))
+      if (TREE_CODE (type) == INTEGER_TYPE
+	  ? !CONVERT_EXPR_CODE_P (rhs1_code)
+	  : rhs1_code != FIXED_CONVERT_EXPR)
 	return false;
-      rhs1_convop = gimple_assign_rhs1 (rhs1_stmt);
-      type1 = TREE_TYPE (rhs1_convop);
+      unwidened_rhs1 = gimple_assign_rhs1 (rhs1_stmt);
+      type1 = TREE_TYPE (unwidened_rhs1);
       if (TYPE_PRECISION (type1) * 2 != TYPE_PRECISION (type))
 	return false;
     }
-  else if (TREE_CODE (rhs1) != INTEGER_CST)
+  else if (TREE_CODE (rhs1) != INTEGER_CST
+	   && TREE_CODE (rhs1) != FIXED_CST)
     return false;
 
   if (TREE_CODE (rhs2) == SSA_NAME)
@@ -1303,52 +1314,73 @@ convert_mult_to_widen (gimple stmt)
       if (!is_gimple_assign (rhs2_stmt))
 	return false;
       rhs2_code = gimple_assign_rhs_code (rhs2_stmt);
-      if (!CONVERT_EXPR_CODE_P (rhs2_code))
+      if (TREE_CODE (type) == INTEGER_TYPE
+	  ? !CONVERT_EXPR_CODE_P (rhs2_code)
+	  : rhs2_code != FIXED_CONVERT_EXPR)
 	return false;
-      rhs2_convop = gimple_assign_rhs1 (rhs2_stmt);
-      type2 = TREE_TYPE (rhs2_convop);
+      unwidened_rhs2 = gimple_assign_rhs1 (rhs2_stmt);
+      type2 = TREE_TYPE (unwidened_rhs2);
       if (TYPE_PRECISION (type2) * 2 != TYPE_PRECISION (type))
 	return false;
     }
-  else if (TREE_CODE (rhs2) != INTEGER_CST)
+  else if (TREE_CODE (rhs2) != INTEGER_CST
+	   && TREE_CODE (rhs2) != FIXED_CST)
     return false;
 
   if (rhs1_stmt == NULL && rhs2_stmt == NULL)
     return false;
 
+  /* At present, WIDEN_MULT_EXPR only supports integer types,
+     not fixed-point ones.  Processing fixed-point types is only
+     useful if the caller wants the unextended operands.  */
+  if (TREE_CODE (type) == FIXED_POINT_TYPE)
+    use_widen_mult_p = false;
   /* 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))
-	  == CODE_FOR_nothing))
-    return false;
+  else if ((rhs1_stmt == NULL || TYPE_UNSIGNED (type1))
+	   && (rhs2_stmt == NULL || TYPE_UNSIGNED (type2))
+	   && (optab_handler (umul_widen_optab, TYPE_MODE (type))
+	       == CODE_FOR_nothing))
+    use_widen_mult_p = false;
   else if ((rhs1_stmt == NULL || !TYPE_UNSIGNED (type1))
 	   && (rhs2_stmt == NULL || !TYPE_UNSIGNED (type2))
 	   && (optab_handler (smul_widen_optab, TYPE_MODE (type))
 	       == CODE_FOR_nothing))
-    return false;
+    use_widen_mult_p = false;
   else if (rhs1_stmt != NULL && rhs2_stmt != NULL
 	   && (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
 	   && (optab_handler (usmul_widen_optab, TYPE_MODE (type))
 	       == CODE_FOR_nothing))
+    use_widen_mult_p = false;
+  else
+    use_widen_mult_p = true;
+
+  if (!use_widen_mult_p && rhs_out == NULL)
     return false;
 
   if ((rhs1_stmt == NULL && !int_fits_type_p (rhs1, type2))
       || (rhs2_stmt == NULL && !int_fits_type_p (rhs2, type1)))
     return false;
 
-  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);
+  if (unwidened_rhs1 == NULL)
+    unwidened_rhs1 = fold_convert (type2, rhs1);
+  if (unwidened_rhs2 == NULL)
+    unwidened_rhs2 = fold_convert (type1, rhs2);
+
+  if (rhs_out)
+    {
+      rhs_out[0] = unwidened_rhs1;
+      rhs_out[1] = unwidened_rhs2;
+    }
+
+  if (use_widen_mult_p)
+    {
+      gimple_assign_set_rhs1 (stmt, unwidened_rhs1);
+      gimple_assign_set_rhs2 (stmt, unwidened_rhs2);
+      gimple_assign_set_rhs_code (stmt, WIDEN_MULT_EXPR);
+      update_stmt (stmt);
+    }
   return true;
 }
 
@@ -1364,14 +1396,15 @@ convert_plusminus_to_widen (gimple_stmt_
 {
   gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
   tree type;
-  tree lhs, rhs1, rhs2, mult_rhs1, mult_rhs2, add_rhs;
+  tree lhs, rhs1, rhs2, mult_rhs[2], 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)
+  if (TREE_CODE (type) != INTEGER_TYPE
+      && TREE_CODE (type) != FIXED_POINT_TYPE)
     return false;
 
   if (code == MINUS_EXPR)
@@ -1407,29 +1440,28 @@ convert_plusminus_to_widen (gimple_stmt_
   else
     return false;
 
-  if (rhs1_code == MULT_EXPR)
+  if (code == PLUS_EXPR && rhs1_code == MULT_EXPR)
     {
-      if (!convert_mult_to_widen (rhs1_stmt))
+      if (!convert_mult_to_widen (rhs1_stmt, mult_rhs))
 	return false;
-      rhs1_code = gimple_assign_rhs_code (rhs1_stmt);
+      add_rhs = rhs2;
     }
-  if (rhs2_code == MULT_EXPR)
+  else if (rhs2_code == MULT_EXPR)
     {
-      if (!convert_mult_to_widen (rhs2_stmt))
+      if (!convert_mult_to_widen (rhs2_stmt, mult_rhs))
 	return false;
-      rhs2_code = gimple_assign_rhs_code (rhs2_stmt);
+      add_rhs = rhs1;
     }
-  
-  if (code == PLUS_EXPR && rhs1_code == WIDEN_MULT_EXPR)
+  else if (code == PLUS_EXPR && rhs1_code == WIDEN_MULT_EXPR)
     {
-      mult_rhs1 = gimple_assign_rhs1 (rhs1_stmt);
-      mult_rhs2 = gimple_assign_rhs2 (rhs1_stmt);
+      mult_rhs[0] = gimple_assign_rhs1 (rhs1_stmt);
+      mult_rhs[1] = gimple_assign_rhs2 (rhs1_stmt);
       add_rhs = rhs2;
     }
   else if (rhs2_code == WIDEN_MULT_EXPR)
     {
-      mult_rhs1 = gimple_assign_rhs1 (rhs2_stmt);
-      mult_rhs2 = gimple_assign_rhs2 (rhs2_stmt);
+      mult_rhs[0] = gimple_assign_rhs1 (rhs2_stmt);
+      mult_rhs[1] = gimple_assign_rhs2 (rhs2_stmt);
       add_rhs = rhs1;
     }
   else
@@ -1437,7 +1469,7 @@ convert_plusminus_to_widen (gimple_stmt_
 
   /* ??? May need some type verification here?  */
 
-  gimple_assign_set_rhs_with_ops_1 (gsi, wmult_code, mult_rhs1, mult_rhs2,
+  gimple_assign_set_rhs_with_ops_1 (gsi, wmult_code, mult_rhs[0], mult_rhs[1],
 				    add_rhs);
   update_stmt (gsi_stmt (*gsi));
   return true;
@@ -1467,7 +1499,7 @@ execute_optimize_widening_mul (void)
 
 	  code = gimple_assign_rhs_code (stmt);
 	  if (code == MULT_EXPR)
-	    changed |= convert_mult_to_widen (stmt);
+	    changed |= convert_mult_to_widen (stmt, NULL);
 	  else if (code == PLUS_EXPR || code == MINUS_EXPR)
 	    changed |= convert_plusminus_to_widen (&gsi, stmt, code);
 	}
