diff gcc/config/rs6000/rs6000.md @ 69:1b10fe6932e1

merge 69
author Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp>
date Sun, 21 Aug 2011 07:53:12 +0900
parents 326d9e06c2e3 f6334be47118
children ab0bcb71f44d
line wrap: on
line diff
--- a/gcc/config/rs6000/rs6000.md	Tue Dec 14 03:58:33 2010 +0900
+++ b/gcc/config/rs6000/rs6000.md	Sun Aug 21 07:53:12 2011 +0900
@@ -1,6 +1,6 @@
 ;; Machine description for IBM RISC System 6000 (POWER) for GNU C compiler
 ;; Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
 ;; Free Software Foundation, Inc.
 ;; Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
@@ -39,7 +39,7 @@
    (CR6_REGNO			74)
    (CR7_REGNO			75)
    (MAX_CR_REGNO		75)
-   (XER_REGNO			76)
+   (CA_REGNO			76)
    (FIRST_ALTIVEC_REGNO		77)
    (LAST_ALTIVEC_REGNO		108)
    (VRSAVE_REGNO		109)
@@ -103,6 +103,13 @@
    (UNSPEC_TOCREL		49)
    (UNSPEC_MACHOPIC_OFFSET	50)
    (UNSPEC_BPERM		51)
+   (UNSPEC_COPYSIGN		52)
+   (UNSPEC_PARITY		53)
+   (UNSPEC_FCTIW		54)
+   (UNSPEC_FCTID		55)
+   (UNSPEC_LFIWAX		56)
+   (UNSPEC_LFIWZX		57)
+   (UNSPEC_FCTIWUZ		58)
   ])
 
 ;;
@@ -113,6 +120,7 @@
   [(UNSPECV_BLOCK		0)
    (UNSPECV_LL			1)	; load-locked
    (UNSPECV_SC			2)	; store-conditional
+   (UNSPECV_PROBE_STACK_RANGE	3)	; probe range of stack addresses
    (UNSPECV_EH_RR		9)	; eh_reg_restore
   ])
 
@@ -140,7 +148,7 @@
 ;; Processor type -- this attribute must exactly match the processor_type
 ;; enumeration in rs6000.h.
 
-(define_attr "cpu" "rios1,rios2,rs64a,mpccore,ppc403,ppc405,ppc440,ppc476,ppc601,ppc603,ppc604,ppc604e,ppc620,ppc630,ppc750,ppc7400,ppc7450,ppc8540,ppce300c2,ppce300c3,ppce500mc,ppce500mc64,power4,power5,power6,power7,cell,ppca2"
+(define_attr "cpu" "rios1,rios2,rs64a,mpccore,ppc403,ppc405,ppc440,ppc476,ppc601,ppc603,ppc604,ppc604e,ppc620,ppc630,ppc750,ppc7400,ppc7450,ppc8540,ppce300c2,ppce300c3,ppce500mc,ppce500mc64,power4,power5,power6,power7,cell,ppca2,titan"
   (const (symbol_ref "rs6000_cpu_attr")))
 
 
@@ -175,6 +183,7 @@
 (include "cell.md")
 (include "xfpu.md")
 (include "a2.md")
+(include "titan.md")
 
 (include "predicates.md")
 (include "constraints.md")
@@ -201,7 +210,7 @@
 (define_mode_iterator SDI [SI DI])
 
 ; The size of a pointer.  Also, the size of the value that a record-condition
-; (one with a '.') will compare.
+; (one with a '.') will compare; and the size used for arithmetic carries.
 (define_mode_iterator P [(SI "TARGET_32BIT") (DI "TARGET_64BIT")])
 
 ; Any hardware-supported floating-point mode
@@ -217,6 +226,26 @@
   (DD "TARGET_DFP")
   (TD "TARGET_DFP")])
 
+; Any fma capable floating-point mode.
+(define_mode_iterator FMA_F [
+  (SF "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT")
+  (DF "(TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
+       || VECTOR_UNIT_VSX_P (DFmode)")
+  (V2SF "TARGET_PAIRED_FLOAT")
+  (V4SF "VECTOR_UNIT_ALTIVEC_OR_VSX_P (V4SFmode)")
+  (V2DF "VECTOR_UNIT_ALTIVEC_OR_VSX_P (V2DFmode)")
+  ])
+
+; These modes do not fit in integer registers in 32-bit mode.
+; but on e500v2, the gpr are 64 bit registers
+(define_mode_iterator DIFD [DI (DF "!TARGET_E500_DOUBLE") DD])
+
+; Iterator for reciprocal estimate instructions
+(define_mode_iterator RECIPF [SF DF V4SF V2DF])
+
+; Iterator for just SF/DF
+(define_mode_iterator SFDF [SF DF])
+
 ; Various instructions that come in SI and DI forms.
 ; A generic w/d attribute, for things like cmpw/cmpd.
 (define_mode_attr wd [(QI "b") (HI "h") (SI "w") (DI "d")])
@@ -237,6 +266,22 @@
 (define_mode_attr mptrsize [(SI "si")
 			    (DI "di")])
 
+(define_mode_attr rreg [(SF   "f")
+			(DF   "ws")
+			(V4SF "wf")
+			(V2DF "wd")])
+
+(define_mode_attr rreg2 [(SF   "f")
+			 (DF   "d")])
+
+(define_mode_attr SI_CONVERT_FP [(SF "TARGET_FCFIDS")
+				 (DF "TARGET_FCFID")])
+
+(define_mode_attr E500_CONVERT [(SF "!TARGET_FPRS")
+				(DF "TARGET_E500_DOUBLE")])
+
+(define_mode_attr TARGET_FLOAT [(SF "TARGET_SINGLE_FLOAT")
+				(DF "TARGET_DOUBLE_FLOAT")])
 
 ;; Start with fixed-point load and store insns.  Here we put only the more
 ;; complex forms.  Basic data transfer is done later.
@@ -2092,10 +2137,10 @@
 	(compare:CC (match_dup 1)
 		    (const_int 0)))
    (set (match_dup 0)
-	(if_then_else:GPR (ge (match_dup 3)
+	(if_then_else:GPR (lt (match_dup 3)
 			      (const_int 0))
-			  (match_dup 1)
-			  (match_dup 2)))]
+			  (match_dup 2)
+			  (match_dup 1)))]
   "")
 
 (define_insn_and_split "nabs<mode>2_isel"
@@ -2111,10 +2156,10 @@
 	(compare:CC (match_dup 1)
 		    (const_int 0)))
    (set (match_dup 0)
-	(if_then_else:GPR (ge (match_dup 3)
+	(if_then_else:GPR (lt (match_dup 3)
 			      (const_int 0))
-			  (match_dup 2)
-			  (match_dup 1)))]
+			  (match_dup 1)
+			  (match_dup 2)))]
   "")
 
 (define_insn_and_split "abssi2_nopower"
@@ -2259,17 +2304,11 @@
   "TARGET_POPCNTB"
   "popcntb %0,%1")
 
-(define_insn "popcntwsi2"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
-	(popcount:SI (match_operand:SI 1 "gpc_reg_operand" "r")))]
+(define_insn "popcntd<mode>2"
+  [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
+	(popcount:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))]
   "TARGET_POPCNTD"
-  "popcntw %0,%1")
-
-(define_insn "popcntddi2"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
-	(popcount:DI (match_operand:DI 1 "gpc_reg_operand" "r")))]
-  "TARGET_POPCNTD && TARGET_POWERPC64"
-  "popcntd %0,%1")
+  "popcnt<wd> %0,%1")
 
 (define_expand "popcount<mode>2"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "")
@@ -2280,6 +2319,12 @@
     DONE;
   })
 
+(define_insn "parity<mode>2_cmpb"
+  [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
+	(unspec:GPR [(match_operand:GPR 1 "gpc_reg_operand" "r")] UNSPEC_PARITY))]
+  "TARGET_CMPB && TARGET_POPCNTB"
+  "prty<wd> %0,%1")
+
 (define_expand "parity<mode>2"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "")
 	(parity:GPR (match_operand:GPR 1 "gpc_reg_operand" "")))]
@@ -5560,6 +5605,45 @@
   [(set_attr "type" "var_delayed_compare,delayed_compare,var_delayed_compare,delayed_compare")
    (set_attr "length" "4,4,8,8")])
 
+;; Builtins to replace a division to generate FRE reciprocal estimate
+;; instructions and the necessary fixup instructions
+(define_expand "recip<mode>3"
+  [(match_operand:RECIPF 0 "gpc_reg_operand" "")
+   (match_operand:RECIPF 1 "gpc_reg_operand" "")
+   (match_operand:RECIPF 2 "gpc_reg_operand" "")]
+  "RS6000_RECIP_HAVE_RE_P (<MODE>mode)"
+{
+   rs6000_emit_swdiv (operands[0], operands[1], operands[2], false);
+   DONE;
+})
+
+;; Split to create division from FRE/FRES/etc. and fixup instead of the normal
+;; hardware division.  This is only done before register allocation and with
+;; -ffast-math.  This must appear before the divsf3/divdf3 insns.
+(define_split
+  [(set (match_operand:RECIPF 0 "gpc_reg_operand" "")
+	(div:RECIPF (match_operand 1 "gpc_reg_operand" "")
+		    (match_operand 2 "gpc_reg_operand" "")))]
+  "RS6000_RECIP_AUTO_RE_P (<MODE>mode)
+   && can_create_pseudo_p () && optimize_insn_for_speed_p ()
+   && flag_finite_math_only && !flag_trapping_math && flag_reciprocal_math"
+  [(const_int 0)]
+{
+  rs6000_emit_swdiv (operands[0], operands[1], operands[2], true);
+  DONE;
+})
+
+;; Builtins to replace 1/sqrt(x) with instructions using RSQRTE and the
+;; appropriate fixup.
+(define_expand "rsqrt<mode>2"
+  [(match_operand:RECIPF 0 "gpc_reg_operand" "")
+   (match_operand:RECIPF 1 "gpc_reg_operand" "")]
+  "RS6000_RECIP_HAVE_RSQRTE_P (<MODE>mode)"
+{
+  rs6000_emit_swrsqrt (operands[0], operands[1]);
+  DONE;
+})
+
 (define_split
   [(set (match_operand:CC 3 "cc_reg_not_micro_cr0_operand" "")
 	(compare:CC (ashiftrt:SI (match_operand:SI 1 "gpc_reg_operand" "")
@@ -5763,147 +5847,70 @@
   "{fd|fdiv} %0,%1,%2"
   [(set_attr "type" "ddiv")])
 
-(define_expand "recipsf3"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")
-		    (match_operand:SF 2 "gpc_reg_operand" "f")]
-		   UNSPEC_FRES))]
-  "TARGET_RECIP && TARGET_HARD_FLOAT && TARGET_PPC_GFXOPT && !optimize_size
-   && flag_finite_math_only && !flag_trapping_math"
-{
-   rs6000_emit_swdivsf (operands[0], operands[1], operands[2]);
-   DONE;
-})
-
 (define_insn "fres"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
 	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")] UNSPEC_FRES))]
-  "TARGET_PPC_GFXOPT && flag_finite_math_only"
+  "TARGET_FRES"
   "fres %0,%1"
   [(set_attr "type" "fp")])
 
-(define_insn "*fmaddsf4_powerpc"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(plus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-			  (match_operand:SF 2 "gpc_reg_operand" "f"))
-		 (match_operand:SF 3 "gpc_reg_operand" "f")))]
-  "TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS
-   && TARGET_SINGLE_FLOAT && TARGET_FUSED_MADD"
-  "fmadds %0,%1,%2,%3"
-  [(set_attr "type" "fp")
-   (set_attr "fp_type" "fp_maddsub_s")])
-
-(define_insn "*fmaddsf4_power"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(plus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-			  (match_operand:SF 2 "gpc_reg_operand" "f"))
-		 (match_operand:SF 3 "gpc_reg_operand" "f")))]
-  "! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD"
-  "{fma|fmadd} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")])
-
-(define_insn "*fmsubsf4_powerpc"
+; builtin fmaf support
+(define_insn "*fmasf4_fpr"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(minus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-			   (match_operand:SF 2 "gpc_reg_operand" "f"))
-		  (match_operand:SF 3 "gpc_reg_operand" "f")))]
-  "TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS
-   && TARGET_SINGLE_FLOAT && TARGET_FUSED_MADD"
-  "fmsubs %0,%1,%2,%3"
-  [(set_attr "type" "fp")
-   (set_attr "fp_type" "fp_maddsub_s")])
-
-(define_insn "*fmsubsf4_power"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(minus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-			   (match_operand:SF 2 "gpc_reg_operand" "f"))
-		  (match_operand:SF 3 "gpc_reg_operand" "f")))]
-  "! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD"
-  "{fms|fmsub} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")])
-
-(define_insn "*fnmaddsf4_powerpc_1"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(neg:SF (plus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-				  (match_operand:SF 2 "gpc_reg_operand" "f"))
-			 (match_operand:SF 3 "gpc_reg_operand" "f"))))]
-  "TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD
-   && TARGET_SINGLE_FLOAT"
-  "fnmadds %0,%1,%2,%3"
+	(fma:SF (match_operand:SF 1 "gpc_reg_operand" "f")
+		(match_operand:SF 2 "gpc_reg_operand" "f")
+		(match_operand:SF 3 "gpc_reg_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+{
+  return (TARGET_POWERPC
+	  ? "fmadds %0,%1,%2,%3"
+	  : "{fma|fmadd} %0,%1,%2,%3");
+}
   [(set_attr "type" "fp")
    (set_attr "fp_type" "fp_maddsub_s")])
 
-(define_insn "*fnmaddsf4_powerpc_2"
+(define_insn "*fmssf4_fpr"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(minus:SF (mult:SF (neg:SF (match_operand:SF 1 "gpc_reg_operand" "f"))
-			   (match_operand:SF 2 "gpc_reg_operand" "f"))
-			 (match_operand:SF 3 "gpc_reg_operand" "f")))]
-  "TARGET_POWERPC && TARGET_SINGLE_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD
-   && ! HONOR_SIGNED_ZEROS (SFmode)"
-  "fnmadds %0,%1,%2,%3"
+	(fma:SF (match_operand:SF 1 "gpc_reg_operand" "f")
+		(match_operand:SF 2 "gpc_reg_operand" "f")
+		(neg:SF (match_operand:SF 3 "gpc_reg_operand" "f"))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+{
+  return (TARGET_POWERPC
+	  ? "fmsubs %0,%1,%2,%3"
+	  : "{fms|fmsub} %0,%1,%2,%3");
+}
   [(set_attr "type" "fp")
    (set_attr "fp_type" "fp_maddsub_s")])
 
-(define_insn "*fnmaddsf4_power_1"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(neg:SF (plus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-				  (match_operand:SF 2 "gpc_reg_operand" "f"))
-			 (match_operand:SF 3 "gpc_reg_operand" "f"))))]
-  "! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD"
-  "{fnma|fnmadd} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")])
-
-(define_insn "*fnmaddsf4_power_2"
+(define_insn "*nfmasf4_fpr"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(minus:SF (mult:SF (neg:SF (match_operand:SF 1 "gpc_reg_operand" "f"))
-			   (match_operand:SF 2 "gpc_reg_operand" "f"))
-			 (match_operand:SF 3 "gpc_reg_operand" "f")))]
-  "! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD
-   && ! HONOR_SIGNED_ZEROS (SFmode)"
-  "{fnma|fnmadd} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")])
-
-(define_insn "*fnmsubsf4_powerpc_1"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(neg:SF (minus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-				   (match_operand:SF 2 "gpc_reg_operand" "f"))
-			  (match_operand:SF 3 "gpc_reg_operand" "f"))))]
-  "TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD
-   && TARGET_SINGLE_FLOAT"
-  "fnmsubs %0,%1,%2,%3"
+	(neg:SF (fma:SF (match_operand:SF 1 "gpc_reg_operand" "f")
+			(match_operand:SF 2 "gpc_reg_operand" "f")
+			(match_operand:SF 3 "gpc_reg_operand" "f"))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+{
+  return (TARGET_POWERPC
+	  ? "fnmadds %0,%1,%2,%3"
+	  : "{fnma|fnmadd} %0,%1,%2,%3");
+}
   [(set_attr "type" "fp")
    (set_attr "fp_type" "fp_maddsub_s")])
 
-(define_insn "*fnmsubsf4_powerpc_2"
+(define_insn "*nfmssf4_fpr"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(minus:SF (match_operand:SF 3 "gpc_reg_operand" "f")
-		  (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-			   (match_operand:SF 2 "gpc_reg_operand" "f"))))]
-  "TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD
-   && TARGET_SINGLE_FLOAT && ! HONOR_SIGNED_ZEROS (SFmode)"
-  "fnmsubs %0,%1,%2,%3"
+	(neg:SF (fma:SF (match_operand:SF 1 "gpc_reg_operand" "f")
+			(match_operand:SF 2 "gpc_reg_operand" "f")
+			(neg:SF (match_operand:SF 3 "gpc_reg_operand" "f")))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+{
+  return (TARGET_POWERPC
+	  ? "fnmsubs %0,%1,%2,%3"
+	  : "{fnms|fnmsub} %0,%1,%2,%3");
+}
   [(set_attr "type" "fp")
    (set_attr "fp_type" "fp_maddsub_s")])
 
-(define_insn "*fnmsubsf4_power_1"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(neg:SF (minus:SF (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-				   (match_operand:SF 2 "gpc_reg_operand" "f"))
-			  (match_operand:SF 3 "gpc_reg_operand" "f"))))]
-  "! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD"
-  "{fnms|fnmsub} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")])
-
-(define_insn "*fnmsubsf4_power_2"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(minus:SF (match_operand:SF 3 "gpc_reg_operand" "f")
-		  (mult:SF (match_operand:SF 1 "gpc_reg_operand" "%f")
-			   (match_operand:SF 2 "gpc_reg_operand" "f"))))]
-  "! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD
-   && ! HONOR_SIGNED_ZEROS (SFmode)"
-  "{fnms|fnmsub} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")])
-
 (define_expand "sqrtsf2"
   [(set (match_operand:SF 0 "gpc_reg_operand" "")
 	(sqrt:SF (match_operand:SF 1 "gpc_reg_operand" "")))]
@@ -5928,69 +5935,53 @@
   "fsqrt %0,%1"
   [(set_attr "type" "dsqrt")])
 
-(define_expand "rsqrtsf2"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")]
-		   UNSPEC_RSQRT))]
-  "TARGET_RECIP && TARGET_HARD_FLOAT && TARGET_PPC_GFXOPT && !optimize_size
-   && flag_finite_math_only && !flag_trapping_math"
-{
-  rs6000_emit_swrsqrtsf (operands[0], operands[1]);
-  DONE;
-})
-
-(define_insn "*rsqrt_internal1"
+(define_insn "*rsqrtsf_internal1"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
 	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")]
 		   UNSPEC_RSQRT))]
-  "TARGET_HARD_FLOAT && TARGET_PPC_GFXOPT"
-  "frsqrte %0,%1"
+  "TARGET_FRSQRTES"
+  "frsqrtes %0,%1"
   [(set_attr "type" "fp")])
 
-(define_expand "copysignsf3"
+(define_expand "copysign<mode>3"
   [(set (match_dup 3)
-        (abs:SF (match_operand:SF 1 "gpc_reg_operand" "")))
+        (abs:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "")))
    (set (match_dup 4)
-	(neg:SF (abs:SF (match_dup 1))))
-   (set (match_operand:SF 0 "gpc_reg_operand" "")
-        (if_then_else:SF (ge (match_operand:SF 2 "gpc_reg_operand" "")
-	                     (match_dup 5))
+	(neg:SFDF (abs:SFDF (match_dup 1))))
+   (set (match_operand:SFDF 0 "gpc_reg_operand" "")
+        (if_then_else:SFDF (ge (match_operand:SFDF 2 "gpc_reg_operand" "")
+			       (match_dup 5))
 			 (match_dup 3)
 			 (match_dup 4)))]
-  "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
-   && !HONOR_NANS (SFmode) && !HONOR_SIGNED_ZEROS (SFmode)"
-  {
-     operands[3] = gen_reg_rtx (SFmode);
-     operands[4] = gen_reg_rtx (SFmode);
-     operands[5] = CONST0_RTX (SFmode);
+  "TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>
+   && ((TARGET_PPC_GFXOPT
+        && !HONOR_NANS (<MODE>mode)
+        && !HONOR_SIGNED_ZEROS (<MODE>mode))
+       || TARGET_CMPB
+       || VECTOR_UNIT_VSX_P (<MODE>mode))"
+{
+  if (TARGET_CMPB || VECTOR_UNIT_VSX_P (<MODE>mode))
+    {
+      emit_insn (gen_copysign<mode>3_fcpsgn (operands[0], operands[1],
+					     operands[2]));
+      DONE;
+    }
+
+   operands[3] = gen_reg_rtx (<MODE>mode);
+   operands[4] = gen_reg_rtx (<MODE>mode);
+   operands[5] = CONST0_RTX (<MODE>mode);
   })
 
-(define_expand "copysigndf3"
-  [(set (match_dup 3)
-        (abs:DF (match_operand:DF 1 "gpc_reg_operand" "")))
-   (set (match_dup 4)
-	(neg:DF (abs:DF (match_dup 1))))
-   (set (match_operand:DF 0 "gpc_reg_operand" "")
-        (if_then_else:DF (ge (match_operand:DF 2 "gpc_reg_operand" "")
-	                     (match_dup 5))
-			 (match_dup 3)
-			 (match_dup 4)))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
-   && ((TARGET_PPC_GFXOPT
-        && !HONOR_NANS (DFmode)
-        && !HONOR_SIGNED_ZEROS (DFmode))
-       || VECTOR_UNIT_VSX_P (DFmode))"
-  {
-     if (VECTOR_UNIT_VSX_P (DFmode))
-       {
-	 emit_insn (gen_vsx_copysigndf3 (operands[0], operands[1],
-					 operands[2], CONST0_RTX (DFmode)));
-	 DONE;
-       }
-     operands[3] = gen_reg_rtx (DFmode);
-     operands[4] = gen_reg_rtx (DFmode);
-     operands[5] = CONST0_RTX (DFmode);
-  })
+;; Use an unspec rather providing an if-then-else in RTL, to prevent the
+;; compiler from optimizing -0.0
+(define_insn "copysign<mode>3_fcpsgn"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<rreg2>")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")
+		      (match_operand:SFDF 2 "gpc_reg_operand" "<rreg2>")]
+		     UNSPEC_COPYSIGN))]
+  "TARGET_CMPB && !VECTOR_UNIT_VSX_P (<MODE>mode)"
+  "fcpsgn %0,%2,%1"
+  [(set_attr "type" "fp")])
 
 ;; For MIN, MAX, and conditional move, we use DEFINE_EXPAND's that involve a
 ;; fsel instruction and some auxiliary computations.  Then we just have a
@@ -6053,9 +6044,41 @@
 ;; change the mode underneath our feet and then gets confused trying
 ;; to reload the value.
 (define_insn "isel_signed_<mode>"
+  [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r")
+	(if_then_else:GPR
+	 (match_operator 1 "scc_comparison_operator"
+			 [(match_operand:CC 4 "cc_reg_operand" "y,y")
+			  (const_int 0)])
+	 (match_operand:GPR 2 "reg_or_cint_operand" "O,b")
+	 (match_operand:GPR 3 "gpc_reg_operand" "r,r")))]
+  "TARGET_ISEL<sel>"
+  "*
+{ return output_isel (operands); }"
+  [(set_attr "type" "isel")
+   (set_attr "length" "4")])
+
+(define_insn "isel_unsigned_<mode>"
+  [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r")
+	(if_then_else:GPR
+	 (match_operator 1 "scc_comparison_operator"
+			 [(match_operand:CCUNS 4 "cc_reg_operand" "y,y")
+			  (const_int 0)])
+	 (match_operand:GPR 2 "reg_or_cint_operand" "O,b")
+	 (match_operand:GPR 3 "gpc_reg_operand" "r,r")))]
+  "TARGET_ISEL<sel>"
+  "*
+{ return output_isel (operands); }"
+  [(set_attr "type" "isel")
+   (set_attr "length" "4")])
+
+;; These patterns can be useful for combine; they let combine know that
+;; isel can handle reversed comparisons so long as the operands are
+;; registers.
+
+(define_insn "*isel_reversed_signed_<mode>"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
 	(if_then_else:GPR
-	 (match_operator 1 "comparison_operator"
+	 (match_operator 1 "scc_rev_comparison_operator"
 			 [(match_operand:CC 4 "cc_reg_operand" "y")
 			  (const_int 0)])
 	 (match_operand:GPR 2 "gpc_reg_operand" "b")
@@ -6066,10 +6089,10 @@
   [(set_attr "type" "isel")
    (set_attr "length" "4")])
 
-(define_insn "isel_unsigned_<mode>"
+(define_insn "*isel_reversed_unsigned_<mode>"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
 	(if_then_else:GPR
-	 (match_operator 1 "comparison_operator"
+	 (match_operator 1 "scc_rev_comparison_operator"
 			 [(match_operand:CCUNS 4 "cc_reg_operand" "y")
 			  (const_int 0)])
 	 (match_operand:GPR 2 "gpc_reg_operand" "b")
@@ -6219,98 +6242,74 @@
   "{fd|fdiv} %0,%1,%2"
   [(set_attr "type" "ddiv")])
 
-(define_expand "recipdf3"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")
-		    (match_operand:DF 2 "gpc_reg_operand" "d")]
-		   UNSPEC_FRES))]
-  "TARGET_RECIP && TARGET_HARD_FLOAT && TARGET_POPCNTB && !optimize_size
-   && flag_finite_math_only && !flag_trapping_math"
-{
-   rs6000_emit_swdivdf (operands[0], operands[1], operands[2]);
-   DONE;
-})
-
-(define_expand "fred"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")] UNSPEC_FRES))]
-  "(TARGET_POPCNTB || VECTOR_UNIT_VSX_P (DFmode)) && flag_finite_math_only"
-  "")
-
 (define_insn "*fred_fpr"
   [(set (match_operand:DF 0 "gpc_reg_operand" "=f")
 	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "f")] UNSPEC_FRES))]
-  "TARGET_POPCNTB && flag_finite_math_only && !VECTOR_UNIT_VSX_P (DFmode)"
+  "TARGET_FRE && !VECTOR_UNIT_VSX_P (DFmode)"
   "fre %0,%1"
   [(set_attr "type" "fp")])
 
-(define_insn "*fmadddf4_fpr"
+(define_insn "*rsqrtdf_internal1"
   [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(plus:DF (mult:DF (match_operand:DF 1 "gpc_reg_operand" "%d")
-			  (match_operand:DF 2 "gpc_reg_operand" "d"))
-		 (match_operand:DF 3 "gpc_reg_operand" "d")))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD && TARGET_DOUBLE_FLOAT
+	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")]
+		   UNSPEC_RSQRT))]
+  "TARGET_FRSQRTE && !VECTOR_UNIT_VSX_P (DFmode)"
+  "frsqrte %0,%1"
+  [(set_attr "type" "fp")])
+
+; builtin fma support
+(define_insn "*fmadf4_fpr"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=f")
+	(fma:DF (match_operand:DF 1 "gpc_reg_operand" "f")
+		(match_operand:DF 2 "gpc_reg_operand" "f")
+		(match_operand:DF 3 "gpc_reg_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
    && VECTOR_UNIT_NONE_P (DFmode)"
   "{fma|fmadd} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")
-   (set_attr "fp_type" "fp_maddsub_d")])
-
-(define_insn "*fmsubdf4_fpr"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(minus:DF (mult:DF (match_operand:DF 1 "gpc_reg_operand" "%d")
-			   (match_operand:DF 2 "gpc_reg_operand" "d"))
-		  (match_operand:DF 3 "gpc_reg_operand" "d")))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD && TARGET_DOUBLE_FLOAT
+  [(set_attr "type" "fp")
+   (set_attr "fp_type" "fp_maddsub_s")])
+
+(define_insn "*fmsdf4_fpr"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=f")
+	(fma:DF (match_operand:DF 1 "gpc_reg_operand" "f")
+		(match_operand:DF 2 "gpc_reg_operand" "f")
+		(neg:DF (match_operand:DF 3 "gpc_reg_operand" "f"))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
    && VECTOR_UNIT_NONE_P (DFmode)"
   "{fms|fmsub} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")
-   (set_attr "fp_type" "fp_maddsub_d")])
-
-(define_insn "*fnmadddf4_fpr_1"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(neg:DF (plus:DF (mult:DF (match_operand:DF 1 "gpc_reg_operand" "%d")
-				  (match_operand:DF 2 "gpc_reg_operand" "d"))
-			 (match_operand:DF 3 "gpc_reg_operand" "d"))))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD && TARGET_DOUBLE_FLOAT
+  [(set_attr "type" "fp")
+   (set_attr "fp_type" "fp_maddsub_s")])
+
+(define_insn "*nfmadf4_fpr"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=f")
+	(neg:DF (fma:DF (match_operand:DF 1 "gpc_reg_operand" "f")
+			(match_operand:DF 2 "gpc_reg_operand" "f")
+			(match_operand:DF 3 "gpc_reg_operand" "f"))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
    && VECTOR_UNIT_NONE_P (DFmode)"
   "{fnma|fnmadd} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")
-   (set_attr "fp_type" "fp_maddsub_d")])
-
-(define_insn "*fnmadddf4_fpr_2"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(minus:DF (mult:DF (neg:DF (match_operand:DF 1 "gpc_reg_operand" "d"))
-			   (match_operand:DF 2 "gpc_reg_operand" "d"))
-		  (match_operand:DF 3 "gpc_reg_operand" "d")))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD && TARGET_DOUBLE_FLOAT
-   && ! HONOR_SIGNED_ZEROS (DFmode) && VECTOR_UNIT_NONE_P (DFmode)"
-  "{fnma|fnmadd} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")
-   (set_attr "fp_type" "fp_maddsub_d")])
-
-(define_insn "*fnmsubdf4_fpr_1"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(neg:DF (minus:DF (mult:DF (match_operand:DF 1 "gpc_reg_operand" "%d")
-				   (match_operand:DF 2 "gpc_reg_operand" "d"))
-			  (match_operand:DF 3 "gpc_reg_operand" "d"))))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD && TARGET_DOUBLE_FLOAT
+  [(set_attr "type" "fp")
+   (set_attr "fp_type" "fp_maddsub_s")])
+
+(define_insn "*nfmsdf4_fpr"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=f")
+	(neg:DF (fma:DF (match_operand:DF 1 "gpc_reg_operand" "f")
+			(match_operand:DF 2 "gpc_reg_operand" "f")
+			(neg:DF (match_operand:DF 3 "gpc_reg_operand" "f")))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
    && VECTOR_UNIT_NONE_P (DFmode)"
   "{fnms|fnmsub} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")
-   (set_attr "fp_type" "fp_maddsub_d")])
-
-(define_insn "*fnmsubdf4_fpr_2"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(minus:DF (match_operand:DF 3 "gpc_reg_operand" "d")
-	          (mult:DF (match_operand:DF 1 "gpc_reg_operand" "%d")
-			   (match_operand:DF 2 "gpc_reg_operand" "d"))))]
-  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_FUSED_MADD && TARGET_DOUBLE_FLOAT
-   && ! HONOR_SIGNED_ZEROS (DFmode) && VECTOR_UNIT_NONE_P (DFmode)"
-  "{fnms|fnmsub} %0,%1,%2,%3"
-  [(set_attr "type" "dmul")
-   (set_attr "fp_type" "fp_maddsub_d")])
-
-(define_insn "sqrtdf2"
+  [(set_attr "type" "fp")
+   (set_attr "fp_type" "fp_maddsub_s")])
+
+(define_expand "sqrtdf2"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "")
+	(sqrt:DF (match_operand:DF 1 "gpc_reg_operand" "")))]
+  "(TARGET_PPC_GPOPT || TARGET_POWER2) && TARGET_HARD_FLOAT && TARGET_FPRS 
+   && TARGET_DOUBLE_FLOAT"
+  "")
+
+(define_insn "*sqrtdf2_fpr"
   [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
 	(sqrt:DF (match_operand:DF 1 "gpc_reg_operand" "d")))]
   "(TARGET_PPC_GPOPT || TARGET_POWER2) && TARGET_HARD_FLOAT && TARGET_FPRS 
@@ -6392,29 +6391,154 @@
 
 ;; Conversions to and from floating-point.
 
-(define_expand "fixuns_truncsfsi2"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "")
-	(unsigned_fix:SI (match_operand:SF 1 "gpc_reg_operand" "")))]
-  "TARGET_HARD_FLOAT && !TARGET_FPRS && TARGET_SINGLE_FLOAT"
-  "")
-
-(define_expand "fix_truncsfsi2"
- [(set (match_operand:SI 0 "gpc_reg_operand" "")
-      (fix:SI (match_operand:SF 1 "gpc_reg_operand" "")))]
- "TARGET_HARD_FLOAT && !TARGET_FPRS && TARGET_SINGLE_FLOAT"
- "")
-
-(define_expand "fixuns_truncdfsi2"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "")
-	(unsigned_fix:SI (match_operand:DF 1 "gpc_reg_operand" "")))]
-  "TARGET_HARD_FLOAT && TARGET_E500_DOUBLE"
-  "")
-
-(define_expand "fixuns_truncdfdi2"
-  [(set (match_operand:DI 0 "register_operand" "")
-	(unsigned_fix:DI (match_operand:DF 1 "register_operand" "")))]
-  "TARGET_HARD_FLOAT && TARGET_VSX"
-  "")
+; We don't define lfiwax/lfiwzx with the normal definition, because we
+; don't want to support putting SImode in FPR registers.
+(define_insn "lfiwax"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
+	(unspec:DI [(match_operand:SI 1 "indexed_or_indirect_operand" "Z")]
+		   UNSPEC_LFIWAX))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWAX"
+  "lfiwax %0,%y1"
+  [(set_attr "type" "fpload")])
+
+; This split must be run before register allocation because it allocates the
+; memory slot that is needed to move values to/from the FPR.  We don't allocate
+; it earlier to allow for the combiner to merge insns together where it might
+; not be needed and also in case the insns are deleted as dead code.
+
+(define_insn_and_split "floatsi<mode>2_lfiwax"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d")
+	(float:SFDF (match_operand:SI 1 "nonimmediate_operand" "r")))
+   (clobber (match_scratch:DI 2 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWAX
+   && <SI_CONVERT_FP> && can_create_pseudo_p ()"
+  "#"
+  ""
+  [(pc)]
+  "
+{
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  rtx tmp;
+
+  if (!MEM_P (src) && TARGET_MFPGPR && TARGET_POWERPC64)
+    tmp = convert_to_mode (DImode, src, false);
+  else
+    {
+      tmp = operands[2];
+      if (GET_CODE (tmp) == SCRATCH)
+	tmp = gen_reg_rtx (DImode);
+      if (MEM_P (src))
+	{
+	  src = rs6000_address_for_fpconvert (src);
+	  emit_insn (gen_lfiwax (tmp, src));
+	}
+      else
+	{
+	  rtx stack = rs6000_allocate_stack_temp (SImode, false, true);
+	  emit_move_insn (stack, src);
+	  emit_insn (gen_lfiwax (tmp, stack));
+	}
+    }
+  emit_insn (gen_floatdi<mode>2 (dest, tmp));
+  DONE;
+}"
+  [(set_attr "length" "12")
+   (set_attr "type" "fpload")])
+
+(define_insn_and_split "floatsi<mode>2_lfiwax_mem"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d,<rreg2>")
+	(float:SFDF
+	 (sign_extend:DI
+	  (match_operand:SI 1 "memory_operand" "Z,Z"))))
+   (clobber (match_scratch:DI 2 "=0,d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWAX
+   && <SI_CONVERT_FP>"
+  "#"
+  ""
+  [(pc)]
+  "
+{
+  operands[1] = rs6000_address_for_fpconvert (operands[1]);
+  if (GET_CODE (operands[2]) == SCRATCH)
+    operands[2] = gen_reg_rtx (DImode);
+  emit_insn (gen_lfiwax (operands[2], operands[1]));
+  emit_insn (gen_floatdi<mode>2 (operands[0], operands[2]));
+  DONE;
+}"
+  [(set_attr "length" "8")
+   (set_attr "type" "fpload")])
+
+(define_insn "lfiwzx"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
+	(unspec:DI [(match_operand:SI 1 "indexed_or_indirect_operand" "Z")]
+		   UNSPEC_LFIWZX))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWZX"
+  "lfiwzx %0,%y1"
+  [(set_attr "type" "fpload")])
+
+(define_insn_and_split "floatunssi<mode>2_lfiwzx"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d")
+	(unsigned_float:SFDF (match_operand:SI 1 "nonimmediate_operand" "r")))
+   (clobber (match_scratch:DI 2 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWZX
+   && <SI_CONVERT_FP>"
+  "#"
+  ""
+  [(pc)]
+  "
+{
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  rtx tmp;
+
+  if (!MEM_P (src) && TARGET_MFPGPR && TARGET_POWERPC64)
+    tmp = convert_to_mode (DImode, src, true);
+  else
+    {
+      tmp = operands[2];
+      if (GET_CODE (tmp) == SCRATCH)
+	tmp = gen_reg_rtx (DImode);
+      if (MEM_P (src))
+	{
+	  src = rs6000_address_for_fpconvert (src);
+	  emit_insn (gen_lfiwzx (tmp, src));
+	}
+      else
+	{
+	  rtx stack = rs6000_allocate_stack_temp (SImode, false, true);
+	  emit_move_insn (stack, src);
+	  emit_insn (gen_lfiwzx (tmp, stack));
+	}
+    }
+  emit_insn (gen_floatdi<mode>2 (dest, tmp));
+  DONE;
+}"
+  [(set_attr "length" "12")
+   (set_attr "type" "fpload")])
+
+(define_insn_and_split "floatunssi<mode>2_lfiwzx_mem"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d,<rreg2>")
+	(unsigned_float:SFDF
+	 (zero_extend:DI
+	  (match_operand:SI 1 "memory_operand" "Z,Z"))))
+   (clobber (match_scratch:DI 2 "=0,d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWZX
+   && <SI_CONVERT_FP>"
+  "#"
+  ""
+  [(pc)]
+  "
+{
+  operands[1] = rs6000_address_for_fpconvert (operands[1]);
+  if (GET_CODE (operands[2]) == SCRATCH)
+    operands[2] = gen_reg_rtx (DImode);
+  emit_insn (gen_lfiwzx (operands[2], operands[1]));
+  emit_insn (gen_floatdi<mode>2 (operands[0], operands[2]));
+  DONE;
+}"
+  [(set_attr "length" "8")
+   (set_attr "type" "fpload")])
 
 ; For each of these conversions, there is a define_expand, a define_insn
 ; with a '#' template, and a define_split (with C code).  The idea is
@@ -6423,7 +6547,7 @@
 
 (define_expand "floatsidf2"
   [(parallel [(set (match_operand:DF 0 "gpc_reg_operand" "")
-		   (float:DF (match_operand:SI 1 "gpc_reg_operand" "")))
+		   (float:DF (match_operand:SI 1 "nonimmediate_operand" "")))
 	      (use (match_dup 2))
 	      (use (match_dup 3))
 	      (clobber (match_dup 4))
@@ -6435,19 +6559,31 @@
 {
   if (TARGET_E500_DOUBLE)
     {
+      if (!REG_P (operands[1]))
+	operands[1] = force_reg (SImode, operands[1]);
       emit_insn (gen_spe_floatsidf2 (operands[0], operands[1]));
       DONE;
     }
-  if (TARGET_POWERPC64)
-    {
-      rtx x = convert_to_mode (DImode, operands[1], 0);
-      emit_insn (gen_floatdidf2 (operands[0], x));
+  else if (TARGET_LFIWAX && TARGET_FCFID)
+    {
+      emit_insn (gen_floatsidf2_lfiwax (operands[0], operands[1]));
       DONE;
     }
-
+  else if (TARGET_FCFID)
+    {
+      rtx dreg = operands[1];
+      if (!REG_P (dreg))
+	dreg = force_reg (SImode, dreg);
+      dreg = convert_to_mode (DImode, dreg, false);
+      emit_insn (gen_floatdidf2 (operands[0], dreg));
+      DONE;
+    }
+
+  if (!REG_P (operands[1]))
+    operands[1] = force_reg (SImode, operands[1]);
   operands[2] = force_reg (SImode, GEN_INT (0x43300000));
   operands[3] = force_reg (DFmode, CONST_DOUBLE_ATOF (\"4503601774854144\", DFmode));
-  operands[4] = assign_stack_temp (DFmode, GET_MODE_SIZE (DFmode), 0);
+  operands[4] = rs6000_allocate_stack_temp (DFmode, true, false);
   operands[5] = gen_reg_rtx (DFmode);
   operands[6] = gen_reg_rtx (SImode);
 }")
@@ -6460,7 +6596,7 @@
    (clobber (match_operand:DF 4 "offsettable_mem_operand" "=o"))
    (clobber (match_operand:DF 5 "gpc_reg_operand" "=&d"))
    (clobber (match_operand:SI 6 "gpc_reg_operand" "=&r"))]
-  "! TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
+  "! TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
   "#"
   ""
   [(pc)]
@@ -6484,39 +6620,82 @@
   emit_insn (gen_subdf3 (operands[0], operands[5], operands[3]));
   DONE;
 }"
-  [(set_attr "length" "24")])
-
+  [(set_attr "length" "24")
+   (set_attr "type" "fp")])
+
+;; If we don't have a direct conversion to single precision, don't enable this
+;; conversion for 32-bit without fast math, because we don't have the insn to
+;; generate the fixup swizzle to avoid double rounding problems.
 (define_expand "floatunssisf2"
   [(set (match_operand:SF 0 "gpc_reg_operand" "")
-        (unsigned_float:SF (match_operand:SI 1 "gpc_reg_operand" "")))]
-  "TARGET_HARD_FLOAT && !TARGET_FPRS && TARGET_SINGLE_FLOAT"
-  "")
+        (unsigned_float:SF (match_operand:SI 1 "nonimmediate_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_SINGLE_FLOAT
+   && (!TARGET_FPRS
+       || (TARGET_FPRS
+	   && ((TARGET_FCFIDUS && TARGET_LFIWZX)
+	       || (TARGET_DOUBLE_FLOAT && TARGET_FCFID
+		   && (TARGET_POWERPC64 || flag_unsafe_math_optimizations)))))"
+  "
+{
+  if (!TARGET_FPRS)
+    {
+      if (!REG_P (operands[1]))
+	operands[1] = force_reg (SImode, operands[1]);
+    }
+  else if (TARGET_LFIWZX && TARGET_FCFIDUS)
+    {
+      emit_insn (gen_floatunssisf2_lfiwzx (operands[0], operands[1]));
+      DONE;
+    }
+  else
+    {
+      rtx dreg = operands[1];
+      if (!REG_P (dreg))
+	dreg = force_reg (SImode, dreg);
+      dreg = convert_to_mode (DImode, dreg, true);
+      emit_insn (gen_floatdisf2 (operands[0], dreg));
+      DONE;
+    }
+}")
 
 (define_expand "floatunssidf2"
   [(parallel [(set (match_operand:DF 0 "gpc_reg_operand" "")
-		   (unsigned_float:DF (match_operand:SI 1 "gpc_reg_operand" "")))
+		   (unsigned_float:DF (match_operand:SI 1 "nonimmediate_operand" "")))
 	      (use (match_dup 2))
 	      (use (match_dup 3))
 	      (clobber (match_dup 4))
 	      (clobber (match_dup 5))])]
-  "TARGET_HARD_FLOAT && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)"
+  "TARGET_HARD_FLOAT
+   && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)"
   "
 {
   if (TARGET_E500_DOUBLE)
     {
+      if (!REG_P (operands[1]))
+	operands[1] = force_reg (SImode, operands[1]);
       emit_insn (gen_spe_floatunssidf2 (operands[0], operands[1]));
       DONE;
     }
-  if (TARGET_POWERPC64)
-    {
-      rtx x = convert_to_mode (DImode, operands[1], 1);
-      emit_insn (gen_floatdidf2 (operands[0], x));
+  else if (TARGET_LFIWZX && TARGET_FCFID)
+    {
+      emit_insn (gen_floatunssidf2_lfiwzx (operands[0], operands[1]));
       DONE;
     }
-
+  else if (TARGET_FCFID)
+    {
+      rtx dreg = operands[1];
+      if (!REG_P (dreg))
+	dreg = force_reg (SImode, dreg);
+      dreg = convert_to_mode (DImode, dreg, true);
+      emit_insn (gen_floatdidf2 (operands[0], dreg));
+      DONE;
+    }
+
+  if (!REG_P (operands[1]))
+    operands[1] = force_reg (SImode, operands[1]);
   operands[2] = force_reg (SImode, GEN_INT (0x43300000));
   operands[3] = force_reg (DFmode, CONST_DOUBLE_ATOF (\"4503599627370496\", DFmode));
-  operands[4] = assign_stack_temp (DFmode, GET_MODE_SIZE (DFmode), 0);
+  operands[4] = rs6000_allocate_stack_temp (DFmode, true, false);
   operands[5] = gen_reg_rtx (DFmode);
 }")
 
@@ -6527,7 +6706,8 @@
    (use (match_operand:DF 3 "gpc_reg_operand" "d"))
    (clobber (match_operand:DF 4 "offsettable_mem_operand" "=o"))
    (clobber (match_operand:DF 5 "gpc_reg_operand" "=&d"))]
-  "! TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
+  "! TARGET_FCFIDU && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
+   && !(TARGET_FCFID && TARGET_POWERPC64)"
   "#"
   ""
   [(pc)]
@@ -6549,50 +6729,83 @@
   emit_insn (gen_subdf3 (operands[0], operands[5], operands[3]));
   DONE;
 }"
-  [(set_attr "length" "20")])
-
-(define_expand "fix_truncdfsi2"
-  [(parallel [(set (match_operand:SI 0 "fix_trunc_dest_operand" "")
-		   (fix:SI (match_operand:DF 1 "gpc_reg_operand" "")))
-	      (clobber (match_dup 2))
-	      (clobber (match_dup 3))])]
-  "(TARGET_POWER2 || TARGET_POWERPC)
-   && TARGET_HARD_FLOAT && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)"
-  "
-{
-  if (TARGET_E500_DOUBLE)
-    {
-     emit_insn (gen_spe_fix_truncdfsi2 (operands[0], operands[1]));
-     DONE;
-    }
-  operands[2] = gen_reg_rtx (DImode);
-  if (TARGET_POWERPC64 && TARGET_MFPGPR && TARGET_HARD_FLOAT && TARGET_FPRS
-      && gpc_reg_operand(operands[0], GET_MODE (operands[0])))
-    {
-      operands[3] = gen_reg_rtx (DImode);
-      emit_insn (gen_fix_truncdfsi2_mfpgpr (operands[0], operands[1],
-					    operands[2], operands[3]));
+  [(set_attr "length" "20")
+   (set_attr "type" "fp")])
+
+(define_expand "fix_trunc<mode>si2"
+  [(set (match_operand:SI 0 "gpc_reg_operand" "")
+	(fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "")))]
+  "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT
+   && ((TARGET_FPRS && <TARGET_FLOAT>) || <E500_CONVERT>)"
+  "
+{
+  if (!<E500_CONVERT>)
+    {
+      rtx tmp, stack;
+
+      if (TARGET_STFIWX)
+	emit_insn (gen_fix_trunc<mode>si2_stfiwx (operands[0], operands[1]));
+      else
+	{
+	  tmp = gen_reg_rtx (DImode);
+	  stack = rs6000_allocate_stack_temp (DImode, true, false);
+	  emit_insn (gen_fix_trunc<mode>si2_internal (operands[0], operands[1],
+						      tmp, stack));
+	}
       DONE;
     }
-  if (TARGET_PPC_GFXOPT)
-    {
-      rtx orig_dest = operands[0];
-      if (! memory_operand (orig_dest, GET_MODE (orig_dest)))
-	operands[0] = assign_stack_temp (SImode, GET_MODE_SIZE (SImode), 0);
-      emit_insn (gen_fix_truncdfsi2_internal_gfxopt (operands[0], operands[1],
-						     operands[2]));
-      if (operands[0] != orig_dest)
-	emit_move_insn (orig_dest, operands[0]);
+}")
+
+; Like the convert to float patterns, this insn must be split before
+; register allocation so that it can allocate the memory slot if it
+; needed
+(define_insn_and_split "fix_trunc<mode>si2_stfiwx"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+	(fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d")))
+   (clobber (match_scratch:DI 2 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
+   && (<MODE>mode != SFmode || TARGET_SINGLE_FLOAT)
+   && TARGET_STFIWX && can_create_pseudo_p ()"
+  "#"
+  ""
+  [(pc)]
+{
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  rtx tmp = operands[2];
+
+  if (GET_CODE (tmp) == SCRATCH)
+    tmp = gen_reg_rtx (DImode);
+
+  emit_insn (gen_fctiwz_<mode> (tmp, src));
+  if (MEM_P (dest))
+    {
+      dest = rs6000_address_for_fpconvert (dest);
+      emit_insn (gen_stfiwx (dest, tmp));
       DONE;
     }
-  operands[3] = assign_stack_temp (DImode, GET_MODE_SIZE (DImode), 0);
-}")
-
-(define_insn_and_split "*fix_truncdfsi2_internal"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
-	(fix:SI (match_operand:DF 1 "gpc_reg_operand" "d")))
-   (clobber (match_operand:DI 2 "gpc_reg_operand" "=d"))
-   (clobber (match_operand:DI 3 "offsettable_mem_operand" "=o"))]
+  else if (TARGET_MFPGPR && TARGET_POWERPC64)
+    {
+      dest = gen_lowpart (DImode, dest);
+      emit_move_insn (dest, tmp);
+      DONE;
+    }
+  else
+    {
+      rtx stack = rs6000_allocate_stack_temp (SImode, false, true);
+      emit_insn (gen_stfiwx (stack, tmp));
+      emit_move_insn (dest, stack);
+      DONE;
+    }
+}
+  [(set_attr "length" "12")
+   (set_attr "type" "fp")])
+
+(define_insn_and_split "fix_trunc<mode>si2_internal"
+  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,?r")
+	(fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d,<rreg>")))
+   (clobber (match_operand:DI 2 "gpc_reg_operand" "=1,d"))
+   (clobber (match_operand:DI 3 "offsettable_mem_operand" "=o,o"))]
   "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS 
    && TARGET_DOUBLE_FLOAT"
   "#"
@@ -6604,143 +6817,269 @@
   gcc_assert (MEM_P (operands[3]));
   lowword = adjust_address (operands[3], SImode, WORDS_BIG_ENDIAN ? 4 : 0);
 
-  emit_insn (gen_fctiwz (operands[2], operands[1]));
+  emit_insn (gen_fctiwz_<mode> (operands[2], operands[1]));
   emit_move_insn (operands[3], operands[2]);
   emit_move_insn (operands[0], lowword);
   DONE;
 }"
-  [(set_attr "length" "16")])
-
-(define_insn_and_split "fix_truncdfsi2_internal_gfxopt"
-  [(set (match_operand:SI 0 "memory_operand" "=Z")
-	(fix:SI (match_operand:DF 1 "gpc_reg_operand" "d")))
-   (clobber (match_operand:DI 2 "gpc_reg_operand" "=d"))]
-  "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS 
-   && TARGET_DOUBLE_FLOAT 
-   && TARGET_PPC_GFXOPT"
+  [(set_attr "length" "16")
+   (set_attr "type" "fp")])
+
+(define_expand "fix_trunc<mode>di2"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "")
+	(fix:DI (match_operand:SFDF 1 "gpc_reg_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS
+   && TARGET_FCFID"
+  "")
+
+(define_insn "*fix_trunc<mode>di2_fctidz"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
+	(fix:DI (match_operand:SFDF 1 "gpc_reg_operand" "d")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS
+    && TARGET_FCFID && !VECTOR_UNIT_VSX_P (<MODE>mode)"
+  "fctidz %0,%1"
+  [(set_attr "type" "fp")])
+
+(define_expand "fixuns_trunc<mode>si2"
+  [(set (match_operand:SI 0 "gpc_reg_operand" "")
+	(unsigned_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "")))]
+  "TARGET_HARD_FLOAT
+   && ((TARGET_FPRS && <TARGET_FLOAT> && TARGET_FCTIWUZ && TARGET_STFIWX)
+       || <E500_CONVERT>)"
+  "
+{
+  if (!<E500_CONVERT>)
+    {
+      emit_insn (gen_fixuns_trunc<mode>si2_stfiwx (operands[0], operands[1]));
+      DONE;
+    }
+}")
+
+(define_insn_and_split "fixuns_trunc<mode>si2_stfiwx"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+	(unsigned_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d")))
+   (clobber (match_scratch:DI 2 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT> && TARGET_FCTIWUZ
+   && TARGET_STFIWX && can_create_pseudo_p ()"
   "#"
-  "&& 1"
+  ""
   [(pc)]
-  "
-{
-  emit_insn (gen_fctiwz (operands[2], operands[1]));
-  emit_insn (gen_stfiwx (operands[0], operands[2]));
-  DONE;
-}"
-  [(set_attr "length" "16")])
-
-(define_insn_and_split "fix_truncdfsi2_mfpgpr"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
-	(fix:SI (match_operand:DF 1 "gpc_reg_operand" "d")))
-   (clobber (match_operand:DI 2 "gpc_reg_operand" "=d"))
-   (clobber (match_operand:DI 3 "gpc_reg_operand" "=r"))]
-  "TARGET_POWERPC64 && TARGET_MFPGPR && TARGET_HARD_FLOAT && TARGET_FPRS 
-   && TARGET_DOUBLE_FLOAT"
-  "#"
-  "&& 1"
-  [(set (match_dup 2) (unspec:DI [(fix:SI (match_dup 1))] UNSPEC_FCTIWZ))
-   (set (match_dup 3) (match_dup 2))
-   (set (match_dup 0) (subreg:SI (match_dup 3) 4))]
-  ""
-  [(set_attr "length" "12")])
+{
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  rtx tmp = operands[2];
+
+  if (GET_CODE (tmp) == SCRATCH)
+    tmp = gen_reg_rtx (DImode);
+
+  emit_insn (gen_fctiwuz_<mode> (tmp, src));
+  if (MEM_P (dest))
+    {
+      dest = rs6000_address_for_fpconvert (dest);
+      emit_insn (gen_stfiwx (dest, tmp));
+      DONE;
+    }
+  else if (TARGET_MFPGPR && TARGET_POWERPC64)
+    {
+      dest = gen_lowpart (DImode, dest);
+      emit_move_insn (dest, tmp);
+      DONE;
+    }
+  else
+    {
+      rtx stack = rs6000_allocate_stack_temp (SImode, false, true);
+      emit_insn (gen_stfiwx (stack, tmp));
+      emit_move_insn (dest, stack);
+      DONE;
+    }
+}
+  [(set_attr "length" "12")
+   (set_attr "type" "fp")])
+
+(define_expand "fixuns_trunc<mode>di2"
+  [(set (match_operand:DI 0 "register_operand" "")
+	(unsigned_fix:DI (match_operand:SFDF 1 "register_operand" "")))]
+  "TARGET_HARD_FLOAT && (TARGET_FCTIDUZ || VECTOR_UNIT_VSX_P (<MODE>mode))"
+  "")
+
+(define_insn "*fixuns_trunc<mode>di2_fctiduz"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
+	(unsigned_fix:DI (match_operand:SFDF 1 "gpc_reg_operand" "d")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS
+    && TARGET_FCTIDUZ && !VECTOR_UNIT_VSX_P (<MODE>mode)"
+  "fctiduz %0,%1"
+  [(set_attr "type" "fp")])
 
 ; Here, we use (set (reg) (unspec:DI [(fix:SI ...)] UNSPEC_FCTIWZ))
 ; rather than (set (subreg:SI (reg)) (fix:SI ...))
 ; because the first makes it clear that operand 0 is not live
 ; before the instruction.
-(define_insn "fctiwz"
+(define_insn "fctiwz_<mode>"
   [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
-	(unspec:DI [(fix:SI (match_operand:DF 1 "gpc_reg_operand" "d"))]
+	(unspec:DI [(fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d"))]
 		   UNSPEC_FCTIWZ))]
   "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS 
    && TARGET_DOUBLE_FLOAT"
   "{fcirz|fctiwz} %0,%1"
   [(set_attr "type" "fp")])
 
-(define_expand "btruncdf2"
+(define_insn "fctiwuz_<mode>"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
+	(unspec:DI [(unsigned_fix:SI
+		     (match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>"))]
+		   UNSPEC_FCTIWUZ))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT> && TARGET_FCTIWUZ"
+  "fctiwuz %0,%1"
+  [(set_attr "type" "fp")])
+
+;; Only optimize (float (fix x)) -> frz if we are in fast-math mode, since
+;; since the friz instruction does not truncate the value if the floating
+;; point value is < LONG_MIN or > LONG_MAX.
+(define_insn "*friz"
   [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")] UNSPEC_FRIZ))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
-  "")
-
-(define_insn "*btruncdf2_fpr"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=f")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "f")] UNSPEC_FRIZ))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
-   && !VECTOR_UNIT_VSX_P (DFmode)"
-  "friz %0,%1"
-  [(set_attr "type" "fp")])
-
-(define_insn "btruncsf2"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")] UNSPEC_FRIZ))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+	(float:DF (fix:DI (match_operand:DF 1 "gpc_reg_operand" "d"))))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_FPRND
+   && !VECTOR_UNIT_VSX_P (DFmode) && flag_unsafe_math_optimizations
+   && !flag_trapping_math && TARGET_FRIZ"
   "friz %0,%1"
   [(set_attr "type" "fp")])
 
-(define_expand "ceildf2"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "")] UNSPEC_FRIP))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
-  "")
-
-(define_insn "*ceildf2_fpr"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")] UNSPEC_FRIP))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
-   && !VECTOR_UNIT_VSX_P (DFmode)"
-  "frip %0,%1"
+;; Since FCTIWZ doesn't sign extend the upper bits, we have to do a store and a
+;; load to properly sign extend the value, but at least doing a store, load
+;; into a GPR to sign extend, a store from the GPR and a load back into the FPR
+;; if we have 32-bit memory ops
+(define_insn_and_split "*round32<mode>2_fprs"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d")
+	(float:SFDF
+	 (fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d"))))
+   (clobber (match_scratch:DI 2 "=d"))
+   (clobber (match_scratch:DI 3 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
+   && <SI_CONVERT_FP> && TARGET_LFIWAX && TARGET_STFIWX && TARGET_FCFID
+   && can_create_pseudo_p ()"
+  "#"
+  ""
+  [(pc)]
+{
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  rtx tmp1 = operands[2];
+  rtx tmp2 = operands[3];
+  rtx stack = rs6000_allocate_stack_temp (SImode, false, true);
+
+  if (GET_CODE (tmp1) == SCRATCH)
+    tmp1 = gen_reg_rtx (DImode);
+  if (GET_CODE (tmp2) == SCRATCH)
+    tmp2 = gen_reg_rtx (DImode);
+
+  emit_insn (gen_fctiwz_<mode> (tmp1, src));
+  emit_insn (gen_stfiwx (stack, tmp1));
+  emit_insn (gen_lfiwax (tmp2, stack));
+  emit_insn (gen_floatdi<mode>2 (dest, tmp2));
+  DONE;
+}
+  [(set_attr "type" "fpload")
+   (set_attr "length" "16")])
+
+(define_insn_and_split "*roundu32<mode>2_fprs"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d")
+	(unsigned_float:SFDF
+	 (unsigned_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d"))))
+   (clobber (match_scratch:DI 2 "=d"))
+   (clobber (match_scratch:DI 3 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
+   && TARGET_LFIWZX && TARGET_STFIWX && TARGET_FCFIDU
+   && can_create_pseudo_p ()"
+  "#"
+  ""
+  [(pc)]
+{
+  rtx dest = operands[0];
+  rtx src = operands[1];
+  rtx tmp1 = operands[2];
+  rtx tmp2 = operands[3];
+  rtx stack = rs6000_allocate_stack_temp (SImode, false, true);
+
+  if (GET_CODE (tmp1) == SCRATCH)
+    tmp1 = gen_reg_rtx (DImode);
+  if (GET_CODE (tmp2) == SCRATCH)
+    tmp2 = gen_reg_rtx (DImode);
+
+  emit_insn (gen_fctiwuz_<mode> (tmp1, src));
+  emit_insn (gen_stfiwx (stack, tmp1));
+  emit_insn (gen_lfiwzx (tmp2, stack));
+  emit_insn (gen_floatdi<mode>2 (dest, tmp2));
+  DONE;
+}
+  [(set_attr "type" "fpload")
+   (set_attr "length" "16")])
+
+;; No VSX equivalent to fctid
+(define_insn "lrint<mode>di2"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=d")
+	(unspec:DI [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")]
+		   UNSPEC_FCTID))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>"
+  "fctid %0,%1"
   [(set_attr "type" "fp")])
 
-(define_insn "ceilsf2"
- [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")] UNSPEC_FRIP))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT "
+(define_expand "btrunc<mode>2"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "")]
+		     UNSPEC_FRIZ))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>"
+  "")
+
+(define_insn "*btrunc<mode>2_fpr"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<rreg2>")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")]
+		     UNSPEC_FRIZ))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>
+   && !VECTOR_UNIT_VSX_P (<MODE>mode)"
+  "friz %0,%1"
+  [(set_attr "type" "fp")])
+
+(define_expand "ceil<mode>2"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "")]
+		     UNSPEC_FRIP))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>"
+  "")
+
+(define_insn "*ceil<mode>2_fpr"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<rreg2>")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")]
+		     UNSPEC_FRIP))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>
+   && !VECTOR_UNIT_VSX_P (<MODE>mode)"
   "frip %0,%1"
   [(set_attr "type" "fp")])
 
-(define_expand "floordf2"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "")] UNSPEC_FRIM))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
-  "")
-
-(define_insn "*floordf2_fpr"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")] UNSPEC_FRIM))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
-   && !VECTOR_UNIT_VSX_P (DFmode)"
-  "frim %0,%1"
-  [(set_attr "type" "fp")])
-
-(define_insn "floorsf2"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")] UNSPEC_FRIM))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT "
+(define_expand "floor<mode>2"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "")]
+		     UNSPEC_FRIM))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>"
+  "")
+
+(define_insn "*floor<mode>2_fpr"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<rreg2>")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")]
+		     UNSPEC_FRIM))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>
+   && !VECTOR_UNIT_VSX_P (<MODE>mode)"
   "frim %0,%1"
   [(set_attr "type" "fp")])
 
 ;; No VSX equivalent to frin
-(define_insn "rounddf2"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d")] UNSPEC_FRIN))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
+(define_insn "round<mode>2"
+  [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<rreg2>")
+	(unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")]
+		     UNSPEC_FRIN))]
+  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT>"
   "frin %0,%1"
   [(set_attr "type" "fp")])
 
-(define_insn "roundsf2"
-  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-	(unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "f")] UNSPEC_FRIN))]
-  "TARGET_FPRND && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT "
-  "frin %0,%1"
-  [(set_attr "type" "fp")])
-
-(define_expand "ftruncdf2"
-  [(set (match_operand:DF 0 "gpc_reg_operand" "")
-  	(fix:DF (match_operand:DF 1 "gpc_reg_operand" "")))]
-  "VECTOR_UNIT_VSX_P (DFmode)"
-  "")
-
 ; An UNSPEC is used so we don't have to support SImode in FP registers.
 (define_insn "stfiwx"
   [(set (match_operand:SI 0 "memory_operand" "=Z")
@@ -6750,83 +7089,173 @@
   "stfiwx %1,%y0"
   [(set_attr "type" "fpstore")])
 
+;; If we don't have a direct conversion to single precision, don't enable this
+;; conversion for 32-bit without fast math, because we don't have the insn to
+;; generate the fixup swizzle to avoid double rounding problems.
 (define_expand "floatsisf2"
   [(set (match_operand:SF 0 "gpc_reg_operand" "")
-        (float:SF (match_operand:SI 1 "gpc_reg_operand" "")))]
-  "TARGET_HARD_FLOAT && !TARGET_FPRS"
-  "")
+        (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_SINGLE_FLOAT
+   && (!TARGET_FPRS
+       || (TARGET_FPRS
+	   && ((TARGET_FCFIDS && TARGET_LFIWAX)
+	       || (TARGET_DOUBLE_FLOAT && TARGET_FCFID
+		   && (TARGET_POWERPC64 || flag_unsafe_math_optimizations)))))"
+  "
+{
+  if (!TARGET_FPRS)
+    {
+      if (!REG_P (operands[1]))
+	operands[1] = force_reg (SImode, operands[1]);
+    }
+  else if (TARGET_FCFIDS && TARGET_LFIWAX)
+    {
+      emit_insn (gen_floatsisf2_lfiwax (operands[0], operands[1]));
+      DONE;
+    }
+  else if (TARGET_FCFID && TARGET_LFIWAX)
+    {
+      rtx dfreg = gen_reg_rtx (DFmode);
+      emit_insn (gen_floatsidf2_lfiwax (dfreg, operands[1]));
+      emit_insn (gen_truncdfsf2 (operands[0], dfreg));
+      DONE;
+    }
+  else
+    {
+      rtx dreg = operands[1];
+      if (!REG_P (dreg))
+	dreg = force_reg (SImode, dreg);
+      dreg = convert_to_mode (DImode, dreg, false);
+      emit_insn (gen_floatdisf2 (operands[0], dreg));
+      DONE;
+    }
+}")
 
 (define_expand "floatdidf2"
   [(set (match_operand:DF 0 "gpc_reg_operand" "")
 	(float:DF (match_operand:DI 1 "gpc_reg_operand" "")))]
-  "(TARGET_POWERPC64 || TARGET_XILINX_FPU || VECTOR_UNIT_VSX_P (DFmode))
-   && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS"
+  "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS"
   "")
 
 (define_insn "*floatdidf2_fpr"
   [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
-	(float:DF (match_operand:DI 1 "gpc_reg_operand" "!d#r")))]
-  "(TARGET_POWERPC64 || TARGET_XILINX_FPU)
-   && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS
+	(float:DF (match_operand:DI 1 "gpc_reg_operand" "d")))]
+  "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS
    && !VECTOR_UNIT_VSX_P (DFmode)"
   "fcfid %0,%1"
   [(set_attr "type" "fp")])
 
+; Allow the combiner to merge source memory operands to the conversion so that
+; the optimizer/register allocator doesn't try to load the value too early in a
+; GPR and then use store/load to move it to a FPR and suffer from a store-load
+; hit.  We will split after reload to avoid the trip through the GPRs
+
+(define_insn_and_split "*floatdidf2_mem"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
+	(float:DF (match_operand:DI 1 "memory_operand" "m")))
+   (clobber (match_scratch:DI 2 "=d"))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS && TARGET_FCFID"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (float:DF (match_dup 2)))]
+  ""
+  [(set_attr "length" "8")
+   (set_attr "type" "fpload")])
+
 (define_expand "floatunsdidf2"
   [(set (match_operand:DF 0 "gpc_reg_operand" "")
-	(unsigned_float:DF (match_operand:DI 1 "gpc_reg_operand" "")))]
-  "TARGET_VSX"
-  "")
-
-(define_expand "fix_truncdfdi2"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "")
-	(fix:DI (match_operand:DF 1 "gpc_reg_operand" "")))]
-  "(TARGET_POWERPC64 || TARGET_XILINX_FPU || VECTOR_UNIT_VSX_P (DFmode))
-    && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS"
-  "")
-
-(define_insn "*fix_truncdfdi2_fpr"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "=!d#r")
-	(fix:DI (match_operand:DF 1 "gpc_reg_operand" "d")))]
-  "(TARGET_POWERPC64 || TARGET_XILINX_FPU)
-    && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS
-    && !VECTOR_UNIT_VSX_P (DFmode)"
-  "fctidz %0,%1"
-  [(set_attr "type" "fp")])
+	(unsigned_float:DF
+	 (match_operand:DI 1 "gpc_reg_operand" "")))]
+  "TARGET_HARD_FLOAT && (TARGET_FCFIDU || VECTOR_UNIT_VSX_P (DFmode))"
+  "")
+
+(define_insn "*floatunsdidf2_fcfidu"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
+	(unsigned_float:DF (match_operand:DI 1 "gpc_reg_operand" "d")))]
+  "TARGET_HARD_FLOAT && TARGET_FCFIDU && !VECTOR_UNIT_VSX_P (DFmode)"
+  "fcfidu %0,%1"
+  [(set_attr "type" "fp")
+   (set_attr "length" "4")])
+
+(define_insn_and_split "*floatunsdidf2_mem"
+  [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
+	(unsigned_float:DF (match_operand:DI 1 "memory_operand" "m")))
+   (clobber (match_scratch:DI 2 "=d"))]
+  "TARGET_HARD_FLOAT && (TARGET_FCFIDU || VECTOR_UNIT_VSX_P (DFmode))"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (unsigned_float:DF (match_dup 2)))]
+  ""
+  [(set_attr "length" "8")
+   (set_attr "type" "fpload")])
 
 (define_expand "floatdisf2"
   [(set (match_operand:SF 0 "gpc_reg_operand" "")
         (float:SF (match_operand:DI 1 "gpc_reg_operand" "")))]
-  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT "
-  "
-{
-  rtx val = operands[1];
-  if (!flag_unsafe_math_optimizations)
-    {
-      rtx label = gen_label_rtx ();
-      val = gen_reg_rtx (DImode);
-      emit_insn (gen_floatdisf2_internal2 (val, operands[1], label));
-      emit_label (label);
-    }
-  emit_insn (gen_floatdisf2_internal1 (operands[0], val));
+  "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
+   && (TARGET_FCFIDS || TARGET_POWERPC64 || flag_unsafe_math_optimizations)"
+  "
+{
+  if (!TARGET_FCFIDS)
+    {
+      rtx val = operands[1];
+      if (!flag_unsafe_math_optimizations)
+	{
+	  rtx label = gen_label_rtx ();
+	  val = gen_reg_rtx (DImode);
+	  emit_insn (gen_floatdisf2_internal2 (val, operands[1], label));
+	  emit_label (label);
+	}
+      emit_insn (gen_floatdisf2_internal1 (operands[0], val));
+      DONE;
+    }
+}")
+
+(define_insn "floatdisf2_fcfids"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
+	(float:SF (match_operand:DI 1 "gpc_reg_operand" "d")))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
+   && TARGET_DOUBLE_FLOAT && TARGET_FCFIDS"
+  "fcfids %0,%1"
+  [(set_attr "type" "fp")])
+
+(define_insn_and_split "*floatdisf2_mem"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
+	(float:SF (match_operand:DI 1 "memory_operand" "m")))
+   (clobber (match_scratch:DI 2 "=f"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
+   && TARGET_DOUBLE_FLOAT && TARGET_FCFIDS"
+  "#"
+  "&& reload_completed"
+  [(pc)]
+  "
+{
+  emit_move_insn (operands[2], operands[1]);
+  emit_insn (gen_floatdisf2_fcfids (operands[0], operands[2]));
   DONE;
-}")
+}"
+  [(set_attr "length" "8")])
 
 ;; This is not IEEE compliant if rounding mode is "round to nearest".
 ;; If the DI->DF conversion is inexact, then it's possible to suffer
 ;; from double rounding.
+;; Instead of creating a new cpu type for two FP operations, just use fp
 (define_insn_and_split "floatdisf2_internal1"
   [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
-        (float:SF (match_operand:DI 1 "gpc_reg_operand" "!d#r")))
+        (float:SF (match_operand:DI 1 "gpc_reg_operand" "d")))
    (clobber (match_scratch:DF 2 "=d"))]
-  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+  "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
   "#"
   "&& reload_completed"
   [(set (match_dup 2)
         (float:DF (match_dup 1)))
    (set (match_dup 0)
         (float_truncate:SF (match_dup 2)))]
-  "")
+  ""
+  [(set_attr "length" "8")
+   (set_attr "type" "fp")])
 
 ;; Twiddles bits to avoid double rounding.
 ;; Bits that might be truncated when converting to DFmode are replaced
@@ -6859,6 +7288,39 @@
   operands[3] = gen_reg_rtx (DImode);
   operands[4] = gen_reg_rtx (CCUNSmode);
 }")
+
+(define_expand "floatunsdisf2"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "")
+        (unsigned_float:SF (match_operand:DI 1 "gpc_reg_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
+   && TARGET_DOUBLE_FLOAT && TARGET_FCFIDUS"
+  "")
+
+(define_insn "floatunsdisf2_fcfidus"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
+        (unsigned_float:SF (match_operand:DI 1 "gpc_reg_operand" "d")))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
+   && TARGET_DOUBLE_FLOAT && TARGET_FCFIDUS"
+  "fcfidus %0,%1"
+  [(set_attr "type" "fp")])
+
+(define_insn_and_split "*floatunsdisf2_mem"
+  [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
+	(unsigned_float:SF (match_operand:DI 1 "memory_operand" "m")))
+   (clobber (match_scratch:DI 2 "=f"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT
+   && TARGET_DOUBLE_FLOAT && TARGET_FCFIDUS"
+  "#"
+  "&& reload_completed"
+  [(pc)]
+  "
+{
+  emit_move_insn (operands[2], operands[1]);
+  emit_insn (gen_floatunsdisf2_fcfidus (operands[0], operands[2]));
+  DONE;
+}"
+  [(set_attr "length" "8")
+   (set_attr "type" "fpload")])
 
 ;; Define the DImode operations that can be done in a small number
 ;; of instructions.  The & constraints are to prevent the register
@@ -9141,73 +9603,9 @@
     default:
       gcc_unreachable ();
     case 0:
-      /* We normally copy the low-numbered register first.  However, if
-	 the first register operand 0 is the same as the second register
-	 of operand 1, we must copy in the opposite order.  */
-      if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
-	return \"mr %L0,%L1\;mr %0,%1\";
-      else
-	return \"mr %0,%1\;mr %L0,%L1\";
     case 1:
-      if (rs6000_offsettable_memref_p (operands[1])
-	  || (GET_CODE (operands[1]) == MEM
-	      && (GET_CODE (XEXP (operands[1], 0)) == LO_SUM
-		  || GET_CODE (XEXP (operands[1], 0)) == PRE_INC
-		  || GET_CODE (XEXP (operands[1], 0)) == PRE_DEC
-		  || GET_CODE (XEXP (operands[1], 0)) == PRE_MODIFY)))
-	{
-	  /* If the low-address word is used in the address, we must load
-	     it last.  Otherwise, load it first.  Note that we cannot have
-	     auto-increment in that case since the address register is
-	     known to be dead.  */
-	  if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
-				 operands[1], 0))
-	    return \"{l|lwz} %L0,%L1\;{l|lwz} %0,%1\";
-	  else
-	    return \"{l%U1%X1|lwz%U1%X1} %0,%1\;{l|lwz} %L0,%L1\";
-	}
-      else
-	{
-	  rtx addreg;
-
-	  addreg = find_addr_reg (XEXP (operands[1], 0));
-	  if (refers_to_regno_p (REGNO (operands[0]),
-				 REGNO (operands[0]) + 1,
-				 operands[1], 0))
-	    {
-	      output_asm_insn (\"{cal|la} %0,4(%0)\", &addreg);
-	      output_asm_insn (\"{l%X1|lwz%X1} %L0,%1\", operands);
-	      output_asm_insn (\"{cal|la} %0,-4(%0)\", &addreg);
-	      return \"{l%X1|lwz%X1} %0,%1\";
-	    }
-	  else
-	    {
-	      output_asm_insn (\"{l%X1|lwz%X1} %0,%1\", operands);
-	      output_asm_insn (\"{cal|la} %0,4(%0)\", &addreg);
-	      output_asm_insn (\"{l%X1|lwz%X1} %L0,%1\", operands);
-	      output_asm_insn (\"{cal|la} %0,-4(%0)\", &addreg);
-	      return \"\";
-	    }
-	}
     case 2:
-      if (rs6000_offsettable_memref_p (operands[0])
-	  || (GET_CODE (operands[0]) == MEM
-	      && (GET_CODE (XEXP (operands[0], 0)) == LO_SUM
-		  || GET_CODE (XEXP (operands[0], 0)) == PRE_INC
-		  || GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
-		  || GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)))
-	return \"{st%U0%X0|stw%U0%X0} %1,%0\;{st|stw} %L1,%L0\";
-      else
-	{
-	  rtx addreg;
-
-	  addreg = find_addr_reg (XEXP (operands[0], 0));
-	  output_asm_insn (\"{st%X0|stw%X0} %1,%0\", operands);
-	  output_asm_insn (\"{cal|la} %0,4(%0)\", &addreg);
-	  output_asm_insn (\"{st%X0|stw%X0} %L1,%0\", operands);
-	  output_asm_insn (\"{cal|la} %0,-4(%0)\", &addreg);
-	  return \"\";
-	}
+      return \"#\";
     case 3:
     case 4:
       return \"xxlor %x0,%x1,%x1\";
@@ -9242,38 +9640,7 @@
        || TARGET_SOFT_FLOAT || TARGET_E500_SINGLE)
    && (gpc_reg_operand (operands[0], DFmode)
        || gpc_reg_operand (operands[1], DFmode))"
-  "*
-{
-  switch (which_alternative)
-    {
-    default:
-      gcc_unreachable ();
-    case 0:
-      /* We normally copy the low-numbered register first.  However, if
-	 the first register operand 0 is the same as the second register of
-	 operand 1, we must copy in the opposite order.  */
-      if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
-	return \"mr %L0,%L1\;mr %0,%1\";
-      else
-	return \"mr %0,%1\;mr %L0,%L1\";
-    case 1:
-      /* If the low-address word is used in the address, we must load
-	 it last.  Otherwise, load it first.  Note that we cannot have
-	 auto-increment in that case since the address register is
-	 known to be dead.  */
-      if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
-			     operands[1], 0))
-	return \"{l|lwz} %L0,%L1\;{l|lwz} %0,%1\";
-      else
-	return \"{l%U1%X1|lwz%U1%X1} %0,%1\;{l|lwz} %L0,%L1\";
-    case 2:
-      return \"{st%U0%X0|stw%U0%X0} %1,%0\;{st|stw} %L1,%L0\";
-    case 3:
-    case 4:
-    case 5:
-      return \"#\";
-    }
-}"
+  "#"
   [(set_attr "type" "two,load,store,*,*,*")
    (set_attr "length" "8,8,8,8,12,16")])
 
@@ -9603,7 +9970,7 @@
   gcc_assert (MEM_P (operands[5]));
   lowword = adjust_address (operands[5], SImode, WORDS_BIG_ENDIAN ? 4 : 0);
 
-  emit_insn (gen_fctiwz (operands[4], operands[2]));
+  emit_insn (gen_fctiwz_df (operands[4], operands[2]));
   emit_move_insn (operands[5], operands[4]);
   emit_move_insn (operands[0], lowword);
   DONE;
@@ -9685,8 +10052,8 @@
 ; 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 "rs6000_nonimmediate_operand" "=o<>,r,r,*d,*d,m,r")
-	(match_operand:DI 1 "input_operand" "r,r,m,d,m,d,IJKnGHF"))]
+  [(set (match_operand:DI 0 "rs6000_nonimmediate_operand" "=o<>,r,r,*d,*d,m,r,?wa")
+	(match_operand:DI 1 "input_operand" "r,r,m,d,m,d,IJKnGHF,O"))]
   "! TARGET_POWERPC64
    && (gpc_reg_operand (operands[0], DImode)
        || gpc_reg_operand (operands[1], DImode))"
@@ -9697,13 +10064,15 @@
    fmr %0,%1
    lfd%U1%X1 %0,%1
    stfd%U0%X0 %1,%0
-   #"
-  [(set_attr "type" "load,*,store,fp,fpload,fpstore,*")])
+   #
+   xxlxor %x0,%x0,%x0"
+  [(set_attr "type" "load,*,store,fp,fpload,fpstore,*,vecsimple")])
 
 (define_split
   [(set (match_operand:DI 0 "gpc_reg_operand" "")
 	(match_operand:DI 1 "const_int_operand" ""))]
-  "! TARGET_POWERPC64 && reload_completed"
+  "! TARGET_POWERPC64 && reload_completed
+   && gpr_or_gpr_p (operands[0], operands[1])"
   [(set (match_dup 2) (match_dup 4))
    (set (match_dup 3) (match_dup 1))]
   "
@@ -9722,8 +10091,8 @@
 }")
 
 (define_split
-  [(set (match_operand:DI 0 "rs6000_nonimmediate_operand" "")
-        (match_operand:DI 1 "input_operand" ""))]
+  [(set (match_operand:DIFD 0 "rs6000_nonimmediate_operand" "")
+        (match_operand:DIFD 1 "input_operand" ""))]
   "reload_completed && !TARGET_POWERPC64
    && gpr_or_gpr_p (operands[0], operands[1])"
   [(pc)]
@@ -9755,8 +10124,8 @@
    (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4,4,4")])
 
 (define_insn "*movdi_internal64"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,r,r,r,*d,*d,m,r,*h,*h")
-	(match_operand:DI 1 "input_operand" "r,m,r,I,L,nF,R,d,m,d,*h,r,0"))]
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,r,r,r,*d,*d,m,r,*h,*h,?wa")
+	(match_operand:DI 1 "input_operand" "r,m,r,I,L,nF,R,d,m,d,*h,r,0,O"))]
   "TARGET_POWERPC64 && (!TARGET_MFPGPR || !TARGET_HARD_FLOAT || !TARGET_FPRS)
    && (gpc_reg_operand (operands[0], DImode)
        || gpc_reg_operand (operands[1], DImode))"
@@ -9773,9 +10142,10 @@
    stfd%U0%X0 %1,%0
    mf%1 %0
    mt%0 %1
-   {cror 0,0,0|nop}"
-  [(set_attr "type" "*,load,store,*,*,*,*,fp,fpload,fpstore,mfjmpr,mtjmpr,*")
-   (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4")])
+   {cror 0,0,0|nop}
+   xxlxor %x0,%x0,%x0"
+  [(set_attr "type" "*,load,store,*,*,*,*,fp,fpload,fpstore,mfjmpr,mtjmpr,*,vecsimple")
+   (set_attr "length" "4,4,4,4,4,20,4,4,4,4,4,4,4,4")])
 
 ;; immediate value valid for a single instruction hiding in a const_double
 (define_insn ""
@@ -11065,7 +11435,12 @@
 		   UNSPEC_TLSGD)
    (clobber (reg:SI LR_REGNO))]
   "HAVE_AS_TLS && DEFAULT_ABI == ABI_AIX"
-  "addi %0,%1,%2@got@tlsgd\;bl %z3\;%."
+{
+  if (TARGET_CMODEL != CMODEL_SMALL)
+    return "addis %0,%1,%2@got@tlsgd@ha\;addi %0,%0,%2@got@tlsgd@l\;bl %z3\;%.";
+  else
+    return "addi %0,%1,%2@got@tlsgd\;bl %z3\;%.";
+}
   "&& TARGET_TLS_MARKERS"
   [(set (match_dup 0)
 	(unspec:TLSmode [(match_dup 1)
@@ -11078,7 +11453,10 @@
 	      (clobber (reg:SI LR_REGNO))])]
   ""
   [(set_attr "type" "two")
-   (set_attr "length" "12")])
+   (set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 16)
+     		   (const_int 12)))])
 
 (define_insn_and_split "tls_gd_sysv<TLSmode:tls_sysv_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
@@ -11114,13 +11492,47 @@
   [(set_attr "type" "two")
    (set_attr "length" "8")])
 
-(define_insn "*tls_gd<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "*tls_gd<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
 			 (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
 			UNSPEC_TLSGD))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS"
   "addi %0,%1,%2@got@tlsgd"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 3)
+  	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD))))
+   (set (match_dup 0)
+   	(lo_sum:TLSmode (match_dup 3)
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD)))]
+  "
+{
+  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_gd_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	  (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			  UNSPEC_TLSGD))))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addis %0,%1,%2@got@tlsgd@ha"
+  [(set_attr "length" "4")])
+
+(define_insn "*tls_gd_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+		       UNSPEC_TLSGD)))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addi %0,%1,%2@got@tlsgd@l"
   [(set_attr "length" "4")])
 
 (define_insn "*tls_gd_call_aix<TLSmode:tls_abi_suffix>"
@@ -11163,7 +11575,12 @@
 		   UNSPEC_TLSLD)
    (clobber (reg:SI LR_REGNO))]
   "HAVE_AS_TLS && DEFAULT_ABI == ABI_AIX"
-  "addi %0,%1,%&@got@tlsld\;bl %z2\;%."
+{
+  if (TARGET_CMODEL != CMODEL_SMALL)
+    return "addis %0,%1,%&@got@tlsld@ha\;addi %0,%0,%&@got@tlsld@l\;bl %z2\;%.";
+  else
+    return "addi %0,%1,%&@got@tlsld\;bl %z2\;%.";
+}
   "&& TARGET_TLS_MARKERS"
   [(set (match_dup 0)
 	(unspec:TLSmode [(match_dup 1)]
@@ -11174,7 +11591,11 @@
 	      (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)
 	      (clobber (reg:SI LR_REGNO))])]
   ""
-  [(set_attr "length" "12")])
+  [(set_attr "type" "two")
+   (set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 16)
+     		   (const_int 12)))])
 
 (define_insn_and_split "tls_ld_sysv<TLSmode:tls_sysv_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
@@ -11207,12 +11628,44 @@
   ""
   [(set_attr "length" "8")])
 
-(define_insn "*tls_ld<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "*tls_ld<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")]
 			UNSPEC_TLSLD))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS"
   "addi %0,%1,%&@got@tlsld"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 2)
+  	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD))))
+   (set (match_dup 0)
+   	(lo_sum:TLSmode (match_dup 2)
+	    (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))]
+  "
+{
+  operands[2] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_ld_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	  (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD))))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addis %0,%1,%&@got@tlsld@ha"
+  [(set_attr "length" "4")])
+
+(define_insn "*tls_ld_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD)))]
+  "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
+  "addi %0,%1,%&@got@tlsld@l"
   [(set_attr "length" "4")])
 
 (define_insn "*tls_ld_call_aix<TLSmode:tls_abi_suffix>"
@@ -11269,13 +11722,48 @@
   "HAVE_AS_TLS"
   "addi %0,%1,%2@dtprel@l")
 
-(define_insn "tls_got_dtprel_<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "tls_got_dtprel_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
 			 (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
 			UNSPEC_TLSGOTDTPREL))]
   "HAVE_AS_TLS"
-  "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel(%1)")
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel(%1)"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 3)
+	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL))))
+   (set (match_dup 0)
+	(lo_sum:TLSmode (match_dup 3)
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTDTPREL)))]
+  "
+{
+  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_got_dtprel_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	 (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			 UNSPEC_TLSGOTDTPREL))))]
+  "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
+  "addis %0,%1,%2@got@dtprel@ha"
+  [(set_attr "length" "4")])
+
+(define_insn "*tls_got_dtprel_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+	 (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			 UNSPEC_TLSGOTDTPREL)))]
+  "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel@l(%1)"
+  [(set_attr "length" "4")])
 
 (define_insn "tls_tprel_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
@@ -11304,13 +11792,48 @@
 ;; "b" output constraint here and on tls_tls input to support linker tls
 ;; optimization.  The linker may edit the instructions emitted by a
 ;; tls_got_tprel/tls_tls pair to addis,addi.
-(define_insn "tls_got_tprel_<TLSmode:tls_abi_suffix>"
+(define_insn_and_split "tls_got_tprel_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
 	(unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")
 			 (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
 			UNSPEC_TLSGOTTPREL))]
   "HAVE_AS_TLS"
-  "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel(%1)")
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel(%1)"
+  "&& TARGET_CMODEL != CMODEL_SMALL"
+  [(set (match_dup 3)
+	(plus:TLSmode (match_dup 1)
+	  (high:TLSmode
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL))))
+   (set (match_dup 0)
+	(lo_sum:TLSmode (match_dup 3)
+	    (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGOTTPREL)))]
+  "
+{
+  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+}"
+  [(set (attr "length")
+     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
+     		   (const_int 8)
+     		   (const_int 4)))])
+
+(define_insn "*tls_got_tprel_high<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b")
+     (plus:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+       (high:TLSmode
+	 (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			 UNSPEC_TLSGOTTPREL))))]
+  "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
+  "addis %0,%1,%2@got@tprel@ha"
+  [(set_attr "length" "4")])
+
+(define_insn "*tls_got_tprel_low<TLSmode:tls_abi_suffix>"
+  [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
+     (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b")
+	 (unspec:TLSmode [(match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")]
+			 UNSPEC_TLSGOTTPREL)))]
+  "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL"
+  "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel@l(%1)"
+  [(set_attr "length" "4")])
 
 (define_insn "tls_tls_<TLSmode:tls_abi_suffix>"
   [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r")
@@ -11319,7 +11842,6 @@
 			UNSPEC_TLSTLS))]
   "HAVE_AS_TLS"
   "add %0,%1,%2@tls")
-
 
 ;; Next come insns related to the calling sequence.
 ;;
@@ -11521,10 +12043,12 @@
 
 (define_insn "load_toc_v4_PIC_1b"
   [(set (reg:SI LR_REGNO)
-	(unspec:SI [(match_operand:SI 0 "immediate_operand" "s")]
-		UNSPEC_TOCPTR))]
+	(unspec:SI [(match_operand:SI 0 "immediate_operand" "s")
+		    (label_ref (match_operand 1 "" ""))]
+		UNSPEC_TOCPTR))
+   (match_dup 1)]
   "TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2"
-  "bcl 20,31,$+8\\n\\t.long %0-$"
+  "bcl 20,31,$+8\;.long %0-$"
   [(set_attr "type" "branch")
    (set_attr "length" "8")])
 
@@ -11538,8 +12062,8 @@
   [(set_attr "type" "load")])
 
 (define_insn "load_toc_v4_PIC_3b"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "=b")
-	(plus:SI (match_operand:SI 1 "gpc_reg_operand" "r")
+  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
+	(plus:SI (match_operand:SI 1 "gpc_reg_operand" "b")
 		 (high:SI
 		   (minus:SI (match_operand:SI 2 "symbol_ref_operand" "s")
 			     (match_operand:SI 3 "symbol_ref_operand" "s")))))]
@@ -11607,6 +12131,21 @@
    "@
     {cal|la} %0,%2@l(%1)
     {ai|addic} %0,%1,%K2")
+
+;; Largetoc support
+(define_insn "largetoc_high"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=b")
+        (plus:DI (match_operand:DI 1 "gpc_reg_operand" "b")
+	         (high:DI (match_operand:DI 2 "" ""))))]
+   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
+   "{cau|addis} %0,%1,%2@ha")
+
+(define_insn "largetoc_low"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+        (lo_sum:DI (match_operand:DI 1 "gpc_reg_operand" "b")
+	           (match_operand:DI 2 "" "")))]
+   "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL"
+   "{cal %0,%2@l(%1)|addi %0,%1,%2@l}")
 
 ;; A function pointer under AIX is a pointer to a data area whose first word
 ;; contains the actual address of the function, whose second word contains a
@@ -12587,6 +13126,15 @@
   "{st%U0%X0|stw%U0%X0} 0,%0"
   [(set_attr "type" "store")
    (set_attr "length" "4")])
+
+(define_insn "probe_stack_range<P:mode>"
+  [(set (match_operand:P 0 "register_operand" "=r")
+	(unspec_volatile:P [(match_operand:P 1 "register_operand" "0")
+			    (match_operand:P 2 "register_operand" "r")]
+			   UNSPECV_PROBE_STACK_RANGE))]
+  ""
+  "* return output_probe_stack_range (operands[0], operands[2]);"
+  [(set_attr "type" "three")])
 
 ;; Compare insns are next.  Note that the RS/6000 has two types of compares,
 ;; signed & unsigned, and one type of branch.
@@ -12910,26 +13458,27 @@
     (clobber (match_scratch:DF 7 "=d"))
     (clobber (match_scratch:DF 8 "=d"))
     (clobber (match_scratch:DF 9 "=d"))
-    (clobber (match_scratch:DF 10 "=d"))]
+    (clobber (match_scratch:DF 10 "=d"))
+    (clobber (match_scratch:GPR 11 "=b"))]
   "!TARGET_IEEEQUAD && TARGET_XL_COMPAT
    && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LONG_DOUBLE_128"
   "#"
   "&& reload_completed"
-  [(set (match_dup 3) (match_dup 13))
-   (set (match_dup 4) (match_dup 14))
+  [(set (match_dup 3) (match_dup 14))
+   (set (match_dup 4) (match_dup 15))
    (set (match_dup 9) (abs:DF (match_dup 5)))
    (set (match_dup 0) (compare:CCFP (match_dup 9) (match_dup 3)))
    (set (pc) (if_then_else (ne (match_dup 0) (const_int 0))
-			   (label_ref (match_dup 11))
+			   (label_ref (match_dup 12))
 			   (pc)))
    (set (match_dup 0) (compare:CCFP (match_dup 5) (match_dup 7)))
-   (set (pc) (label_ref (match_dup 12)))
-   (match_dup 11)
+   (set (pc) (label_ref (match_dup 13)))
+   (match_dup 12)
    (set (match_dup 10) (minus:DF (match_dup 5) (match_dup 7)))
    (set (match_dup 9) (minus:DF (match_dup 6) (match_dup 8)))
    (set (match_dup 9) (plus:DF (match_dup 10) (match_dup 9)))
-   (set (match_dup 0) (compare:CCFP (match_dup 7) (match_dup 4)))
-   (match_dup 12)]
+   (set (match_dup 0) (compare:CCFP (match_dup 9) (match_dup 4)))
+   (match_dup 13)]
 {
   REAL_VALUE_TYPE rv;
   const int lo_word = FLOAT_WORDS_BIG_ENDIAN ? GET_MODE_SIZE (DFmode) : 0;
@@ -12939,22 +13488,23 @@
   operands[6] = simplify_gen_subreg (DFmode, operands[1], TFmode, lo_word);
   operands[7] = simplify_gen_subreg (DFmode, operands[2], TFmode, hi_word);
   operands[8] = simplify_gen_subreg (DFmode, operands[2], TFmode, lo_word);
-  operands[11] = gen_label_rtx ();
   operands[12] = gen_label_rtx ();
+  operands[13] = gen_label_rtx ();
   real_inf (&rv);
-  operands[13] = force_const_mem (DFmode,
+  operands[14] = force_const_mem (DFmode,
 				  CONST_DOUBLE_FROM_REAL_VALUE (rv, DFmode));
-  operands[14] = force_const_mem (DFmode,
+  operands[15] = force_const_mem (DFmode,
 				  CONST_DOUBLE_FROM_REAL_VALUE (dconst0,
 								DFmode));
   if (TARGET_TOC)
     {
-      operands[13] = gen_const_mem (DFmode,
-				    create_TOC_reference (XEXP (operands[13], 0)));
-      operands[14] = gen_const_mem (DFmode,
-				    create_TOC_reference (XEXP (operands[14], 0)));
-      set_mem_alias_set (operands[13], get_TOC_alias_set ());
+      rtx tocref;
+      tocref = create_TOC_reference (XEXP (operands[14], 0), operands[11]);
+      operands[14] = gen_const_mem (DFmode, tocref);
+      tocref = create_TOC_reference (XEXP (operands[15], 0), operands[11]);
+      operands[15] = gen_const_mem (DFmode, tocref);
       set_mem_alias_set (operands[14], get_TOC_alias_set ());
+      set_mem_alias_set (operands[15], get_TOC_alias_set ());
     }
 })
 
@@ -15400,6 +15950,15 @@
   ""
   [(set_attr "length" "0")])
 
+; Like stack_tie, but depend on both fp and sp based memory.
+(define_insn "frame_tie"
+  [(set (match_operand:BLK 0 "memory_operand" "+m")
+	(unspec:BLK [(match_dup 0)
+		     (match_operand:BLK 1 "memory_operand" "m")] UNSPEC_TIE))]
+  ""
+  ""
+  [(set_attr "length" "0")])
+
 
 (define_expand "epilogue"
   [(use (const_int 0))]
@@ -15579,6 +16138,73 @@
   [(set_attr "type" "integer")])
 
 
+;; Builtin fma support.  Handle 
+;; Note that the conditions for expansion are in the FMA_F iterator.
+
+(define_expand "fma<mode>4"
+  [(set (match_operand:FMA_F 0 "register_operand" "")
+	(fma:FMA_F
+	  (match_operand:FMA_F 1 "register_operand" "")
+	  (match_operand:FMA_F 2 "register_operand" "")
+	  (match_operand:FMA_F 3 "register_operand" "")))]
+  ""
+  "")
+
+; Altivec only has fma and nfms.
+(define_expand "fms<mode>4"
+  [(set (match_operand:FMA_F 0 "register_operand" "")
+	(fma:FMA_F
+	  (match_operand:FMA_F 1 "register_operand" "")
+	  (match_operand:FMA_F 2 "register_operand" "")
+	  (neg:FMA_F (match_operand:FMA_F 3 "register_operand" ""))))]
+  "!VECTOR_UNIT_ALTIVEC_P (<MODE>mode)"
+  "")
+
+;; If signed zeros are ignored, -(a * b - c) = -a * b + c.
+(define_expand "fnma<mode>4"
+  [(set (match_operand:FMA_F 0 "register_operand" "")
+	(neg:FMA_F
+	  (fma:FMA_F
+	    (match_operand:FMA_F 1 "register_operand" "")
+	    (match_operand:FMA_F 2 "register_operand" "")
+	    (neg:FMA_F (match_operand:FMA_F 3 "register_operand" "")))))]
+  "!HONOR_SIGNED_ZEROS (<MODE>mode)"
+  "")
+
+;; If signed zeros are ignored, -(a * b + c) = -a * b - c.
+(define_expand "fnms<mode>4"
+  [(set (match_operand:FMA_F 0 "register_operand" "")
+	(neg:FMA_F
+	  (fma:FMA_F
+	    (match_operand:FMA_F 1 "register_operand" "")
+	    (match_operand:FMA_F 2 "register_operand" "")
+	    (match_operand:FMA_F 3 "register_operand" ""))))]
+  "!HONOR_SIGNED_ZEROS (<MODE>mode) && !VECTOR_UNIT_ALTIVEC_P (<MODE>mode)"
+  "")
+
+; Not an official optab name, but used from builtins.
+(define_expand "nfma<mode>4"
+  [(set (match_operand:FMA_F 0 "register_operand" "")
+	(neg:FMA_F
+	  (fma:FMA_F
+	    (match_operand:FMA_F 1 "register_operand" "")
+	    (match_operand:FMA_F 2 "register_operand" "")
+	    (match_operand:FMA_F 3 "register_operand" ""))))]
+  "!VECTOR_UNIT_ALTIVEC_P (<MODE>mode)"
+  "")
+
+; Not an official optab name, but used from builtins.
+(define_expand "nfms<mode>4"
+  [(set (match_operand:FMA_F 0 "register_operand" "")
+	(neg:FMA_F
+	  (fma:FMA_F
+	    (match_operand:FMA_F 1 "register_operand" "")
+	    (match_operand:FMA_F 2 "register_operand" "")
+	    (neg:FMA_F (match_operand:FMA_F 3 "register_operand" "")))))]
+  ""
+  "")
+
+
 
 (include "sync.md")
 (include "vector.md")