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]

[PATCH, rs6000] Fix order of TDmode in FPRs in little-endian mode


Hello,

when loading a TDmode value into floating-point registers, they need to go
into a register pair with the even register holding the most significant
word and the odd register holding the least significant word, because this
is what the instruction set expects.  This hold for both big-endian and
little-endian mode.

However, in little-endian mode, the usual subreg logic will place the
least significant word into the lower-numbered register, which is wrong.
This causes all _Decimal128 tests to fail when using a --with-cpu that
includes DFP instructions.

To fix this, the patch below:

- disables subregs of TDmode in FPRs in little-endian mode in
  rs6000_cannot_change_mode_class, to ensure nobody is using the
  generic subreg logic for such values, and

- changes rs6000_split_multireg_move to avoid using simplify_gen_subreg
  on such values, but instead create the correct register numbers
  "by hand"

Note that this patch implies an ABI change on little-endian for passing
_Decimal128 values in register pairs.

Also note that this patch does not change how TDmode values are loaded
into GPRs: on little-endian, this means we do get the usual LE subreg
order there (least significant word in lowest-numbered register).  This
does still seem the right thing to do, because there are no instructions
that operate on DFP values in GPRs, and because this allows us to keep
using subregs on GPRs in that case, and it allows moving TDmode between
memory and GPRs (e.g. for vararg arguments) in the usual way.


Tested on powerpc64le-linux, fixes all remaining DFP-related test suite
failures.

OK for mainline?

Bye,
Ulrich


ChangeLog:

	* config/rs6000/rs6000.c (rs6000_cannot_change_mode_class): Do not
	allow subregs of TDmode in FPRs of smaller size in little-endian.
	(rs6000_split_multireg_move): When splitting an access to TDmode
	in FPRs, do not use simplify_gen_subreg.

Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 204948)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -16664,6 +16664,13 @@
 	  if (TARGET_IEEEQUAD && (to == TFmode || from == TFmode))
 	    return true;
 
+	  /* TDmode in floating-mode registers must always go into a register
+	     pair with the most significant word in the even-numbered register
+	     to match ISA requirements.  In little-endian mode, this does not
+	     match subreg numbering, so we cannot allow subregs.  */
+	  if (!BYTES_BIG_ENDIAN && (to == TDmode || from == TDmode))
+	    return true;
+
 	  if (from_size < 8 || to_size < 8)
 	    return true;
 
@@ -19606,6 +19613,39 @@
 
   gcc_assert (reg_mode_size * nregs == GET_MODE_SIZE (mode));
 
+  /* TDmode residing in FP registers is special, since the ISA requires that
+     the lower-numbered word of a register pair is always the most significant
+     word, even in little-endian mode.  This does not match the usual subreg
+     semantics, so we cannnot use simplify_gen_subreg in those cases.  Access
+     the appropriate constituent registers "by hand" in little-endian mode.
+
+     Note we do not need to check for destructive overlap here since TDmode
+     can only reside in even/odd register pairs.  */
+  if (FP_REGNO_P (reg) && DECIMAL_FLOAT_MODE_P (mode) && !BYTES_BIG_ENDIAN)
+    {
+      rtx p_src, p_dst;
+      int i;
+
+      for (i = 0; i < nregs; i++)
+	{
+	  if (REG_P (src) && FP_REGNO_P (REGNO (src)))
+	    p_src = gen_rtx_REG (reg_mode, REGNO (src) + nregs - 1 - i);
+	  else
+	    p_src = simplify_gen_subreg (reg_mode, src, mode,
+					 i * reg_mode_size);
+
+	  if (REG_P (dst) && FP_REGNO_P (REGNO (dst)))
+	    p_dst = gen_rtx_REG (reg_mode, REGNO (dst) + nregs - 1 - i);
+	  else
+	    p_dst = simplify_gen_subreg (reg_mode, dst, mode,
+					 i * reg_mode_size);
+
+	  emit_insn (gen_rtx_SET (VOIDmode, p_dst, p_src));
+	}
+
+      return;
+    }
+
   if (REG_P (src) && REG_P (dst) && (REGNO (src) < REGNO (dst)))
     {
       /* Move register range backwards, if we might have destructive
-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com


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