Index: optabs.c
===================================================================
--- optabs.c	(revision 123998)
+++ optabs.c	(working copy)
@@ -5439,6 +5439,8 @@
   smul_widen_optab = init_optab (UNKNOWN);
   umul_widen_optab = init_optab (UNKNOWN);
   usmul_widen_optab = init_optab (UNKNOWN);
+  smadd_widen_optab = init_optab (UNKNOWN);
+  umadd_widen_optab = init_optab (UNKNOWN);
   sdiv_optab = init_optab (DIV);
   sdivv_optab = init_optabv (DIV);
   sdivmod_optab = init_optab (UNKNOWN);
Index: optabs.h
===================================================================
--- optabs.h	(revision 123998)
+++ optabs.h	(working copy)
@@ -86,6 +86,10 @@
   OTI_umul_widen,
   /* Widening multiply of one unsigned and one signed operand.  */
   OTI_usmul_widen,
+  /* Signed multiply and add with result one machine mode wider than args */
+  OTI_smadd_widen,
+  /* Unigned multiply and add with result one machine mode wider than args */
+  OTI_umadd_widen,
 
   /* Signed divide */
   OTI_sdiv,
@@ -307,6 +311,8 @@
 #define smul_widen_optab (optab_table[OTI_smul_widen])
 #define umul_widen_optab (optab_table[OTI_umul_widen])
 #define usmul_widen_optab (optab_table[OTI_usmul_widen])
+#define smadd_widen_optab (optab_table[OTI_smadd_widen])
+#define umadd_widen_optab (optab_table[OTI_umadd_widen])
 #define sdiv_optab (optab_table[OTI_sdiv])
 #define smulv_optab (optab_table[OTI_smulv])
 #define sdivv_optab (optab_table[OTI_sdivv])
Index: genopinit.c
===================================================================
--- genopinit.c	(revision 123998)
+++ genopinit.c	(working copy)
@@ -85,6 +85,8 @@
   "smul_widen_optab->handlers[$B].insn_code = CODE_FOR_$(mul$a$b3$)$N",
   "umul_widen_optab->handlers[$B].insn_code = CODE_FOR_$(umul$a$b3$)$N",
   "usmul_widen_optab->handlers[$B].insn_code = CODE_FOR_$(usmul$a$b3$)$N",
+  "smadd_widen_optab->handlers[$B].insn_code = CODE_FOR_$(madd$a$b4$)$N",
+  "umadd_widen_optab->handlers[$B].insn_code = CODE_FOR_$(umadd$a$b4$)$N",
   "sdiv_optab->handlers[$A].insn_code = CODE_FOR_$(div$a3$)",
   "sdivv_optab->handlers[$A].insn_code = CODE_FOR_$(div$V$I$a3$)",
   "udiv_optab->handlers[$A].insn_code = CODE_FOR_$(udiv$I$a3$)",
Index: expr.c
===================================================================
--- expr.c	(revision 123998)
+++ expr.c	(working copy)
@@ -6824,7 +6824,7 @@
 expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 		    enum expand_modifier modifier, rtx *alt_rtl)
 {
-  rtx op0, op1, temp, decl_rtl;
+  rtx op0, op1, op2, temp, decl_rtl;
   tree type;
   int unsignedp;
   enum machine_mode mode;
@@ -7977,6 +7977,80 @@
       return op0;
 
     case PLUS_EXPR:
+      /* Check if this is a case for multiplication and addition.  */
+      if (TREE_CODE (type) == INTEGER_TYPE
+	  && TREE_CODE (TREE_OPERAND (exp, 0)) == MULT_EXPR)
+	{
+	  subexp0 = TREE_OPERAND (exp, 0);
+	  tree subsubexp0 = TREE_OPERAND (subexp0, 0);
+	  tree subsubexp1 = TREE_OPERAND (subexp0, 1);
+	  enum tree_code code0 = TREE_CODE (subsubexp0);
+	  enum tree_code code1 = TREE_CODE (subsubexp1);
+	  if (code0 == NOP_EXPR && code1 == NOP_EXPR
+	      && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subsubexp0, 0)))
+		 < TYPE_PRECISION (TREE_TYPE (subsubexp0))
+	      && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subsubexp1, 0)))
+		 < TYPE_PRECISION (TREE_TYPE (subsubexp1))
+	      && TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subsubexp0, 0)))
+		 == TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subsubexp1, 0))))
+	    {
+	      tree op0type = TREE_TYPE (TREE_OPERAND (subsubexp0, 0));
+	      enum machine_mode innermode = TYPE_MODE (op0type);
+	      bool zextend_p = TYPE_UNSIGNED (op0type);
+	      this_optab = zextend_p ? umadd_widen_optab : smadd_widen_optab;
+	      if (mode == GET_MODE_2XWIDER_MODE (innermode)
+		  && this_optab->handlers[(int) mode].insn_code
+		     != CODE_FOR_nothing)
+		{
+		  int icode = this_optab->handlers[(int) mode].insn_code;
+		  enum machine_mode target_mode, mode0, mode1, mode2;
+		  rtx xop0, xop1, xop2;
+		  rtx last, pat, temp;
+
+		  expand_operands (TREE_OPERAND (subsubexp0, 0),
+				   TREE_OPERAND (subsubexp1, 0),
+				   NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+		  op2 = expand_expr (TREE_OPERAND (exp, 1), subtarget,
+				     VOIDmode, 0);
+		  target_mode = insn_data[icode].operand[0].mode;
+		  mode0 = insn_data[icode].operand[1].mode;
+		  mode1 = insn_data[icode].operand[2].mode;
+		  mode2 = insn_data[icode].operand[3].mode;
+		  xop0 = op0;
+		  xop1 = op1;
+		  xop2 = op2;
+		  last = get_last_insn ();
+
+		  if (target && modifier != EXPAND_STACK_PARM)
+		    temp = target;
+		  else
+		    temp = gen_reg_rtx (mode);
+
+		  if (!insn_data[icode].operand[0].predicate (temp,
+							      target_mode))
+		    temp = gen_reg_rtx (target_mode);
+		  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
+		      && mode0 != VOIDmode)
+		    xop0 = copy_to_mode_reg (mode0, xop0);
+		  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
+		      && mode1 != VOIDmode)
+		    xop1 = copy_to_mode_reg (mode1, xop1);
+		  if (!insn_data[icode].operand[3].predicate (xop2, mode2)
+		      && mode2 != VOIDmode)
+		    xop2 = copy_to_mode_reg (mode2, xop2);
+		  pat = GEN_FCN (icode) (temp, xop0, xop1, xop2);
+		  if (pat)
+		    {
+		      emit_insn (pat);
+		      return REDUCE_BIT_FIELD (temp);
+		    }
+		  else
+		    delete_insns_since (last);
+		}
+	    }
+	}
+
+
       /* If we are adding a constant, a VAR_DECL that is sp, fp, or ap, and
 	 something else, make sure we add the register to the constant and
 	 then to the other thing.  This case can occur during strength
Index: config/mips/mips-dspr2.md
===================================================================
--- config/mips/mips-dspr2.md	(revision 123998)
+++ config/mips/mips-dspr2.md	(working copy)
@@ -624,3 +624,29 @@
   [(set_attr "type"	"imadd")
    (set_attr "mode"	"SI")])
 
+(define_insn "maddsidi4"
+  [(set (match_operand:DI 0 "register_operand" "=a")
+	(plus:DI
+	 (mult:DI (sign_extend:DI
+		   (match_operand:SI 1 "register_operand" "d"))
+		  (sign_extend:DI
+		   (match_operand:SI 2 "register_operand" "d")))
+	 (match_operand:DI 3 "register_operand" "0")))]
+  "TARGET_DSPR2 && !TARGET_64BIT"
+  "madd\t%q0,%1,%2"
+  [(set_attr "type"	"imadd")
+   (set_attr "mode"	"SI")])
+
+(define_insn "umaddsidi4"
+  [(set (match_operand:DI 0 "register_operand" "=a")
+	(plus:DI
+	 (mult:DI (zero_extend:DI
+		   (match_operand:SI 1 "register_operand" "d"))
+		  (zero_extend:DI
+		   (match_operand:SI 2 "register_operand" "d")))
+	 (match_operand:DI 3 "register_operand" "0")))]
+  "TARGET_DSPR2 && !TARGET_64BIT"
+  "maddu\t%q0,%1,%2"
+  [(set_attr "type"	"imadd")
+   (set_attr "mode"	"SI")])
+
