diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index bb1d5ab39c1590f3cdadc7cc6c66bf6bb5103171..b3ab01ee76669890fdb1ef52bb12ffd1ef04a07d 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2005-11-20  Bernd Schmidt  <bernd.schmidt@analog.com>
+
+	* expr.c (expand_expr_real): Use usmul_optab for widening
+	signed * unsigned multiplies.
+	* genopinit.c (optabs): Add usmul_widen_optab.
+	* optabs.c (init_optabs): Likewise.
+	* optabs.h (enum optab_index): Add OTI_usmul_widen.
+	(usmul_widen_optab): Define.
+	* config/bfin/bfin.md (usmulhisi3): New pattern.
+
+	* doc/md.texi (usmulqihi3, usmulhisi3, usmulsidi3): Document.
+
 2005-11-20  Graham Stott <btinternet.com>
 
 	* gensupport.c (std_preds): Fixed extraneous `false` in last change.
diff --git a/gcc/config/bfin/bfin.md b/gcc/config/bfin/bfin.md
index 494b27e3730cbbd7f6ff1f4282914e504528cbd9..63fc99ca1fe7b31055897d4ebbbc5d948738376e 100644
--- a/gcc/config/bfin/bfin.md
+++ b/gcc/config/bfin/bfin.md
@@ -908,6 +908,14 @@
   "%0 = %h1 * %h2 (FU);"
   [(set_attr "type" "dsp32")])
 
+(define_insn "usmulhisi3"
+  [(set (match_operand:SI 0 "register_operand" "=W")
+	(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "W"))
+		 (sign_extend:SI (match_operand:HI 2 "register_operand" "W"))))]
+  ""
+  "%0 = %h2 * %h1 (IS,M);"
+  [(set_attr "type" "dsp32")])
+
 ;; The processor also supports ireg += mreg or ireg -= mreg, but these
 ;; are unusable if we don't ensure that the corresponding lreg is zero.
 ;; The same applies to the add/subtract constant versions involving
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 087f4441cc83ddbf69b069d8c0f87271305ccf3a..115de4a2aba4e21a8383baac281ab9b8e9438403 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -3125,6 +3125,14 @@ Similar widening-multiplication instructions of other widths.
 Similar widening-multiplication instructions that do unsigned
 multiplication.
 
+@cindex @code{usmulqihi3} instruction pattern
+@cindex @code{usmulhisi3} instruction pattern
+@cindex @code{usmulsidi3} instruction pattern
+@item @samp{usmulqihi3}, @samp{usmulhisi3}, @samp{usmulsidi3}
+Similar widening-multiplication instructions that interpret the first
+operand as unsigned and the second operand as signed, then do a signed
+multiplication.
+
 @cindex @code{smul@var{m}3_highpart} instruction pattern
 @item @samp{smul@var{m}3_highpart}
 Perform a signed multiplication of operands 1 and 2, which have mode
diff --git a/gcc/expr.c b/gcc/expr.c
index 13dd782588ef922680e9bb998972e8cb228b5499..60582c91db6c13c8cb0688cadc672612f74654e8 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6543,7 +6543,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
   optab this_optab;
   rtx subtarget, original_target;
   int ignore;
-  tree context;
+  tree context, subexp0, subexp1;
   bool reduce_bit_field = false;
 #define REDUCE_BIT_FIELD(expr)	(reduce_bit_field && !ignore		  \
 				 ? reduce_to_bit_field_precision ((expr), \
@@ -7824,7 +7824,43 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	 from a narrower type.  If this machine supports multiplying
 	 in that narrower type with a result in the desired type,
 	 do it that way, and avoid the explicit type-conversion.  */
-      if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR
+
+      subexp0 = TREE_OPERAND (exp, 0);
+      subexp1 = TREE_OPERAND (exp, 1);
+      /* First, check if we have a multiplication of one signed and one
+	 unsigned operand.  */
+      if (TREE_CODE (subexp0) == NOP_EXPR
+	  && TREE_CODE (subexp1) == NOP_EXPR
+	  && TREE_CODE (type) == INTEGER_TYPE
+	  && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subexp0, 0)))
+	      < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))
+	  && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subexp0, 0)))
+	      == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subexp1, 0))))
+	  && (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subexp0, 0)))
+	      != TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subexp1, 0)))))
+	{
+	  enum machine_mode innermode
+	    = TYPE_MODE (TREE_TYPE (TREE_OPERAND (subexp0, 0)));
+	  this_optab = usmul_widen_optab;
+	  if (mode == GET_MODE_WIDER_MODE (innermode))
+	    {
+	      if (this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+		{
+		  if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subexp0, 0))))
+		    expand_operands (TREE_OPERAND (subexp0, 0),
+				     TREE_OPERAND (subexp1, 0),
+				     NULL_RTX, &op0, &op1, 0);
+		  else
+		    expand_operands (TREE_OPERAND (subexp0, 0),
+				     TREE_OPERAND (subexp1, 0),
+				     NULL_RTX, &op1, &op0, 0);
+
+		  goto binop2;
+		}
+	    }
+	}
+      /* Check for a multiplication with matching signedness.  */
+      else if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR
 	  && TREE_CODE (type) == INTEGER_TYPE
 	  && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
 	      < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))
diff --git a/gcc/genopinit.c b/gcc/genopinit.c
index 8bf574743e96a172b50e0109b14a7e85e2ff872a..c48d07979bd28b98a19f36b0853fa65217eafc39 100644
--- a/gcc/genopinit.c
+++ b/gcc/genopinit.c
@@ -84,6 +84,7 @@ static const char * const optabs[] =
   "smul_highpart_optab->handlers[$A].insn_code = CODE_FOR_$(smul$a3_highpart$)",
   "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",
   "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$)",
diff --git a/gcc/optabs.c b/gcc/optabs.c
index d3e1db69eddbbc7a0e06fbdb3a50d4b9c3e14364..b10dfa4b1319cbfedaf0c79b5083643bb1a71b1f 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -5010,6 +5010,7 @@ init_optabs (void)
   umul_highpart_optab = init_optab (UNKNOWN);
   smul_widen_optab = init_optab (UNKNOWN);
   umul_widen_optab = init_optab (UNKNOWN);
+  usmul_widen_optab = init_optab (UNKNOWN);
   sdiv_optab = init_optab (DIV);
   sdivv_optab = init_optabv (DIV);
   sdivmod_optab = init_optab (UNKNOWN);
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 582684f40381d13df7ceff75171d43504710858e..78cf53b15ff117cc150d6fdf8e6190597ef99927 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -82,6 +82,8 @@ enum optab_index
   /* Signed multiply with result one machine mode wider than args */
   OTI_smul_widen,
   OTI_umul_widen,
+  /* Widening multiply of one unsigned and one signed operand.  */
+  OTI_usmul_widen,
 
   /* Signed divide */
   OTI_sdiv,
@@ -268,6 +270,7 @@ extern GTY(()) optab optab_table[OTI_MAX];
 #define umul_highpart_optab (optab_table[OTI_umul_highpart])
 #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 sdiv_optab (optab_table[OTI_sdiv])
 #define smulv_optab (optab_table[OTI_smulv])
 #define sdivv_optab (optab_table[OTI_sdivv])