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]

E500 long double support


This patch adds IBM long double support for E500 (both v1 and v2, but 
almost all the complexity is for v2; v1 is essentially just like the 
soft-float long double support).

This patch is based on mainline, *plus* David Edelsohn's soft-float long 
double support patch attached to glibc bug 2749 (some bits of which are 
now in mainline), *plus* my patch 
<http://gcc.gnu.org/ml/gcc-patches/2006-12/msg01387.html> to correct 
handling of some subregs that arise with this code.

My view is that the soft-float long double patch, minus the soft-fp 
change that can't go in because it isn't in FSF glibc, should go in trunk 
and 4.2 branch now.  It's well-tested, a lot safer than the long double 
changes that went into 4.1 branch shortly before 4.1 was released, and 
effectively is a regression fix regarding buildability of glibc.  The 
release notes for 4.2 should then have a link added to the soft-fp patch, 
to the effect that IBM long double support has been added for soft-float 
PowerPC, but an additional patch is needed.  This patch should then be 
considered for trunk, and potentially for 4.2 branch if it proves stable 
on trunk.

Tested with no regressions with a cross to powerpc-none-linux-gnuspe 
--enable-e500_double, also tested the gcc testsuite with 
-mlong-double-128, the 4.1 version also tested with building and testing 
glibc and GCC defaulting to 128-bit long double with the associated glibc 
port but of course you can't build glibc with GCC trunk at all at present.

2006-12-19  Joseph Myers  <joseph@codesourcery.com>

	* config/rs6000/rs6000-c.c (rs6000_cpu_cpp_builtins): Define
	_SOFT_DOUBLE if doubles use software floating-point.
	* config/rs6000/libgcc-ppc-glibc.ver: Export additional long
	double functions if _SOFT_DOUBLE, not _SOFT_FLOAT.
	* config/rs6000/darwin-ldouble.c: Also compile functions for
	hard-float without FPRs.  Use fmsub function for all __NO_FPRS__
	cases.  Compile extra functions if _SOFT_DOUBLE, not _SOFT_FLOAT.
	* config/rs6000/linuxspe.h (SUBSUBTARGET_OVERRIDE_OPTIONS): Remove
	commented-out long double override.
	(CPP_LONGDOUBLE_DEFAULT_SPEC): Likewise.
	* config/rs6000/eabispe.h: Likewise.
	* config/rs6000/rs6000.c (rs6000_override_options): Don't override
	long double for non-SPE.
	(rs6000_handle_option): Likewise.
	(invalid_e500_subreg): Disallow more subregs involding DImode,
	DFmode, TImode or TFmode.
	(rs6000_legitimate_offset_address_p): Check TFmode offsets for
	E500 double.
	(legitimate_lo_sum_address_p): Also check for TFmode for E500
	double.
	(rs6000_legitimize_address): Also handle TFmode for E500 double.
	(rs6000_legitimize_reload_address): Also handle TFmode for E500
	double.
	(rs6000_legitimate_address): Also check for TFmode for E500
	double.
	(rs6000_emit_move): Use DFmode subregs of TFmode for E500 double.
	(spe_build_register_parallel): Handle TFmode and TCmode.
	(rs6000_spe_function_arg): Handle TFmode and TCmode for E500
	double.
	(function_arg): Handle TFmode and TCmode for E500 double.
	(rs6000_init_libfuncs): Initialize extra libfuncs for soft double
	in general.
	(print_operand): Handle TFmode and TImode for %y.
	(rs6000_generate_compare): Handle TFmode comparisons for E500
	double.
	(spe_func_has_64bit_regs_p): Check for TFmode for E500 double.
	(rs6000_function_value): Handle TFmode and TCmode for E500 double.
	(rs6000_libcall_value): Handle TFmode and TCmode for E500 double.
	* config/rs6000/rs6000.h (CANNOT_CHANGE_MODE_CLASS): Check for
	TFmode for E500 double.
	* config/rs6000/rs6000.md (FP): Allow TF for E500 double.
	(floatsidf2): Enable for E500 double.
	(movtf_softfloat): Use rs6000_nonimmediate_operand.
	(extenddftf2): Change to extenddftf2_fprs.
	(extenddftf2): Call gen_spe_extenddftf2 or gen_extenddftf2_fprs
	depending on TARGET_E500_DOUBLE.
	(extendsftf2): Enable for E500 double.
	(trunctfdf2): Enable for E500 double.
	(trunctfsf2): Change to trunctfsf2_fprs.
	(trunctfsf2): Call gen_spe_trunctfsf2 or gen_trunctfsf2_fprs
	depending on TARGET_E500_DOUBLE.
	(floatsitf2): Enable for E500 double.
	(fix_trunctfsi2): Change to fix_trunctfsi2_fprs.
	(fix_trunctfsi2): Call gen_spe_fix_trunctfsi2 or
	gen_fix_trunctfsi2_fprs depending on TARGET_E500_DOUBLE.
	(negtf2): Change to negtf2_internal.
	(negtf2): New expander.
	(abstf2): Enable for E500 double.  Call gen_spe_abstf2_tst,
	gen_spe_abstf2_cmp or gen_abstf2_internal depending on
	TARGET_E500_DOUBLE and flag_unsafe_math_optimizations.
	(movdi_internal32): Use rs6000_nonimmediate_operand.
	(unnamed splitter): Likewise.
	* config/rs6000/spe.md (CMPTFEQ_GPR, TSTTFEQ_GPR, CMPTFGT_GPR,
	TSTTFGT_GPR, CMPTFLT_GPR, TSTTFLT_GPR): New unspecs.
	(SPE64TF, DITI): New mode macros.
	(frob_df_di): Change to frob_<SPE64:mode>_<DITI:mode>; allow more
	modes.
	(frob_tf_ti): New.
	(frob_<mode>_di_2): New.
	(frob_tf_di_8_2): New.
	(frob_di_df): Change to frob_di_<mode>; allow more modes.
	(frob_ti_tf): New.
	(frob_di_df_2): Change to frob_<DITI:mode>_<SPE64:mode>_2; allow
	more modes.
	(frob_ti_<mode>_8_2): New.
	(frob_ti_tf_2): New.
	(mov_si<mode>_e500_subreg0, mov_si<mode>_e500_subreg0_2,
	mov_si<mode>_e500_subreg4, mov_si<mode>_e500_subreg4_2): Allow
	TFmode.
	(mov_sitf_e500_subreg8, mov_sitf_e500_subreg8_2,
	mov_sitf_e500_subreg12, mov_sitf_e500_subreg12_2): New.
	(spe_trunctfdf2_internal1, spe_trunctfsf2, spe_extenddftf2,
	spe_fix_trunctfsi2, spe_fix_trunctfsi2_internal,
	spe_negtf2_internal, spe_abstf2_cmp, spe_abstf2_tst): New.
	(cmptfeq_gpr, tsttfeq_gpr, cmptfgt_gpr, tsttfgt_gpr, cmptflt_gpr,
	tsttflt_gp): New.

diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/darwin-ldouble.c gcc-4.3/gcc/config/rs6000/darwin-ldouble.c
--- gcc-4.3-nold/gcc/config/rs6000/darwin-ldouble.c	2006-12-18 15:49:35.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/darwin-ldouble.c	2006-12-18 16:16:12.000000000 -0800
@@ -49,8 +49,7 @@ Software Foundation, 51 Franklin Street,
 
    This code currently assumes big-endian.  */
 
-#if ((!defined (__NO_FPRS__) || defined (_SOFT_FLOAT)) \
-     && !defined (__LITTLE_ENDIAN__) \
+#if (!defined (__LITTLE_ENDIAN__) \
      && (defined (__MACH__) || defined (__powerpc__) || defined (_AIX)))
 
 #define fabs(x) __builtin_fabs(x)
@@ -145,7 +144,7 @@ __gcc_qsub (double a, double b, double c
   return __gcc_qadd (a, b, -c, -d);
 }
 
-#ifdef _SOFT_FLOAT
+#ifdef __NO_FPRS__
 static double fmsub (double, double, double);
 #endif
 
@@ -164,7 +163,7 @@ __gcc_qmul (double a, double b, double c
   /* Sum terms of two highest orders. */
   
   /* Use fused multiply-add to get low part of a * c.  */
-#ifndef _SOFT_FLOAT
+#ifndef __NO_FPRS__
   asm ("fmsub %0,%1,%2,%3" : "=f"(tau) : "f"(a), "f"(c), "f"(t));
 #else
   tau = fmsub (a, c, t);
@@ -201,7 +200,7 @@ __gcc_qdiv (double a, double b, double c
 			   numerically necessary.  */
   
   /* Use fused multiply-add to get low part of c * t.	 */
-#ifndef _SOFT_FLOAT
+#ifndef __NO_FPRS__
   asm ("fmsub %0,%1,%2,%3" : "=f"(sigma) : "f"(c), "f"(t), "f"(s));
 #else
   sigma = fmsub (c, t, s);
@@ -219,7 +218,7 @@ __gcc_qdiv (double a, double b, double c
   return z.ldval;
 }
 
-#ifdef _SOFT_FLOAT
+#ifdef _SOFT_DOUBLE
 
 long double __gcc_qneg (double, double);
 int __gcc_qeq (double, double, double, double);
@@ -362,6 +361,10 @@ __gcc_utoq (unsigned int a)
   return __gcc_dtoq ((double) a);
 }
 
+#endif
+
+#ifdef __NO_FPRS__
+
 #include "config/soft-fp/soft-fp.h"
 #include "config/soft-fp/double.h"
 #include "config/soft-fp/quad.h"
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/eabispe.h gcc-4.3/gcc/config/rs6000/eabispe.h
--- gcc-4.3-nold/gcc/config/rs6000/eabispe.h	2006-11-23 11:58:51.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/eabispe.h	2006-12-18 16:16:12.000000000 -0800
@@ -35,9 +35,6 @@
     rs6000_spe_abi = 1; \
   if (!rs6000_explicit_options.float_gprs) \
     rs6000_float_gprs = 1; \
-  /* See note below.  */ \
-  /*if (!rs6000_explicit_options.long_double)*/ \
-  /*  rs6000_long_double_type_size = 128;*/ \
   if (!rs6000_explicit_options.spe) \
     rs6000_spe = 1; \
   if (!rs6000_explicit_options.isel) \
@@ -52,8 +49,7 @@
    specifications, until I properly fix the emulation.
 
    Enable these later.
-#undef CPP_LONGDOUBLE_DEFAULT_SPEC
-#define CPP_LONGDOUBLE_DEFAULT_SPEC "-D__LONG_DOUBLE_128__=1"
+#define RS6000_DEFAULT_LONG_DOUBLE_SIZE (TARGET_SPE ? 128 : 64)
 */
 
 #undef  ASM_DEFAULT_SPEC
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/libgcc-ppc-glibc.ver gcc-4.3/gcc/config/rs6000/libgcc-ppc-glibc.ver
--- gcc-4.3-nold/gcc/config/rs6000/libgcc-ppc-glibc.ver	2006-12-18 15:49:35.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/libgcc-ppc-glibc.ver	2006-12-18 16:16:12.000000000 -0800
@@ -31,7 +31,7 @@ GCC_4.2.0 {
   __gcc_qmul
   __gcc_qdiv
 
-%ifdef _SOFT_FLOAT
+%ifdef _SOFT_DOUBLE
   __gcc_qneg
   __gcc_qeq
   __gcc_qne
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/linuxspe.h gcc-4.3/gcc/config/rs6000/linuxspe.h
--- gcc-4.3-nold/gcc/config/rs6000/linuxspe.h	2006-11-23 11:58:51.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/linuxspe.h	2006-12-18 16:16:12.000000000 -0800
@@ -51,9 +51,6 @@
     rs6000_spe_abi = 1; \
   if (!rs6000_explicit_options.float_gprs) \
     rs6000_float_gprs = 1; \
-  /* See note below.  */ \
-  /*if (!rs6000_explicit_options.long_double)*/ \
-  /*  rs6000_long_double_type_size = 128;*/ \
   if (!rs6000_explicit_options.spe) \
     rs6000_spe = 1; \
   if (!rs6000_explicit_options.isel) \
@@ -61,16 +58,5 @@
   if (target_flags & MASK_64BIT) \
     error ("-m64 not supported in this configuration")
 
-/* The e500 ABI says that either long doubles are 128 bits, or if
-   implemented in any other size, the compiler/linker should error out.
-   We have no emulation libraries for 128 bit long doubles, and I hate
-   the dozens of failures on the regression suite.  So I'm breaking ABI
-   specifications, until I properly fix the emulation.
-
-   Enable these later.
-#undef CPP_LONGDOUBLE_DEFAULT_SPEC
-#define CPP_LONGDOUBLE_DEFAULT_SPEC "-D__LONG_DOUBLE_128__=1"
-*/
-
 #undef  ASM_DEFAULT_SPEC
 #define	ASM_DEFAULT_SPEC "-mppc -mspe -me500"
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/rs6000-c.c gcc-4.3/gcc/config/rs6000/rs6000-c.c
--- gcc-4.3-nold/gcc/config/rs6000/rs6000-c.c	2006-11-16 13:28:29.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/rs6000-c.c	2006-12-18 16:16:12.000000000 -0800
@@ -124,6 +124,8 @@ rs6000_cpu_cpp_builtins (cpp_reader *pfi
     builtin_define ("__SPE__");
   if (TARGET_SOFT_FLOAT)
     builtin_define ("_SOFT_FLOAT");
+  if (!(TARGET_HARD_FLOAT && (TARGET_FPRS || TARGET_E500_DOUBLE)))
+    builtin_define ("_SOFT_DOUBLE");
   /* Used by lwarx/stwcx. errata work-around.  */
   if (rs6000_cpu == PROCESSOR_PPC405)
     builtin_define ("__PPC405__");
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/rs6000.c gcc-4.3/gcc/config/rs6000/rs6000.c
--- gcc-4.3-nold/gcc/config/rs6000/rs6000.c	2006-12-18 15:49:35.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/rs6000.c	2006-12-18 16:16:12.000000000 -0800
@@ -1467,8 +1467,6 @@ rs6000_override_options (const char *def
 	rs6000_float_gprs = 0;
       if (!rs6000_explicit_options.isel)
 	rs6000_isel = 0;
-      if (!rs6000_explicit_options.long_double)
-	rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
     }
 
   rs6000_always_hint = (rs6000_cpu != PROCESSOR_POWER4
@@ -1892,9 +1890,6 @@ rs6000_handle_option (size_t code, const
     case OPT_mspe_:
       rs6000_explicit_options.spe = true;
       rs6000_parse_yes_no_option ("spe", arg, &(rs6000_spe));
-      /* No SPE means 64-bit long doubles, even if an E500.  */
-      if (!rs6000_spe)
-	rs6000_long_double_type_size = 64;
       break;
 
     case OPT_mdebug_:
@@ -2718,18 +2713,22 @@ invalid_e500_subreg (rtx op, enum machin
 {
   if (TARGET_E500_DOUBLE)
     {
-      /* Reject (subreg:SI (reg:DF)).  */
+      /* Reject (subreg:SI (reg:DF)); likewise with subreg:DI or
+	 subreg:TI and reg:TF.  */
       if (GET_CODE (op) == SUBREG
-	  && mode == SImode
+	  && (mode == SImode || mode == DImode || mode == TImode)
 	  && REG_P (SUBREG_REG (op))
-	  && GET_MODE (SUBREG_REG (op)) == DFmode)
+	  && (GET_MODE (SUBREG_REG (op)) == DFmode
+	      || GET_MODE (SUBREG_REG (op)) == TFmode))
 	return true;
 
-      /* Reject (subreg:DF (reg:DI)).  */
+      /* Reject (subreg:DF (reg:DI)); likewise with subreg:TF and
+	 reg:TI.  */
       if (GET_CODE (op) == SUBREG
-	  && mode == DFmode
+	  && (mode == DFmode || mode == TFmode)
 	  && REG_P (SUBREG_REG (op))
-	  && GET_MODE (SUBREG_REG (op)) == DImode)
+	  && (GET_MODE (SUBREG_REG (op)) == DImode
+	      || GET_MODE (SUBREG_REG (op)) == TImode))
 	return true;
     }
 
@@ -2989,6 +2988,10 @@ rs6000_legitimate_offset_address_p (enum
       break;
 
     case TFmode:
+      if (TARGET_E500_DOUBLE)
+	return (SPE_CONST_OFFSET_OK (offset)
+		&& SPE_CONST_OFFSET_OK (offset + 8));
+
     case TImode:
       if (mode == TFmode || !TARGET_POWERPC64)
 	extra = 12;
@@ -3067,7 +3070,8 @@ legitimate_lo_sum_address_p (enum machin
   if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))
     return false;
   /* Restrict addressing for DI because of our SUBREG hackery.  */
-  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode))
+  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+			     || mode == DImode))
     return false;
   x = XEXP (x, 1);
 
@@ -3165,7 +3169,7 @@ rs6000_legitimize_address (rtx x, rtx ol
       return reg;
     }
   else if (SPE_VECTOR_MODE (mode)
-	   || (TARGET_E500_DOUBLE && (mode == DFmode
+	   || (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
 				      || mode == DImode)))
     {
       if (mode == DImode)
@@ -3571,7 +3575,7 @@ rs6000_legitimize_reload_address (rtx x,
       && REG_MODE_OK_FOR_BASE_P (XEXP (x, 0), mode)
       && GET_CODE (XEXP (x, 1)) == CONST_INT
       && !SPE_VECTOR_MODE (mode)
-      && !(TARGET_E500_DOUBLE && (mode == DFmode
+      && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
 				  || mode == DImode))
       && !ALTIVEC_VECTOR_MODE (mode))
     {
@@ -3708,7 +3712,8 @@ rs6000_legitimate_address (enum machine_
       && !SPE_VECTOR_MODE (mode)
       && mode != TFmode
       /* Restrict addressing for DI because of our SUBREG hackery.  */
-      && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode))
+      && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+				  || mode == DImode))
       && TARGET_UPDATE
       && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict))
     return 1;
@@ -4208,14 +4213,15 @@ rs6000_emit_move (rtx dest, rtx source, 
     {
       /* DImode is used, not DFmode, because simplify_gen_subreg doesn't
 	 know how to get a DFmode SUBREG of a TFmode.  */
-      rs6000_emit_move (simplify_gen_subreg (DImode, operands[0], mode, 0),
-			simplify_gen_subreg (DImode, operands[1], mode, 0),
-			DImode);
-      rs6000_emit_move (simplify_gen_subreg (DImode, operands[0], mode,
-					     GET_MODE_SIZE (DImode)),
-			simplify_gen_subreg (DImode, operands[1], mode,
-					     GET_MODE_SIZE (DImode)),
-			DImode);
+      enum machine_mode imode = (TARGET_E500_DOUBLE ? DFmode : DImode);
+      rs6000_emit_move (simplify_gen_subreg (imode, operands[0], mode, 0),
+			simplify_gen_subreg (imode, operands[1], mode, 0),
+			imode);
+      rs6000_emit_move (simplify_gen_subreg (imode, operands[0], mode,
+					     GET_MODE_SIZE (imode)),
+			simplify_gen_subreg (imode, operands[1], mode,
+					     GET_MODE_SIZE (imode)),
+			imode);
       return;
     }
 
@@ -5005,7 +5011,7 @@ function_arg_advance (CUMULATIVE_ARGS *c
 static rtx
 spe_build_register_parallel (enum machine_mode mode, int gregno)
 {
-  rtx r1, r3;
+  rtx r1, r3, r5, r7;
 
   switch (mode)
     {
@@ -5015,12 +5021,24 @@ spe_build_register_parallel (enum machin
       return gen_rtx_PARALLEL (mode, gen_rtvec (1, r1));
 
     case DCmode:
+    case TFmode:
       r1 = gen_rtx_REG (DImode, gregno);
       r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
       r3 = gen_rtx_REG (DImode, gregno + 2);
       r3 = gen_rtx_EXPR_LIST (VOIDmode, r3, GEN_INT (8));
       return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r3));
 
+    case TCmode:
+      r1 = gen_rtx_REG (DImode, gregno);
+      r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
+      r3 = gen_rtx_REG (DImode, gregno + 2);
+      r3 = gen_rtx_EXPR_LIST (VOIDmode, r3, GEN_INT (8));
+      r5 = gen_rtx_REG (DImode, gregno + 4);
+      r5 = gen_rtx_EXPR_LIST (VOIDmode, r5, GEN_INT (16));
+      r7 = gen_rtx_REG (DImode, gregno + 6);
+      r7 = gen_rtx_EXPR_LIST (VOIDmode, r7, GEN_INT (24));
+      return gen_rtx_PARALLEL (mode, gen_rtvec (4, r1, r3, r5, r7));
+
     default:
       gcc_unreachable ();
     }
@@ -5035,7 +5053,8 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS
 
   /* On E500 v2, double arithmetic is done on the full 64-bit GPR, but
      are passed and returned in a pair of GPRs for ABI compatibility.  */
-  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DCmode))
+  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DCmode
+			     || mode == TFmode || mode == TCmode))
     {
       int n_words = rs6000_arg_size (mode, type);
 
@@ -5453,7 +5472,9 @@ function_arg (CUMULATIVE_ARGS *cum, enum
   else if (TARGET_SPE_ABI && TARGET_SPE
 	   && (SPE_VECTOR_MODE (mode)
 	       || (TARGET_E500_DOUBLE && (mode == DFmode
-					  || mode == DCmode))))
+					  || mode == DCmode
+					  || mode == TFmode
+					  || mode == TCmode))))
     return rs6000_spe_function_arg (cum, mode, type);
 
   else if (abi == ABI_V4)
@@ -9407,7 +9428,7 @@ rs6000_init_libfuncs (void)
 	set_optab_libfunc (smul_optab, TFmode, "__gcc_qmul");
 	set_optab_libfunc (sdiv_optab, TFmode, "__gcc_qdiv");
 
-	if (TARGET_SOFT_FLOAT)
+	if (!(TARGET_HARD_FLOAT && (TARGET_FPRS || TARGET_E500_DOUBLE)))
 	  {
 	    set_optab_libfunc (neg_optab, TFmode, "__gcc_qneg");
 	    set_optab_libfunc (eq_optab, TFmode, "__gcc_qeq");
@@ -11020,7 +11041,9 @@ print_operand (FILE *file, rtx x, int co
 	tmp = XEXP (x, 0);
 
 	/* Ugly hack because %y is overloaded.  */
-	if (TARGET_E500 && GET_MODE_SIZE (GET_MODE (x)) == 8)
+	if (TARGET_E500 && (GET_MODE_SIZE (GET_MODE (x)) == 8
+			    || GET_MODE (x) == TFmode
+			    || GET_MODE (x) == TImode))
 	  {
 	    /* Handle [reg].  */
 	    if (GET_CODE (tmp) == REG)
@@ -11352,6 +11375,14 @@ rs6000_generate_compare (enum rtx_code c
 				   rs6000_compare_op1);
 	      break;
 
+	    case TFmode:
+	      cmp = flag_unsafe_math_optimizations
+		? gen_tsttfeq_gpr (compare_result, rs6000_compare_op0,
+				   rs6000_compare_op1)
+		: gen_cmptfeq_gpr (compare_result, rs6000_compare_op0,
+				   rs6000_compare_op1);
+	      break;
+
 	    default:
 	      gcc_unreachable ();
 	    }
@@ -11376,6 +11407,14 @@ rs6000_generate_compare (enum rtx_code c
 				   rs6000_compare_op1);
 	      break;
 
+	    case TFmode:
+	      cmp = flag_unsafe_math_optimizations
+		? gen_tsttfgt_gpr (compare_result, rs6000_compare_op0,
+				   rs6000_compare_op1)
+		: gen_cmptfgt_gpr (compare_result, rs6000_compare_op0,
+				   rs6000_compare_op1);
+	      break;
+
 	    default:
 	      gcc_unreachable ();
 	    }
@@ -11400,6 +11439,14 @@ rs6000_generate_compare (enum rtx_code c
 				   rs6000_compare_op1);
 	      break;
 
+	    case TFmode:
+	      cmp = flag_unsafe_math_optimizations
+		? gen_tsttflt_gpr (compare_result, rs6000_compare_op0,
+				   rs6000_compare_op1)
+		: gen_cmptflt_gpr (compare_result, rs6000_compare_op0,
+				   rs6000_compare_op1);
+	      break;
+
 	    default:
 	      gcc_unreachable ();
 	    }
@@ -11443,6 +11490,14 @@ rs6000_generate_compare (enum rtx_code c
 				   rs6000_compare_op1);
 	      break;
 
+	    case TFmode:
+	      cmp = flag_unsafe_math_optimizations
+		? gen_tsttfeq_gpr (compare_result2, rs6000_compare_op0,
+				   rs6000_compare_op1)
+		: gen_cmptfeq_gpr (compare_result2, rs6000_compare_op0,
+				   rs6000_compare_op1);
+	      break;
+
 	    default:
 	      gcc_unreachable ();
 	    }
@@ -13481,7 +13536,7 @@ spe_func_has_64bit_regs_p (void)
 
 	      if (SPE_VECTOR_MODE (mode))
 		return true;
-	      if (TARGET_E500_DOUBLE && mode == DFmode)
+	      if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode))
 		return true;
 	    }
 	}
@@ -20250,7 +20305,8 @@ rs6000_function_value (tree valtype, tre
 	   && ALTIVEC_VECTOR_MODE (mode))
     regno = ALTIVEC_ARG_RETURN;
   else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
-	   && (mode == DFmode || mode == DCmode))
+	   && (mode == DFmode || mode == DCmode
+	       || mode == TFmode || mode == TCmode))
     return spe_build_register_parallel (mode, GP_ARG_RETURN);
   else
     regno = GP_ARG_RETURN;
@@ -20290,7 +20346,8 @@ rs6000_libcall_value (enum machine_mode 
   else if (COMPLEX_MODE_P (mode) && targetm.calls.split_complex_arg)
     return rs6000_complex_function_value (mode);
   else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
-	   && (mode == DFmode || mode == DCmode))
+	   && (mode == DFmode || mode == DCmode
+	       || mode == TFmode || mode == TCmode))
     return spe_build_register_parallel (mode, GP_ARG_RETURN);
   else
     regno = GP_ARG_RETURN;
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/rs6000.h gcc-4.3/gcc/config/rs6000/rs6000.h
--- gcc-4.3-nold/gcc/config/rs6000/rs6000.h	2006-11-16 13:27:22.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/rs6000.h	2006-12-18 16:16:12.000000000 -0800
@@ -1159,6 +1159,7 @@ enum reg_class
       && reg_classes_intersect_p (FLOAT_REGS, CLASS))			\
    : (((TARGET_E500_DOUBLE						\
 	&& ((((TO) == DFmode) + ((FROM) == DFmode)) == 1		\
+	    || (((TO) == TFmode) + ((FROM) == TFmode)) == 1		\
 	    || (((TO) == DImode) + ((FROM) == DImode)) == 1))		\
        || (TARGET_SPE							\
 	   && (SPE_VECTOR_MODE (FROM) + SPE_VECTOR_MODE (TO)) == 1))	\
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/rs6000.md gcc-4.3/gcc/config/rs6000/rs6000.md
--- gcc-4.3-nold/gcc/config/rs6000/rs6000.md	2006-12-18 07:12:11.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/rs6000.md	2006-12-18 16:16:12.000000000 -0800
@@ -176,7 +176,9 @@
 (define_mode_macro FP [(SF "TARGET_HARD_FLOAT")
   (DF "TARGET_HARD_FLOAT && (TARGET_FPRS || TARGET_E500_DOUBLE)")
   (TF "!TARGET_IEEEQUAD
-   && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128")])
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128")])
 
 ; Various instructions that come in SI and DI forms.
 ; A generic w/d attribute, for things like cmpw/cmpd.
@@ -5730,7 +5732,7 @@
 	      (clobber (match_dup 4))
 	      (clobber (match_dup 5))
 	      (clobber (match_dup 6))])]
-  "TARGET_HARD_FLOAT && TARGET_FPRS"
+  "TARGET_HARD_FLOAT && (TARGET_FPRS || TARGET_E500_DOUBLE)"
   "
 {
   if (TARGET_E500_DOUBLE)
@@ -8538,7 +8540,7 @@
   [(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")
+  [(set (match_operand:TF 0 "rs6000_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
@@ -8551,6 +8553,21 @@
   [(set_attr "length" "20,20,16")])
 
 (define_expand "extenddftf2"
+  [(set (match_operand:TF 0 "nonimmediate_operand" "")
+	(float_extend:TF (match_operand:DF 1 "input_operand" "")))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
+{
+  if (TARGET_E500_DOUBLE)
+    emit_insn (gen_spe_extenddftf2 (operands[0], operands[1]));
+  else
+    emit_insn (gen_extenddftf2_fprs (operands[0], operands[1]));
+  DONE;
+})
+
+(define_expand "extenddftf2_fprs"
   [(parallel [(set (match_operand:TF 0 "nonimmediate_operand" "")
 		   (float_extend:TF (match_operand:DF 1 "input_operand" "")))
 	      (use (match_dup 2))])]
@@ -8586,7 +8603,9 @@
   [(set (match_operand:TF 0 "nonimmediate_operand" "")
 	(float_extend:TF (match_operand:SF 1 "gpc_reg_operand" "")))]
   "!TARGET_IEEEQUAD
-   && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128"
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
 {
   rtx tmp = gen_reg_rtx (DFmode);
   emit_insn (gen_extendsfdf2 (tmp, operands[1]));
@@ -8598,7 +8617,9 @@
   [(set (match_operand:DF 0 "gpc_reg_operand" "")
 	(float_truncate:DF (match_operand:TF 1 "gpc_reg_operand" "")))]
   "!TARGET_IEEEQUAD
-   && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128"
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
   "")
 
 (define_insn_and_split "trunctfdf2_internal1"
@@ -8625,7 +8646,22 @@
   "fadd %0,%1,%L1"
   [(set_attr "type" "fp")])
 
-(define_insn_and_split "trunctfsf2"
+(define_expand "trunctfsf2"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "")
+	(float_truncate:SF (match_operand:TF 1 "gpc_reg_operand" "")))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
+{
+  if (TARGET_E500_DOUBLE)
+    emit_insn (gen_spe_trunctfsf2 (operands[0], operands[1]));
+  else
+    emit_insn (gen_trunctfsf2_fprs (operands[0], operands[1]));
+  DONE;
+})
+
+(define_insn_and_split "trunctfsf2_fprs"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
 	(float_truncate:SF (match_operand:TF 1 "gpc_reg_operand" "f")))
    (clobber (match_scratch:DF 2 "=f"))]
@@ -8643,7 +8679,9 @@
   [(set (match_operand:TF 0 "gpc_reg_operand" "")
         (float:TF (match_operand:SI 1 "gpc_reg_operand" "")))]
   "!TARGET_IEEEQUAD
-   && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128"
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
 {
   rtx tmp = gen_reg_rtx (DFmode);
   expand_float (tmp, operands[1], false);
@@ -8664,6 +8702,22 @@
    (set_attr "length" "20")])
 
 (define_expand "fix_trunctfsi2"
+  [(set (match_operand:SI 0 "gpc_reg_operand" "")
+	(fix:SI (match_operand:TF 1 "gpc_reg_operand" "")))]
+  "!TARGET_IEEEQUAD
+   && (TARGET_POWER2 || TARGET_POWERPC)
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
+{
+  if (TARGET_E500_DOUBLE)
+    emit_insn (gen_spe_fix_trunctfsi2 (operands[0], operands[1]));
+  else
+    emit_insn (gen_fix_trunctfsi2_fprs (operands[0], operands[1]));
+  DONE;
+})
+
+(define_expand "fix_trunctfsi2_fprs"
   [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "")
 		   (fix:SI (match_operand:TF 1 "gpc_reg_operand" "")))
 	      (clobber (match_dup 2))
@@ -8705,7 +8759,16 @@
   DONE;
 })
 
-(define_insn "negtf2"
+(define_expand "negtf2"
+  [(set (match_operand:TF 0 "gpc_reg_operand" "")
+	(neg:TF (match_operand:TF 1 "gpc_reg_operand" "")))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
+  "")
+
+(define_insn "negtf2_internal"
   [(set (match_operand:TF 0 "gpc_reg_operand" "=f")
 	(neg:TF (match_operand:TF 1 "gpc_reg_operand" "f")))]
   "!TARGET_IEEEQUAD
@@ -8721,14 +8784,24 @@
    (set_attr "length" "8")])
 
 (define_expand "abstf2"
-  [(set (match_operand:TF 0 "gpc_reg_operand" "=f")
-	(abs:TF (match_operand:TF 1 "gpc_reg_operand" "f")))]
+  [(set (match_operand:TF 0 "gpc_reg_operand" "")
+	(abs:TF (match_operand:TF 1 "gpc_reg_operand" "")))]
   "!TARGET_IEEEQUAD
-   && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128"
+   && TARGET_HARD_FLOAT
+   && (TARGET_FPRS || TARGET_E500_DOUBLE)
+   && TARGET_LONG_DOUBLE_128"
   "
 {
   rtx label = gen_label_rtx ();
-  emit_insn (gen_abstf2_internal (operands[0], operands[1], label));
+  if (TARGET_E500_DOUBLE)
+    {
+      if (flag_unsafe_math_optimizations)
+	emit_insn (gen_spe_abstf2_tst (operands[0], operands[1], label));
+      else
+	emit_insn (gen_spe_abstf2_cmp (operands[0], operands[1], label));
+    }
+  else
+    emit_insn (gen_abstf2_internal (operands[0], operands[1], label));
   emit_label (label);
   DONE;
 }")
@@ -8761,7 +8834,7 @@
 ; List r->r after r->"o<>", otherwise reload will try to reload a
 ; non-offsettable address by using r->r which won't make progress.
 (define_insn "*movdi_internal32"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=o<>,r,r,*f,*f,m,r")
+  [(set (match_operand:DI 0 "rs6000_nonimmediate_operand" "=o<>,r,r,*f,*f,m,r")
 	(match_operand:DI 1 "input_operand" "r,r,m,f,m,f,IJKnGHF"))]
   "! TARGET_POWERPC64
    && (gpc_reg_operand (operands[0], DImode)
@@ -8798,7 +8871,7 @@
 }")
 
 (define_split
-  [(set (match_operand:DI 0 "nonimmediate_operand" "")
+  [(set (match_operand:DI 0 "rs6000_nonimmediate_operand" "")
         (match_operand:DI 1 "input_operand" ""))]
   "reload_completed && !TARGET_POWERPC64
    && gpr_or_gpr_p (operands[0], operands[1])"
diff -rupN --exclude=.svn gcc-4.3-nold/gcc/config/rs6000/spe.md gcc-4.3/gcc/config/rs6000/spe.md
--- gcc-4.3-nold/gcc/config/rs6000/spe.md	2006-11-21 12:30:24.000000000 -0800
+++ gcc-4.3/gcc/config/rs6000/spe.md	2006-12-18 16:16:12.000000000 -0800
@@ -29,12 +29,24 @@
    (TSTDFGT_GPR		1009)
    (CMPDFLT_GPR		1010)
    (TSTDFLT_GPR		1011)
-   (E500_CR_IOR_COMPARE 1012)
+   (CMPTFEQ_GPR		1012)
+   (TSTTFEQ_GPR		1013)
+   (CMPTFGT_GPR		1014)
+   (TSTTFGT_GPR		1015)
+   (CMPTFLT_GPR		1016)
+   (TSTTFLT_GPR		1017)
+   (E500_CR_IOR_COMPARE 1018)
    ])
 
 ;; Modes using a 64-bit register.
 (define_mode_macro SPE64 [DF V4HI V2SF V1DI V2SI])
 
+;; Likewise, but allow TFmode (two registers) as well.
+(define_mode_macro SPE64TF [DF V4HI V2SF V1DI V2SI TF])
+
+;; DImode and TImode.
+(define_mode_macro DITI [DI TI])
+
 (define_insn "*negsf2_gpr"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=r")
         (neg:SF (match_operand:SF 1 "gpc_reg_operand" "r")))]
@@ -2198,25 +2210,57 @@
 ;; Double-precision floating point instructions.
 
 ;; FIXME: Add o=r option.
-(define_insn "*frob_df_di"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r")
-        (subreg:DF (match_operand:DI 1 "input_operand" "r,m") 0))]
+(define_insn "*frob_<SPE64:mode>_<DITI:mode>"
+  [(set (match_operand:SPE64 0 "nonimmediate_operand" "=r,r")
+        (subreg:SPE64 (match_operand:DITI 1 "input_operand" "r,m") 0))]
+  "(TARGET_E500_DOUBLE && <SPE64:MODE>mode == DFmode)
+   || (TARGET_SPE && <SPE64:MODE>mode != DFmode)"
+  "@
+   evmergelo %0,%1,%L1
+   evldd%X1 %0,%y1")
+
+(define_insn "*frob_tf_ti"
+  [(set (match_operand:TF 0 "gpc_reg_operand" "=r")
+        (subreg:TF (match_operand:TI 1 "gpc_reg_operand" "r") 0))]
   "TARGET_E500_DOUBLE"
+  "evmergelo %0,%1,%L1\;evmergelo %L0,%Y1,%Z1")
+
+(define_insn "*frob_<mode>_di_2"
+  [(set (subreg:DI (match_operand:SPE64TF 0 "nonimmediate_operand" "+&r,r") 0)
+        (match_operand:DI 1 "input_operand" "r,m"))]
+  "(TARGET_E500_DOUBLE && (<MODE>mode == DFmode || <MODE>mode == TFmode))
+   || (TARGET_SPE && <MODE>mode != DFmode && <MODE>mode != TFmode)"
   "@
    evmergelo %0,%1,%L1
    evldd%X1 %0,%y1")
 
-(define_insn "*frob_di_df"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r")
-        (subreg:DI (match_operand:DF 1 "input_operand" "r") 0))]
+(define_insn "*frob_tf_di_8_2"
+  [(set (subreg:DI (match_operand:TF 0 "nonimmediate_operand" "+&r,r") 8)
+        (match_operand:DI 1 "input_operand" "r,m"))]
   "TARGET_E500_DOUBLE"
+  "@
+   evmergelo %L0,%1,%L1
+   evldd%X1 %L0,%y1")
+
+(define_insn "*frob_di_<mode>"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r")
+        (subreg:DI (match_operand:SPE64TF 1 "input_operand" "r") 0))]
+  "(TARGET_E500_DOUBLE && (<MODE>mode == DFmode || <MODE>mode == TFmode))
+   || (TARGET_SPE && <MODE>mode != DFmode && <MODE>mode != TFmode)"
   "evmergehi %0,%1,%1\;mr %L0,%1"
   [(set_attr "length" "8")])
 
-(define_insn "*frob_di_df_2"
-  [(set (subreg:DF (match_operand:DI 0 "register_operand" "=&r,r") 0)
-	(match_operand:DF 1 "input_operand" "r,m"))]
+(define_insn "*frob_ti_tf"
+  [(set (match_operand:TI 0 "nonimmediate_operand" "=&r")
+        (subreg:TI (match_operand:TF 1 "input_operand" "r") 0))]
   "TARGET_E500_DOUBLE"
+  "evmergehi %0,%1,%1\;mr %L0,%1\;evmergehi %Y0,%L1,%L1\;mr %Z0,%L1")
+
+(define_insn "*frob_<DITI:mode>_<SPE64:mode>_2"
+  [(set (subreg:SPE64 (match_operand:DITI 0 "register_operand" "+&r,r") 0)
+	(match_operand:SPE64 1 "input_operand" "r,m"))]
+  "(TARGET_E500_DOUBLE && <SPE64:MODE>mode == DFmode)
+   || (TARGET_SPE && <SPE64:MODE>mode != DFmode)"
   "*
 {
   switch (which_alternative)
@@ -2244,10 +2288,43 @@
 }"
   [(set_attr "length" "8,8")])
 
+; As the above, but TImode at offset 8.
+(define_insn "*frob_ti_<mode>_8_2"
+  [(set (subreg:SPE64 (match_operand:TI 0 "register_operand" "+&r,r") 8)
+	(match_operand:SPE64 1 "input_operand" "r,m"))]
+  "(TARGET_E500_DOUBLE && <MODE>mode == DFmode)
+   || (TARGET_SPE && <MODE>mode != DFmode)"
+  "*
+{
+  switch (which_alternative)
+    {
+    default: 
+      gcc_unreachable ();
+    case 0:
+      return \"evmergehi %Y0,%1,%1\;mr %Z0,%1\";
+    case 1:
+      if (!offsettable_nonstrict_memref_p (operands[1]))
+	return \"evldd%X1 %Z0,%y1\;evmergehi %Y0,%Z0,%Z0\";
+      if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
+			     operands[1], 0))
+	return \"{l|lwz} %Z0,%L1\;{l|lwz} %Y0,%1\";
+      else
+        return \"{l%U1%X1|lwz%U1%X1} %Y0,%1\;{l|lwz} %Z0,%L1\";
+    }
+}"
+  [(set_attr "length" "8,8")])
+
+(define_insn "*frob_ti_tf_2"
+  [(set (subreg:TF (match_operand:TI 0 "gpc_reg_operand" "=&r") 0)
+	(match_operand:TF 1 "gpc_reg_operand" "r"))]
+  "TARGET_E500_DOUBLE"
+  "evmergehi %0,%1,%1\;mr %L0,%1\;evmergehi %Y0,%L1,%L1\;mr %Z0,%L1")
+
 (define_insn "*mov_si<mode>_e500_subreg0"
-  [(set (subreg:SI (match_operand:SPE64 0 "register_operand" "+r,&r") 0)
+  [(set (subreg:SI (match_operand:SPE64TF 0 "register_operand" "+r,&r") 0)
 	(match_operand:SI 1 "input_operand" "r,m"))]
-  "(TARGET_E500_DOUBLE && <MODE>mode == DFmode) || (TARGET_SPE && <MODE>mode != DFmode)"
+  "(TARGET_E500_DOUBLE && (<MODE>mode == DFmode || <MODE>mode == TFmode))
+   || (TARGET_SPE && <MODE>mode != DFmode && <MODE>mode != TFmode)"
   "@
    evmergelo %0,%1,%0
    evmergelohi %0,%0,%0\;{l%U1%X1|lwz%U1%X1} %0,%1\;evmergelohi %0,%0,%0")
@@ -2256,28 +2333,63 @@
 ;; the offset.
 (define_insn "*mov_si<mode>_e500_subreg0_2"
   [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "+r,m")
-	(subreg:SI (match_operand:SPE64 1 "register_operand" "+r,&r") 0))]
-  "(TARGET_E500_DOUBLE && <MODE>mode == DFmode) || (TARGET_SPE && <MODE>mode != DFmode)"
+	(subreg:SI (match_operand:SPE64TF 1 "register_operand" "+r,&r") 0))]
+  "(TARGET_E500_DOUBLE && (<MODE>mode == DFmode || <MODE>mode == TFmode))
+   || (TARGET_SPE && <MODE>mode != DFmode && <MODE>mode != TFmode)"
   "@
    evmergehi %0,%0,%1
    evmergelohi %1,%1,%1\;{st%U0%X0|stw%U0%X0} %1,%0")
 
 (define_insn "*mov_si<mode>_e500_subreg4"
-  [(set (subreg:SI (match_operand:SPE64 0 "register_operand" "+r,r") 4)
+  [(set (subreg:SI (match_operand:SPE64TF 0 "register_operand" "+r,r") 4)
 	(match_operand:SI 1 "input_operand" "r,m"))]
-  "(TARGET_E500_DOUBLE && <MODE>mode == DFmode) || (TARGET_SPE && <MODE>mode != DFmode)"
+  "(TARGET_E500_DOUBLE && (<MODE>mode == DFmode || <MODE>mode == TFmode))
+   || (TARGET_SPE && <MODE>mode != DFmode && <MODE>mode != TFmode)"
   "@
    mr %0,%1
    {l%U1%X1|lwz%U1%X1} %0,%1")
 
 (define_insn "*mov_si<mode>_e500_subreg4_2"
   [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "+r,m")
-	(subreg:SI (match_operand:SPE64 1 "register_operand" "r,r") 4))]
-  "(TARGET_E500_DOUBLE && <MODE>mode == DFmode) || (TARGET_SPE && <MODE>mode != DFmode)"
+	(subreg:SI (match_operand:SPE64TF 1 "register_operand" "r,r") 4))]
+  "(TARGET_E500_DOUBLE && (<MODE>mode == DFmode || <MODE>mode == TFmode))
+   || (TARGET_SPE && <MODE>mode != DFmode && <MODE>mode != TFmode)"
   "@
    mr %0,%1
    {st%U0%X0|stw%U0%X0} %1,%0")
 
+(define_insn "*mov_sitf_e500_subreg8"
+  [(set (subreg:SI (match_operand:TF 0 "register_operand" "+r,&r") 8)
+	(match_operand:SI 1 "input_operand" "r,m"))]
+  "TARGET_E500_DOUBLE"
+  "@
+   evmergelo %L0,%1,%L0
+   evmergelohi %L0,%L0,%L0\;{l%U1%X1|lwz%U1%X1} %L0,%1\;evmergelohi %L0,%L0,%L0")
+
+(define_insn "*mov_sitf_e500_subreg8_2"
+  [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "+r,m")
+	(subreg:SI (match_operand:TF 1 "register_operand" "+r,&r") 8))]
+  "TARGET_E500_DOUBLE"
+  "@
+   evmergehi %0,%0,%L1
+   evmergelohi %L1,%L1,%L1\;{st%U0%X0|stw%U0%X0} %L1,%0")
+
+(define_insn "*mov_sitf_e500_subreg12"
+  [(set (subreg:SI (match_operand:TF 0 "register_operand" "+r,r") 12)
+	(match_operand:SI 1 "input_operand" "r,m"))]
+  "TARGET_E500_DOUBLE"
+  "@
+   mr %L0,%1
+   {l%U1%X1|lwz%U1%X1} %L0,%1")
+
+(define_insn "*mov_sitf_e500_subreg12_2"
+  [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "+r,m")
+	(subreg:SI (match_operand:TF 1 "register_operand" "r,r") 12))]
+  "TARGET_E500_DOUBLE"
+  "@
+   mr %0,%L1
+   {st%U0%X0|stw%U0%X0} %L1,%0")
+
 ;; FIXME: Allow r=CONST0.
 (define_insn "*movdf_e500_double"
   [(set (match_operand:DF 0 "rs6000_nonimmediate_operand" "=r,r,m")
@@ -2354,6 +2466,133 @@
   "TARGET_HARD_FLOAT && TARGET_E500_DOUBLE"
   "efddiv %0,%1,%2")
 
+;; Double-precision floating point instructions for IBM long double.
+
+(define_insn_and_split "spe_trunctfdf2_internal1"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=r,?r")
+	(float_truncate:DF (match_operand:TF 1 "gpc_reg_operand" "0,r")))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "@
+   #
+   evor %0,%1,%1"
+  "&& reload_completed && REGNO (operands[0]) == REGNO (operands[1])"
+  [(const_int 0)]
+{
+  emit_note (NOTE_INSN_DELETED);
+  DONE;
+})
+
+(define_insn_and_split "spe_trunctfsf2"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "=r")
+	(float_truncate:SF (match_operand:TF 1 "gpc_reg_operand" "r")))
+   (clobber (match_scratch:DF 2 "=r"))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2)
+	(float_truncate:DF (match_dup 1)))
+   (set (match_dup 0)
+	(float_truncate:SF (match_dup 2)))]
+  "")
+
+(define_insn "spe_extenddftf2"
+  [(set (match_operand:TF 0 "rs6000_nonimmediate_operand" "=r,?r,r,o")
+	(float_extend:TF (match_operand:DF 1 "input_operand" "0,r,m,r")))
+   (clobber (match_scratch:DF 2 "=X,X,X,&r"))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "@
+   evxor %L0,%L0,%L0
+   evor %0,%1,%1\;evxor %L0,%L0,%L0
+   evldd%X1 %0,%y1\;evxor %L0,%L0,%L0
+   evstdd%X0 %1,%y0\;evxor %2,%2,%2\;evstdd %2,%Y0")
+
+(define_expand "spe_fix_trunctfsi2"
+  [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "")
+		   (fix:SI (match_operand:TF 1 "gpc_reg_operand" "")))
+	      (clobber (match_dup 2))
+	      (clobber (match_dup 3))
+	      (clobber (match_dup 4))])]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+{
+  operands[2] = gen_reg_rtx (DFmode);
+  operands[3] = gen_reg_rtx (SImode);
+  operands[4] = gen_reg_rtx (SImode);
+})
+
+; Like fix_trunc_helper, add with rounding towards 0.
+(define_insn "spe_fix_trunctfsi2_internal"
+  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
+        (fix:SI (match_operand:TF 1 "gpc_reg_operand" "r")))
+   (clobber (match_operand:DF 2 "gpc_reg_operand" "=r"))
+   (clobber (match_operand:SI 3 "gpc_reg_operand" "=&r"))
+   (clobber (match_operand:SI 4 "gpc_reg_operand" "=&r"))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "mfspefscr %3\;rlwinm %4,%3,0,0,29\;ori %4,%4,1\;efdadd %2,%1,%L1\;mtspefscr %3\;efdctsiz %0, %2")
+
+(define_insn "spe_negtf2_internal"
+  [(set (match_operand:TF 0 "gpc_reg_operand" "=r")
+	(neg:TF (match_operand:TF 1 "gpc_reg_operand" "r")))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "*
+{
+  if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
+    return \"efdneg %L0,%L1\;efdneg %0,%1\";
+  else
+    return \"efdneg %0,%1\;efdneg %L0,%L1\";
+}")
+
+(define_expand "spe_abstf2_cmp"
+  [(set (match_operand:TF 0 "gpc_reg_operand" "=f")
+	(match_operand:TF 1 "gpc_reg_operand" "f"))
+   (set (match_dup 3) (match_dup 5))
+   (set (match_dup 5) (abs:DF (match_dup 5)))
+   (set (match_dup 4) (unspec:CCFP [(compare:CCFP (match_dup 3)
+                                                  (match_dup 5))] CMPDFEQ_GPR))
+   (set (pc) (if_then_else (eq (match_dup 4) (const_int 0))
+			   (label_ref (match_operand 2 "" ""))
+			   (pc)))
+   (set (match_dup 6) (neg:DF (match_dup 6)))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "
+{
+  const int hi_word = FLOAT_WORDS_BIG_ENDIAN ? 0 : GET_MODE_SIZE (DFmode);
+  const int lo_word = FLOAT_WORDS_BIG_ENDIAN ? GET_MODE_SIZE (DFmode) : 0;
+  operands[3] = gen_reg_rtx (DFmode);
+  operands[4] = gen_reg_rtx (CCFPmode);
+  operands[5] = simplify_gen_subreg (DFmode, operands[0], TFmode, hi_word);
+  operands[6] = simplify_gen_subreg (DFmode, operands[0], TFmode, lo_word);
+}")
+
+(define_expand "spe_abstf2_tst"
+  [(set (match_operand:TF 0 "gpc_reg_operand" "=f")
+	(match_operand:TF 1 "gpc_reg_operand" "f"))
+   (set (match_dup 3) (match_dup 5))
+   (set (match_dup 5) (abs:DF (match_dup 5)))
+   (set (match_dup 4) (unspec:CCFP [(compare:CCFP (match_dup 3)
+                                                  (match_dup 5))] TSTDFEQ_GPR))
+   (set (pc) (if_then_else (eq (match_dup 4) (const_int 0))
+			   (label_ref (match_operand 2 "" ""))
+			   (pc)))
+   (set (match_dup 6) (neg:DF (match_dup 6)))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128"
+  "
+{
+  const int hi_word = FLOAT_WORDS_BIG_ENDIAN ? 0 : GET_MODE_SIZE (DFmode);
+  const int lo_word = FLOAT_WORDS_BIG_ENDIAN ? GET_MODE_SIZE (DFmode) : 0;
+  operands[3] = gen_reg_rtx (DFmode);
+  operands[4] = gen_reg_rtx (CCFPmode);
+  operands[5] = simplify_gen_subreg (DFmode, operands[0], TFmode, hi_word);
+  operands[6] = simplify_gen_subreg (DFmode, operands[0], TFmode, lo_word);
+}")
+
 ;; Vector move instructions.
 
 (define_expand "movv2si"
@@ -2803,6 +3042,80 @@
   "efdtstlt %0,%1,%2"
   [(set_attr "type" "veccmpsimple")])
 
+;; Same thing, but for IBM long double.
+
+(define_insn "cmptfeq_gpr"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(unspec:CCFP
+	 [(compare:CCFP (match_operand:TF 1 "gpc_reg_operand" "r")
+			(match_operand:TF 2 "gpc_reg_operand" "r"))]
+	 CMPTFEQ_GPR))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128
+   && !flag_unsafe_math_optimizations"
+  "efdcmpeq %0,%1,%2\;bng %0,$+8\;efdcmpeq %0,%L1,%L2"
+  [(set_attr "type" "veccmp")])
+
+(define_insn "tsttfeq_gpr"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(unspec:CCFP
+	 [(compare:CCFP (match_operand:TF 1 "gpc_reg_operand" "r")
+			(match_operand:TF 2 "gpc_reg_operand" "r"))]
+	 TSTTFEQ_GPR))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128
+   && flag_unsafe_math_optimizations"
+  "efdtsteq %0,%1,%2\;bng %0,$+8\;efdtsteq %0,%L1,%L2"
+  [(set_attr "type" "veccmpsimple")])
+
+(define_insn "cmptfgt_gpr"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(unspec:CCFP
+	 [(compare:CCFP (match_operand:TF 1 "gpc_reg_operand" "r")
+			(match_operand:TF 2 "gpc_reg_operand" "r"))]
+	 CMPTFGT_GPR))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128
+   && !flag_unsafe_math_optimizations"
+  "efdcmpgt %0,%1,%2\;bgt %0,$+16\;efdcmpeq %0,%1,%2\;bng %0,$+8\;efdcmpgt %0,%L1,%L2"
+  [(set_attr "type" "veccmp")])
+
+(define_insn "tsttfgt_gpr"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(unspec:CCFP
+	 [(compare:CCFP (match_operand:TF 1 "gpc_reg_operand" "r")
+			(match_operand:TF 2 "gpc_reg_operand" "r"))]
+	 TSTTFGT_GPR))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128
+   && flag_unsafe_math_optimizations"
+  "efdtstgt %0,%1,%2\;bgt %0,$+16\;efdtsteq %0,%1,%2\;bng %0,$+8\;efdtstgt %0,%L1,%L2"
+  [(set_attr "type" "veccmpsimple")])
+
+(define_insn "cmptflt_gpr"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(unspec:CCFP
+	 [(compare:CCFP (match_operand:TF 1 "gpc_reg_operand" "r")
+			(match_operand:TF 2 "gpc_reg_operand" "r"))]
+	 CMPTFLT_GPR))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128
+   && !flag_unsafe_math_optimizations"
+  "efdcmplt %0,%1,%2\;bgt %0,$+16\;efdcmpeq %0,%1,%2\;bng %0,$+8\;efdcmplt %0,%L1,%L2"
+  [(set_attr "type" "veccmp")])
+
+(define_insn "tsttflt_gpr"
+  [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")
+	(unspec:CCFP
+	 [(compare:CCFP (match_operand:TF 1 "gpc_reg_operand" "r")
+			(match_operand:TF 2 "gpc_reg_operand" "r"))]
+	 TSTTFLT_GPR))]
+  "!TARGET_IEEEQUAD
+   && TARGET_HARD_FLOAT && TARGET_E500_DOUBLE && TARGET_LONG_DOUBLE_128
+   && flag_unsafe_math_optimizations"
+  "efdtstlt %0,%1,%2\;bgt %0,$+16\;efdtsteq %0,%1,%2\;bng %0,$+8\;efdtstlt %0,%L1,%L2"
+  [(set_attr "type" "veccmpsimple")])
+
 ;; Like cceq_ior_compare, but compare the GT bits.
 (define_insn "e500_cr_ior_compare"
   [(set (match_operand:CCFP 0 "cc_reg_operand" "=y")

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