diff mbox series

Add constant folding support for next{after,toward}{,f,l} (PR libstdc++/85466)

Message ID 20180421173625.GN8577@tucnak
State New
Headers show
Series Add constant folding support for next{after,toward}{,f,l} (PR libstdc++/85466) | expand

Commit Message

Jakub Jelinek April 21, 2018, 5:36 p.m. UTC
Hi!

This patch adds constant folding for next{after,toward}{,f,l}.
It doesn't handle decimal (we don't have a builtins that would need it),
nor composite modes (IBM double double; nextafter/nexttoward for variable
precision types isn't really well defined; we handle nexttoward where long
double is IBM double double) and for now punts also on formats without
denormals or infinities (don't really know what the library nextafter does
for those).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for stage1?

2018-04-21  Jakub Jelinek  <jakub@redhat.com>

	PR libstdc++/85466
	* real.h (real_nextafter): Declare.
	* real.c (real_nextafter): New function.
	* fold-const-call.c (fold_const_nextafter): New function.
	(fold_const_call_sss): Call it for CASE_CFN_NEXTAFTER and
	CASE_CFN_NEXTTOWARD.
	(fold_const_call_1): For CASE_CFN_NEXTTOWARD call fold_const_call_sss
	even when arg1_mode is different from arg0_mode.

	* gcc.dg/nextafter-1.c: New test.
	* gcc.dg/nextafter-2.c: New test.
	* gcc.dg/nextafter-3.c: New test.
	* gcc.dg/nextafter-4.c: New test.


	Jakub

Comments

Richard Biener May 3, 2018, 9:44 a.m. UTC | #1
On Sat, 21 Apr 2018, Jakub Jelinek wrote:

> Hi!
> 
> This patch adds constant folding for next{after,toward}{,f,l}.
> It doesn't handle decimal (we don't have a builtins that would need it),
> nor composite modes (IBM double double; nextafter/nexttoward for variable
> precision types isn't really well defined; we handle nexttoward where long
> double is IBM double double) and for now punts also on formats without
> denormals or infinities (don't really know what the library nextafter does
> for those).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for stage1?
> 
> 2018-04-21  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR libstdc++/85466
> 	* real.h (real_nextafter): Declare.
> 	* real.c (real_nextafter): New function.
> 	* fold-const-call.c (fold_const_nextafter): New function.
> 	(fold_const_call_sss): Call it for CASE_CFN_NEXTAFTER and
> 	CASE_CFN_NEXTTOWARD.
> 	(fold_const_call_1): For CASE_CFN_NEXTTOWARD call fold_const_call_sss
> 	even when arg1_mode is different from arg0_mode.
> 
> 	* gcc.dg/nextafter-1.c: New test.
> 	* gcc.dg/nextafter-2.c: New test.
> 	* gcc.dg/nextafter-3.c: New test.
> 	* gcc.dg/nextafter-4.c: New test.
> 
> --- gcc/real.h.jj	2018-01-03 10:19:54.349533828 +0100
> +++ gcc/real.h	2018-04-20 12:44:30.707350855 +0200
> @@ -507,6 +507,10 @@ extern void real_copysign (REAL_VALUE_TY
>  extern bool real_isinteger (const REAL_VALUE_TYPE *, format_helper);
>  extern bool real_isinteger (const REAL_VALUE_TYPE *, HOST_WIDE_INT *);
>  
> +/* Calculate nextafter (X, Y) in format FMT.  */
> +extern bool real_nextafter (REAL_VALUE_TYPE *, format_helper,
> +			    const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);
> +
>  /* Write into BUF the maximum representable finite floating-point
>     number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
>     float string.  BUF must be large enough to contain the result.  */
> --- gcc/real.c.jj	2018-01-03 10:19:55.003533933 +0100
> +++ gcc/real.c	2018-04-21 19:29:09.103584254 +0200
> @@ -5048,6 +5048,102 @@ real_isinteger (const REAL_VALUE_TYPE *c
>    return false;
>  }
>  
> +/* Calculate nextafter (X, Y) or nexttoward (X, Y).  Return true if
> +   underflow or overflow needs to be raised.  */
> +
> +bool
> +real_nextafter (REAL_VALUE_TYPE *r, format_helper fmt,
> +		const REAL_VALUE_TYPE *x, const REAL_VALUE_TYPE *y)
> +{
> +  int cmp = do_compare (x, y, 2);
> +  /* If either operand is NaN, return qNaN.  */
> +  if (cmp == 2)
> +    {
> +      get_canonical_qnan (r, 0);
> +      return false;
> +    }
> +  /* If x == y, return y cast to target type.  */
> +  if (cmp == 0)
> +    {
> +      real_convert (r, fmt, y);
> +      return false;
> +    }
> +
> +  if (x->cl == rvc_zero)
> +    {
> +      get_zero (r, y->sign);
> +      r->cl = rvc_normal;
> +      SET_REAL_EXP (r, fmt->emin - fmt->p + 1);
> +      r->sig[SIGSZ - 1] = SIG_MSB;
> +      return false;
> +    }
> +
> +  int np2 = SIGNIFICAND_BITS - fmt->p;
> +  /* For denormals adjust np2 correspondingly.  */
> +  if (x->cl == rvc_normal && REAL_EXP (x) < fmt->emin)
> +    np2 += fmt->emin - REAL_EXP (x);
> +
> +  REAL_VALUE_TYPE u;
> +  get_zero (r, x->sign);
> +  get_zero (&u, 0);
> +  set_significand_bit (&u, np2);
> +  r->cl = rvc_normal;
> +  SET_REAL_EXP (r, REAL_EXP (x));
> +
> +  if (x->cl == rvc_inf)
> +    {
> +      bool borrow = sub_significands (r, r, &u, 0);
> +      gcc_assert (borrow);
> +      SET_REAL_EXP (r, fmt->emax);
> +    }
> +  else if (cmp == (x->sign ? 1 : -1))
> +    {
> +      if (add_significands (r, x, &u))
> +	{
> +	  /* Overflow.  Means the significand had been all ones, and
> +	     is now all zeros.  Need to increase the exponent, and
> +	     possibly re-normalize it.  */
> +	  SET_REAL_EXP (r, REAL_EXP (r) + 1);
> +	  if (REAL_EXP (r) > fmt->emax)
> +	    {
> +	      get_inf (r, x->sign);
> +	      return true;
> +	    }
> +	  r->sig[SIGSZ - 1] = SIG_MSB;
> +	}
> +    }
> +  else
> +    {
> +      if (REAL_EXP (x) > fmt->emin && x->sig[SIGSZ - 1] == SIG_MSB)
> +	{
> +	  int i;
> +	  for (i = SIGSZ - 2; i >= 0; i--)
> +	    if (x->sig[i])
> +	      break;
> +	  if (i < 0)
> +	    {
> +	      /* When mantissa is 1.0, we need to subtract only
> +		 half of u: nextafter (1.0, 0.0) is 1.0 - __DBL_EPSILON__ / 2
> +		 rather than 1.0 - __DBL_EPSILON__.  */
> +	      clear_significand_bit (&u, np2);
> +	      np2--;
> +	      set_significand_bit (&u, np2);
> +	    }
> +	}
> +      sub_significands (r, x, &u, 0);
> +    }
> +
> +  /* Clear out trailing garbage.  */
> +  clear_significand_below (r, np2);
> +  normalize (r);
> +  if (REAL_EXP (r) <= fmt->emin - fmt->p)
> +    {
> +      get_zero (r, x->sign);
> +      return true;
> +    }
> +  return r->cl == rvc_zero;
> +}
> +
>  /* Write into BUF the maximum representable finite floating-point
>     number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
>     float string.  LEN is the size of BUF, and the buffer must be large
> --- gcc/fold-const-call.c.jj	2018-01-14 17:16:52.873836266 +0100
> +++ gcc/fold-const-call.c	2018-04-20 19:36:40.987942433 +0200
> @@ -529,6 +529,49 @@ fold_const_pow (real_value *result, cons
>  
>  /* Try to evaluate:
>  
> +      *RESULT = nextafter (*ARG0, *ARG1)
> +
> +   or
> +
> +      *RESULT = nexttoward (*ARG0, *ARG1)
> +
> +   in format FORMAT.  Return true on success.  */
> +
> +static bool
> +fold_const_nextafter (real_value *result, const real_value *arg0,
> +		      const real_value *arg1, const real_format *format)
> +{
> +  if (flag_signaling_nans

HONOR_SNANS ()?

> +      && (REAL_VALUE_ISSIGNALING_NAN (*arg0)
> +	  || REAL_VALUE_ISSIGNALING_NAN (*arg1)))
> +    return false;
> +
> +  /* Don't handle composite modes, nor decimal, nor modes without
> +     inf or denorm at least for now.  */
> +  if (format->pnan < format->p
> +      || format->b == 10
> +      || !format->has_inf
> +      || !format->has_denorm)
> +    return false;

I wonder if we should assert in real_nextafter that we can actually
handle the input?  It likely returns garbage if fed with decimal float
stuff?

Otherwise looks OK.

Thanks,
Richard.

> +  if (real_nextafter (result, format, arg0, arg1)
> +      /* If raising underflow or overflow and setting errno to ERANGE,
> +	 fail if we care about those side-effects.  */
> +      && (flag_trapping_math || flag_errno_math))
> +    return false;
> +  /* Similarly for nextafter (0, 1) raising underflow.  */
> +  else if (flag_trapping_math
> +	   && arg0->cl == rvc_zero
> +	   && result->cl != rvc_zero)
> +    return false;
> +
> +  real_convert (result, format, result);
> +
> +  return true;
> +}
> +
> +/* Try to evaluate:
> +
>        *RESULT = ldexp (*ARG0, ARG1)
>  
>     in format FORMAT.  Return true on success.  */
> @@ -1260,6 +1303,10 @@ fold_const_call_sss (real_value *result,
>      CASE_CFN_POW:
>        return fold_const_pow (result, arg0, arg1, format);
>  
> +    CASE_CFN_NEXTAFTER:
> +    CASE_CFN_NEXTTOWARD:
> +      return fold_const_nextafter (result, arg0, arg1, format);
> +
>      default:
>        return false;
>      }
> @@ -1365,20 +1412,33 @@ fold_const_call_1 (combined_fn fn, tree
>    machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
>    machine_mode arg1_mode = TYPE_MODE (TREE_TYPE (arg1));
>  
> -  if (arg0_mode == arg1_mode
> +  if (mode == arg0_mode
>        && real_cst_p (arg0)
>        && real_cst_p (arg1))
>      {
>        gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
> -      if (mode == arg0_mode)
> +      REAL_VALUE_TYPE result;
> +      if (arg0_mode == arg1_mode)
>  	{
>  	  /* real, real -> real.  */
> -	  REAL_VALUE_TYPE result;
>  	  if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
>  				   TREE_REAL_CST_PTR (arg1),
>  				   REAL_MODE_FORMAT (mode)))
>  	    return build_real (type, result);
>  	}
> +      else if (arg1_mode == TYPE_MODE (long_double_type_node))
> +	switch (fn)
> +	  {
> +	  CASE_CFN_NEXTTOWARD:
> +	    /* real, long double -> real.  */
> +	    if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
> +				     TREE_REAL_CST_PTR (arg1),
> +				     REAL_MODE_FORMAT (mode)))
> +	      return build_real (type, result);
> +	    break;
> +	  default:
> +	    break;
> +	  }
>        return NULL_TREE;
>      }
>  
> --- gcc/testsuite/gcc.dg/nextafter-1.c.jj	2018-04-20 20:53:15.418133039 +0200
> +++ gcc/testsuite/gcc.dg/nextafter-1.c	2018-04-20 20:59:27.598468003 +0200
> @@ -0,0 +1,159 @@
> +/* PR libstdc++/85466 */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fno-math-errno -fno-trapping-math -fdump-tree-optimized" } */
> +/* { dg-add-options ieee } */
> +/* { dg-final { scan-tree-dump-not "nextafter" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not "nexttoward" "optimized" } } */
> +
> +float nextafterf (float, float);
> +double nextafter (double, double);
> +long double nextafterl (long double, long double);
> +float nexttowardf (float, long double);
> +double nexttoward (double, long double);
> +long double nexttowardl (long double, long double);
> +
> +#define CHECK(x) if (!(x)) __builtin_abort ()
> +
> +#ifndef NEED_ERRNO
> +#define NEED_ERRNO 0
> +#endif
> +#ifndef NEED_EXC
> +#define NEED_EXC 0
> +#endif
> +
> +#define TEST(name, fn, type, L1, L2, l1, l2, MIN1,			     \
> +	     MAX1, DENORM_MIN1, EPSILON1, MIN2, MAX2, DENORM_MIN2)	     \
> +void									     \
> +name (void)								     \
> +{									     \
> +  const type a = fn (0.0##L1, 0.0##L2);					     \
> +  CHECK (a == 0.0##L1 && !__builtin_signbit (a));			     \
> +  const type b = fn (0.0##L1, -0.0##L2);				     \
> +  CHECK (b == 0.0##L1 && __builtin_signbit (b));			     \
> +  const type c = fn (__builtin_nan##l1 (""), 0.0##L2);			     \
> +  CHECK (__builtin_isnan##l1 (c));					     \
> +  const type d = fn (2.0##L1, __builtin_nan##l2 (""));			     \
> +  CHECK (__builtin_isnan##l1 (d));					     \
> +  const type e = NEED_EXC ? DENORM_MIN1 : fn (0.0##L1, 8.0##L2);	     \
> +  CHECK (e == DENORM_MIN1);						     \
> +  const type f = fn (1.0##L1, 8.0##L2);					     \
> +  CHECK (f == 1.0##L1 + EPSILON1);					     \
> +  const type g = fn (1.0##L1, -8.0##L2);				     \
> +  CHECK (g == 1.0##L1 - EPSILON1 / 2.0##L1);				     \
> +  const type h = fn (__builtin_inf (), 0.0##L2);			     \
> +  CHECK (h == MAX1);							     \
> +  const type i = fn (-1.0##L1, -__builtin_inf ());			     \
> +  CHECK (i == -1.0##L1 - EPSILON1);					     \
> +  const type j = fn (1.5##L1, __builtin_inf ());			     \
> +  CHECK (j == 1.5##L1 + EPSILON1);					     \
> +  const type k = fn (1.5##L1 - EPSILON1, 100.0##L2);			     \
> +  CHECK (k == 1.5##L1);							     \
> +  const type l								     \
> +    = (NEED_EXC || NEED_ERRNO) ? 0.0##L1 : fn (DENORM_MIN1, 0.0##L2);	     \
> +  CHECK (l == 0.0##L1 && !__builtin_signbit (l));			     \
> +  const type m								     \
> +    = (NEED_EXC || NEED_ERRNO) ? __builtin_inf##l1 ()			     \
> +      : fn (MAX1, __builtin_inf ());					     \
> +  CHECK (__builtin_isinf##l1 (m) && !__builtin_signbit (m));		     \
> +  const type n = fn (DENORM_MIN1, 12.0##L2);				     \
> +  CHECK (n == 2.0##L1 * DENORM_MIN1);					     \
> +  const type o = fn (n, 24.0##L2);					     \
> +  CHECK (o == 3.0##L1 * DENORM_MIN1);					     \
> +  const type p = fn (o, 132.0##L2);					     \
> +  CHECK (p == 4.0##L1 * DENORM_MIN1);					     \
> +  const type q = fn (2.0##L1 * DENORM_MIN1, -__builtin_inf ());		     \
> +  CHECK (q == DENORM_MIN1);						     \
> +  const type r = fn (3.0##L1 * DENORM_MIN1, DENORM_MIN2);		     \
> +  CHECK (r == 2.0##L1 * DENORM_MIN1);					     \
> +  const type s = fn (4.0##L1 * DENORM_MIN1, 2.0##L2 * DENORM_MIN2);	     \
> +  CHECK (s == 3.0##L1 * DENORM_MIN1);					     \
> +  const type t = fn (MIN1, 0.0##L2);					     \
> +  CHECK (t == MIN1 - DENORM_MIN1);					     \
> +  const type u = fn (MIN1 - DENORM_MIN1, -MIN2);			     \
> +  CHECK (u == MIN1 - 2.0##L1 * DENORM_MIN1);				     \
> +  const type v = fn (MIN1 - 2.0##L1 * DENORM_MIN1, 100.0##L2);		     \
> +  CHECK (v == MIN1 - DENORM_MIN1);					     \
> +  const type w = fn (MIN1 - DENORM_MIN1, MAX2);				     \
> +  CHECK (w == MIN1);							     \
> +  const type x = fn (MIN1, 17.0##L2);					     \
> +  CHECK (x == MIN1 + DENORM_MIN1);					     \
> +  const type y = fn (MIN1 + DENORM_MIN1, __builtin_inf##l2 ());		     \
> +  CHECK (y == MIN1 + 2.0##L1 * DENORM_MIN1);				     \
> +  const type z = fn (MIN1 / 2.0##L1, -MIN2);				     \
> +  CHECK (z == MIN1 / 2.0##L1 - DENORM_MIN1);				     \
> +  const type aa = fn (-MIN1 / 4.0##L1, MIN2);				     \
> +  CHECK (aa == -MIN1 / 4.0##L1 + DENORM_MIN1);				     \
> +  const type ab = fn (MIN1 * 2.0##L1, -MIN2);				     \
> +  CHECK (ab == MIN1 * 2.0##L1 - DENORM_MIN1);				     \
> +  const type ac = fn (MIN1 * 4.0##L1, MIN2);				     \
> +  CHECK (ac == MIN1 * 4.0##L1 - DENORM_MIN1 * 2.0##L1);			     \
> +  const type ad = fn (MIN1 * 64.0##L1, MIN2);				     \
> +  CHECK (ad == MIN1 * 64.0##L1 - DENORM_MIN1 * 32.0##L1);		     \
> +  const type ae = fn (MIN1 / 2.0##L1 - DENORM_MIN1, 100.0##L2);		     \
> +  CHECK (ae == MIN1 / 2.0##L1);						     \
> +  const type af = fn (-MIN1 / 4 + DENORM_MIN1, -100.0##L2);		     \
> +  CHECK (af == -MIN1 / 4.0##L1);					     \
> +  const type ag = fn (MIN1 * 2.0##L1 - DENORM_MIN1, 100.0##L2);		     \
> +  CHECK (ag == MIN1 * 2.0##L1);						     \
> +  const type ah = fn (MIN1 * 4.0##L1 - 2.0##L1 * DENORM_MIN1, 100.0##L2);    \
> +  CHECK (ah == MIN1 * 4.0##L1);						     \
> +  const type ai = fn (MIN1 * 64.0##L1 - 32.0##L1 * DENORM_MIN1, 100.0##L2);  \
> +  CHECK (ai == MIN1 * 64.0##L1);					     \
> +  const type aj = fn (MIN1 * 64.0##L1, 100.0##L2);			     \
> +  CHECK (aj == MIN1 * 64.0##L1 + 64.0##L1 * DENORM_MIN1);		     \
> +  const type ak = fn (MIN1 * 64.0##L1 + DENORM_MIN1 * 64.0##L1, 1024.0##L2); \
> +  CHECK (ak == MIN1 * 64.0##L1 + 128.0##L1 * DENORM_MIN1);		     \
> +  const type al = fn (128.0##L1, 128.0##L2);				     \
> +  CHECK (al == 128.0##L1);						     \
> +  const type am = fn (128.0##L1, 129.0##L2);				     \
> +  CHECK (am == 128.0##L1 + 128.0##L1 * EPSILON1);			     \
> +  const type an = fn (-128.0##L1 + -128.0##L1 * EPSILON1, -130.0##L2);	     \
> +  CHECK (an == -128.0##L1 - 256.0##L1 * EPSILON1);			     \
> +  const type ao = fn (128.0##L1 + 256.0##L1 * EPSILON1, 256.0##L2);	     \
> +  CHECK (ao == 128.0##L1 + 384.0##L1 * EPSILON1);			     \
> +  const type ap = fn (128.0##L1 + 384.0##L1 * EPSILON1, -0.0##L2);	     \
> +  CHECK (ap == 128.0##L1 + 256.0##L1 * EPSILON1);			     \
> +  const type aq = fn (128.0##L1 + 256.0##L1 * EPSILON1, 1.0##L2);	     \
> +  CHECK (aq == 128.0##L1 + 128.0##L1 * EPSILON1);			     \
> +  const type ar = fn (128.0##L1 + 128.0##L1 * EPSILON1, 0.0##L2);	     \
> +  CHECK (ar == 128.0##L1);						     \
> +  const type as = fn (128.0##L1, 0.0##L2);				     \
> +  CHECK (as == 128.0##L1 - 64.0##L1 * EPSILON1);			     \
> +  const type at = fn (128.0##L1 - 64.0##L1 * EPSILON1, 5.0##L2);	     \
> +  CHECK (at == 128.0##L1 - 128.0##L1 * EPSILON1);			     \
> +}
> +
> +TEST (test1, nextafterf, float, F, F, f, f, __FLT_MIN__, __FLT_MAX__,
> +      __FLT_DENORM_MIN__, __FLT_EPSILON__, __FLT_MIN__, __FLT_MAX__,
> +      __FLT_DENORM_MIN__)
> +TEST (test2, nextafter, double, , , , , __DBL_MIN__, __DBL_MAX__,
> +      __DBL_DENORM_MIN__, __DBL_EPSILON__, __DBL_MIN__, __DBL_MAX__,
> +      __DBL_DENORM_MIN__)
> +#if __LDBL_MANT_DIG__ != 106
> +TEST (test3, nextafterl, long double, L, L, l, l, __LDBL_MIN__, __LDBL_MAX__,
> +      __LDBL_DENORM_MIN__, __LDBL_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
> +      __LDBL_DENORM_MIN__)
> +TEST (test4, nexttowardf, float, F, L, f, l, __FLT_MIN__, __FLT_MAX__,
> +      __FLT_DENORM_MIN__, __FLT_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
> +      __LDBL_DENORM_MIN__)
> +TEST (test5, nexttoward, double, , L, , l, __DBL_MIN__, __DBL_MAX__,
> +      __DBL_DENORM_MIN__, __DBL_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
> +      __LDBL_DENORM_MIN__)
> +TEST (test6, nexttowardl, long double, L, L, l, l, __LDBL_MIN__, __LDBL_MAX__,
> +      __LDBL_DENORM_MIN__, __LDBL_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
> +      __LDBL_DENORM_MIN__)
> +#endif
> +
> +int
> +main ()
> +{
> +  test1 ();
> +  test2 ();
> +#if __LDBL_MANT_DIG__ != 106
> +  test3 ();
> +  test4 ();
> +  test5 ();
> +  test6 ();
> +#endif
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/nextafter-2.c.jj	2018-04-20 20:53:24.662141363 +0200
> +++ gcc/testsuite/gcc.dg/nextafter-2.c	2018-04-20 20:54:01.031174134 +0200
> @@ -0,0 +1,6 @@
> +/* PR libstdc++/85466 */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fno-builtin" } */
> +/* { dg-add-options ieee } */
> +
> +#include "nextafter-1.c"
> --- gcc/testsuite/gcc.dg/nextafter-3.c.jj	2018-04-20 20:54:09.867182101 +0200
> +++ gcc/testsuite/gcc.dg/nextafter-3.c	2018-04-20 20:59:48.280486574 +0200
> @@ -0,0 +1,9 @@
> +/* PR libstdc++/85466 */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fmath-errno -fno-trapping-math -fdump-tree-optimized" } */
> +/* { dg-add-options ieee } */
> +/* { dg-final { scan-tree-dump-not "nextafter" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not "nexttoward" "optimized" } } */
> +
> +#define NEED_ERRNO 1
> +#include "nextafter-1.c"
> --- gcc/testsuite/gcc.dg/nextafter-4.c.jj	2018-04-20 20:55:11.880237977 +0200
> +++ gcc/testsuite/gcc.dg/nextafter-4.c	2018-04-20 20:59:54.927492541 +0200
> @@ -0,0 +1,10 @@
> +/* PR libstdc++/85466 */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fmath-errno -ftrapping-math -fdump-tree-optimized" } */
> +/* { dg-add-options ieee } */
> +/* { dg-final { scan-tree-dump-not "nextafter" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not "nexttoward" "optimized" } } */
> +
> +#define NEED_ERRNO 1
> +#define NEED_EXC 1
> +#include "nextafter-1.c"
> 
> 	Jakub
> 
>
Jakub Jelinek May 3, 2018, 4:35 p.m. UTC | #2
On Thu, May 03, 2018 at 11:44:09AM +0200, Richard Biener wrote:
> > +      *RESULT = nextafter (*ARG0, *ARG1)
> > +
> > +   or
> > +
> > +      *RESULT = nexttoward (*ARG0, *ARG1)
> > +
> > +   in format FORMAT.  Return true on success.  */
> > +
> > +static bool
> > +fold_const_nextafter (real_value *result, const real_value *arg0,
> > +		      const real_value *arg1, const real_format *format)
> > +{
> > +  if (flag_signaling_nans
> 
> HONOR_SNANS ()?

That requires a machine_mode or tree, but I don't have either of those,
nor the caller (fold_const_call_sss) has those.

I could change it to:
  if (flag_signalling_nans
      && !flag_finite_math_only
      && format->has_nans
so that it would better duplicate what HONOR_SNANS actually tests.
Though, I think it is ok to punt if one of the operands is a signalling nan
even if flag_signalling_nans.

> > +      && (REAL_VALUE_ISSIGNALING_NAN (*arg0)
> > +	  || REAL_VALUE_ISSIGNALING_NAN (*arg1)))
> > +    return false;
> > +
> > +  /* Don't handle composite modes, nor decimal, nor modes without
> > +     inf or denorm at least for now.  */
> > +  if (format->pnan < format->p
> > +      || format->b == 10
> > +      || !format->has_inf
> > +      || !format->has_denorm)
> > +    return false;
> 
> I wonder if we should assert in real_nextafter that we can actually
> handle the input?  It likely returns garbage if fed with decimal float
> stuff?

Yes, but nobody would call it even if we removed the || format->b == 10
condition, the only builtins we fold this for have non-decimal
float/double/long double arguments.
I can surely add an assert with the same set of checks as the above ones.

	Jakub
Jakub Jelinek May 3, 2018, 4:53 p.m. UTC | #3
On Thu, May 03, 2018 at 06:35:50PM +0200, Jakub Jelinek wrote:
> That requires a machine_mode or tree, but I don't have either of those,
> nor the caller (fold_const_call_sss) has those.
> 
> I could change it to:
>   if (flag_signalling_nans
>       && !flag_finite_math_only
>       && format->has_nans
> so that it would better duplicate what HONOR_SNANS actually tests.
> Though, I think it is ok to punt if one of the operands is a signalling nan
> even if flag_signalling_nans.

Though in theory the arg0's mode for which we have format pointer could
not have nans, but arg1's mode (for which we only have the REAL_VALUE_TYPE)
could have nans (or vice versa).

	Jakub
Richard Biener May 4, 2018, 6:58 a.m. UTC | #4
On Thu, 3 May 2018, Jakub Jelinek wrote:

> On Thu, May 03, 2018 at 06:35:50PM +0200, Jakub Jelinek wrote:
> > That requires a machine_mode or tree, but I don't have either of those,
> > nor the caller (fold_const_call_sss) has those.
> > 
> > I could change it to:
> >   if (flag_signalling_nans
> >       && !flag_finite_math_only
> >       && format->has_nans
> > so that it would better duplicate what HONOR_SNANS actually tests.
> > Though, I think it is ok to punt if one of the operands is a signalling nan
> > even if flag_signalling_nans.
> 
> Though in theory the arg0's mode for which we have format pointer could
> not have nans, but arg1's mode (for which we only have the REAL_VALUE_TYPE)
> could have nans (or vice versa).

Hmm, indeed.  OTOH what's wrong with just removing the 
flag_singalling_nans check, just keeping the actual checks for
REAL_VALUE_ISSIGNALING_NAN?

Thus the patch is ok with just those checks.  The requested assert
shouldn't be neccessary indeed.

Thanks,
Richard.
Tom de Vries May 7, 2018, 10:04 a.m. UTC | #5
On 04/21/2018 07:36 PM, Jakub Jelinek wrote:
> 	* gcc.dg/nextafter-2.c: New test.

Hi,

FTR, I ran into a link error "unresolved symbol nexttowardf" using the 
standalone nvptx toolchain:
...
PASS: gcc.dg/nextafter-1.c (test for excess errors)
PASS: gcc.dg/nextafter-1.c execution test
PASS: gcc.dg/nextafter-1.c scan-tree-dump-not optimized "nextafter"
PASS: gcc.dg/nextafter-1.c scan-tree-dump-not optimized "nexttoward"
FAIL: gcc.dg/nextafter-2.c (test for excess errors)
UNRESOLVED: gcc.dg/nextafter-2.c compilation failed to produce executable
PASS: gcc.dg/nextafter-3.c (test for excess errors)
PASS: gcc.dg/nextafter-3.c execution test
PASS: gcc.dg/nextafter-3.c scan-tree-dump-not optimized "nextafter"
PASS: gcc.dg/nextafter-3.c scan-tree-dump-not optimized "nexttoward"
PASS: gcc.dg/nextafter-4.c (test for excess errors)
PASS: gcc.dg/nextafter-4.c execution test
PASS: gcc.dg/nextafter-4.c scan-tree-dump-not optimized "nextafter"
PASS: gcc.dg/nextafter-4.c scan-tree-dump-not optimized "nexttoward"
...

This failure exposes a newlib bug. I've submitted a patch here ( 
https://sourceware.org/ml/newlib/2018/msg00350.html ).

Thanks,
- Tom
Christophe Lyon May 7, 2018, 1:41 p.m. UTC | #6
On 7 May 2018 at 12:04, Tom de Vries <Tom_deVries@mentor.com> wrote:
> On 04/21/2018 07:36 PM, Jakub Jelinek wrote:
>>
>>         * gcc.dg/nextafter-2.c: New test.
>
>
> Hi,
>
> FTR, I ran into a link error "unresolved symbol nexttowardf" using the
> standalone nvptx toolchain:
> ...
> PASS: gcc.dg/nextafter-1.c (test for excess errors)
> PASS: gcc.dg/nextafter-1.c execution test
> PASS: gcc.dg/nextafter-1.c scan-tree-dump-not optimized "nextafter"
> PASS: gcc.dg/nextafter-1.c scan-tree-dump-not optimized "nexttoward"
> FAIL: gcc.dg/nextafter-2.c (test for excess errors)
> UNRESOLVED: gcc.dg/nextafter-2.c compilation failed to produce executable
> PASS: gcc.dg/nextafter-3.c (test for excess errors)
> PASS: gcc.dg/nextafter-3.c execution test
> PASS: gcc.dg/nextafter-3.c scan-tree-dump-not optimized "nextafter"
> PASS: gcc.dg/nextafter-3.c scan-tree-dump-not optimized "nexttoward"
> PASS: gcc.dg/nextafter-4.c (test for excess errors)
> PASS: gcc.dg/nextafter-4.c execution test
> PASS: gcc.dg/nextafter-4.c scan-tree-dump-not optimized "nextafter"
> PASS: gcc.dg/nextafter-4.c scan-tree-dump-not optimized "nexttoward"
> ...
>
> This failure exposes a newlib bug. I've submitted a patch here (
> https://sourceware.org/ml/newlib/2018/msg00350.html ).
>

Hi

I noticed the same problem on arm and aarch64 bare-metal targets using newlib,
and I thought it was a matter of adding the c99_runtime effective target,
as done in the attached patch.

Even if newlib gets a fix for this, the effective target will still
claim c99_runtime
is not supported on such targets....

Thanks,

Christophe


> Thanks,
> - Tom
gcc/testsuite/ChangeLog:

2018-05-07  Christophe Lyon  <christophe.lyon@linaro.org>

	* ChangeLog:
	* gcc.dg/nextafter-2.c: Add c99_runtime effective target and
	options.
Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog	(revision 259991)
+++ gcc/testsuite/ChangeLog	(working copy)
@@ -1,3 +1,8 @@
+2018-05-07  Christophe Lyon  <christophe.lyon@linaro.org>
+
+	* gcc.dg/nextafter-2.c: Add c99_runtime effective target
+	and options.
+
 2018-05-06  Jakub Jelinek  <jakub@redhat.com>
 
 	PR c++/85659
Index: gcc/testsuite/gcc.dg/nextafter-2.c
===================================================================
--- gcc/testsuite/gcc.dg/nextafter-2.c	(revision 259991)
+++ gcc/testsuite/gcc.dg/nextafter-2.c	(working copy)
@@ -1,6 +1,8 @@
 /* PR libstdc++/85466 */
 /* { dg-do run } */
+/* { dg-require-effective-target c99_runtime } */
 /* { dg-options "-O2 -fno-builtin" } */
 /* { dg-add-options ieee } */
+/* { dg-add-options c99_runtime } */
 
 #include "nextafter-1.c"
Jakub Jelinek May 7, 2018, 1:43 p.m. UTC | #7
On Mon, May 07, 2018 at 03:41:58PM +0200, Christophe Lyon wrote:
> I noticed the same problem on arm and aarch64 bare-metal targets using newlib,
> and I thought it was a matter of adding the c99_runtime effective target,
> as done in the attached patch.
> 
> Even if newlib gets a fix for this, the effective target will still
> claim c99_runtime
> is not supported on such targets....

Ok for trunk.

> 2018-05-07  Christophe Lyon  <christophe.lyon@linaro.org>
> 
> 	* ChangeLog:
> 	* gcc.dg/nextafter-2.c: Add c99_runtime effective target and
> 	options.

> --- gcc/testsuite/gcc.dg/nextafter-2.c	(revision 259991)
> +++ gcc/testsuite/gcc.dg/nextafter-2.c	(working copy)
> @@ -1,6 +1,8 @@
>  /* PR libstdc++/85466 */
>  /* { dg-do run } */
> +/* { dg-require-effective-target c99_runtime } */
>  /* { dg-options "-O2 -fno-builtin" } */
>  /* { dg-add-options ieee } */
> +/* { dg-add-options c99_runtime } */
>  
>  #include "nextafter-1.c"


	Jakub
Tom de Vries May 8, 2018, 7:53 a.m. UTC | #8
On 05/07/2018 03:41 PM, Christophe Lyon wrote:
> On 7 May 2018 at 12:04, Tom de Vries <Tom_deVries@mentor.com> wrote:
>> On 04/21/2018 07:36 PM, Jakub Jelinek wrote:
>>>
>>>          * gcc.dg/nextafter-2.c: New test.
>>
>>
>> Hi,
>>
>> FTR, I ran into a link error "unresolved symbol nexttowardf" using the
>> standalone nvptx toolchain:
>> ...
>> PASS: gcc.dg/nextafter-1.c (test for excess errors)
>> PASS: gcc.dg/nextafter-1.c execution test
>> PASS: gcc.dg/nextafter-1.c scan-tree-dump-not optimized "nextafter"
>> PASS: gcc.dg/nextafter-1.c scan-tree-dump-not optimized "nexttoward"
>> FAIL: gcc.dg/nextafter-2.c (test for excess errors)
>> UNRESOLVED: gcc.dg/nextafter-2.c compilation failed to produce executable
>> PASS: gcc.dg/nextafter-3.c (test for excess errors)
>> PASS: gcc.dg/nextafter-3.c execution test
>> PASS: gcc.dg/nextafter-3.c scan-tree-dump-not optimized "nextafter"
>> PASS: gcc.dg/nextafter-3.c scan-tree-dump-not optimized "nexttoward"
>> PASS: gcc.dg/nextafter-4.c (test for excess errors)
>> PASS: gcc.dg/nextafter-4.c execution test
>> PASS: gcc.dg/nextafter-4.c scan-tree-dump-not optimized "nextafter"
>> PASS: gcc.dg/nextafter-4.c scan-tree-dump-not optimized "nexttoward"
>> ...
>>
>> This failure exposes a newlib bug. I've submitted a patch here (
>> https://sourceware.org/ml/newlib/2018/msg00350.html ).
>>
> 
> Hi
> 
> I noticed the same problem on arm and aarch64 bare-metal targets using newlib,
> and I thought it was a matter of adding the c99_runtime effective target,
> as done in the attached patch.
> 
> Even if newlib gets a fix for this, the effective target will still
> claim c99_runtime
> is not supported on such targets....
> 

Hi Christophe,

It's true that newlib does not support c99 fully, but given that with 
the fix mentioned above (which was applied upstream) the test-case is 
linking, the c99 functions required by the test-case are at least present.

With the fix applied, the test-case now fails in execution for nvptx. I 
don't know yet whether that's a target issue or a newlib issue. Can you 
remove the c99 effective target test and run with updated newlib and see 
if it passes or fails in execution for arm/aarch64?

FWIW, also this test fails for me in execution on ubuntu 16.04 with 
glibc 2.23.

Thanks,
- Tom
Steve Ellcey May 10, 2018, 6:27 p.m. UTC | #9
I think this patch is causing a glibc testing error.  The
tests math/bug-nextafter and math/bug-nexttoward are failing
due to underflow not getting set.  Here is a test case that
should print nothing but is currently printing the
'did not underflow' message.

#include <fenv.h>
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include <stdio.h>

float zero = 0.0;
float inf = INFINITY;

int
main (void)
{
  int result = 0;

  float i = INFINITY;
  float m = FLT_MAX;

  i = 0;
  m = FLT_MIN;
  feclearexcept (FE_ALL_EXCEPT);
  i = nextafterf (m, i);
  if (i < 0 || i >= FLT_MIN)
    {
      printf ("nextafterf+ failed\n");
      ++result;
    }
  if (fetestexcept (FE_UNDERFLOW) == 0)
    {
      printf ("nextafterf+ did not underflow\n");
      ++result;
    }
  return result;
}
diff mbox series

Patch

--- gcc/real.h.jj	2018-01-03 10:19:54.349533828 +0100
+++ gcc/real.h	2018-04-20 12:44:30.707350855 +0200
@@ -507,6 +507,10 @@  extern void real_copysign (REAL_VALUE_TY
 extern bool real_isinteger (const REAL_VALUE_TYPE *, format_helper);
 extern bool real_isinteger (const REAL_VALUE_TYPE *, HOST_WIDE_INT *);
 
+/* Calculate nextafter (X, Y) in format FMT.  */
+extern bool real_nextafter (REAL_VALUE_TYPE *, format_helper,
+			    const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);
+
 /* Write into BUF the maximum representable finite floating-point
    number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
    float string.  BUF must be large enough to contain the result.  */
--- gcc/real.c.jj	2018-01-03 10:19:55.003533933 +0100
+++ gcc/real.c	2018-04-21 19:29:09.103584254 +0200
@@ -5048,6 +5048,102 @@  real_isinteger (const REAL_VALUE_TYPE *c
   return false;
 }
 
+/* Calculate nextafter (X, Y) or nexttoward (X, Y).  Return true if
+   underflow or overflow needs to be raised.  */
+
+bool
+real_nextafter (REAL_VALUE_TYPE *r, format_helper fmt,
+		const REAL_VALUE_TYPE *x, const REAL_VALUE_TYPE *y)
+{
+  int cmp = do_compare (x, y, 2);
+  /* If either operand is NaN, return qNaN.  */
+  if (cmp == 2)
+    {
+      get_canonical_qnan (r, 0);
+      return false;
+    }
+  /* If x == y, return y cast to target type.  */
+  if (cmp == 0)
+    {
+      real_convert (r, fmt, y);
+      return false;
+    }
+
+  if (x->cl == rvc_zero)
+    {
+      get_zero (r, y->sign);
+      r->cl = rvc_normal;
+      SET_REAL_EXP (r, fmt->emin - fmt->p + 1);
+      r->sig[SIGSZ - 1] = SIG_MSB;
+      return false;
+    }
+
+  int np2 = SIGNIFICAND_BITS - fmt->p;
+  /* For denormals adjust np2 correspondingly.  */
+  if (x->cl == rvc_normal && REAL_EXP (x) < fmt->emin)
+    np2 += fmt->emin - REAL_EXP (x);
+
+  REAL_VALUE_TYPE u;
+  get_zero (r, x->sign);
+  get_zero (&u, 0);
+  set_significand_bit (&u, np2);
+  r->cl = rvc_normal;
+  SET_REAL_EXP (r, REAL_EXP (x));
+
+  if (x->cl == rvc_inf)
+    {
+      bool borrow = sub_significands (r, r, &u, 0);
+      gcc_assert (borrow);
+      SET_REAL_EXP (r, fmt->emax);
+    }
+  else if (cmp == (x->sign ? 1 : -1))
+    {
+      if (add_significands (r, x, &u))
+	{
+	  /* Overflow.  Means the significand had been all ones, and
+	     is now all zeros.  Need to increase the exponent, and
+	     possibly re-normalize it.  */
+	  SET_REAL_EXP (r, REAL_EXP (r) + 1);
+	  if (REAL_EXP (r) > fmt->emax)
+	    {
+	      get_inf (r, x->sign);
+	      return true;
+	    }
+	  r->sig[SIGSZ - 1] = SIG_MSB;
+	}
+    }
+  else
+    {
+      if (REAL_EXP (x) > fmt->emin && x->sig[SIGSZ - 1] == SIG_MSB)
+	{
+	  int i;
+	  for (i = SIGSZ - 2; i >= 0; i--)
+	    if (x->sig[i])
+	      break;
+	  if (i < 0)
+	    {
+	      /* When mantissa is 1.0, we need to subtract only
+		 half of u: nextafter (1.0, 0.0) is 1.0 - __DBL_EPSILON__ / 2
+		 rather than 1.0 - __DBL_EPSILON__.  */
+	      clear_significand_bit (&u, np2);
+	      np2--;
+	      set_significand_bit (&u, np2);
+	    }
+	}
+      sub_significands (r, x, &u, 0);
+    }
+
+  /* Clear out trailing garbage.  */
+  clear_significand_below (r, np2);
+  normalize (r);
+  if (REAL_EXP (r) <= fmt->emin - fmt->p)
+    {
+      get_zero (r, x->sign);
+      return true;
+    }
+  return r->cl == rvc_zero;
+}
+
 /* Write into BUF the maximum representable finite floating-point
    number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
    float string.  LEN is the size of BUF, and the buffer must be large
--- gcc/fold-const-call.c.jj	2018-01-14 17:16:52.873836266 +0100
+++ gcc/fold-const-call.c	2018-04-20 19:36:40.987942433 +0200
@@ -529,6 +529,49 @@  fold_const_pow (real_value *result, cons
 
 /* Try to evaluate:
 
+      *RESULT = nextafter (*ARG0, *ARG1)
+
+   or
+
+      *RESULT = nexttoward (*ARG0, *ARG1)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_nextafter (real_value *result, const real_value *arg0,
+		      const real_value *arg1, const real_format *format)
+{
+  if (flag_signaling_nans
+      && (REAL_VALUE_ISSIGNALING_NAN (*arg0)
+	  || REAL_VALUE_ISSIGNALING_NAN (*arg1)))
+    return false;
+
+  /* Don't handle composite modes, nor decimal, nor modes without
+     inf or denorm at least for now.  */
+  if (format->pnan < format->p
+      || format->b == 10
+      || !format->has_inf
+      || !format->has_denorm)
+    return false;
+
+  if (real_nextafter (result, format, arg0, arg1)
+      /* If raising underflow or overflow and setting errno to ERANGE,
+	 fail if we care about those side-effects.  */
+      && (flag_trapping_math || flag_errno_math))
+    return false;
+  /* Similarly for nextafter (0, 1) raising underflow.  */
+  else if (flag_trapping_math
+	   && arg0->cl == rvc_zero
+	   && result->cl != rvc_zero)
+    return false;
+
+  real_convert (result, format, result);
+
+  return true;
+}
+
+/* Try to evaluate:
+
       *RESULT = ldexp (*ARG0, ARG1)
 
    in format FORMAT.  Return true on success.  */
@@ -1260,6 +1303,10 @@  fold_const_call_sss (real_value *result,
     CASE_CFN_POW:
       return fold_const_pow (result, arg0, arg1, format);
 
+    CASE_CFN_NEXTAFTER:
+    CASE_CFN_NEXTTOWARD:
+      return fold_const_nextafter (result, arg0, arg1, format);
+
     default:
       return false;
     }
@@ -1365,20 +1412,33 @@  fold_const_call_1 (combined_fn fn, tree
   machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
   machine_mode arg1_mode = TYPE_MODE (TREE_TYPE (arg1));
 
-  if (arg0_mode == arg1_mode
+  if (mode == arg0_mode
       && real_cst_p (arg0)
       && real_cst_p (arg1))
     {
       gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
-      if (mode == arg0_mode)
+      REAL_VALUE_TYPE result;
+      if (arg0_mode == arg1_mode)
 	{
 	  /* real, real -> real.  */
-	  REAL_VALUE_TYPE result;
 	  if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
 				   TREE_REAL_CST_PTR (arg1),
 				   REAL_MODE_FORMAT (mode)))
 	    return build_real (type, result);
 	}
+      else if (arg1_mode == TYPE_MODE (long_double_type_node))
+	switch (fn)
+	  {
+	  CASE_CFN_NEXTTOWARD:
+	    /* real, long double -> real.  */
+	    if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
+				     TREE_REAL_CST_PTR (arg1),
+				     REAL_MODE_FORMAT (mode)))
+	      return build_real (type, result);
+	    break;
+	  default:
+	    break;
+	  }
       return NULL_TREE;
     }
 
--- gcc/testsuite/gcc.dg/nextafter-1.c.jj	2018-04-20 20:53:15.418133039 +0200
+++ gcc/testsuite/gcc.dg/nextafter-1.c	2018-04-20 20:59:27.598468003 +0200
@@ -0,0 +1,159 @@ 
+/* PR libstdc++/85466 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fno-math-errno -fno-trapping-math -fdump-tree-optimized" } */
+/* { dg-add-options ieee } */
+/* { dg-final { scan-tree-dump-not "nextafter" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "nexttoward" "optimized" } } */
+
+float nextafterf (float, float);
+double nextafter (double, double);
+long double nextafterl (long double, long double);
+float nexttowardf (float, long double);
+double nexttoward (double, long double);
+long double nexttowardl (long double, long double);
+
+#define CHECK(x) if (!(x)) __builtin_abort ()
+
+#ifndef NEED_ERRNO
+#define NEED_ERRNO 0
+#endif
+#ifndef NEED_EXC
+#define NEED_EXC 0
+#endif
+
+#define TEST(name, fn, type, L1, L2, l1, l2, MIN1,			     \
+	     MAX1, DENORM_MIN1, EPSILON1, MIN2, MAX2, DENORM_MIN2)	     \
+void									     \
+name (void)								     \
+{									     \
+  const type a = fn (0.0##L1, 0.0##L2);					     \
+  CHECK (a == 0.0##L1 && !__builtin_signbit (a));			     \
+  const type b = fn (0.0##L1, -0.0##L2);				     \
+  CHECK (b == 0.0##L1 && __builtin_signbit (b));			     \
+  const type c = fn (__builtin_nan##l1 (""), 0.0##L2);			     \
+  CHECK (__builtin_isnan##l1 (c));					     \
+  const type d = fn (2.0##L1, __builtin_nan##l2 (""));			     \
+  CHECK (__builtin_isnan##l1 (d));					     \
+  const type e = NEED_EXC ? DENORM_MIN1 : fn (0.0##L1, 8.0##L2);	     \
+  CHECK (e == DENORM_MIN1);						     \
+  const type f = fn (1.0##L1, 8.0##L2);					     \
+  CHECK (f == 1.0##L1 + EPSILON1);					     \
+  const type g = fn (1.0##L1, -8.0##L2);				     \
+  CHECK (g == 1.0##L1 - EPSILON1 / 2.0##L1);				     \
+  const type h = fn (__builtin_inf (), 0.0##L2);			     \
+  CHECK (h == MAX1);							     \
+  const type i = fn (-1.0##L1, -__builtin_inf ());			     \
+  CHECK (i == -1.0##L1 - EPSILON1);					     \
+  const type j = fn (1.5##L1, __builtin_inf ());			     \
+  CHECK (j == 1.5##L1 + EPSILON1);					     \
+  const type k = fn (1.5##L1 - EPSILON1, 100.0##L2);			     \
+  CHECK (k == 1.5##L1);							     \
+  const type l								     \
+    = (NEED_EXC || NEED_ERRNO) ? 0.0##L1 : fn (DENORM_MIN1, 0.0##L2);	     \
+  CHECK (l == 0.0##L1 && !__builtin_signbit (l));			     \
+  const type m								     \
+    = (NEED_EXC || NEED_ERRNO) ? __builtin_inf##l1 ()			     \
+      : fn (MAX1, __builtin_inf ());					     \
+  CHECK (__builtin_isinf##l1 (m) && !__builtin_signbit (m));		     \
+  const type n = fn (DENORM_MIN1, 12.0##L2);				     \
+  CHECK (n == 2.0##L1 * DENORM_MIN1);					     \
+  const type o = fn (n, 24.0##L2);					     \
+  CHECK (o == 3.0##L1 * DENORM_MIN1);					     \
+  const type p = fn (o, 132.0##L2);					     \
+  CHECK (p == 4.0##L1 * DENORM_MIN1);					     \
+  const type q = fn (2.0##L1 * DENORM_MIN1, -__builtin_inf ());		     \
+  CHECK (q == DENORM_MIN1);						     \
+  const type r = fn (3.0##L1 * DENORM_MIN1, DENORM_MIN2);		     \
+  CHECK (r == 2.0##L1 * DENORM_MIN1);					     \
+  const type s = fn (4.0##L1 * DENORM_MIN1, 2.0##L2 * DENORM_MIN2);	     \
+  CHECK (s == 3.0##L1 * DENORM_MIN1);					     \
+  const type t = fn (MIN1, 0.0##L2);					     \
+  CHECK (t == MIN1 - DENORM_MIN1);					     \
+  const type u = fn (MIN1 - DENORM_MIN1, -MIN2);			     \
+  CHECK (u == MIN1 - 2.0##L1 * DENORM_MIN1);				     \
+  const type v = fn (MIN1 - 2.0##L1 * DENORM_MIN1, 100.0##L2);		     \
+  CHECK (v == MIN1 - DENORM_MIN1);					     \
+  const type w = fn (MIN1 - DENORM_MIN1, MAX2);				     \
+  CHECK (w == MIN1);							     \
+  const type x = fn (MIN1, 17.0##L2);					     \
+  CHECK (x == MIN1 + DENORM_MIN1);					     \
+  const type y = fn (MIN1 + DENORM_MIN1, __builtin_inf##l2 ());		     \
+  CHECK (y == MIN1 + 2.0##L1 * DENORM_MIN1);				     \
+  const type z = fn (MIN1 / 2.0##L1, -MIN2);				     \
+  CHECK (z == MIN1 / 2.0##L1 - DENORM_MIN1);				     \
+  const type aa = fn (-MIN1 / 4.0##L1, MIN2);				     \
+  CHECK (aa == -MIN1 / 4.0##L1 + DENORM_MIN1);				     \
+  const type ab = fn (MIN1 * 2.0##L1, -MIN2);				     \
+  CHECK (ab == MIN1 * 2.0##L1 - DENORM_MIN1);				     \
+  const type ac = fn (MIN1 * 4.0##L1, MIN2);				     \
+  CHECK (ac == MIN1 * 4.0##L1 - DENORM_MIN1 * 2.0##L1);			     \
+  const type ad = fn (MIN1 * 64.0##L1, MIN2);				     \
+  CHECK (ad == MIN1 * 64.0##L1 - DENORM_MIN1 * 32.0##L1);		     \
+  const type ae = fn (MIN1 / 2.0##L1 - DENORM_MIN1, 100.0##L2);		     \
+  CHECK (ae == MIN1 / 2.0##L1);						     \
+  const type af = fn (-MIN1 / 4 + DENORM_MIN1, -100.0##L2);		     \
+  CHECK (af == -MIN1 / 4.0##L1);					     \
+  const type ag = fn (MIN1 * 2.0##L1 - DENORM_MIN1, 100.0##L2);		     \
+  CHECK (ag == MIN1 * 2.0##L1);						     \
+  const type ah = fn (MIN1 * 4.0##L1 - 2.0##L1 * DENORM_MIN1, 100.0##L2);    \
+  CHECK (ah == MIN1 * 4.0##L1);						     \
+  const type ai = fn (MIN1 * 64.0##L1 - 32.0##L1 * DENORM_MIN1, 100.0##L2);  \
+  CHECK (ai == MIN1 * 64.0##L1);					     \
+  const type aj = fn (MIN1 * 64.0##L1, 100.0##L2);			     \
+  CHECK (aj == MIN1 * 64.0##L1 + 64.0##L1 * DENORM_MIN1);		     \
+  const type ak = fn (MIN1 * 64.0##L1 + DENORM_MIN1 * 64.0##L1, 1024.0##L2); \
+  CHECK (ak == MIN1 * 64.0##L1 + 128.0##L1 * DENORM_MIN1);		     \
+  const type al = fn (128.0##L1, 128.0##L2);				     \
+  CHECK (al == 128.0##L1);						     \
+  const type am = fn (128.0##L1, 129.0##L2);				     \
+  CHECK (am == 128.0##L1 + 128.0##L1 * EPSILON1);			     \
+  const type an = fn (-128.0##L1 + -128.0##L1 * EPSILON1, -130.0##L2);	     \
+  CHECK (an == -128.0##L1 - 256.0##L1 * EPSILON1);			     \
+  const type ao = fn (128.0##L1 + 256.0##L1 * EPSILON1, 256.0##L2);	     \
+  CHECK (ao == 128.0##L1 + 384.0##L1 * EPSILON1);			     \
+  const type ap = fn (128.0##L1 + 384.0##L1 * EPSILON1, -0.0##L2);	     \
+  CHECK (ap == 128.0##L1 + 256.0##L1 * EPSILON1);			     \
+  const type aq = fn (128.0##L1 + 256.0##L1 * EPSILON1, 1.0##L2);	     \
+  CHECK (aq == 128.0##L1 + 128.0##L1 * EPSILON1);			     \
+  const type ar = fn (128.0##L1 + 128.0##L1 * EPSILON1, 0.0##L2);	     \
+  CHECK (ar == 128.0##L1);						     \
+  const type as = fn (128.0##L1, 0.0##L2);				     \
+  CHECK (as == 128.0##L1 - 64.0##L1 * EPSILON1);			     \
+  const type at = fn (128.0##L1 - 64.0##L1 * EPSILON1, 5.0##L2);	     \
+  CHECK (at == 128.0##L1 - 128.0##L1 * EPSILON1);			     \
+}
+
+TEST (test1, nextafterf, float, F, F, f, f, __FLT_MIN__, __FLT_MAX__,
+      __FLT_DENORM_MIN__, __FLT_EPSILON__, __FLT_MIN__, __FLT_MAX__,
+      __FLT_DENORM_MIN__)
+TEST (test2, nextafter, double, , , , , __DBL_MIN__, __DBL_MAX__,
+      __DBL_DENORM_MIN__, __DBL_EPSILON__, __DBL_MIN__, __DBL_MAX__,
+      __DBL_DENORM_MIN__)
+#if __LDBL_MANT_DIG__ != 106
+TEST (test3, nextafterl, long double, L, L, l, l, __LDBL_MIN__, __LDBL_MAX__,
+      __LDBL_DENORM_MIN__, __LDBL_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
+      __LDBL_DENORM_MIN__)
+TEST (test4, nexttowardf, float, F, L, f, l, __FLT_MIN__, __FLT_MAX__,
+      __FLT_DENORM_MIN__, __FLT_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
+      __LDBL_DENORM_MIN__)
+TEST (test5, nexttoward, double, , L, , l, __DBL_MIN__, __DBL_MAX__,
+      __DBL_DENORM_MIN__, __DBL_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
+      __LDBL_DENORM_MIN__)
+TEST (test6, nexttowardl, long double, L, L, l, l, __LDBL_MIN__, __LDBL_MAX__,
+      __LDBL_DENORM_MIN__, __LDBL_EPSILON__, __LDBL_MIN__, __LDBL_MAX__,
+      __LDBL_DENORM_MIN__)
+#endif
+
+int
+main ()
+{
+  test1 ();
+  test2 ();
+#if __LDBL_MANT_DIG__ != 106
+  test3 ();
+  test4 ();
+  test5 ();
+  test6 ();
+#endif
+  return 0;
+}
--- gcc/testsuite/gcc.dg/nextafter-2.c.jj	2018-04-20 20:53:24.662141363 +0200
+++ gcc/testsuite/gcc.dg/nextafter-2.c	2018-04-20 20:54:01.031174134 +0200
@@ -0,0 +1,6 @@ 
+/* PR libstdc++/85466 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fno-builtin" } */
+/* { dg-add-options ieee } */
+
+#include "nextafter-1.c"
--- gcc/testsuite/gcc.dg/nextafter-3.c.jj	2018-04-20 20:54:09.867182101 +0200
+++ gcc/testsuite/gcc.dg/nextafter-3.c	2018-04-20 20:59:48.280486574 +0200
@@ -0,0 +1,9 @@ 
+/* PR libstdc++/85466 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fmath-errno -fno-trapping-math -fdump-tree-optimized" } */
+/* { dg-add-options ieee } */
+/* { dg-final { scan-tree-dump-not "nextafter" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "nexttoward" "optimized" } } */
+
+#define NEED_ERRNO 1
+#include "nextafter-1.c"
--- gcc/testsuite/gcc.dg/nextafter-4.c.jj	2018-04-20 20:55:11.880237977 +0200
+++ gcc/testsuite/gcc.dg/nextafter-4.c	2018-04-20 20:59:54.927492541 +0200
@@ -0,0 +1,10 @@ 
+/* PR libstdc++/85466 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fmath-errno -ftrapping-math -fdump-tree-optimized" } */
+/* { dg-add-options ieee } */
+/* { dg-final { scan-tree-dump-not "nextafter" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "nexttoward" "optimized" } } */
+
+#define NEED_ERRNO 1
+#define NEED_EXC 1
+#include "nextafter-1.c"