This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [4.3 PATCH] SPU float format: Round towards zero


Richard Guenther wrote:
> On Tue, 19 Aug 2008, Joseph S. Myers wrote:
> 
> > On Tue, 19 Aug 2008, Ulrich Weigand wrote:
> > 
> > > Would this be OK for the 4.3 branch?  It is a somewhat large patch,
> > > but it shouldn't have any effect on non-SPU architectures, and the
> > > patch has been in mainline for a week ...
> > 
> > I'll leave Richard to comment on it for 4.3.
> 
> I was asking Ulrich to raise this issue on the list ...
> 
> Why from my POV the patches are simple enough to not cause problems
> they are still large and do not fix regressions (arguably they fix
> wrong-code bugs, which is one thing in favor of them).
> 
> So - if there is no objection from another RM these patches are
> ok for the branch.

Thanks, Richard.  I've checked it in now in order to make the 17:00 UTC
deadline.  In case there are objections from another RM, please let me
know if I should revert it ...

For reference, here's the final version I checked in, including re-enabling
the two test cases Joseph pointed out.

Bye,
Ulrich


ChangeLog:

	* real.h (struct real_format): New member round_towards_zero.
	* real.c (round_for_format): Respect fmt->round_towards_zero.
	(ieee_single_format, mips_single_format, motorola_single_format,
	spu_single_format, ieee_double_format, mips_double_format,
	motorola_double_format, ieee_extended_motorola_format,
	ieee_extended_intel_96_format, ieee_extended_intel_128_format,
	ieee_extended_intel_96_round_53_format, ibm_extended_format,
	mips_extended_format, ieee_quad_format, mips_quad_format,
	vax_f_format, vax_d_format, vax_g_format): Initialize it.
	* config/pdp11/pdp11.c (pdp11_f_format, pdp11_d_format): Likewise.

	* builtins.s (do_mpfr_arg1): Consider round_towards_zero member of
	real_format to choose rounding mode when calling MPFR functions.
	(do_mpfr_arg2, do_mpfr_arg3, do_mpfr_sincos): Likewise.
	(do_mpfr_bessel_n, do_mpfr_remquo, do_mpfr_lgamma_r): Likewise.

	* real.h (real_to_decimal_for_mode): Add prototype.
	* real.c (real_to_decimal_for_mode): Renames old real_to_decimal.
	Respect target rounding mode when generating decimal representation.
	(real_to_decimal): New stub for backwards compatibility.
	* c-cppbuiltin.c (builtin_define_with_hex_fp_value): Use
	real_to_decimal_for_mode instead of real_to_decimal.

	* config/spu/spu.md ("floatdisf2", "floatunsdisf2"): New.

	* config/spu/float_disf.c: New file.
	* config/spu/float_unsdisf.c: New file.
	* config/spu/t-elf (LIB2FUNCS_STATIC_EXTRA): Add them.
	(LIB2FUNCS_EXCLUDE): Define.

testsuite/ChangeLog:

	* gcc.c-torture/execute/20031003-1.c (main): Update test to
	accommodate SPU single-precision rounding mode.
	* gcc.c-torture/execute/conversion.c (test_float_to_integer,
	test_float_to_longlong_integer): Likewise.
	* gcc.c-torture/execute/ieee/rbug.c (main): Likewise.
	* gcc.dg/hex-round-1.c: Skip test on SPU.
	* gcc.dg/hex-round-2.c: Likewise.

	* gcc.dg/torture/fp-int-convert-float.c: Reenable test on SPU.
	* gcc.dg/torture/fp-int-convert-timode.c: Reenable "float" test on SPU.


diff -urNp -x .svn gcc-4_3-orig/gcc/builtins.c gcc-4_3/gcc/builtins.c
--- gcc-4_3-orig/gcc/builtins.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/builtins.c	2008-08-18 17:41:42.000000000 +0200
@@ -12688,14 +12688,16 @@ do_mpfr_arg1 (tree arg, tree type, int (
 	  && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min))
 	  && (!max || real_compare (inclusive ? LE_EXPR: LT_EXPR , ra, max)))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  int inexact;
 	  mpfr_t m;
 
 	  mpfr_init2 (m, prec);
 	  mpfr_from_real (m, ra, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  inexact = func (m, m, GMP_RNDN);
+	  inexact = func (m, m, rnd);
 	  result = do_mpfr_ckconv (m, type, inexact);
 	  mpfr_clear (m);
 	}
@@ -12730,7 +12732,9 @@ do_mpfr_arg2 (tree arg1, tree arg2, tree
 
       if (real_isfinite (ra1) && real_isfinite (ra2))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  int inexact;
 	  mpfr_t m1, m2;
 
@@ -12738,7 +12742,7 @@ do_mpfr_arg2 (tree arg1, tree arg2, tree
 	  mpfr_from_real (m1, ra1, GMP_RNDN);
 	  mpfr_from_real (m2, ra2, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  inexact = func (m1, m1, m2, GMP_RNDN);
+	  inexact = func (m1, m1, m2, rnd);
 	  result = do_mpfr_ckconv (m1, type, inexact);
 	  mpfr_clears (m1, m2, NULL);
 	}
@@ -12776,7 +12780,9 @@ do_mpfr_arg3 (tree arg1, tree arg2, tree
 
       if (real_isfinite (ra1) && real_isfinite (ra2) && real_isfinite (ra3))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  int inexact;
 	  mpfr_t m1, m2, m3;
 
@@ -12785,7 +12791,7 @@ do_mpfr_arg3 (tree arg1, tree arg2, tree
 	  mpfr_from_real (m2, ra2, GMP_RNDN);
 	  mpfr_from_real (m3, ra3, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  inexact = func (m1, m1, m2, m3, GMP_RNDN);
+	  inexact = func (m1, m1, m2, m3, rnd);
 	  result = do_mpfr_ckconv (m1, type, inexact);
 	  mpfr_clears (m1, m2, m3, NULL);
 	}
@@ -12819,7 +12825,9 @@ do_mpfr_sincos (tree arg, tree arg_sinp,
 
       if (real_isfinite (ra))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  tree result_s, result_c;
 	  int inexact;
 	  mpfr_t m, ms, mc;
@@ -12827,7 +12835,7 @@ do_mpfr_sincos (tree arg, tree arg_sinp,
 	  mpfr_inits2 (prec, m, ms, mc, NULL);
 	  mpfr_from_real (m, ra, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  inexact = mpfr_sin_cos (ms, mc, m, GMP_RNDN);
+	  inexact = mpfr_sin_cos (ms, mc, m, rnd);
 	  result_s = do_mpfr_ckconv (ms, type, inexact);
 	  result_c = do_mpfr_ckconv (mc, type, inexact);
 	  mpfr_clears (m, ms, mc, NULL);
@@ -12892,14 +12900,16 @@ do_mpfr_bessel_n (tree arg1, tree arg2, 
 	  && real_isfinite (ra)
 	  && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min)))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  int inexact;
 	  mpfr_t m;
 
 	  mpfr_init2 (m, prec);
 	  mpfr_from_real (m, ra, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  inexact = func (m, n, m, GMP_RNDN);
+	  inexact = func (m, n, m, rnd);
 	  result = do_mpfr_ckconv (m, type, inexact);
 	  mpfr_clear (m);
 	}
@@ -12933,7 +12943,9 @@ do_mpfr_remquo (tree arg0, tree arg1, tr
 
       if (real_isfinite (ra0) && real_isfinite (ra1))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  tree result_rem;
 	  long integer_quo;
 	  mpfr_t m0, m1;
@@ -12942,7 +12954,7 @@ do_mpfr_remquo (tree arg0, tree arg1, tr
 	  mpfr_from_real (m0, ra0, GMP_RNDN);
 	  mpfr_from_real (m1, ra1, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  mpfr_remquo (m0, &integer_quo, m0, m1, GMP_RNDN);
+	  mpfr_remquo (m0, &integer_quo, m0, m1, rnd);
 	  /* Remquo is independent of the rounding mode, so pass
 	     inexact=0 to do_mpfr_ckconv().  */
 	  result_rem = do_mpfr_ckconv (m0, type, /*inexact=*/ 0);
@@ -13010,7 +13022,9 @@ do_mpfr_lgamma_r (tree arg, tree arg_sg,
 	  && ra->cl != rvc_zero
 	  && !(real_isneg(ra) && real_isinteger(ra, TYPE_MODE (type))))
         {
-	  const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+	  const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+	  const int prec = fmt->p;
+	  const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
 	  int inexact, sg;
 	  mpfr_t m;
 	  tree result_lg;
@@ -13018,7 +13032,7 @@ do_mpfr_lgamma_r (tree arg, tree arg_sg,
 	  mpfr_init2 (m, prec);
 	  mpfr_from_real (m, ra, GMP_RNDN);
 	  mpfr_clear_flags ();
-	  inexact = mpfr_lgamma (m, &sg, m, GMP_RNDN);
+	  inexact = mpfr_lgamma (m, &sg, m, rnd);
 	  result_lg = do_mpfr_ckconv (m, type, inexact);
 	  mpfr_clear (m);
 	  if (result_lg)
diff -urNp -x .svn gcc-4_3-orig/gcc/c-cppbuiltin.c gcc-4_3/gcc/c-cppbuiltin.c
--- gcc-4_3-orig/gcc/c-cppbuiltin.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/c-cppbuiltin.c	2008-08-18 17:41:42.000000000 +0200
@@ -837,7 +837,7 @@ builtin_define_with_int_value (const cha
 /* Pass an object-like macro a hexadecimal floating-point value.  */
 static void
 builtin_define_with_hex_fp_value (const char *macro,
-				  tree type ATTRIBUTE_UNUSED, int digits,
+				  tree type, int digits,
 				  const char *hex_str, 
 				  const char *fp_suffix,
 				  const char *fp_cast)
@@ -856,7 +856,8 @@ builtin_define_with_hex_fp_value (const 
      then print it back out as decimal.  */
 
   real_from_string (&real, hex_str);
-  real_to_decimal (dec_str, &real, sizeof (dec_str), digits, 0);
+  real_to_decimal_for_mode (dec_str, &real, sizeof (dec_str), digits, 0,
+			    TYPE_MODE (type));
 
   /* Assemble the macro in the following fashion
      macro = fp_cast [dec_str fp_suffix] */
diff -urNp -x .svn gcc-4_3-orig/gcc/config/pdp11/pdp11.c gcc-4_3/gcc/config/pdp11/pdp11.c
--- gcc-4_3-orig/gcc/config/pdp11/pdp11.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/config/pdp11/pdp11.c	2008-08-18 17:41:42.000000000 +0200
@@ -78,6 +78,7 @@ const struct real_format pdp11_f_format 
     false,
     false,
     false,
+    false,
     false
   };
 
@@ -97,6 +98,7 @@ const struct real_format pdp11_d_format 
     false,
     false,
     false,
+    false,
     false
   };
 
diff -urNp -x .svn gcc-4_3-orig/gcc/config/spu/float_disf.c gcc-4_3/gcc/config/spu/float_disf.c
--- gcc-4_3-orig/gcc/config/spu/float_disf.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-4_3/gcc/config/spu/float_disf.c	2008-08-18 17:41:42.000000000 +0200
@@ -0,0 +1,30 @@
+/* Copyright (C) 2008 Free Software Foundation, Inc.
+  
+   This file is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+  
+   This file is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this file; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License.  The exception does not
+   however invalidate any other reasons why the executable file might be covered
+   by the GNU General Public License. */
+
+float __floatdisf (long long x)
+{
+  /* The SPU back-end now generates inline code for this conversion.
+     This file is solely used to provide the __floatdisf functions
+     for objects generated with prior versions of GCC.  */
+  return x;
+}
diff -urNp -x .svn gcc-4_3-orig/gcc/config/spu/float_unsdisf.c gcc-4_3/gcc/config/spu/float_unsdisf.c
--- gcc-4_3-orig/gcc/config/spu/float_unsdisf.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-4_3/gcc/config/spu/float_unsdisf.c	2008-08-18 17:41:42.000000000 +0200
@@ -0,0 +1,30 @@
+/* Copyright (C) 2008 Free Software Foundation, Inc.
+  
+   This file is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+  
+   This file is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this file; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License.  The exception does not
+   however invalidate any other reasons why the executable file might be covered
+   by the GNU General Public License. */
+
+float __floatundisf (unsigned long long x)
+{
+  /* The SPU back-end now generates inline code for this conversion.
+     This file is solely used to provide the __floatundisf function
+     for objects generated with prior versions of GCC.  */
+  return x;
+}
diff -urNp -x .svn gcc-4_3-orig/gcc/config/spu/spu.md gcc-4_3/gcc/config/spu/spu.md
--- gcc-4_3-orig/gcc/config/spu/spu.md	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/config/spu/spu.md	2008-08-18 17:41:42.000000000 +0200
@@ -661,6 +661,65 @@
   "frds\t%0,%1"
   [(set_attr "type" "fpd")])
 
+(define_expand "floatdisf2"
+  [(set (match_operand:SF 0 "register_operand" "")
+	(float:SF (match_operand:DI 1 "register_operand" "")))]
+  ""
+  {
+    rtx c0 = gen_reg_rtx (SImode);
+    rtx r0 = gen_reg_rtx (DImode);
+    rtx r1 = gen_reg_rtx (SFmode);
+    rtx r2 = gen_reg_rtx (SImode);
+    rtx setneg = gen_reg_rtx (SImode);
+    rtx isneg = gen_reg_rtx (SImode);
+    rtx neg = gen_reg_rtx (DImode);
+    rtx mask = gen_reg_rtx (DImode);
+
+    emit_move_insn (c0, GEN_INT (-0x80000000ll));
+
+    emit_insn (gen_negdi2 (neg, operands[1]));
+    emit_insn (gen_cgt_di_m1 (isneg, operands[1]));
+    emit_insn (gen_extend_compare (mask, isneg));
+    emit_insn (gen_selb (r0, neg, operands[1], mask));
+    emit_insn (gen_andc_si (setneg, c0, isneg));
+
+    emit_insn (gen_floatunsdisf2 (r1, r0));
+
+    emit_insn (gen_iorsi3 (r2, gen_rtx_SUBREG (SImode, r1, 0), setneg));
+    emit_move_insn (operands[0], gen_rtx_SUBREG (SFmode, r2, 0));
+    DONE;
+  })
+
+(define_insn_and_split "floatunsdisf2"
+  [(set (match_operand:SF 0 "register_operand" "=r")
+        (unsigned_float:SF (match_operand:DI 1 "register_operand" "r")))
+   (clobber (match_scratch:SF 2 "=r"))
+   (clobber (match_scratch:SF 3 "=r"))
+   (clobber (match_scratch:SF 4 "=r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup:SF 0)
+        (unsigned_float:SF (match_dup:DI 1)))]
+  {
+    rtx op1_v4si = gen_rtx_REG (V4SImode, REGNO (operands[1]));
+    rtx op2_v4sf = gen_rtx_REG (V4SFmode, REGNO (operands[2]));
+    rtx op2_ti = gen_rtx_REG (TImode, REGNO (operands[2]));
+    rtx op3_ti = gen_rtx_REG (TImode, REGNO (operands[3]));
+
+    REAL_VALUE_TYPE scale;
+    real_2expN (&scale, 32, SFmode);
+
+    emit_insn (gen_floatunsv4siv4sf2 (op2_v4sf, op1_v4si));
+    emit_insn (gen_shlqby_ti (op3_ti, op2_ti, GEN_INT (4)));
+
+    emit_move_insn (operands[4],
+		    CONST_DOUBLE_FROM_REAL_VALUE (scale, SFmode));
+    emit_insn (gen_fma_sf (operands[0],
+			   operands[2], operands[4], operands[3]));
+    DONE;
+  })
+
 ;; Do (double)(operands[1]+0x80000000u)-(double)0x80000000
 (define_expand "floatsidf2"
   [(set (match_operand:DF 0 "register_operand" "")
diff -urNp -x .svn gcc-4_3-orig/gcc/config/spu/t-spu-elf gcc-4_3/gcc/config/spu/t-spu-elf
--- gcc-4_3-orig/gcc/config/spu/t-spu-elf	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/config/spu/t-spu-elf	2008-08-18 17:41:42.000000000 +0200
@@ -21,8 +21,15 @@ CROSS_LIBGCC1 =
 
 TARGET_LIBGCC2_CFLAGS = -fPIC -mwarn-reloc -D__IN_LIBGCC2
 
+# We exclude those because the libgcc2.c default versions do not support
+# the SPU single-precision format (round towards zero).  We provide our
+# own versions below.
+LIB2FUNCS_EXCLUDE = _floatdisf _floatundisf
+
 LIB2FUNCS_STATIC_EXTRA = $(srcdir)/config/spu/float_unssidf.c \
 			 $(srcdir)/config/spu/float_unsdidf.c \
+			 $(srcdir)/config/spu/float_unsdisf.c \
+			 $(srcdir)/config/spu/float_disf.c \
 			 $(srcdir)/config/spu/mfc_tag_table.c \
 			 $(srcdir)/config/spu/mfc_tag_reserve.c \
 			 $(srcdir)/config/spu/mfc_tag_release.c \
diff -urNp -x .svn gcc-4_3-orig/gcc/real.c gcc-4_3/gcc/real.c
--- gcc-4_3-orig/gcc/real.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/real.c	2008-08-18 17:41:42.000000000 +0200
@@ -1443,20 +1443,30 @@ rtd_divmod (REAL_VALUE_TYPE *num, REAL_V
 /* Render R as a decimal floating point constant.  Emit DIGITS significant
    digits in the result, bounded by BUF_SIZE.  If DIGITS is 0, choose the
    maximum for the representation.  If CROP_TRAILING_ZEROS, strip trailing
-   zeros.  */
+   zeros.  If MODE is VOIDmode, round to nearest value.  Otherwise, round
+   to a string that, when parsed back in mode MODE, yields the same value.  */
 
 #define M_LOG10_2	0.30102999566398119521
 
 void
-real_to_decimal (char *str, const REAL_VALUE_TYPE *r_orig, size_t buf_size,
-		 size_t digits, int crop_trailing_zeros)
+real_to_decimal_for_mode (char *str, const REAL_VALUE_TYPE *r_orig,
+			  size_t buf_size, size_t digits,
+			  int crop_trailing_zeros, enum machine_mode mode)
 {
+  const struct real_format *fmt = NULL;
   const REAL_VALUE_TYPE *one, *ten;
   REAL_VALUE_TYPE r, pten, u, v;
   int dec_exp, cmp_one, digit;
   size_t max_digits;
   char *p, *first, *last;
   bool sign;
+  bool round_up;
+
+  if (mode != VOIDmode)
+   {
+     fmt = REAL_MODE_FORMAT (mode);
+     gcc_assert (fmt);
+   }
 
   r = *r_orig;
   switch (r.cl)
@@ -1671,17 +1681,31 @@ real_to_decimal (char *str, const REAL_V
   digit = rtd_divmod (&r, &pten);
 
   /* Round the result.  */
-  if (digit == 5)
+  if (fmt && fmt->round_towards_zero)
     {
-      /* Round to nearest.  If R is nonzero there are additional
-	 nonzero digits to be extracted.  */
+      /* If the format uses round towards zero when parsing the string
+	 back in, we need to always round away from zero here.  */
       if (cmp_significand_0 (&r))
 	digit++;
-      /* Round to even.  */
-      else if ((p[-1] - '0') & 1)
-	digit++;
+      round_up = digit > 0;
     }
-  if (digit > 5)
+  else
+    {
+      if (digit == 5)
+	{
+	  /* Round to nearest.  If R is nonzero there are additional
+	     nonzero digits to be extracted.  */
+	  if (cmp_significand_0 (&r))
+	    digit++;
+	  /* Round to even.  */
+	  else if ((p[-1] - '0') & 1)
+	    digit++;
+	}
+
+      round_up = digit > 5;
+    }
+
+  if (round_up)
     {
       while (p > first)
 	{
@@ -1715,6 +1739,26 @@ real_to_decimal (char *str, const REAL_V
 
   /* Append the exponent.  */
   sprintf (last, "e%+d", dec_exp);
+
+#ifdef ENABLE_CHECKING
+  /* Verify that we can read the original value back in.  */
+  if (mode != VOIDmode)
+    {
+      real_from_string (&r, str);
+      real_convert (&r, mode, &r);
+      gcc_assert (real_identical (&r, r_orig));
+    }
+#endif
+}
+
+/* Likewise, except always uses round-to-nearest.  */
+
+void
+real_to_decimal (char *str, const REAL_VALUE_TYPE *r_orig, size_t buf_size,
+		 size_t digits, int crop_trailing_zeros)
+{
+  real_to_decimal_for_mode (str, r_orig, buf_size,
+			    digits, crop_trailing_zeros, VOIDmode);
 }
 
 /* Render R as a hexadecimal floating point constant.  Emit DIGITS
@@ -2328,9 +2372,8 @@ static void
 round_for_format (const struct real_format *fmt, REAL_VALUE_TYPE *r)
 {
   int p2, np2, i, w;
-  unsigned long sticky;
-  bool guard, lsb;
   int emin2m1, emax2;
+  bool round_up = false;
 
   if (r->decimal)
     {
@@ -2402,21 +2445,28 @@ round_for_format (const struct real_form
 	}
     }
 
-  /* There are P2 true significand bits, followed by one guard bit,
-     followed by one sticky bit, followed by stuff.  Fold nonzero
-     stuff into the sticky bit.  */
-
-  sticky = 0;
-  for (i = 0, w = (np2 - 1) / HOST_BITS_PER_LONG; i < w; ++i)
-    sticky |= r->sig[i];
-  sticky |=
-    r->sig[w] & (((unsigned long)1 << ((np2 - 1) % HOST_BITS_PER_LONG)) - 1);
+  if (!fmt->round_towards_zero)
+    {
+      /* There are P2 true significand bits, followed by one guard bit,
+         followed by one sticky bit, followed by stuff.  Fold nonzero
+         stuff into the sticky bit.  */
+      unsigned long sticky;
+      bool guard, lsb;
+
+      sticky = 0;
+      for (i = 0, w = (np2 - 1) / HOST_BITS_PER_LONG; i < w; ++i)
+	sticky |= r->sig[i];
+      sticky |= r->sig[w]
+		& (((unsigned long)1 << ((np2 - 1) % HOST_BITS_PER_LONG)) - 1);
 
-  guard = test_significand_bit (r, np2 - 1);
-  lsb = test_significand_bit (r, np2);
+      guard = test_significand_bit (r, np2 - 1);
+      lsb = test_significand_bit (r, np2);
 
-  /* Round to even.  */
-  if (guard && (sticky || lsb))
+      /* Round to even.  */
+      round_up = guard && (sticky || lsb);
+    }
+
+  if (round_up)
     {
       REAL_VALUE_TYPE u;
       get_zero (&u, 0);
@@ -2756,6 +2806,7 @@ const struct real_format ieee_single_for
     128,
     31,
     31,
+    false,
     true,
     true,
     true,
@@ -2775,6 +2826,7 @@ const struct real_format mips_single_for
     128,
     31,
     31,
+    false,
     true,
     true,
     true,
@@ -2794,6 +2846,7 @@ const struct real_format motorola_single
     128,
     31,
     31,
+    false,
     true,
     true,
     true,
@@ -2824,6 +2877,7 @@ const struct real_format spu_single_form
     129,
     31,
     31,
+    true,
     false,
     false,
     true,
@@ -3031,6 +3085,7 @@ const struct real_format ieee_double_for
     1024,
     63,
     63,
+    false,
     true,
     true,
     true,
@@ -3050,6 +3105,7 @@ const struct real_format mips_double_for
     1024,
     63,
     63,
+    false,
     true,
     true,
     true,
@@ -3069,6 +3125,7 @@ const struct real_format motorola_double
     1024,
     63,
     63,
+    false,
     true,
     true,
     true,
@@ -3406,6 +3463,7 @@ const struct real_format ieee_extended_m
     16384,
     95,
     95,
+    false,
     true,
     true,
     true,
@@ -3425,6 +3483,7 @@ const struct real_format ieee_extended_i
     16384,
     79,
     79,
+    false,
     true,
     true,
     true,
@@ -3444,6 +3503,7 @@ const struct real_format ieee_extended_i
     16384,
     79,
     79,
+    false,
     true,
     true,
     true,
@@ -3465,6 +3525,7 @@ const struct real_format ieee_extended_i
     16384,
     79,
     79,
+    false,
     true,
     true,
     true,
@@ -3551,6 +3612,7 @@ const struct real_format ibm_extended_fo
     1024,
     127,
     -1,
+    false,
     true,
     true,
     true,
@@ -3570,6 +3632,7 @@ const struct real_format mips_extended_f
     1024,
     127,
     -1,
+    false,
     true,
     true,
     true,
@@ -3831,6 +3894,7 @@ const struct real_format ieee_quad_forma
     16384,
     127,
     127,
+    false,
     true,
     true,
     true,
@@ -3850,6 +3914,7 @@ const struct real_format mips_quad_forma
     16384,
     127,
     127,
+    false,
     true,
     true,
     true,
@@ -4172,6 +4237,7 @@ const struct real_format vax_d_format =
     false,
     false,
     false,
+    false,
     false
   };
 
@@ -4191,6 +4257,7 @@ const struct real_format vax_g_format =
     false,
     false,
     false,
+    false,
     false
   };
 
@@ -4260,6 +4327,7 @@ const struct real_format decimal_single_
     96,
     31,
     31,
+    false,
     true,
     true,
     true,
@@ -4280,6 +4348,7 @@ const struct real_format decimal_double_
     384,
     63,
     63,
+    false,
     true,
     true,
     true,
@@ -4300,6 +4369,7 @@ const struct real_format decimal_quad_fo
     6144,
     127,
     127,
+    false,
     true,
     true,
     true, 
@@ -4343,6 +4413,7 @@ const struct real_format real_internal_f
     MAX_EXP,
     -1,
     -1,
+    false,
     true,
     true,
     false,
diff -urNp -x .svn gcc-4_3-orig/gcc/real.h gcc-4_3/gcc/real.h
--- gcc-4_3-orig/gcc/real.h	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/real.h	2008-08-18 17:41:43.000000000 +0200
@@ -147,6 +147,9 @@ struct real_format
      or -1 for a complex encoding.  */
   int signbit_rw;
 
+  /* Default rounding mode for operations on this format.  */
+  bool round_towards_zero;
+
   /* Properties of the format.  */
   bool has_nans;
   bool has_inf;
@@ -216,6 +219,11 @@ extern bool exact_real_truncate (enum ma
 extern void real_to_decimal (char *, const REAL_VALUE_TYPE *, size_t,
 			     size_t, int);
 
+/* Render R as a decimal floating point constant, rounded so as to be
+   parsed back to the same value when interpreted in mode MODE.  */
+extern void real_to_decimal_for_mode (char *, const REAL_VALUE_TYPE *, size_t,
+				      size_t, int, enum machine_mode);
+
 /* Render R as a hexadecimal floating point constant.  */
 extern void real_to_hexadecimal (char *, const REAL_VALUE_TYPE *,
 				 size_t, size_t, int);
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.c-torture/execute/20031003-1.c gcc-4_3/gcc/testsuite/gcc.c-torture/execute/20031003-1.c
--- gcc-4_3-orig/gcc/testsuite/gcc.c-torture/execute/20031003-1.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.c-torture/execute/20031003-1.c	2008-08-18 17:41:43.000000000 +0200
@@ -19,9 +19,15 @@ int main()
 #if INT_MAX == 2147483647
   if (f1() != 2147483647)
     abort ();
+#ifdef __SPU__
+  /* SPU float rounds towards zero.  */
+  if (f2() != 0x7fffff80)
+    abort ();
+#else
   if (f2() != 2147483647)
     abort ();
 #endif
+#endif
   return 0;
 }
 
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.c-torture/execute/conversion.c gcc-4_3/gcc/testsuite/gcc.c-torture/execute/conversion.c
--- gcc-4_3-orig/gcc/testsuite/gcc.c-torture/execute/conversion.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.c-torture/execute/conversion.c	2008-08-18 17:41:43.000000000 +0200
@@ -284,9 +284,15 @@ test_float_to_integer()
     abort();
   if (f2u(1.99) != 1)
     abort();
+#ifdef __SPU__
+  /* SPU float rounds towards zero.  */
+  if (f2u((float) ((~0U) >> 1)) != 0x7fffff80)
+    abort();
+#else
   if (f2u((float) ((~0U) >> 1)) != (~0U) >> 1 &&	/* 0x7fffffff */
       f2u((float) ((~0U) >> 1)) != ((~0U) >> 1) + 1)
     abort();
+#endif
   if (f2u((float) ~((~0U) >> 1)) != ~((~0U) >> 1))	/* 0x80000000 */
     abort();
 
@@ -439,9 +445,15 @@ test_float_to_longlong_integer()
     abort();
   if (f2ull(1.99) != 1LL)
     abort();
+#ifdef __SPU__
+  /* SPU float rounds towards zero.  */
+  if (f2ull((float) ((~0ULL) >> 1)) != 0x7fffff8000000000ULL)
+    abort();
+#else
   if (f2ull((float) ((~0ULL) >> 1)) != (~0ULL) >> 1 &&	/* 0x7fffffff */
       f2ull((float) ((~0ULL) >> 1)) != ((~0ULL) >> 1) + 1)
     abort();
+#endif
   if (f2ull((float) ~((~0ULL) >> 1)) != ~((~0ULL) >> 1)) /* 0x80000000 */
     abort();
 
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.c-torture/execute/ieee/rbug.c gcc-4_3/gcc/testsuite/gcc.c-torture/execute/ieee/rbug.c
--- gcc-4_3-orig/gcc/testsuite/gcc.c-torture/execute/ieee/rbug.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.c-torture/execute/ieee/rbug.c	2008-08-18 17:41:43.000000000 +0200
@@ -41,8 +41,14 @@ main ()
   k = 0x8234508000000001ULL;
   x = s (k);
   k = (unsigned long long) x;
+#ifdef __SPU__
+  /* SPU float rounds towards zero.  */
+  if (k != 0x8234500000000000ULL)
+    abort ();
+#else
   if (k != 0x8234510000000000ULL)
     abort ();
+#endif
 
   exit (0);
 }
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.dg/hex-round-1.c gcc-4_3/gcc/testsuite/gcc.dg/hex-round-1.c
--- gcc-4_3-orig/gcc/testsuite/gcc.dg/hex-round-1.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.dg/hex-round-1.c	2008-08-18 17:41:43.000000000 +0200
@@ -1,6 +1,7 @@
 /* Test for hexadecimal float rounding: bug 21720.  */
 /* { dg-do link } */
 /* { dg-options "-O -std=gnu99" } */
+/* { dg-skip-if "SPU float rounds towards zero" { spu-*-* } } */
 
 #include <float.h>
 
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.dg/hex-round-2.c gcc-4_3/gcc/testsuite/gcc.dg/hex-round-2.c
--- gcc-4_3-orig/gcc/testsuite/gcc.dg/hex-round-2.c	2008-08-18 17:41:34.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.dg/hex-round-2.c	2008-08-18 17:41:43.000000000 +0200
@@ -2,6 +2,7 @@
    in number.  */
 /* { dg-do link } */
 /* { dg-options "-O -std=gnu99" } */
+/* { dg-skip-if "SPU float rounds towards zero" { spu-*-* } } */
 
 #include <float.h>
 
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.dg/torture/fp-int-convert-float.c gcc-4_3/gcc/testsuite/gcc.dg/torture/fp-int-convert-float.c
--- gcc-4_3-orig/gcc/testsuite/gcc.dg/torture/fp-int-convert-float.c	2008-07-02 17:34:51.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.dg/torture/fp-int-convert-float.c	2008-08-19 18:17:15.000000000 +0200
@@ -3,7 +3,6 @@
 /* Skipped for MIPS16 targets because of PR 35232 */
 /* { dg-do run { target { { ! mips*-*-* } || nomips16 } } } */
 /* { dg-options "" } */
-/* { dg-skip-if "Round to zero" { spu-*-* } } */
 
 #include <float.h>
 #include "fp-int-convert.h"
diff -urNp -x .svn gcc-4_3-orig/gcc/testsuite/gcc.dg/torture/fp-int-convert-timode.c gcc-4_3/gcc/testsuite/gcc.dg/torture/fp-int-convert-timode.c
--- gcc-4_3-orig/gcc/testsuite/gcc.dg/torture/fp-int-convert-timode.c	2008-07-02 17:34:52.000000000 +0200
+++ gcc-4_3/gcc/testsuite/gcc.dg/torture/fp-int-convert-timode.c	2008-08-19 18:17:15.000000000 +0200
@@ -9,10 +9,7 @@
 int
 main (void)
 {
-#ifndef __SPU__
-  /* Single-precision floating point on SPU always rounds to zero.  */
   TEST_I_F(TItype, UTItype, float, FLT_MANT_DIG);
-#endif
   TEST_I_F(TItype, UTItype, double, DBL_MANT_DIG);
   /* Disable the long double tests when using IBM Extended Doubles.
      They have variable precision, but constants calculated by gcc's

-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]