This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Subregs and objects with holes (new patch)


In r100700, Geoff Keating adapted subreg_offset_representable_p and
subreg_regno_offset to handle a case of objects with holes
<http://gcc.gnu.org/ml/gcc-patches/2005-06/msg00576.html> that only
applies on 32-bit x86 with -m128bit-long-double.

The logic chosen for determining whether an object has holes -
checking whether the integer type of the same size as its units takes
more registers than its units do - breaks when registers can store
different amounts of data depending on the mode they are being used to
store, as on E500.  This problem was reported in bug 24036.  David
Edelsohn had a patch, discussed in the thread starting at
<http://gcc.gnu.org/ml/gcc-patches/2006-08/msg00451.html>; Geoff
Keating had some objections to both the patch and the analysis
surrounding it.

As I note in the last comment on that bug, there appear to be more
problem cases than the original patch was designed to address.  I've
prepared this alternative patch.

This patch adds target macros to detect the case of objects with holes
and ensure that it only arises when intended.  Explicit detection is
added for the E500 case where registers store different amounts of
data in the different modes, making subreg_offset_representable_p return
false in the problem cases.

In order to avoid ICEs on tests using -mlong-double-128, it was
necessary to include the TFmode move support from David Edelsohn's
soft-float long double support patch (attached to glibc bug 2749) in
this patch, and to teach rs6000_split_multireg_move about TFmode in
the E500 double case.

This patch fixes the following test failures in testing for
powerpc-none-linux-gnuspe --enable-e500_double (with no regressions),
4.1, 4.2 and mainline:

FAIL: gcc.c-torture/execute/20040709-2.c execution,  -O1 
FAIL: gcc.c-torture/execute/20040709-2.c execution,  -O2 
FAIL: gcc.c-torture/execute/20040709-2.c execution,  -Os 

(more will also be fixed in conjunction with another pending patch
that depends on this one).  It also fixes the testcase given in bug
24036.  Bootstrapped with no regressions on i686-pc-linux-gnu.  OK to
commit to mainline, 4.2, 4.1?

2006-11-23  Joseph Myers  <joseph@codesourcery.com>
            David Edelsohn  <edelsohn@gnu.org>

	PR target/24036
	* doc/tm.texi (MODE_BIGGER_IN_MEMORY,
	HARD_REGNO_NREGS_WITH_PADDING): Document new target macros.
	* defaults.h (MODE_BIGGER_IN_MEMORY,
	HARD_REGNO_NREGS_WITH_PADDING): Define.
	* config/i386/i386.h (MODE_BIGGER_IN_MEMORY,
	HARD_REGNO_NREGS_WITH_PADDING): Define.
	* rtlanal.c (subreg_regno_offset, subreg_offset_representable_p):
	Use new macros to detect modes with holes; do not look at integer
	units.
	(subreg_offset_representable_p): Check for and disallow cases
	where the modes use different numbers of bits from registers.
	* config/rs6000/rs6000.c (rs6000_emit_move): Handle TFmode
	constant for soft-float.
	(rs6000_hard_regno_nregs): Use UNITS_PER_FP_WORD for e500 GPRs
	containing doubles.
	(rs6000_split_multireg_move): Use DFmode reg_mode for TFmode moves
	in E500 double case.
	* config/rs6000/rs6000.md (movtf): Allow soft-float.
	(movtf_softfloat): New.

Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi	(revision 119097)
+++ gcc/doc/tm.texi	(working copy)
@@ -1959,6 +1959,24 @@
 @end smallexample
 @end defmac
 
+@defmac MODE_BIGGER_IN_MEMORY (@var{mode}, @var{regno})
+A C expression that is nonzero if a value of mode @var{mode}, stored
+in memory, ends with padding that causes it to take up more space than
+in registers starting at register number @var{regno}.  By default this
+is zero.
+
+For example, if a floating-point value is stored in three 32-bit
+registers but takes up 128 bits in memory, then this would be
+nonzero.
+@end defmac
+
+@defmac HARD_REGNO_NREGS_WITH_PADDING (@var{regno}, @var{mode})
+For values of @var{regno} and @var{mode} for which
+@code{MODE_BIGGER_IN_MEMORY} returns nonzero, a C expression returning
+the greater number of registers required to hold the value including
+any padding.  In the example above, the value would be four.
+@end defmac
+
 @defmac REGMODE_NATURAL_SIZE (@var{mode})
 Define this macro if the natural size of registers that hold values
 of mode @var{mode} is not the word size.  It is a C expression that
Index: gcc/defaults.h
===================================================================
--- gcc/defaults.h	(revision 119097)
+++ gcc/defaults.h	(working copy)
@@ -879,4 +879,9 @@
 #define INCOMING_FRAME_SP_OFFSET 0
 #endif
 
+#ifndef MODE_BIGGER_IN_MEMORY
+#define MODE_BIGGER_IN_MEMORY(MODE, REGNO) 0
+#define HARD_REGNO_NREGS_WITH_PADDING(REGNO, MODE) -1
+#endif
+
 #endif  /* ! GCC_DEFAULTS_H */
Index: gcc/rtlanal.c
===================================================================
--- gcc/rtlanal.c	(revision 119097)
+++ gcc/rtlanal.c	(working copy)
@@ -3187,34 +3187,15 @@
 subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
 		     unsigned int offset, enum machine_mode ymode)
 {
-  int nregs_xmode, nregs_ymode, nregs_xmode_unit_int;
+  int nregs_xmode, nregs_ymode;
   int mode_multiple, nregs_multiple;
   int y_offset;
-  enum machine_mode xmode_unit, xmode_unit_int;
 
   gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
 
-  if (GET_MODE_INNER (xmode) == VOIDmode)
-    xmode_unit = xmode;
-  else
-    xmode_unit = GET_MODE_INNER (xmode);
-  
-  if (FLOAT_MODE_P (xmode_unit))
-    {
-      xmode_unit_int = int_mode_for_mode (xmode_unit);
-      if (xmode_unit_int == BLKmode)
-	/* It's probably bad to be here; a port should have an integer mode
-	   that's the same size as anything of which it takes a SUBREG.  */
-	xmode_unit_int = xmode_unit;
-    }
-  else
-    xmode_unit_int = xmode_unit;
-
-  nregs_xmode_unit_int = hard_regno_nregs[xregno][xmode_unit_int];
-
   /* Adjust nregs_xmode to allow for 'holes'.  */
-  if (nregs_xmode_unit_int != hard_regno_nregs[xregno][xmode_unit])
-    nregs_xmode = nregs_xmode_unit_int * GET_MODE_NUNITS (xmode);
+  if (MODE_BIGGER_IN_MEMORY (xmode, xregno))
+    nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode);
   else
     nregs_xmode = hard_regno_nregs[xregno][xmode];
     
@@ -3252,39 +3233,32 @@
 subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
 			       unsigned int offset, enum machine_mode ymode)
 {
-  int nregs_xmode, nregs_ymode, nregs_xmode_unit, nregs_xmode_unit_int;
+  int nregs_xmode, nregs_ymode;
   int mode_multiple, nregs_multiple;
   int y_offset;
-  enum machine_mode xmode_unit, xmode_unit_int;
+  int regsize_xmode, regsize_ymode;
 
   gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
 
-  if (GET_MODE_INNER (xmode) == VOIDmode)
-    xmode_unit = xmode;
-  else
-    xmode_unit = GET_MODE_INNER (xmode);
-  
-  if (FLOAT_MODE_P (xmode_unit))
-    {
-      xmode_unit_int = int_mode_for_mode (xmode_unit);
-      if (xmode_unit_int == BLKmode)
-	/* It's probably bad to be here; a port should have an integer mode
-	   that's the same size as anything of which it takes a SUBREG.  */
-	xmode_unit_int = xmode_unit;
-    }
-  else
-    xmode_unit_int = xmode_unit;
-
-  nregs_xmode_unit = hard_regno_nregs[xregno][xmode_unit];
-  nregs_xmode_unit_int = hard_regno_nregs[xregno][xmode_unit_int];
-
   /* If there are holes in a non-scalar mode in registers, we expect
      that it is made up of its units concatenated together.  */
-  if (nregs_xmode_unit != nregs_xmode_unit_int)
+  if (MODE_BIGGER_IN_MEMORY (xmode, xregno))
     {
-      gcc_assert (nregs_xmode_unit * GET_MODE_NUNITS (xmode)
-		  == hard_regno_nregs[xregno][xmode]);
+      enum machine_mode xmode_unit;
 
+      nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode);
+      if (GET_MODE_INNER (xmode) == VOIDmode)
+	xmode_unit = xmode;
+      else
+	xmode_unit = GET_MODE_INNER (xmode);
+      gcc_assert (MODE_BIGGER_IN_MEMORY (xmode_unit, xregno));
+      gcc_assert (nregs_xmode
+		  == (GET_MODE_NUNITS (xmode)
+		      * HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode_unit)));
+      gcc_assert (hard_regno_nregs[xregno][xmode]
+		  == (hard_regno_nregs[xregno][xmode_unit]
+		      * GET_MODE_NUNITS (xmode)));
+
       /* You can only ask for a SUBREG of a value with holes in the middle
 	 if you don't cross the holes.  (Such a SUBREG should be done by
 	 picking a different register class, or doing it in memory if
@@ -3293,15 +3267,12 @@
 	 3 for each part, but in memory it's two 128-bit parts.  
 	 Padding is assumed to be at the end (not necessarily the 'high part')
 	 of each unit.  */
-      if (nregs_xmode_unit != nregs_xmode_unit_int
-	  && (offset / GET_MODE_SIZE (xmode_unit_int) + 1 
-	      < GET_MODE_NUNITS (xmode))
-	  && (offset / GET_MODE_SIZE (xmode_unit_int) 
+      if ((offset / GET_MODE_SIZE (xmode_unit) + 1 
+	   < GET_MODE_NUNITS (xmode))
+	  && (offset / GET_MODE_SIZE (xmode_unit)
 	      != ((offset + GET_MODE_SIZE (ymode) - 1)
-		  / GET_MODE_SIZE (xmode_unit_int))))
+		  / GET_MODE_SIZE (xmode_unit))))
 	return false;
-
-      nregs_xmode = nregs_xmode_unit_int * GET_MODE_NUNITS (xmode);
     }
   else
     nregs_xmode = hard_regno_nregs[xregno][xmode];
@@ -3315,6 +3286,15 @@
 	  ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
     return true;
 
+  /* If registers store different numbers of bits in the different
+     modes, we cannot generally form this subreg.  */
+  regsize_xmode = GET_MODE_SIZE (xmode) / nregs_xmode;
+  regsize_ymode = GET_MODE_SIZE (ymode) / nregs_ymode;
+  if (regsize_xmode > regsize_ymode && nregs_ymode > 1)
+    return false;
+  if (regsize_ymode > regsize_xmode && nregs_xmode > 1)
+    return false;
+
   /* Lowpart subregs are otherwise valid.  */
   if (offset == subreg_lowpart_offset (ymode, xmode))
     return true;
Index: gcc/config/i386/i386.h
===================================================================
--- gcc/config/i386/i386.h	(revision 119097)
+++ gcc/config/i386/i386.h	(working copy)
@@ -831,6 +831,15 @@
       ? (TARGET_64BIT ? 4 : 6)						\
       : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)))
 
+#define MODE_BIGGER_IN_MEMORY(MODE, REGNO)				\
+  ((TARGET_128BIT_LONG_DOUBLE && !TARGET_64BIT)				\
+   ? (FP_REGNO_P (REGNO) || SSE_REGNO_P (REGNO) || MMX_REGNO_P (REGNO)	\
+      ? 0								\
+      : ((MODE) == XFmode || (MODE) == XCmode))				\
+   : 0)
+
+#define HARD_REGNO_NREGS_WITH_PADDING(REGNO, MODE) ((MODE) == XFmode ? 4 : 8)
+
 #define VALID_SSE2_REG_MODE(MODE) \
     ((MODE) == V16QImode || (MODE) == V8HImode || (MODE) == V2DFmode    \
      || (MODE) == V2DImode || (MODE) == DFmode)
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 119097)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -3570,9 +3570,6 @@
   if (FP_REGNO_P (regno))
     return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD;
 
-  if (TARGET_E500_DOUBLE && mode == DFmode)
-    return 1;
-
   if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode))
     return (GET_MODE_SIZE (mode) + UNITS_PER_SPE_WORD - 1) / UNITS_PER_SPE_WORD;
 
@@ -3580,6 +3577,9 @@
     return
       (GET_MODE_SIZE (mode) + UNITS_PER_ALTIVEC_WORD - 1) / UNITS_PER_ALTIVEC_WORD;
 
+  if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode)
+    return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD;
+
   return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 }
 
@@ -4187,8 +4198,7 @@
 
   /* 128-bit constant floating-point values on Darwin should really be
      loaded as two parts.  */
-  if (!TARGET_IEEEQUAD
-      && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128
+  if (!TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
       && mode == TFmode && GET_CODE (operands[1]) == CONST_DOUBLE)
     {
       /* DImode is used, not DFmode, because simplify_gen_subreg doesn't
@@ -12392,6 +12392,8 @@
     reg_mode = DFmode;
   else if (ALTIVEC_REGNO_P (reg))
     reg_mode = V16QImode;
+  else if (TARGET_E500_DOUBLE && mode == TFmode)
+    reg_mode = DFmode;
   else
     reg_mode = word_mode;
   reg_mode_size = GET_MODE_SIZE (reg_mode);
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 119085)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -8515,8 +8515,7 @@
 (define_expand "movtf"
   [(set (match_operand:TF 0 "general_operand" "")
 	(match_operand:TF 1 "any_operand" ""))]
-  "!TARGET_IEEEQUAD
-   && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128"
+  "!TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128"
   "{ rs6000_emit_move (operands[0], operands[1], TFmode); DONE; }")
 
 ; It's important to list the o->f and f->o moves before f->f because
@@ -8535,6 +8534,19 @@
 { rs6000_split_multireg_move (operands[0], operands[1]); DONE; }
   [(set_attr "length" "8,8,8,20,20,16")])
 
+(define_insn_and_split "*movtf_softfloat"
+  [(set (match_operand:TF 0 "nonimmediate_operand" "=r,Y,r")
+	(match_operand:TF 1 "input_operand"         "YGHF,r,r"))]
+  "!TARGET_IEEEQUAD
+   && (TARGET_SOFT_FLOAT || !TARGET_FPRS) && TARGET_LONG_DOUBLE_128
+   && (gpc_reg_operand (operands[0], TFmode)
+       || gpc_reg_operand (operands[1], TFmode))"
+  "#"
+  "&& reload_completed"
+  [(pc)]
+{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; }
+  [(set_attr "length" "20,20,16")])
+
 (define_expand "extenddftf2"
   [(parallel [(set (match_operand:TF 0 "nonimmediate_operand" "")
 		   (float_extend:TF (match_operand:DF 1 "input_operand" "")))

-- 
Joseph S. Myers
joseph@codesourcery.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]