diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5acc17367d6966c587b8f6689359575ed0aed18e..e99685a65bd3ec95d31b258bb0438bcf601a2381 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,17 @@
+2005-03-25  Geoffrey Keating  <geoffk@apple.com>
+
+	* config/rs6000/rs6000.md (UNSPEC constants): Add UNSPEC_STFIWX.
+	(fix_truncdfsi2): Allow registers or memory as destination.
+	When TARGET_PPC_GFXOPT, generate simplified pattern.
+	(fix_truncdfsi2_internal): Use define_insn_and_split.
+	(fix_truncdfsi2_internal_gfxopt): New.
+	(fctiwz): Don't confuse register allocation by giving it no choices.
+	(stfiwx): New.
+	* config/rs6000/rs6000.h (EXTRA_CONSTRAINT): Add 'Z'.
+	(EXTRA_MEMORY_CONSTRAINT): Likewise.
+	* config/rs6000/rs6000.c (indexed_or_indirect_operand): New. 
+	* config/rs6000/rs6000-protos.h (indexed_or_indirect_operand): New.
+
 2005-03-25  Kazu Hirata  <kazu@cs.umass.edu>
 
 	* dominance.c (free_dominance_info): Speed up by freeing et
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index 018de14ec2d397ea04e58fb3dfbd7f3dcb17c028..dd8590cf9e16144914626338a57da57a69ce89b2 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -49,6 +49,7 @@ extern bool rs6000_legitimate_offset_address_p (enum machine_mode, rtx, int);
 extern rtx rs6000_got_register (rtx);
 extern rtx find_addr_reg (rtx);
 extern int word_offset_memref_operand (rtx, enum machine_mode);
+extern int indexed_or_indirect_operand (rtx, enum machine_mode);
 extern rtx gen_easy_vector_constant_add_self (rtx);
 extern const char *output_vec_const_move (rtx *);
 extern void build_mask64_2_operands (rtx, rtx *);
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 0cf135f01af95c8c5b5c5b08dd8eab4a3d5bffc6..3fc857a459b0b7606060434b37c8cb787e4a3b39 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -2297,6 +2297,25 @@ word_offset_memref_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
   return (off % 4) == 0;
 }
 
+/* Return true if the operand is an indirect or indexed memory operand.  */
+
+int
+indexed_or_indirect_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  rtx addr;
+  if (!memory_operand (op, mode))
+    return 0;
+
+  addr = XEXP (op, 0);
+  if (GET_CODE (addr) == REG)
+    return 1;
+  if (GET_CODE (addr) == PLUS
+      && GET_CODE (XEXP (addr, 0)) == REG
+      && GET_CODE (XEXP (addr, 1)) == REG)
+    return 1;
+  return 0;
+}
+
 /* Return true if either operand is a general purpose register.  */
 
 bool
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index 8e7a2543dc13bc023ab55181e02d4eeb42ae77a3..adef69579d77061050ba9de42a18953234164a3a 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -1357,6 +1357,7 @@ enum reg_class
    'U' is for V.4 small data references.
    'W' is a vector constant that can be easily generated (no mem refs).
    'Y' is a indexed or word-aligned displacement memory operand.
+   'Z' is an indexed or indirect memory operand.
    't' is for AND masks that can be performed by two rldic{l,r} insns.  */
 
 #define EXTRA_CONSTRAINT(OP, C)						\
@@ -1372,6 +1373,7 @@ enum reg_class
 		   && !mask64_operand (OP, DImode))			\
    : (C) == 'W' ? (easy_vector_constant (OP, GET_MODE (OP)))		\
    : (C) == 'Y' ? (word_offset_memref_operand (OP, GET_MODE (OP)))      \
+   : (C) == 'Z' ? (indexed_or_indirect_operand (OP, GET_MODE (OP)))	\
    : 0)
 
 /* Define which constraints are memory constraints.  Tell reload
@@ -1379,7 +1381,7 @@ enum reg_class
    memory address into a base register if required.  */
 
 #define EXTRA_MEMORY_CONSTRAINT(C, STR)				\
-  ((C) == 'Q' || (C) == 'Y')
+  ((C) == 'Q' || (C) == 'Y' || (C) == 'Z')
 
 /* Given an rtx X being reloaded into a reg required to be
    in class CLASS, return the class of reg to actually use.
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index fc4b6f1cfb2aa121f5f84e2adc02457a29775d3d..b9fa39c386df77750ab9bb0c654a0d85569ee2d4 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -51,6 +51,7 @@
    (UNSPEC_TLSTLS		29)
    (UNSPEC_FIX_TRUNC_TF		30)	; fadd, rounding towards zero
    (UNSPEC_MV_CR_GT		31)	; move_from_CR_eq_bit
+   (UNSPEC_STFIWX		32)
   ])
 
 ;;
@@ -5272,7 +5273,7 @@
 }")
 
 (define_expand "fix_truncdfsi2"
-  [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "")
+  [(parallel [(set (match_operand:SI 0 "reg_or_mem_operand" "")
 		   (fix:SI (match_operand:DF 1 "gpc_reg_operand" "")))
 	      (clobber (match_dup 2))
 	      (clobber (match_dup 3))])]
@@ -5286,28 +5287,29 @@
      DONE;
     }
   operands[2] = gen_reg_rtx (DImode);
+  if (TARGET_PPC_GFXOPT)
+    {
+      rtx orig_dest = operands[0];
+      if (GET_CODE (orig_dest) != MEM)
+	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]);
+      DONE;
+    }
   operands[3] = assign_stack_temp (DImode, GET_MODE_SIZE (DImode), 0);
 }")
 
-(define_insn "*fix_truncdfsi2_internal"
+(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" "f")))
    (clobber (match_operand:DI 2 "gpc_reg_operand" "=f"))
    (clobber (match_operand:DI 3 "memory_operand" "=o"))]
   "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS"
   "#"
-  [(set_attr "length" "16")])
-
-(define_split
-  [(set (match_operand:SI 0 "gpc_reg_operand" "")
-	(fix:SI (match_operand:DF 1 "gpc_reg_operand" "")))
-   (clobber (match_operand:DI 2 "gpc_reg_operand" ""))
-   (clobber (match_operand:DI 3 "offsettable_mem_operand" ""))]
-  "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS"
-  [(set (match_operand:SI 0 "gpc_reg_operand" "")
-	(fix:SI (match_operand:DF 1 "gpc_reg_operand" "")))
-   (clobber (match_operand:DI 2 "gpc_reg_operand" ""))
-   (clobber (match_operand:DI 3 "offsettable_mem_operand" ""))]
+  "&& 1"
+  [(pc)]
   "
 {
   rtx lowword;
@@ -5321,20 +5323,47 @@
   emit_move_insn (operands[3], operands[2]);
   emit_move_insn (operands[0], gen_rtx_MEM (SImode, 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" "f")))
+   (clobber (match_operand:DI 2 "gpc_reg_operand" "=f"))]
+  "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS
+   && TARGET_PPC_GFXOPT"
+  "#"
+  "&& 1"
+  [(pc)]
+  "
+{
+  emit_insn (gen_fctiwz (operands[2], operands[1]));
+  emit_insn (gen_stfiwx (operands[0], operands[2]));
+  DONE;
+}"
+  [(set_attr "length" "16")])
 
 ; 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"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "=*f")
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=f")
 	(unspec:DI [(fix:SI (match_operand:DF 1 "gpc_reg_operand" "f"))]
 		   UNSPEC_FCTIWZ))]
   "(TARGET_POWER2 || TARGET_POWERPC) && TARGET_HARD_FLOAT && TARGET_FPRS"
   "{fcirz|fctiwz} %0,%1"
   [(set_attr "type" "fp")])
 
+; 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")
+	(unspec:SI [(match_operand:DI 1 "gpc_reg_operand" "f")]
+		   UNSPEC_STFIWX))]
+  "TARGET_PPC_GFXOPT"
+  "stfiwx %1,%y0"
+  [(set_attr "type" "fpstore")])
+
 (define_expand "floatsisf2"
   [(set (match_operand:SF 0 "gpc_reg_operand" "")
         (float:SF (match_operand:SI 1 "gpc_reg_operand" "")))]
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index d6da70773d721a71acb1b06253ccd68585860c43..7594edc797a8d5460dd82bd168d5610496dcac34 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2005-03-25  Geoffrey Keating  <geoffk@apple.com>
+
+	* gcc.dg/ppc-stfiwx.c: New.
+
 2005-03-25  Steve Ellcey  <sje@cup.hp.com>
 
 	PR target/19888
diff --git a/gcc/testsuite/gcc.dg/ppc-stfiwx.c b/gcc/testsuite/gcc.dg/ppc-stfiwx.c
new file mode 100644
index 0000000000000000000000000000000000000000..47a29ed3f17aad0e9fb4b82dd601bcbe6e611c86
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ppc-stfiwx.c
@@ -0,0 +1,8 @@
+/* { dg-do compile { target powerpc*-*-* } } */
+/* { dg-options "-mpowerpc-gfxopt" } */
+/* { dg-final { scan-assembler "stfiwx" } } */
+
+int foo (double x)
+{
+  return x;
+}