diff mbox

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

Message ID 201311182007.rAIK7Xhx024773@d06av02.portsmouth.uk.ibm.com
State New
Headers show

Commit Message

Ulrich Weigand Nov. 18, 2013, 8:07 p.m. UTC
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.

Comments

David Edelsohn Nov. 20, 2013, 4:12 p.m. UTC | #1
On Mon, Nov 18, 2013 at 3:07 PM, Ulrich Weigand <uweigand@de.ibm.com> wrote:


> 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.

Does this require special support in GDB so that it understands how to
interpret this type in different classes of registers?  However, the
interpretation always will be consistent for a particular class of
register.

> 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.

Okay.

Thanks, David
diff mbox

Patch

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