[backport from gcc-4.7/trunk <mikpe@it.uu.se>]

gcc/

2011-06-01  Richard Sandiford  <rdsandiford@googlemail.com>

	PR target/45074
	* optabs.h (valid_multiword_target_p): Declare.
	* expmed.c (extract_bit_field_1): Check valid_multiword_target_p when
	doing multi-word operations.
	* optabs.c (expand_binop): Likewise.
	(expand_doubleword_bswap): Likewise.
	(expand_absneg_bit): Likewise.
	(expand_unop): Likewise.
	(expand_copysign_bit): Likewise.
	(multiword_target_p): New function.

gcc/testsuite/

2011-06-01  Richard Sandiford  <rdsandiford@googlemail.com>

	PR target/45074
	* gcc.target/mips/pr45074.c: New test.

--- gcc-4.6.1/gcc/expmed.c.~1~	2011-05-22 21:02:59.000000000 +0200
+++ gcc-4.6.1/gcc/expmed.c	2011-08-25 10:50:03.000000000 +0200
@@ -1431,7 +1431,7 @@ extract_bit_field_1 (rtx str_rtx, unsign
       unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
       unsigned int i;
 
-      if (target == 0 || !REG_P (target))
+      if (target == 0 || !REG_P (target) || !valid_multiword_target_p (target))
 	target = gen_reg_rtx (mode);
 
       /* Indicate for flow that the entire target reg is being set.  */
--- gcc-4.6.1/gcc/optabs.c.~1~	2011-03-04 11:27:10.000000000 +0100
+++ gcc-4.6.1/gcc/optabs.c	2011-08-25 10:54:25.000000000 +0200
@@ -1694,7 +1694,10 @@ expand_binop (enum machine_mode mode, op
 
       /* If TARGET is the same as one of the operands, the REG_EQUAL note
 	 won't be accurate, so use a new target.  */
-      if (target == 0 || target == op0 || target == op1)
+      if (target == 0
+	  || target == op0
+	  || target == op1
+	  || !valid_multiword_target_p (target))
 	target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -1762,7 +1765,10 @@ expand_binop (enum machine_mode mode, op
 
 	  /* If TARGET is the same as one of the operands, the REG_EQUAL note
 	     won't be accurate, so use a new target.  */
-	  if (target == 0 || target == op0 || target == op1)
+	  if (target == 0
+	      || target == op0
+	      || target == op1
+	      || !valid_multiword_target_p (target))
 	    target = gen_reg_rtx (mode);
 
 	  start_sequence ();
@@ -1816,7 +1822,11 @@ expand_binop (enum machine_mode mode, op
 	 opportunities, and second because if target and op0 happen to be MEMs
 	 designating the same location, we would risk clobbering it too early
 	 in the code sequence we generate below.  */
-      if (target == 0 || target == op0 || target == op1 || ! REG_P (target))
+      if (target == 0
+	  || target == op0
+	  || target == op1
+	  || !REG_P (target)
+	  || !valid_multiword_target_p (target))
 	target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -1936,7 +1946,7 @@ expand_binop (enum machine_mode mode, op
 
       xtarget = gen_reg_rtx (mode);
 
-      if (target == 0 || !REG_P (target))
+      if (target == 0 || !REG_P (target) || !valid_multiword_target_p (target))
 	target = xtarget;
 
       /* Indicate for flow that the entire target reg is being set.  */
@@ -2689,7 +2699,7 @@ expand_doubleword_bswap (enum machine_mo
   t0 = expand_unop (word_mode, bswap_optab,
 		    operand_subword_force (op, 1, mode), NULL_RTX, true);
 
-  if (target == 0)
+  if (target == 0 || !valid_multiword_target_p (target))
     target = gen_reg_rtx (mode);
   if (REG_P (target))
     emit_clobber (target);
@@ -2932,7 +2942,9 @@ expand_absneg_bit (enum rtx_code code, e
   if (code == ABS)
     mask = double_int_not (mask);
 
-  if (target == 0 || target == op0)
+  if (target == 0
+      || target == op0
+      || (nwords > 1 && !valid_multiword_target_p (target)))
     target = gen_reg_rtx (mode);
 
   if (nwords > 1)
@@ -3140,7 +3152,7 @@ expand_unop (enum machine_mode mode, opt
       int i;
       rtx insns;
 
-      if (target == 0 || target == op0)
+      if (target == 0 || target == op0 || !valid_multiword_target_p (target))
 	target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -3611,7 +3623,10 @@ expand_copysign_bit (enum machine_mode m
 
   mask = double_int_setbit (double_int_zero, bitpos);
 
-  if (target == 0 || target == op0 || target == op1)
+  if (target == 0
+      || target == op0
+      || target == op1
+      || (nwords > 1 && !valid_multiword_target_p (target)))
     target = gen_reg_rtx (mode);
 
   if (nwords > 1)
@@ -7330,4 +7345,21 @@ expand_sync_lock_test_and_set (rtx mem, 
   return NULL_RTX;
 }
 
+/* TARGET is a target of a multiword operation that we are going to
+   implement as a series of word-mode operations.  Return true if
+   TARGET is suitable for this purpose.  */
+
+bool
+valid_multiword_target_p (rtx target)
+{
+  enum machine_mode mode;
+  int i;
+
+  mode = GET_MODE (target);
+  for (i = 0; i < GET_MODE_SIZE (mode); i += UNITS_PER_WORD)
+    if (!validate_subreg (word_mode, mode, target, i))
+      return false;
+  return true;
+}
+
 #include "gt-optabs.h"
--- gcc-4.6.1/gcc/optabs.h.~1~	2011-01-03 21:52:22.000000000 +0100
+++ gcc-4.6.1/gcc/optabs.h	2011-08-25 10:52:37.000000000 +0200
@@ -923,4 +923,7 @@ set_direct_optab_handler (direct_optab o
 extern rtx optab_libfunc (optab optab, enum machine_mode mode);
 extern rtx convert_optab_libfunc (convert_optab optab, enum machine_mode mode1,
 			          enum machine_mode mode2);
+
+extern bool valid_multiword_target_p (rtx);
+
 #endif /* GCC_OPTABS_H */
--- gcc-4.6.1/gcc/testsuite/gcc.target/mips/pr45074.c.~1~	1970-01-01 01:00:00.000000000 +0100
+++ gcc-4.6.1/gcc/testsuite/gcc.target/mips/pr45074.c	2011-08-25 10:50:03.000000000 +0200
@@ -0,0 +1,8 @@
+/* { dg-options "-mhard-float -mgp32 -O" } */
+register double g __asm__("$f20");
+
+NOMIPS16 void
+test (double a)
+{
+  g = -a;
+}
