[committed] Fix PR34042: returning packed single-long-double structs on n32 and n64

Richard Sandiford rsandifo@nildram.co.uk
Mon Nov 12 21:11:00 GMT 2007


PR 34042 is a long-latent bug exposed by my __divdc3 fix from September:

    http://gcc.gnu.org/ml/gcc-patches/2007-09/msg02112.html

If the field in a single-field structure has a floating-point type,
n32 and n64 code returns that field in FPRs.  However, the backend was
picking the wrong mode for the register: it was using the structure's
mode rather than the field's mode.  The two can be different if the
structure is packed, in which case the structure itself has BLKmode.
We would then return a BLKmode FPR, which the target-independent code
would access using whatever integer modes it thought was appropriate.

As explained in the message linked above, it isn't valid to access FPR
values in a different mode like this.  In the PR we actually created an
invalid register -- (reg:TI $f0) -- and then ICEd when trying to move it.
The fix is to return the structure as a single-element PARALLEL instead.
(This actually produces better code for this example.)

Tested on mips-linux-gnu and mipsisa64-elfoabi.  F-X also tested it
on mips-sgi-irix6.5 (thanks!) and reported no regressions.  Applied
to trunk.

The PR was taken from struct-layout-1.exp, so new tests are necessary.

Richard


gcc/
	PR target/34042
	* config/mips/mips.c (mips_return_fpr_single): New function.
	(mips_function_value): Use it when returning single-field
	aggregates in FPRs.
	(mips_expand_call): Handle the PARALLELs created by
	mips_return_fpr_single.

Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	2007-11-11 10:59:30.000000000 +0000
+++ gcc/config/mips/mips.c	2007-11-11 13:48:24.000000000 +0000
@@ -4451,6 +4451,31 @@ mips_return_mode_in_fpr_p (enum machine_
 	  && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
 }
 
+/* Return the representation of an FPR return register when the
+   value being returned in FP_RETURN has mode VALUE_MODE and the
+   return type itself has mode TYPE_MODE.  On NewABI targets,
+   the two modes may be different for structures like:
+
+       struct __attribute__((packed)) foo { float f; }
+
+   where we return the SFmode value of "f" in FP_RETURN, but where
+   the structure itself has mode BLKmode.  */
+
+static rtx
+mips_return_fpr_single (enum machine_mode type_mode,
+			enum machine_mode value_mode)
+{
+  rtx x;
+
+  x = gen_rtx_REG (value_mode, FP_RETURN);
+  if (type_mode != value_mode)
+    {
+      x = gen_rtx_EXPR_LIST (VOIDmode, x, const0_rtx);
+      x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x));
+    }
+  return x;
+}
+
 /* Return a composite value in a pair of floating-point registers.
    MODE1 and OFFSET1 are the mode and byte offset for the first value,
    likewise MODE2 and OFFSET2 for the second.  MODE is the mode of the
@@ -4502,7 +4527,8 @@ mips_function_value (const_tree valtype,
       switch (mips_fpr_return_fields (valtype, fields))
 	{
 	case 1:
-	  return gen_rtx_REG (mode, FP_RETURN);
+	  return mips_return_fpr_single (mode,
+					 TYPE_MODE (TREE_TYPE (fields[0])));
 
 	case 2:
 	  return mips_return_fpr_pair (mode,
@@ -5536,6 +5562,7 @@ mips_expand_call (rtx result, rtx addr, 
 	       : gen_call_internal (addr, args_size));
   else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
     {
+      /* Handle return values created by mips_return_fpr_pair.  */
       rtx reg1, reg2;
 
       reg1 = XEXP (XVECEXP (result, 0, 0), 0);
@@ -5546,9 +5573,14 @@ mips_expand_call (rtx result, rtx addr, 
 	 : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
     }
   else
-    pattern = (sibcall_p
-	       ? gen_sibcall_value_internal (result, addr, args_size)
-	       : gen_call_value_internal (result, addr, args_size));
+    {
+      /* Handle return values created by mips_return_fpr_single.  */
+      if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
+	result = XEXP (XVECEXP (result, 0, 0), 0);
+      pattern = (sibcall_p
+		 ? gen_sibcall_value_internal (result, addr, args_size)
+		 : gen_call_value_internal (result, addr, args_size));
+    }
 
   insn = emit_call_insn (pattern);
 



More information about the Gcc-patches mailing list