public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] builtin fadd variants implementation
@ 2019-08-08 14:17 Tejas Joshi
  2019-08-21 17:24 ` Joseph Myers
  0 siblings, 1 reply; 8+ messages in thread
From: Tejas Joshi @ 2019-08-08 14:17 UTC (permalink / raw)
  To: gcc-patches; +Cc: Martin Jambor, hubicka, joseph

[-- Attachment #1: Type: text/plain, Size: 848 bytes --]

Hi.
This patch includes fadd variants implementation for folding. The
patch bootstraps and survives regression tests on x86_64-linux-gnu.

Thanks,
Tejas

gcc/ChangeLog:

2019-08-08  Tejas Joshi  <tejasjoshi9673@gmail.com>

    * builtin-types.def: New function type variable arguments.
    * builtins.def: New function definitions for fadd variants.
    * fold-const-call.c (fold_const_fadd): New function for folding fadd
    variants.
    (fold_const_call): Add new cases for fadd variants.
    * real.c (real_fadd): New function. Performs addition of two numbers
    and returns result in narrowed down type.
    * real.h: New function declaration for real_fadd.

gcc/testsuite/ChangeLog:

2019-08-08  Tejas Joshi  <tejasjoshi9673@gmail.com>

    * testsuite/gcc.dg/builtin-fadd-1.c: New test.
    * testsuite/gcc.dg/builtin-fadd-2.c: New test.

[-- Attachment #2: fadd-2.diff --]
[-- Type: text/x-patch, Size: 7602 bytes --]

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e063c48..6bc552fa71a 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -387,8 +387,14 @@ DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT_PTR,
 		     BT_VOID, BT_UINT, BT_PTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT_FLOAT_FLOAT,
 		     BT_FLOAT, BT_FLOAT, BT_FLOAT)
+DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT_DOUBLE_DOUBLE,
+		     BT_FLOAT, BT_DOUBLE, BT_DOUBLE)
+DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT_LONGDOUBLE_LONGDOUBLE,
+		     BT_FLOAT, BT_LONGDOUBLE, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_2 (BT_FN_DOUBLE_DOUBLE_DOUBLE,
 		     BT_DOUBLE, BT_DOUBLE, BT_DOUBLE)
+DEF_FUNCTION_TYPE_2 (BT_FN_DOUBLE_LONGDOUBLE_LONGDOUBLE,
+		     BT_DOUBLE, BT_LONGDOUBLE, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_2 (BT_FN_LONGDOUBLE_LONGDOUBLE_LONGDOUBLE,
 		     BT_LONGDOUBLE, BT_LONGDOUBLE, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT16_FLOAT16_FLOAT16,
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 8bb7027aac7..2df616c477e 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -355,6 +355,9 @@ DEF_EXT_LIB_FLOATN_NX_BUILTINS (BUILT_IN_FABS, "fabs", FABS_TYPE, ATTR_CONST_NOT
 DEF_GCC_BUILTIN        (BUILT_IN_FABSD32, "fabsd32", BT_FN_DFLOAT32_DFLOAT32, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_FABSD64, "fabsd64", BT_FN_DFLOAT64_DFLOAT64, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_FABSD128, "fabsd128", BT_FN_DFLOAT128_DFLOAT128, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FADD, "fadd", BT_FN_FLOAT_DOUBLE_DOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FADDL, "faddl", BT_FN_FLOAT_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_DADDL, "daddl", BT_FN_DOUBLE_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN_FDIM, "fdim", BT_FN_DOUBLE_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
 DEF_C99_BUILTIN        (BUILT_IN_FDIMF, "fdimf", BT_FN_FLOAT_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO)
 DEF_C99_BUILTIN        (BUILT_IN_FDIML, "fdiml", BT_FN_LONGDOUBLE_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index d9b546e6803..f895c89de96 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -570,6 +570,32 @@ fold_const_nextafter (real_value *result, const real_value *arg0,
   return true;
 }
 
+/* Try to evaluate:
+
+      *RESULT = fadd (*ARG0, *ARG1)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_fadd (real_value *result, const real_value *arg0,
+		 const real_value *arg1, const real_format *format)
+{
+  if (REAL_VALUE_ISSIGNALING_NAN (*arg0)
+      || REAL_VALUE_ISSIGNALING_NAN (*arg1))
+    return false;
+
+  if (real_fadd (result, format, arg0, arg1)
+      && !flag_errno_math)
+    return false;
+
+  if (!exact_real_truncate (format, result)
+      && !(flag_rounding_math && flag_trapping_math))
+    return false;
+
+  real_convert (result, format, result);
+  return true;
+}
+
 /* Try to evaluate:
 
       *RESULT = ldexp (*ARG0, ARG1)
@@ -1644,6 +1670,25 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
     case CFN_FOLD_LEFT_PLUS:
       return fold_const_fold_left (type, arg0, arg1, PLUS_EXPR);
 
+    case CFN_BUILT_IN_FADD:
+    case CFN_BUILT_IN_FADDL:
+    case CFN_BUILT_IN_DADDL:
+      {
+	if (real_cst_p (arg0)
+	    && real_cst_p (arg1))
+	{
+	  machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
+	  gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
+	  REAL_VALUE_TYPE result;
+	  machine_mode mode = TYPE_MODE (type);
+	  if (fold_const_fadd (&result, TREE_REAL_CST_PTR (arg0),
+			       TREE_REAL_CST_PTR (arg1),
+			       REAL_MODE_FORMAT (mode)))
+	    return build_real (type, result);
+	}
+      }
+      return NULL_TREE;
+
     default:
       return fold_const_call_1 (fn, type, arg0, arg1);
     }
diff --git a/gcc/real.c b/gcc/real.c
index ab71430709f..280e434e897 100644
--- a/gcc/real.c
+++ b/gcc/real.c
@@ -5093,6 +5093,30 @@ real_roundeven (REAL_VALUE_TYPE *r, format_helper fmt,
     real_round (r, fmt, x);
 }
 
+/* Perform addition of X and Y and place the result in R to narrowed
+   down type. Return TRUE if result involves overflow/underflow.  */
+
+bool
+real_fadd (REAL_VALUE_TYPE *r, format_helper fmt,
+	   const REAL_VALUE_TYPE *x, const REAL_VALUE_TYPE *y)
+{
+  do_add (r, x, y, 0);
+
+  /* Overflow/underflow check.  */
+  if (REAL_EXP (r) > fmt->emax)
+  {
+    get_inf (r, r->sign);
+    return true;
+  }
+  if (REAL_EXP (r) < fmt->emin)
+  {
+    get_zero (r, r->sign);
+    return true;
+  }
+
+  return false;
+}
+
 /* Set the sign of R to the sign of X.  */
 
 void
diff --git a/gcc/real.h b/gcc/real.h
index 76889bff0ea..f9d388f491b 100644
--- a/gcc/real.h
+++ b/gcc/real.h
@@ -510,6 +510,10 @@ extern void real_round (REAL_VALUE_TYPE *, format_helper,
 extern void real_roundeven (REAL_VALUE_TYPE *, format_helper,
       const REAL_VALUE_TYPE *);
 
+/* Narrowing type standard math operations functions.  */
+extern bool real_fadd (REAL_VALUE_TYPE *, format_helper,
+	const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);
+
 /* Set the sign of R to the sign of X.  */
 extern void real_copysign (REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);
 
diff --git a/gcc/testsuite/gcc.dg/builtin-fadd-1.c b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
new file mode 100644
index 00000000000..958d5a019d7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define TEST(FN, VAL1, VAL2, RESULT) \
+  if (__builtin_##FN (VAL1, VAL2) != RESULT) __builtin_abort ();
+
+int
+main (void)
+{
+  TEST(fadd, 0, 0, 0.0F);
+  TEST(fadd, 1, -1, 0.0F);
+  TEST(fadd, -1, -1.5, -2.5F);
+  TEST(fadd, 1.4, 1.6, 3.0F);
+  TEST(fadd, 2.5, 1.5, 4.0F);
+  TEST(fadd, __FLT_MAX__, __FLT_MAX__/2, 3*__FLT_MAX__/2);
+  
+  TEST(faddl, 0.0L, 0.0L, 0.0F);
+  TEST(faddl, 1.0L, -1.0L, 0.0F);
+  TEST(faddl, -1.0L, -1.5L, -2.5F);
+  TEST(faddl, 1.4L, 1.6L, 3.0F);
+  TEST(faddl, 2.5L, 1.5L, 4.0F);
+  TEST(faddl, __FLT_MAX__, __FLT_MAX__/2, 3*__FLT_MAX__/2);
+
+  TEST(daddl, 0L, 0L, 0.0);
+  TEST(daddl, 1L, -1L, 0.0);
+  TEST(daddl, -1L, -1.5L, -2.5);
+  TEST(daddl, 1.4L, 1.6L, 3.0);
+  TEST(daddl, 2.5L, 1.5L, 4.0);
+  TEST(daddl, __DBL_MAX__, __DBL_MAX__/2, 3*__DBL_MAX__/2);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-fadd-2.c b/gcc/testsuite/gcc.dg/builtin-fadd-2.c
new file mode 100644
index 00000000000..76c81ae0470
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-fadd-2.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -frounding-math" } */
+
+#define TEST(FN, VAL1, VAL2, RESULT) \
+  if (__builtin_##FN (VAL1, VAL2) != RESULT) __builtin_abort ();
+
+int
+main (void)
+{
+  TEST(fadd, 1, 1.1, 2.1F);
+  TEST(fadd, 3, 4.2, 7.2F);
+  TEST(fadd, 2, 4.3, 6.3F);
+  TEST(fadd, -2, -2.4, -4.4F);
+  TEST(fadd, -3, -3.6, -6.6F); 
+  TEST(fadd, -5.3, -5.4, -10.7F);
+  TEST(fadd, 8, 0.8, 8.8F);
+  TEST(fadd, 0.5, 0.4, 0.9F);
+
+  TEST(faddl, 1.0L, 1.1L, 2.1F);
+  TEST(faddl, 3.0L, 4.2L, 7.2F);
+  TEST(faddl, 2.0L, 4.3L, 6.3F);
+  TEST(faddl, -2.0L, -2.4L, -4.4F);
+  TEST(faddl, -3.0L, -3.6L, -6.6F); 
+  TEST(faddl, -5.3L, -5.4L, -10.7F);
+  TEST(faddl, 8.0L, 0.8L, 8.8F);
+  TEST(faddl, 0.5L, 0.4L, 0.9F);
+
+  TEST(daddl, 1.0L, 1.1L, 2.1);
+  TEST(daddl, 3.0L, 4.2L, 7.2);
+  TEST(daddl, 2.0L, 4.3L, 6.3);
+  TEST(daddl, -2.0L, -2.4L, -4.4);
+  TEST(daddl, -3.0L, -3.6L, -6.6); 
+  TEST(daddl, -5.3L, -5.4L, -10.7);
+  TEST(daddl, 8.0L, 0.8L, 8.8);
+  TEST(daddl, 0.5L, 0.4L, 0.9);
+
+  return 0;
+} 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-08-08 14:17 [PATCH] builtin fadd variants implementation Tejas Joshi
@ 2019-08-21 17:24 ` Joseph Myers
  2019-08-26  5:19   ` Tejas Joshi
  0 siblings, 1 reply; 8+ messages in thread
From: Joseph Myers @ 2019-08-21 17:24 UTC (permalink / raw)
  To: Tejas Joshi; +Cc: gcc-patches, Martin Jambor, hubicka

On Thu, 8 Aug 2019, Tejas Joshi wrote:

> +/* Try to evaluate:
> +
> +      *RESULT = fadd (*ARG0, *ARG1)
> +
> +   in format FORMAT.  Return true on success.  */
> +
> +static bool
> +fold_const_fadd (real_value *result, const real_value *arg0,
> +		 const real_value *arg1, const real_format *format)
> +{
> +  if (REAL_VALUE_ISSIGNALING_NAN (*arg0)
> +      || REAL_VALUE_ISSIGNALING_NAN (*arg1))
> +    return false;
> +
> +  if (real_fadd (result, format, arg0, arg1)
> +      && !flag_errno_math)
> +    return false;

I think that rather than having a real_fadd function, you should just call 
real_arithmetic with a PLUS_EXPR argument and then move the overflow etc. 
checks out to here.

That way, rather than having a fold_const_fadd function and then similar 
functions for fsub / fmul / fdiv, you can have one function that covers 
all four binary narrowing operations, which all have very similar logic, 
with an argument that is PLUS_EXPR etc. passed to the 
fold_const_narrow_binary (or whatever you call it) function.

> +  if (!exact_real_truncate (format, result)
> +      && !(flag_rounding_math && flag_trapping_math))
> +    return false;

I don't think the logic is right here.  What you want is: don't fold for 
inexact results if flag_rounding_math || flag_trapping_math.  I.e.:

  if (!exact_real_truncate (format, result)
      && (flag_rounding_math || flag_trapping_math))

> +/* Perform addition of X and Y and place the result in R to narrowed
> +   down type. Return TRUE if result involves overflow/underflow.  */
> +
> +bool
> +real_fadd (REAL_VALUE_TYPE *r, format_helper fmt,
> +	   const REAL_VALUE_TYPE *x, const REAL_VALUE_TYPE *y)
> +{
> +  do_add (r, x, y, 0);
> +
> +  /* Overflow/underflow check.  */
> +  if (REAL_EXP (r) > fmt->emax)
> +  {
> +    get_inf (r, r->sign);
> +    return true;
> +  }

This isn't the right way to check for overflow.  You can have a result 
that's within the exponent range of the narrower format before rounding, 
but overflows after the rounding is done.  The right way to check for 
overflow (given that the result is inexact, and !flag_rounding_math && 
!flag_trapping_math && flag_errno_math so that inexact results are OK but 
not if they overflow) would be to check after rounding whether the result 
is Inf when the arguments were finite.  (When you get only fdiv, that also 
becomes relevant for exact cases; fdiv (1.0, 0.0) produces an exact 
infinity but can't be folded if flag_errno_math || flag_trapping_math.)

As well as overflow you also need to consider the case of fadd (Inf, -Inf) 
which would set errno to EDOM and raise "invalid" so, although exact, is 
also not allowed to be folded if flag_errno_math || flag_trapping_math.  
The generic logic there is: if the arguments are not NaN but the result is 
NaN, you can't fold if flag_errno_math || flag_trapping_math (but 
flag_rounding_math is irrelevant in that case).

> diff --git a/gcc/testsuite/gcc.dg/builtin-fadd-1.c b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
> new file mode 100644
> index 00000000000..958d5a019d7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
> @@ -0,0 +1,31 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +#define TEST(FN, VAL1, VAL2, RESULT) \
> +  if (__builtin_##FN (VAL1, VAL2) != RESULT) __builtin_abort ();

The tests ought to be tests of what gets optimized away (so causing link 
failures, as in the roundeven tests, if the calls don't get optimized or 
get optimized incorrectly).

The tests should include cases that demonstrate there is only a single 
rounding involved: that there is no intermediate rounding to the wider 
type.  (E.g. fadd (0x1.000001p0, FLT_MIN), as an example from the glibc 
tests: cases where an intermediate rounding produces a result half way 
between two values of the narrower type, but the exact value is such that 
the result of fadd should end up with last bit odd whereas double rounding 
would result in last bit even in such half-way cases.)

> +  TEST(fadd, __FLT_MAX__, __FLT_MAX__/2, 3*__FLT_MAX__/2);

It would seem better to write the expected result here as __builtin_inff 
().

Then you should have some tests of what does *not* get optimized with 
given compiler options if possible.  (Such a test might e.g. define a 
static fadd function locally that checks it gets called as expected, or 
else check the exceptions / errno if you rely on a suitable libm being 
available.)

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-08-21 17:24 ` Joseph Myers
@ 2019-08-26  5:19   ` Tejas Joshi
  2019-08-27 23:47     ` Joseph Myers
  0 siblings, 1 reply; 8+ messages in thread
From: Tejas Joshi @ 2019-08-26  5:19 UTC (permalink / raw)
  To: gcc-patches; +Cc: Martin Jambor, hubicka, joseph

[-- Attachment #1: Type: text/plain, Size: 5925 bytes --]

Hello.
I have made changes in the patch according to the above corrections.
However, I didn't understand how these following testcases are
supposed to handle. Will you please elaborate some more?

> (E.g. fadd (0x1.000001p0, FLT_MIN), as an example from the glibc
> tests: cases where an intermediate rounding produces a result half way
> between two values of the narrower type, but the exact value is such that
> the result of fadd should end up with last bit odd whereas double rounding
> would result in last bit even in such half-way cases.)

> Then you should have some tests of what does *not* get optimized with
> given compiler options if possible.  (Such a test might e.g. define a
> static fadd function locally that checks it gets called as expected, or
> else check the exceptions / errno if you rely on a suitable libm being
> available.)

Given that the changes yet to be done on testcases and some more
changes if any on this patch reviewed, it won't take much time to get
it done with sub/mul/div functions. When FADD folding patches along
with expansion on powerpc (including QP float, etc) are finalized, I
will complete the others as soon as possible.

Thanks,
Tejas


On Wed, 21 Aug 2019 at 22:25, Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Thu, 8 Aug 2019, Tejas Joshi wrote:
>
> > +/* Try to evaluate:
> > +
> > +      *RESULT = fadd (*ARG0, *ARG1)
> > +
> > +   in format FORMAT.  Return true on success.  */
> > +
> > +static bool
> > +fold_const_fadd (real_value *result, const real_value *arg0,
> > +              const real_value *arg1, const real_format *format)
> > +{
> > +  if (REAL_VALUE_ISSIGNALING_NAN (*arg0)
> > +      || REAL_VALUE_ISSIGNALING_NAN (*arg1))
> > +    return false;
> > +
> > +  if (real_fadd (result, format, arg0, arg1)
> > +      && !flag_errno_math)
> > +    return false;
>
> I think that rather than having a real_fadd function, you should just call
> real_arithmetic with a PLUS_EXPR argument and then move the overflow etc.
> checks out to here.
>
> That way, rather than having a fold_const_fadd function and then similar
> functions for fsub / fmul / fdiv, you can have one function that covers
> all four binary narrowing operations, which all have very similar logic,
> with an argument that is PLUS_EXPR etc. passed to the
> fold_const_narrow_binary (or whatever you call it) function.
>
> > +  if (!exact_real_truncate (format, result)
> > +      && !(flag_rounding_math && flag_trapping_math))
> > +    return false;
>
> I don't think the logic is right here.  What you want is: don't fold for
> inexact results if flag_rounding_math || flag_trapping_math.  I.e.:
>
>   if (!exact_real_truncate (format, result)
>       && (flag_rounding_math || flag_trapping_math))
>
> > +/* Perform addition of X and Y and place the result in R to narrowed
> > +   down type. Return TRUE if result involves overflow/underflow.  */
> > +
> > +bool
> > +real_fadd (REAL_VALUE_TYPE *r, format_helper fmt,
> > +        const REAL_VALUE_TYPE *x, const REAL_VALUE_TYPE *y)
> > +{
> > +  do_add (r, x, y, 0);
> > +
> > +  /* Overflow/underflow check.  */
> > +  if (REAL_EXP (r) > fmt->emax)
> > +  {
> > +    get_inf (r, r->sign);
> > +    return true;
> > +  }
>
> This isn't the right way to check for overflow.  You can have a result
> that's within the exponent range of the narrower format before rounding,
> but overflows after the rounding is done.  The right way to check for
> overflow (given that the result is inexact, and !flag_rounding_math &&
> !flag_trapping_math && flag_errno_math so that inexact results are OK but
> not if they overflow) would be to check after rounding whether the result
> is Inf when the arguments were finite.  (When you get only fdiv, that also
> becomes relevant for exact cases; fdiv (1.0, 0.0) produces an exact
> infinity but can't be folded if flag_errno_math || flag_trapping_math.)
>
> As well as overflow you also need to consider the case of fadd (Inf, -Inf)
> which would set errno to EDOM and raise "invalid" so, although exact, is
> also not allowed to be folded if flag_errno_math || flag_trapping_math.
> The generic logic there is: if the arguments are not NaN but the result is
> NaN, you can't fold if flag_errno_math || flag_trapping_math (but
> flag_rounding_math is irrelevant in that case).
>
> > diff --git a/gcc/testsuite/gcc.dg/builtin-fadd-1.c b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
> > new file mode 100644
> > index 00000000000..958d5a019d7
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
> > @@ -0,0 +1,31 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O2" } */
> > +
> > +#define TEST(FN, VAL1, VAL2, RESULT) \
> > +  if (__builtin_##FN (VAL1, VAL2) != RESULT) __builtin_abort ();
>
> The tests ought to be tests of what gets optimized away (so causing link
> failures, as in the roundeven tests, if the calls don't get optimized or
> get optimized incorrectly).
>
> The tests should include cases that demonstrate there is only a single
> rounding involved: that there is no intermediate rounding to the wider
> type.  (E.g. fadd (0x1.000001p0, FLT_MIN), as an example from the glibc
> tests: cases where an intermediate rounding produces a result half way
> between two values of the narrower type, but the exact value is such that
> the result of fadd should end up with last bit odd whereas double rounding
> would result in last bit even in such half-way cases.)
>
> > +  TEST(fadd, __FLT_MAX__, __FLT_MAX__/2, 3*__FLT_MAX__/2);
>
> It would seem better to write the expected result here as __builtin_inff
> ().
>
> Then you should have some tests of what does *not* get optimized with
> given compiler options if possible.  (Such a test might e.g. define a
> static fadd function locally that checks it gets called as expected, or
> else check the exceptions / errno if you rely on a suitable libm being
> available.)
>
> --
> Joseph S. Myers
> joseph@codesourcery.com

[-- Attachment #2: fadd-2.diff --]
[-- Type: text/x-patch, Size: 6520 bytes --]

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e063c48..6bc552fa71a 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -387,8 +387,14 @@ DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT_PTR,
 		     BT_VOID, BT_UINT, BT_PTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT_FLOAT_FLOAT,
 		     BT_FLOAT, BT_FLOAT, BT_FLOAT)
+DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT_DOUBLE_DOUBLE,
+		     BT_FLOAT, BT_DOUBLE, BT_DOUBLE)
+DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT_LONGDOUBLE_LONGDOUBLE,
+		     BT_FLOAT, BT_LONGDOUBLE, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_2 (BT_FN_DOUBLE_DOUBLE_DOUBLE,
 		     BT_DOUBLE, BT_DOUBLE, BT_DOUBLE)
+DEF_FUNCTION_TYPE_2 (BT_FN_DOUBLE_LONGDOUBLE_LONGDOUBLE,
+		     BT_DOUBLE, BT_LONGDOUBLE, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_2 (BT_FN_LONGDOUBLE_LONGDOUBLE_LONGDOUBLE,
 		     BT_LONGDOUBLE, BT_LONGDOUBLE, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_2 (BT_FN_FLOAT16_FLOAT16_FLOAT16,
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 8bb7027aac7..2df616c477e 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -355,6 +355,9 @@ DEF_EXT_LIB_FLOATN_NX_BUILTINS (BUILT_IN_FABS, "fabs", FABS_TYPE, ATTR_CONST_NOT
 DEF_GCC_BUILTIN        (BUILT_IN_FABSD32, "fabsd32", BT_FN_DFLOAT32_DFLOAT32, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_FABSD64, "fabsd64", BT_FN_DFLOAT64_DFLOAT64, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_FABSD128, "fabsd128", BT_FN_DFLOAT128_DFLOAT128, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FADD, "fadd", BT_FN_FLOAT_DOUBLE_DOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FADDL, "faddl", BT_FN_FLOAT_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_DADDL, "daddl", BT_FN_DOUBLE_LONGDOUBLE_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN_FDIM, "fdim", BT_FN_DOUBLE_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
 DEF_C99_BUILTIN        (BUILT_IN_FDIMF, "fdimf", BT_FN_FLOAT_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO)
 DEF_C99_BUILTIN        (BUILT_IN_FDIML, "fdiml", BT_FN_LONGDOUBLE_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 3a14d2a41c1..abf8cfc9131 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -570,6 +570,37 @@ fold_const_nextafter (real_value *result, const real_value *arg0,
   return true;
 }
 
+/* Try to evaluate:
+
+      *RESULT = add/sub/mul/div (*ARG0, *ARG1)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_narrow_binary (real_value *result, const real_value *arg0,
+			  int icode, const real_value *arg1,
+			  const real_format *format)
+{
+  if (REAL_VALUE_ISSIGNALING_NAN (*arg0)
+      || REAL_VALUE_ISSIGNALING_NAN (*arg1))
+    return false;
+
+  if (real_arithmetic (result, icode, arg0, arg1)
+      && (flag_rounding_math || flag_trapping_math))
+    return false;
+
+  real_convert (result, format, result);
+  /* Overflow/underflow condition.  */
+  if (!real_isfinite (result) && !flag_errno_math)
+    return false;
+
+  if (REAL_VALUE_ISNAN (*result)
+      && (flag_errno_math || flag_trapping_math))
+    return false;
+
+  return true;
+}
+
 /* Try to evaluate:
 
       *RESULT = ldexp (*ARG0, ARG1)
@@ -1674,6 +1705,25 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
     case CFN_FOLD_LEFT_PLUS:
       return fold_const_fold_left (type, arg0, arg1, PLUS_EXPR);
 
+    case CFN_BUILT_IN_FADD:
+    case CFN_BUILT_IN_FADDL:
+    case CFN_BUILT_IN_DADDL:
+      {
+	if (real_cst_p (arg0)
+	    && real_cst_p (arg1))
+	  {
+	    machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
+	    gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
+	    REAL_VALUE_TYPE result;
+	    machine_mode mode = TYPE_MODE (type);
+	    if (fold_const_narrow_binary (&result, TREE_REAL_CST_PTR (arg0),
+					  PLUS_EXPR, TREE_REAL_CST_PTR (arg1),
+					  REAL_MODE_FORMAT (mode)))
+	      return build_real (type, result);
+	  }
+      }
+      return NULL_TREE;
+
     default:
       return fold_const_call_1 (fn, type, arg0, arg1);
     }
diff --git a/gcc/testsuite/gcc.dg/builtin-fadd-1.c b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
new file mode 100644
index 00000000000..4e652d0ee59
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-fadd-1.c
@@ -0,0 +1,33 @@
+/* { dg-do link } */
+/* { dg-require-effective-target inf } */
+
+extern int link_error (int);
+
+#define TEST(FN, VAL1, VAL2, RESULT) \
+  if (__builtin_##FN (VAL1, VAL2) != RESULT) link_error (__LINE__);
+
+int
+main (void)
+{
+  TEST(fadd, 0, 0, 0.0F);
+  TEST(fadd, 1, -1, 0.0F);
+  TEST(fadd, -1, -1.5, -2.5F);
+  TEST(fadd, 1.4, 1.6, 3.0F);
+  TEST(fadd, 2.5, 1.5, 4.0F);
+  TEST(fadd, __FLT_MAX__, __FLT_MAX__/2, __builtin_inff ());
+  
+  TEST(faddl, 0.0L, 0.0L, 0.0F);
+  TEST(faddl, 1.0L, -1.0L, 0.0F);
+  TEST(faddl, -1.0L, -1.5L, -2.5F);
+  TEST(faddl, 1.4L, 1.6L, 3.0F);
+  TEST(faddl, 2.5L, 1.5L, 4.0F);
+  TEST(faddl, __FLT_MAX__, __FLT_MAX__/2, __builtin_inff ());
+
+  TEST(daddl, 0L, 0L, 0.0);
+  TEST(daddl, 1L, -1L, 0.0);
+  TEST(daddl, -1L, -1.5L, -2.5);
+  TEST(daddl, 1.4L, 1.6L, 3.0);
+  TEST(daddl, 2.5L, 1.5L, 4.0);
+  TEST(daddl, __DBL_MAX__, __DBL_MAX__/2, __builtin_inf ());
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-fadd-2.c b/gcc/testsuite/gcc.dg/builtin-fadd-2.c
new file mode 100644
index 00000000000..ffec19570ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-fadd-2.c
@@ -0,0 +1,40 @@
+/* { dg-do link } */
+/* { dg-options "-O2 -frounding-math" } */
+
+extern int link_error (int);
+
+#define TEST(FN, VAL1, VAL2, RESULT) \
+  if (__builtin_##FN (VAL1, VAL2) != RESULT) link_error (__LINE__);
+
+int
+main (void)
+{
+  TEST(fadd, 1, 1.1, 2.1F);
+  TEST(fadd, 3, 4.2, 7.2F);
+  TEST(fadd, 2, 4.3, 6.3F);
+  TEST(fadd, -2, -2.4, -4.4F);
+  TEST(fadd, -3, -3.6, -6.6F); 
+  TEST(fadd, -5.3, -5.4, -10.7F);
+  TEST(fadd, 8, 0.8, 8.8F);
+  TEST(fadd, 0.5, 0.4, 0.9F);
+
+  TEST(faddl, 1.0L, 1.1L, 2.1F);
+  TEST(faddl, 3.0L, 4.2L, 7.2F);
+  TEST(faddl, 2.0L, 4.3L, 6.3F);
+  TEST(faddl, -2.0L, -2.4L, -4.4F);
+  TEST(faddl, -3.0L, -3.6L, -6.6F); 
+  TEST(faddl, -5.3L, -5.4L, -10.7F);
+  TEST(faddl, 8.0L, 0.8L, 8.8F);
+  TEST(faddl, 0.5L, 0.4L, 0.9F);
+
+  TEST(daddl, 1.0L, 1.1L, 2.1);
+  TEST(daddl, 3.0L, 4.2L, 7.2);
+  TEST(daddl, 2.0L, 4.3L, 6.3);
+  TEST(daddl, -2.0L, -2.4L, -4.4);
+  TEST(daddl, -3.0L, -3.6L, -6.6); 
+  TEST(daddl, -5.3L, -5.4L, -10.7);
+  TEST(daddl, 8.0L, 0.8L, 8.8);
+  TEST(daddl, 0.5L, 0.4L, 0.9);
+
+  return 0;
+} 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-08-26  5:19   ` Tejas Joshi
@ 2019-08-27 23:47     ` Joseph Myers
  2019-09-02 12:02       ` Tejas Joshi
  2019-09-09 16:06       ` Tejas Joshi
  0 siblings, 2 replies; 8+ messages in thread
From: Joseph Myers @ 2019-08-27 23:47 UTC (permalink / raw)
  To: Tejas Joshi; +Cc: gcc-patches, Martin Jambor, hubicka

On Mon, 26 Aug 2019, Tejas Joshi wrote:

> Hello.
> I have made changes in the patch according to the above corrections.
> However, I didn't understand how these following testcases are
> supposed to handle. Will you please elaborate some more?
> 
> > (E.g. fadd (0x1.000001p0, FLT_MIN), as an example from the glibc
> > tests: cases where an intermediate rounding produces a result half way
> > between two values of the narrower type, but the exact value is such that
> > the result of fadd should end up with last bit odd whereas double rounding
> > would result in last bit even in such half-way cases.)

The point of this is to demonstrate that fadd (x, y) is different from 
(float) (x + y), by testing with inputs for which the two evaluate to 
different values.

> > Then you should have some tests of what does *not* get optimized with
> > given compiler options if possible.  (Such a test might e.g. define a
> > static fadd function locally that checks it gets called as expected, or
> > else check the exceptions / errno if you rely on a suitable libm being
> > available.)

There would include:

* A test where the result is within range but inexact; say fadd (1, 
DBL_MIN).  With -ftrapping-math -fno-rounding-math, or -frounding-math 
-fno-trapping-math, or -frounding-math -ftrapping-math, this should not be 
folded; that is, it should be compiled to call a fadd function (which you 
might define in the test as a staic function that sets a variable to 
indicate that it was called, so the test can verify at runtime that the 
call did not get folded).

* But the same inputs, with -fno-trapping-math -fno-rounding-math 
-fmath-errno, *should* get folded (so test the same inputs with those 
options with a link_error test like those for roundeven).

* Then similarly test overflow / underflow cases (e.g. fadd (DBL_MAX, 
DBL_MAX) or fadd (DBL_MIN, DBL_MIN)) with -fno-trapping-math 
-fno-rounding-math -fmath-errno (make sure they don't get folded), and 
with -fno-trapping-math-fno-rounding-math -fno-math-errno (make sure that 
in that case they do get folded, so link_error tests).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-08-27 23:47     ` Joseph Myers
@ 2019-09-02 12:02       ` Tejas Joshi
  2019-09-02 16:38         ` Joseph Myers
  2019-09-09 16:06       ` Tejas Joshi
  1 sibling, 1 reply; 8+ messages in thread
From: Tejas Joshi @ 2019-09-02 12:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: Martin Jambor, joseph

Hello.
Should a result like 1.4 be considered as inexact if truncating
(narrowing?) from double to float? (due to loss of trailing bits)
Comments of real_arithmetic says that it returns TRUE if the result is
inexact. There's another function, exact_real_truncate which returns
TRUE if truncation is exact. Why are these functions returning
different results for same input like, 1.4 + 0.0? (real_arithmetic
returns false (exact) as well as exact_real_truncate, but here,
inexact ) Does real_arithmetic considers only the exactness in the
same range of its arguments? To check for inexactness of narrowing,
which result should I consider?

Thanks,
Tejas

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-09-02 12:02       ` Tejas Joshi
@ 2019-09-02 16:38         ` Joseph Myers
  0 siblings, 0 replies; 8+ messages in thread
From: Joseph Myers @ 2019-09-02 16:38 UTC (permalink / raw)
  To: Tejas Joshi; +Cc: gcc-patches, Martin Jambor

On Mon, 2 Sep 2019, Tejas Joshi wrote:

> Hello.
> Should a result like 1.4 be considered as inexact if truncating
> (narrowing?) from double to float? (due to loss of trailing bits)

If the mathematical result of the arithmetic operation is literally the 
decimal number 1.4, as opposed to the double value represented by the C 
constant 1.4 which is actually 0x1.6666666666666p+0, then it is inexact 
regardless of the (non-decimal) types involved.  For example, fdiv (7, 5), 
ddivl (7, 5), etc. are always inexact.

If the mathematical result of the arithmetic operation is 
0x1.6666666666666p+0, the closest approximation to 1.4 in IEEE binary64, 
then it is inexact for result formats narrower than binary64 and exact for 
result formats that can represent that value.  For example, fadd (1.4, 
0.0) is inexact (the truncation to float is inexact although the addition 
is exact).  But daddl (1.4, 0.0) - note the arguments are double 
constants, not long double - is exact, because the mathematical result is 
exactly representable in double.  Whereas daddl (1.4L, 0.0L) would be 
inexact if long double is wider than double.

The question is always whether the infinite-precision mathematical result 
of the arithmetic operation - which takes values representable in its 
argument types - is exactly representable in the final result type.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-08-27 23:47     ` Joseph Myers
  2019-09-02 12:02       ` Tejas Joshi
@ 2019-09-09 16:06       ` Tejas Joshi
  2019-09-09 21:24         ` Joseph Myers
  1 sibling, 1 reply; 8+ messages in thread
From: Tejas Joshi @ 2019-09-09 16:06 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, Martin Jambor

Hello.
I am using real_isfinite to check for overflow conditions. How should
I check for underflow? I have tried different ways but they
contradicted other cases to pass.

static bool
fold_const_narrow_binary (real_value *result, const real_value *arg0,
              int icode, const real_value *arg1,
              const real_format *format)
{
  if (REAL_VALUE_ISSIGNALING_NAN (*arg0)
      || REAL_VALUE_ISSIGNALING_NAN (*arg1))
    return false;

  real_arithmetic (result, icode, arg0, arg1);
  if (!exact_real_truncate (format, result)
      && (flag_rounding_math || flag_trapping_math))
    return false;

  real_convert (result, format, result);
  /* Overflow condition.  */
  if (!real_isfinite (result) && flag_errno_math)
    return false;

  if (REAL_VALUE_ISNAN (*result)
      && (flag_errno_math || flag_trapping_math))
    return false;
  return true;
}

Thanks,
Tejas

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] builtin fadd variants implementation
  2019-09-09 16:06       ` Tejas Joshi
@ 2019-09-09 21:24         ` Joseph Myers
  0 siblings, 0 replies; 8+ messages in thread
From: Joseph Myers @ 2019-09-09 21:24 UTC (permalink / raw)
  To: Tejas Joshi; +Cc: gcc-patches, Martin Jambor

On Mon, 9 Sep 2019, Tejas Joshi wrote:

> Hello.
> I am using real_isfinite to check for overflow conditions. How should
> I check for underflow? I have tried different ways but they
> contradicted other cases to pass.

I think the right check for underflow is: *before* calling 
exact_real_truncate, if the result is rvc_normal, and the exponent is 
below the minimum normal exponent for the target format (emin in struct 
real_format), then there might be underflow - but this is only of concern 
if it is *also* the case that the result (of rounding to the target 
format) is inexact (exact underflow can be disregarded for most purposes).

(Strictly, there are two kinds of architectures in IEEE 754, "before 
rounding" and "after rounding".  The rules above are for "before rounding" 
architectures; on "after rounding" architectures, the rule for what counts 
as underflow is more complicated.  The above is safe in all cases and 
seems better than making GCC need to know exactly which architectures use 
which underflow rules.)

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-09-09 21:24 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-08 14:17 [PATCH] builtin fadd variants implementation Tejas Joshi
2019-08-21 17:24 ` Joseph Myers
2019-08-26  5:19   ` Tejas Joshi
2019-08-27 23:47     ` Joseph Myers
2019-09-02 12:02       ` Tejas Joshi
2019-09-02 16:38         ` Joseph Myers
2019-09-09 16:06       ` Tejas Joshi
2019-09-09 21:24         ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).