diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 0ae22cb8a01fd33b361694e0b0c17d0015604de6..7818fed3841370c692006fc7157182670467492a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,16 @@
+2004-02-26  Eric Botcazou  <ebotcazou@act-europe.fr>
+
+	* config/sparc/sparc-protos.h (sparc_emit_floatunsdi): Add 'mode'.
+	(sparc_emit_fixunsdi): New prototype.
+	* config/sparc/sparc.c (sparc_emit_floatunsdi): Use 'mode' argument.
+	(sparc_emit_fixunsdi): New function.
+	* config/sparc/sparc.md (floatunsdisf2): Use 'general_operand' for
+	operand 1.  Pass SFmode to sparc_emit_floatunsdi.
+	(floatunsdidf2): Use 'general_operand' for operand 1.  Pass DFmode
+	to sparc_emit_floatunsdi.
+	(fixuns_truncsfdi2): New expander.
+	(fixuns_truncdfdi2): Likewise.
+
 2004-02-26  Alan Modra  <amodra@bigpond.net.au>
 
 	* gcse.c (delete_null_pointer_checks_1): Do not delete CC setter
diff --git a/gcc/config/sparc/sparc-protos.h b/gcc/config/sparc/sparc-protos.h
index cc164df2d78b2196730ef74b9579313b65f855e8..0908aaa5ea58f53ffbbfa0c8ad01c31ec539b4e2 100644
--- a/gcc/config/sparc/sparc-protos.h
+++ b/gcc/config/sparc/sparc-protos.h
@@ -63,7 +63,8 @@ extern enum machine_mode select_cc_mode (enum rtx_code, rtx, rtx);
 /* Define the function that build the compare insn for scc and bcc.  */
 extern rtx gen_compare_reg (enum rtx_code code, rtx, rtx);
 extern void sparc_emit_float_lib_cmp (rtx, rtx, enum rtx_code);
-extern void sparc_emit_floatunsdi (rtx [2]);
+extern void sparc_emit_floatunsdi (rtx [2], enum machine_mode);
+extern void sparc_emit_fixunsdi (rtx [2], enum machine_mode);
 extern void emit_tfmode_binop (enum rtx_code, rtx *);
 extern void emit_tfmode_unop (enum rtx_code, rtx *);
 extern void emit_tfmode_cvt (enum rtx_code, rtx *);
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 258dfc2d81e545d39f246a28ab5498f9de7a2706..079ed5d9d164a39078ef05efb7066f9e5abeb24c 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -6419,14 +6419,12 @@ sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
    optabs would emit if we didn't have TFmode patterns.  */
 
 void
-sparc_emit_floatunsdi (rtx *operands)
+sparc_emit_floatunsdi (rtx *operands, enum machine_mode mode)
 {
   rtx neglab, donelab, i0, i1, f0, in, out;
-  enum machine_mode mode;
 
   out = operands[0];
   in = force_reg (DImode, operands[1]);
-  mode = GET_MODE (out);
   neglab = gen_label_rtx ();
   donelab = gen_label_rtx ();
   i0 = gen_reg_rtx (DImode);
@@ -6450,6 +6448,47 @@ sparc_emit_floatunsdi (rtx *operands)
   emit_label (donelab);
 }
 
+/* Generate an FP to unsigned DImode conversion.  This is the same code
+   optabs would emit if we didn't have TFmode patterns.  */
+
+void
+sparc_emit_fixunsdi (rtx *operands, enum machine_mode mode)
+{
+  rtx neglab, donelab, i0, i1, f0, in, out, limit;
+
+  out = operands[0];
+  in = force_reg (mode, operands[1]);
+  neglab = gen_label_rtx ();
+  donelab = gen_label_rtx ();
+  i0 = gen_reg_rtx (DImode);
+  i1 = gen_reg_rtx (DImode);
+  limit = gen_reg_rtx (mode);
+  f0 = gen_reg_rtx (mode);
+
+  emit_move_insn (limit,
+		  CONST_DOUBLE_FROM_REAL_VALUE (
+		    REAL_VALUE_ATOF ("9223372036854775808.0", mode), mode));
+  emit_cmp_and_jump_insns (in, limit, GE, NULL_RTX, mode, 0, neglab);
+
+  emit_insn (gen_rtx_SET (VOIDmode,
+			  out,
+			  gen_rtx_FIX (DImode, gen_rtx_FIX (mode, in))));
+  emit_jump_insn (gen_jump (donelab));
+  emit_barrier ();
+
+  emit_label (neglab);
+
+  emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_MINUS (mode, in, limit)));
+  emit_insn (gen_rtx_SET (VOIDmode,
+			  i0,
+			  gen_rtx_FIX (DImode, gen_rtx_FIX (mode, f0))));
+  emit_insn (gen_movdi (i1, const1_rtx));
+  emit_insn (gen_ashldi3 (i1, i1, GEN_INT (63)));
+  emit_insn (gen_xordi3 (out, i0, i1));
+
+  emit_label (donelab);
+}
+
 /* Return the string to output a conditional branch to LABEL, testing
    register REG.  LABEL is the operand number of the label; REG is the
    operand number of the reg.  OP is the conditional expression.  The mode
diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md
index 2a060b85be9cb421209aa922529c23eb7d9934ff..f0e69b173589ad458e2b6ad3a34b3851081241ce 100644
--- a/gcc/config/sparc/sparc.md
+++ b/gcc/config/sparc/sparc.md
@@ -4662,9 +4662,9 @@
 
 (define_expand "floatunsdisf2"
   [(use (match_operand:SF 0 "register_operand" ""))
-   (use (match_operand:DI 1 "register_operand" ""))]
+   (use (match_operand:DI 1 "general_operand" ""))]
   "TARGET_ARCH64 && TARGET_FPU"
-  "sparc_emit_floatunsdi (operands); DONE;")
+  "sparc_emit_floatunsdi (operands, SFmode); DONE;")
 
 (define_insn "floatdidf2"
   [(set (match_operand:DF 0 "register_operand" "=e")
@@ -4676,9 +4676,9 @@
 
 (define_expand "floatunsdidf2"
   [(use (match_operand:DF 0 "register_operand" ""))
-   (use (match_operand:DI 1 "register_operand" ""))]
+   (use (match_operand:DI 1 "general_operand" ""))]
   "TARGET_ARCH64 && TARGET_FPU"
-  "sparc_emit_floatunsdi (operands); DONE;")
+  "sparc_emit_floatunsdi (operands, DFmode); DONE;")
 
 (define_expand "floatditf2"
   [(set (match_operand:TF 0 "nonimmediate_operand" "")
@@ -4747,6 +4747,12 @@
   [(set_attr "type" "fp")
    (set_attr "fptype" "double")])
 
+(define_expand "fixuns_truncsfdi2"
+  [(use (match_operand:DI 0 "register_operand" ""))
+   (use (match_operand:SF 1 "general_operand" ""))]
+  "TARGET_ARCH64 && TARGET_FPU"
+  "sparc_emit_fixunsdi (operands, SFmode); DONE;")
+
 (define_insn "fix_truncdfdi2"
   [(set (match_operand:DI 0 "register_operand" "=e")
 	(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "e"))))]
@@ -4755,6 +4761,12 @@
   [(set_attr "type" "fp")
    (set_attr "fptype" "double")])
 
+(define_expand "fixuns_truncdfdi2"
+  [(use (match_operand:DI 0 "register_operand" ""))
+   (use (match_operand:DF 1 "general_operand" ""))]
+  "TARGET_ARCH64 && TARGET_FPU"
+  "sparc_emit_fixunsdi (operands, DFmode); DONE;")
+
 (define_expand "fix_trunctfdi2"
   [(set (match_operand:DI 0 "register_operand" "")
 	(fix:DI (match_operand:TF 1 "general_operand" "")))]
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e86d65422cdd2486bb16e60a1f37a701c5c3404a..508a19247d5b49b86010bae2103d7a1fe3fa1cb1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2004-02-26  Eric Botcazou  <ebotcazou@act-europe.fr>
+
+	* gcc.dg/fixuns-trunc-1.c: New test.
+
 2004-02-26  John David Anglin  <dave.anglin@nrc-cnrc.gc.ca>
 
 	* gcc.dg/builtins-config.h: Use #elif.
diff --git a/gcc/testsuite/gcc.dg/fixuns-trunc-1.c b/gcc/testsuite/gcc.dg/fixuns-trunc-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..0d0942952497ab6ba2afbc65085ce70c1d71dc3e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fixuns-trunc-1.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-std=c99" } */
+
+unsigned long foo(double d)
+{
+  return (unsigned long) d;
+}
+
+extern void abort(void);
+
+int main(void)
+{
+  double d;
+  unsigned long l;
+
+#ifdef __LP64__
+  d = 9223372036854775808.7;
+  l = 1LL << 63;
+
+  if (foo(d) != l)
+    abort();
+#endif
+
+  d = 122485.2;
+  l = 122485;
+
+  if (foo(d) != l)
+    abort();
+
+  return 0;
+}