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

gcc/

2012-09-08  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-20120907/gcc/config/arm/arm.c.~1~	2012-09-05 12:54:08.000000000 +0200
+++ gcc-4.6-20120907/gcc/config/arm/arm.c	2012-09-08 12:51:19.000000000 +0200
@@ -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);
@@ -2318,7 +2318,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 ...  */
@@ -14189,7 +14189,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])
@@ -14837,8 +14837,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;
@@ -15212,9 +15211,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;
 
@@ -15223,7 +15222,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
@@ -15487,7 +15486,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;
@@ -20444,7 +20443,7 @@ thumb1_extra_regs_pushed (arm_stack_offs
   unsigned long l_mask = live_regs_mask & (for_prologue ? 0x40ff : 0xff);
   /* Then count how many other high registers will need to be pushed.  */
   unsigned long high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
-  int n_free, reg_base, size;
+  int n_free, reg_base;
 
   if (!for_prologue && frame_pointer_needed)
     amount = offsets->locals_base - offsets->saved_regs;
@@ -20483,8 +20482,7 @@ thumb1_extra_regs_pushed (arm_stack_offs
   n_free = 0;
   if (!for_prologue)
     {
-      size = arm_size_return_regs ();
-      reg_base = ARM_NUM_INTS (size);
+      reg_base = arm_number_return_regs ();
       live_regs_mask >>= reg_base;
     }
 
@@ -20516,7 +20514,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 "";
@@ -20532,13 +20530,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 << ARM_NUM_INTS (size);
+      live_regs_mask |= extra_mask << nretregs;
     }
 
   /* The prolog may have pushed some high registers to use as
@@ -20558,9 +20556,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)
@@ -20639,7 +20637,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,
@@ -20650,7 +20648,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,
