Message ID | 20180421173625.GN8577@tucnak |
---|---|
State | New |
Headers | show |
Series | Add constant folding support for next{after,toward}{,f,l} (PR libstdc++/85466) | expand |
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 > >
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
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
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.
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
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"
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
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
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; }
--- 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"