[fix PR49180 wrong-code on Thumb, not yet submitted upstream, rebased 2011-12-11
 due to PR50106 fix on 4.6 branch <mikpe@it.uu.se>]

gcc/

2011-07-05  Mikael Pettersson  <mikpe@it.uu.se>

	PR target/49180
	* config/arm/arm.c (arm_size_return_regs): Rename to
	arm_number_return_regs.  Convert mode size to number of
	registers, including any partially used last register.
	(use_return_insn): Adjust to use arm_number_return_regs.
	(thumb1_compute_save_reg_mask): Likewise.
	(arm_output_epilogue): Likewise.
	(arm_get_frame_offsets): Likewise.
	(thumb1_extra_regs_pushed): Likewise.
	(thumb_unexpanded_epilogue): Likewise.

--- gcc-4.6.2/gcc/config/arm/arm.c.~1~	2011-10-20 11:24:06.000000000 +0200
+++ gcc-4.6.2/gcc/config/arm/arm.c	2011-12-11 20:20:31.000000000 +0100
@@ -83,7 +83,7 @@ static int thumb_far_jump_used_p (void);
 static bool thumb_force_lr_save (void);
 static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code);
 static rtx emit_sfm (int, int);
-static unsigned arm_size_return_regs (void);
+static unsigned arm_number_return_regs (void);
 static bool arm_assemble_integer (rtx, unsigned int, int);
 static void arm_print_operand (FILE *, rtx, int);
 static void arm_print_operand_address (FILE *, rtx);
@@ -2313,7 +2313,7 @@ use_return_insn (int iscond, rtx sibling
 	return 0;
 
       /* ... that it isn't being used for a return value ... */
-      if (arm_size_return_regs () >= (4 * UNITS_PER_WORD))
+      if (arm_number_return_regs () >= 4)
 	return 0;
 
       /* ... or for a tail-call argument ...  */
@@ -14140,7 +14140,7 @@ thumb1_compute_save_reg_mask (void)
       reg = thumb_find_work_register (1 << LAST_LO_REGNUM);
       /* Make sure the register returned by thumb_find_work_register is
 	 not part of the return value.  */
-      if (reg * UNITS_PER_WORD <= (unsigned) arm_size_return_regs ())
+      if (reg <= arm_number_return_regs ())
 	reg = LAST_LO_REGNUM;
 
       if (! call_used_regs[reg])
@@ -14788,8 +14788,7 @@ arm_output_epilogue (rtx sibling)
 		  && !crtl->tail_call_emit)
 		{
 		  unsigned long mask;
-                  /* Preserve return values, of any size.  */
-		  mask = (1 << ((arm_size_return_regs() + 3) / 4)) - 1;
+		  mask = (1 << arm_number_return_regs ()) - 1;
 		  mask ^= 0xf;
 		  mask &= ~saved_regs_mask;
 		  reg = 0;
@@ -15163,9 +15162,9 @@ emit_multi_reg_push (unsigned long mask)
   return par;
 }
 
-/* Calculate the size of the return value that is passed in registers.  */
+/* Calculate the number of registers needed for passing the return value.  */
 static unsigned
-arm_size_return_regs (void)
+arm_number_return_regs (void)
 {
   enum machine_mode mode;
 
@@ -15174,7 +15173,7 @@ arm_size_return_regs (void)
   else
     mode = DECL_MODE (DECL_RESULT (current_function_decl));
 
-  return GET_MODE_SIZE (mode);
+  return (GET_MODE_SIZE (mode) + 3) / 4;
 }
 
 static rtx
@@ -15438,7 +15437,7 @@ arm_get_frame_offsets (void)
 	     generates better code on Thumb-2 by avoiding the need to
 	     use 32-bit push/pop instructions.  */
  	  if (! any_sibcall_uses_r3 ()
-	      && arm_size_return_regs () <= 12
+	      && arm_number_return_regs () <= 3
 	      && (offsets->saved_regs_mask & (1 << 3)) == 0)
 	    {
 	      reg = 3;
@@ -20439,7 +20438,7 @@ thumb1_extra_regs_pushed (arm_stack_offs
   n_free = 0;
   if (!for_prologue)
     {
-      reg_base = arm_size_return_regs () / UNITS_PER_WORD;
+      reg_base = arm_number_return_regs ();
       live_regs_mask >>= reg_base;
     }
 
@@ -20471,7 +20470,7 @@ thumb_unexpanded_epilogue (void)
   int high_regs_pushed = 0;
   int extra_pop;
   int had_to_push_lr;
-  int size;
+  int nretregs;
 
   if (cfun->machine->return_used_this_function != 0)
     return "";
@@ -20487,14 +20486,13 @@ thumb_unexpanded_epilogue (void)
      This is more reliable that examining df_regs_ever_live_p () because that
      will be set if the register is ever used in the function, not just if
      the register is used to hold a return value.  */
-  size = arm_size_return_regs ();
+  nretregs = arm_number_return_regs ();
 
   extra_pop = thumb1_extra_regs_pushed (offsets, false);
   if (extra_pop > 0)
     {
       unsigned long extra_mask = (1 << extra_pop) - 1;
-      live_regs_mask |= extra_mask << ((size + UNITS_PER_WORD - 1) 
-				       / UNITS_PER_WORD);
+      live_regs_mask |= extra_mask << nretregs;
     }
 
   /* The prolog may have pushed some high registers to use as
@@ -20514,9 +20512,9 @@ thumb_unexpanded_epilogue (void)
 
       /* The available low registers depend on the size of the value we are
          returning.  */
-      if (size <= 12)
+      if (nretregs <= 3)
 	mask |=  1 << 3;
-      if (size <= 8)
+      if (nretregs <= 2)
 	mask |= 1 << 2;
 
       if (mask == 0)
@@ -20595,7 +20593,7 @@ thumb_unexpanded_epilogue (void)
 
       if (had_to_push_lr)
 	{
-	  if (size > 12)
+	  if (nretregs > 3)
 	    {
 	      /* We have no free low regs, so save one.  */
 	      asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", IP_REGNUM,
@@ -20606,7 +20604,7 @@ thumb_unexpanded_epilogue (void)
 	  thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0, NULL,
 			 1 << LAST_ARG_REGNUM);
 
-	  if (size > 12)
+	  if (nretregs > 3)
 	    {
 	      /* Move the return address to lr.  */
 	      asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LR_REGNUM,
