diff mbox series

Optimize sin(atan(x)), take 2

Message ID CAEFO=4CzZzMDfFRa7_G4LUnAD0+NRqe9KhYQ9h7GPjkk8Uqcaw@mail.gmail.com
State New
Headers show
Series Optimize sin(atan(x)), take 2 | expand

Commit Message

Giuliano Belinassi Sept. 3, 2018, 7:11 p.m. UTC
Fixed the issues pointed by the previous discussions. Closes PR86829.

Adds substitution rules for sin(atan(x)) and cos(atan(x)), being
careful with overflow issues by constructing a assumed convergence
constant (see comment in real.c).

2018-09-03  Giuliano Belinassi <giuliano.belinassi@usp.br>

    * match.pd: add simplification rules to sin(atan(x)) and cos(atan(x)).
    * real.c: add code for assumed convergence constant to sin(atan(x)).
    * real.h: allows the added code from real.c to be called externally.
    * tree.c: add code for bulding nodes with the convergence constant.
    * tree.h: allows the added code from tree.c to be called externally.
    * sinatan-1.c: tests assumed convergence constant.
    * sinatan-2.c: tests simplification rule.
    * sinatan-3.c: likewise.

There seems to be no broken tests in trunk that are related to this
modification.

Comments

Giuliano Belinassi Sept. 17, 2018, 12:46 p.m. UTC | #1
Ping.

On Mon, Sep 3, 2018 at 4:11 PM, Giuliano Augusto Faulin Belinassi
<giuliano.belinassi@usp.br> wrote:
> Fixed the issues pointed by the previous discussions. Closes PR86829.
>
> Adds substitution rules for sin(atan(x)) and cos(atan(x)), being
> careful with overflow issues by constructing a assumed convergence
> constant (see comment in real.c).
>
> 2018-09-03  Giuliano Belinassi <giuliano.belinassi@usp.br>
>
>     * match.pd: add simplification rules to sin(atan(x)) and cos(atan(x)).
>     * real.c: add code for assumed convergence constant to sin(atan(x)).
>     * real.h: allows the added code from real.c to be called externally.
>     * tree.c: add code for bulding nodes with the convergence constant.
>     * tree.h: allows the added code from tree.c to be called externally.
>     * sinatan-1.c: tests assumed convergence constant.
>     * sinatan-2.c: tests simplification rule.
>     * sinatan-3.c: likewise.
>
> There seems to be no broken tests in trunk that are related to this
> modification.
Giuliano Belinassi Sept. 20, 2018, 10:30 p.m. UTC | #2
Pinging match.pd and real.c maintainers, as suggested in IRC. Sorry if
it is inappropriate
On Mon, Sep 17, 2018 at 9:46 AM Giuliano Augusto Faulin Belinassi
<giuliano.belinassi@usp.br> wrote:
>
> Ping.
>
> On Mon, Sep 3, 2018 at 4:11 PM, Giuliano Augusto Faulin Belinassi
> <giuliano.belinassi@usp.br> wrote:
> > Fixed the issues pointed by the previous discussions. Closes PR86829.
> >
> > Adds substitution rules for sin(atan(x)) and cos(atan(x)), being
> > careful with overflow issues by constructing a assumed convergence
> > constant (see comment in real.c).
> >
> > 2018-09-03  Giuliano Belinassi <giuliano.belinassi@usp.br>
> >
> >     * match.pd: add simplification rules to sin(atan(x)) and cos(atan(x)).
> >     * real.c: add code for assumed convergence constant to sin(atan(x)).
> >     * real.h: allows the added code from real.c to be called externally.
> >     * tree.c: add code for bulding nodes with the convergence constant.
> >     * tree.h: allows the added code from tree.c to be called externally.
> >     * sinatan-1.c: tests assumed convergence constant.
> >     * sinatan-2.c: tests simplification rule.
> >     * sinatan-3.c: likewise.
> >
> > There seems to be no broken tests in trunk that are related to this
> > modification.
Jeff Law Oct. 4, 2018, 4:39 a.m. UTC | #3
On 9/3/18 1:11 PM, Giuliano Augusto Faulin Belinassi wrote:
> Fixed the issues pointed by the previous discussions. Closes PR86829.
> 
> Adds substitution rules for sin(atan(x)) and cos(atan(x)), being
> careful with overflow issues by constructing a assumed convergence
> constant (see comment in real.c).
> 
> 2018-09-03  Giuliano Belinassi <giuliano.belinassi@usp.br>
> 
>     * match.pd: add simplification rules to sin(atan(x)) and cos(atan(x)).
>     * real.c: add code for assumed convergence constant to sin(atan(x)).
>     * real.h: allows the added code from real.c to be called externally.
>     * tree.c: add code for bulding nodes with the convergence constant.
>     * tree.h: allows the added code from tree.c to be called externally.
>     * sinatan-1.c: tests assumed convergence constant.
>     * sinatan-2.c: tests simplification rule.
>     * sinatan-3.c: likewise.
> 
> There seems to be no broken tests in trunk that are related to this
> modification.
Pretty cool.


> 
> 
> sinatanv2.patch
> 
> Index: gcc/match.pd
> ===================================================================
> --- gcc/match.pd	(revisão 264058)
> +++ gcc/match.pd	(cópia de trabalho)
> @@ -4169,6 +4169,39 @@
>     (tans (atans @0))
>     @0)))
>  
> + /* Simplify sin(atan(x)) -> x / sqrt(x*x + 1). */
> + (for sins (SIN)
> +      atans (ATAN)
> +      sqrts (SQRT)
> +  (simplify
> +   (sins (atans:s @0))
> +   (if (SCALAR_FLOAT_TYPE_P (type))
> +    (switch
> +     (if (types_match (type, float_type_node))
> +      (cond (le (abs @0) { build_sinatan_cst (float_type_node); })
> +       (rdiv @0 (sqrts (plus (mult @0 @0) 
> +           {build_one_cst (float_type_node);})))
> +       (BUILT_IN_COPYSIGNF { build_one_cst (float_type_node); } @0)))
> +     (if (types_match (type, double_type_node))
> +      (cond (le (abs @0) { build_sinatan_cst (double_type_node); })
> +       (rdiv @0 (sqrts (plus (mult @0 @0) 
> +           {build_one_cst (double_type_node);})))
> +       (BUILT_IN_COPYSIGN  { build_one_cst (double_type_node); } @0)))
> +     (if (types_match (type, long_double_type_node))
> +      (cond (le (abs @0) { build_sinatan_cst (long_double_type_node); })
> +       (rdiv @0 (sqrts (plus (mult @0 @0) 
> +           {build_one_cst (long_double_type_node);})))
> +       (BUILT_IN_COPYSIGNL { build_one_cst (long_double_type_node); } @0)))))))
So you don't want to build the constants as a float, double or long
double.  Instead you want to build it as "type".  I think that should
let you simplify this a bit.    It turns into

  (if (SCALAR_FLOAT_TYPE_P (type))
   (cond (le (abs @0) {build_sinatan_cst (type); })
     (rdiv @0 (sqrts (plus (mult @0 @0)
                           {build_one_cst (type); })))
     (BUILT_IN_COPYSIGNL { build_one_cst (type); } @0))))

Or something along those lines I think.  Richi is much better at
match.pd stuff than I.






> +
> +/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
> + (for coss (COS)
> +      atans (ATAN)
> +      sqrts (SQRT)
> +  (simplify
> +   (coss (atans:s @0))
> +   (rdiv {build_one_cst (type);} 
> +       (sqrts (plus (mult @0 @0) {build_one_cst (type);})))))
Don't we have the same kinds of issues with the x*x in here?  As X gets
large it will overflow, but the result is going to be approaching zero.
 So we might be able to use a similar trick here.


> +
> +/*  Build real constant used by sin(atan(x)) optimization. 
> +    The logic here is as follows: It can be mathematically 
> +    shown that sin(atan(x)) = x / sqrt(1 + x*x), but notice 
> +    that the second formula has an x*x, which can overflow 
> +    if x is big enough. However, x / sqrt(1 + x*x) converges 
> +    to 1 for large x. What must be the value of x such that 
> +    when computing x / sqrt (1 + x*x) = 1?
> +
> +    Therefore, we must then solve x / sqrt(1 + x*x) > eps 
> +    for x, where eps is the largest number smaller than 1 
> +    representable by the target. Hence, solving for eps 
> +    yields that x > eps / sqrt(1 - eps*eps). This eps can
> +    be easily calculated by calling nextafter. Likewise for
> +    the negative x.  */
Imagine a pause here while I lookup isolation of radicals....  It's been
a long time...   OK.  Sure.  I see what you're doing here...



> +
> +void
> +build_sinatan_real (REAL_VALUE_TYPE * r, tree type)
> +{
> +  REAL_VALUE_TYPE eps;
> +  mpfr_t mpfr_eps, mpfr_const1, c, divisor;
> +  const struct real_format * fmt = NULL;
> +  
> +  fmt = type ? REAL_MODE_FORMAT (TYPE_MODE (type)) : NULL;
I believe we can and should always pass in a value TYPE, so it's never
going to be NULL.  It also seems like initializing fmt to NULL in the
previous statement doesn't make much sense.




> +  
> +  mpfr_inits (divisor, mpfr_eps, mpfr_const1, c, NULL);
> +  mpfr_from_real (mpfr_const1, &dconst1, GMP_RNDN);
> +
> +  real_nextafter (&eps, fmt, &dconst1, &dconst0);
> +  
> +  mpfr_from_real (mpfr_eps, &eps, GMP_RNDZ);
> +  mpfr_mul (divisor, mpfr_eps, mpfr_eps, GMP_RNDU);
> +  mpfr_sub (divisor, mpfr_const1, divisor, GMP_RNDZ);
> +  mpfr_sqrt (divisor, divisor, GMP_RNDZ);
> +  mpfr_div (c, mpfr_eps, divisor, GMP_RNDU);
> +
> +  real_from_mpfr (r, c, fmt, GMP_RNDU);
> +  
> +  /* For safety reasons.  */
> +  times_pten(r, 1);
Not  sure what you mean for safety reasons.  The calculations to produce
"c" then convert it into a REAL_VALUE_TYPE all make sense.  Just not
sure what this line is really meant to do.

build_sinatan_cst needs a function comment.  Something like

/* Build and return the tree constant used by the sin(atan))
   optimization.  */

Or something like that.

This feels really close to being ready.

Jeff
Richard Biener Oct. 4, 2018, 8:26 a.m. UTC | #4
On Thu, Oct 4, 2018 at 6:39 AM Jeff Law <law@redhat.com> wrote:
>
> On 9/3/18 1:11 PM, Giuliano Augusto Faulin Belinassi wrote:
> > Fixed the issues pointed by the previous discussions. Closes PR86829.
> >
> > Adds substitution rules for sin(atan(x)) and cos(atan(x)), being
> > careful with overflow issues by constructing a assumed convergence
> > constant (see comment in real.c).
> >
> > 2018-09-03  Giuliano Belinassi <giuliano.belinassi@usp.br>
> >
> >     * match.pd: add simplification rules to sin(atan(x)) and cos(atan(x)).
> >     * real.c: add code for assumed convergence constant to sin(atan(x)).
> >     * real.h: allows the added code from real.c to be called externally.
> >     * tree.c: add code for bulding nodes with the convergence constant.
> >     * tree.h: allows the added code from tree.c to be called externally.

Look at examples to fix up your ChangeLog, you need to list functions
you add/modify.  I don't think you want the tree.[ch] forwarders, instead use

 (with
   {
      REAL_VALUE_TYPE rcst;
      build_sinatan_real (&cst, type);
      tree tcst = build_real (type, cst);
   }

in the pattern then just use (cond (le (abs @0) { tcst; })

or even include the function here in full (well, maybe not, but maybe
include some of its comment to make it clear what kind of constant
we build here).

> >     * sinatan-1.c: tests assumed convergence constant.
> >     * sinatan-2.c: tests simplification rule.
> >     * sinatan-3.c: likewise.
> >
> > There seems to be no broken tests in trunk that are related to this
> > modification.
> Pretty cool.
>
>
> >
> >
> > sinatanv2.patch
> >
> > Index: gcc/match.pd
> > ===================================================================
> > --- gcc/match.pd      (revisão 264058)
> > +++ gcc/match.pd      (cópia de trabalho)
> > @@ -4169,6 +4169,39 @@
> >     (tans (atans @0))
> >     @0)))
> >
> > + /* Simplify sin(atan(x)) -> x / sqrt(x*x + 1). */
> > + (for sins (SIN)
> > +      atans (ATAN)
> > +      sqrts (SQRT)
> > +  (simplify
> > +   (sins (atans:s @0))
> > +   (if (SCALAR_FLOAT_TYPE_P (type))
> > +    (switch
> > +     (if (types_match (type, float_type_node))
> > +      (cond (le (abs @0) { build_sinatan_cst (float_type_node); })
> > +       (rdiv @0 (sqrts (plus (mult @0 @0)
> > +           {build_one_cst (float_type_node);})))
> > +       (BUILT_IN_COPYSIGNF { build_one_cst (float_type_node); } @0)))
> > +     (if (types_match (type, double_type_node))
> > +      (cond (le (abs @0) { build_sinatan_cst (double_type_node); })
> > +       (rdiv @0 (sqrts (plus (mult @0 @0)
> > +           {build_one_cst (double_type_node);})))
> > +       (BUILT_IN_COPYSIGN  { build_one_cst (double_type_node); } @0)))
> > +     (if (types_match (type, long_double_type_node))
> > +      (cond (le (abs @0) { build_sinatan_cst (long_double_type_node); })
> > +       (rdiv @0 (sqrts (plus (mult @0 @0)
> > +           {build_one_cst (long_double_type_node);})))
> > +       (BUILT_IN_COPYSIGNL { build_one_cst (long_double_type_node); } @0)))))))
> So you don't want to build the constants as a float, double or long
> double.  Instead you want to build it as "type".  I think that should
> let you simplify this a bit.    It turns into
>
>   (if (SCALAR_FLOAT_TYPE_P (type))
>    (cond (le (abs @0) {build_sinatan_cst (type); })
>      (rdiv @0 (sqrts (plus (mult @0 @0)
>                            {build_one_cst (type); })))
>      (BUILT_IN_COPYSIGNL { build_one_cst (type); } @0))))
>
> Or something along those lines I think.  Richi is much better at
> match.pd stuff than I.

Yeah, you'd need to add copysigns (COPYSIGN) to the for iterator
and use that instead of BUILT_IN_COPYSIGNL of course.

Richard.

>
>
>
>
>
>
> > +
> > +/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
> > + (for coss (COS)
> > +      atans (ATAN)
> > +      sqrts (SQRT)
> > +  (simplify
> > +   (coss (atans:s @0))
> > +   (rdiv {build_one_cst (type);}
> > +       (sqrts (plus (mult @0 @0) {build_one_cst (type);})))))
> Don't we have the same kinds of issues with the x*x in here?  As X gets
> large it will overflow, but the result is going to be approaching zero.
>  So we might be able to use a similar trick here.
>
>
> > +
> > +/*  Build real constant used by sin(atan(x)) optimization.
> > +    The logic here is as follows: It can be mathematically
> > +    shown that sin(atan(x)) = x / sqrt(1 + x*x), but notice
> > +    that the second formula has an x*x, which can overflow
> > +    if x is big enough. However, x / sqrt(1 + x*x) converges
> > +    to 1 for large x. What must be the value of x such that
> > +    when computing x / sqrt (1 + x*x) = 1?
> > +
> > +    Therefore, we must then solve x / sqrt(1 + x*x) > eps
> > +    for x, where eps is the largest number smaller than 1
> > +    representable by the target. Hence, solving for eps
> > +    yields that x > eps / sqrt(1 - eps*eps). This eps can
> > +    be easily calculated by calling nextafter. Likewise for
> > +    the negative x.  */
> Imagine a pause here while I lookup isolation of radicals....  It's been
> a long time...   OK.  Sure.  I see what you're doing here...
>
>
>
> > +
> > +void
> > +build_sinatan_real (REAL_VALUE_TYPE * r, tree type)
> > +{
> > +  REAL_VALUE_TYPE eps;
> > +  mpfr_t mpfr_eps, mpfr_const1, c, divisor;
> > +  const struct real_format * fmt = NULL;
> > +
> > +  fmt = type ? REAL_MODE_FORMAT (TYPE_MODE (type)) : NULL;
> I believe we can and should always pass in a value TYPE, so it's never
> going to be NULL.  It also seems like initializing fmt to NULL in the
> previous statement doesn't make much sense.
>
>
>
>
> > +
> > +  mpfr_inits (divisor, mpfr_eps, mpfr_const1, c, NULL);
> > +  mpfr_from_real (mpfr_const1, &dconst1, GMP_RNDN);
> > +
> > +  real_nextafter (&eps, fmt, &dconst1, &dconst0);
> > +
> > +  mpfr_from_real (mpfr_eps, &eps, GMP_RNDZ);
> > +  mpfr_mul (divisor, mpfr_eps, mpfr_eps, GMP_RNDU);
> > +  mpfr_sub (divisor, mpfr_const1, divisor, GMP_RNDZ);
> > +  mpfr_sqrt (divisor, divisor, GMP_RNDZ);
> > +  mpfr_div (c, mpfr_eps, divisor, GMP_RNDU);
> > +
> > +  real_from_mpfr (r, c, fmt, GMP_RNDU);
> > +
> > +  /* For safety reasons.  */
> > +  times_pten(r, 1);
> Not  sure what you mean for safety reasons.  The calculations to produce
> "c" then convert it into a REAL_VALUE_TYPE all make sense.  Just not
> sure what this line is really meant to do.
>
> build_sinatan_cst needs a function comment.  Something like
>
> /* Build and return the tree constant used by the sin(atan))
>    optimization.  */
>
> Or something like that.
>
> This feels really close to being ready.
>
> Jeff
Marc Glisse Oct. 4, 2018, 12:36 p.m. UTC | #5
On Wed, 3 Oct 2018, Jeff Law wrote:

>> +/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
>> + (for coss (COS)
>> +      atans (ATAN)
>> +      sqrts (SQRT)
>> +  (simplify
>> +   (coss (atans:s @0))
>> +   (rdiv {build_one_cst (type);}
>> +       (sqrts (plus (mult @0 @0) {build_one_cst (type);})))))
> Don't we have the same kinds of issues with the x*x in here?  As X gets
> large it will overflow, but the result is going to be approaching zero.
> So we might be able to use a similar trick here.

(I have not read the patch)

The similar trick would say that for X large, this is the same as 1/abs(X) 
I guess. Note that it may be simpler to generate a call to hypot (C99).

A related remark would be: with the precision of double, for x>=cst (about 
2^53), atan(x) is constant, within .5 ulp of pi/2 if the math library is 
super precise (which it probably isn't). Returning 0 for its cos (what 
happens if x*x gives +Inf) is thus completely fine unless you are using 
crlibm, but then you wouldn't use flag_unsafe_math_optimizations. The main 
issue is that if we have -ffast-math, we have -ffinite-math-only, and we 
are possibly introducing an infinity as intermediate result...
Giuliano Belinassi Oct. 5, 2018, 12:09 p.m. UTC | #6
Thank you for the review. I will address all these issues :-).

> Imagine a pause here while I lookup isolation of radicals....  It's been
> a long time...   OK.  Sure.  I see what you're doing here...

Sorry, but I really did not understand your comment. Should I write a
shorter comment for that function?

> Not  sure what you mean for safety reasons.  The calculations to produce
> "c" then convert it into a REAL_VALUE_TYPE all make sense.  Just not
> sure what this line is really meant to do.

Imagine the following case:
Let "c" be the real constant such that it is certain that for every x
> "c",  1/sqrt(x*x + 1) = 1.
Suppose now that our calculation leads us to a c' < "c" due to a minor
imprecision.
The logic here is that 10 * c' > "c" and everything will work, thus it is safer.
Note however that I cannot prove that 10 * c' > "c", but I would be
really surprised
if this does not holds.



> A related remark would be: with the precision of double, for x>=cst (about
> 2^53), atan(x) is constant, within .5 ulp of pi/2 if the math library is
> super precise (which it probably isn't). Returning 0 for its cos (what
> happens if x*x gives +Inf) is thus completely fine unless you are using
> crlibm, but then you wouldn't use flag_unsafe_math_optimizations. The main
> issue is that if we have -ffast-math, we have -ffinite-math-only, and we
> are possibly introducing an infinity as intermediate result...

Thank you. This clarifies the need for a similar constant for the cos(atan(x)).
On Thu, Oct 4, 2018 at 9:36 AM Marc Glisse <marc.glisse@inria.fr> wrote:
>
> On Wed, 3 Oct 2018, Jeff Law wrote:
>
> >> +/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
> >> + (for coss (COS)
> >> +      atans (ATAN)
> >> +      sqrts (SQRT)
> >> +  (simplify
> >> +   (coss (atans:s @0))
> >> +   (rdiv {build_one_cst (type);}
> >> +       (sqrts (plus (mult @0 @0) {build_one_cst (type);})))))
> > Don't we have the same kinds of issues with the x*x in here?  As X gets
> > large it will overflow, but the result is going to be approaching zero.
> > So we might be able to use a similar trick here.
>
> (I have not read the patch)
>
> The similar trick would say that for X large, this is the same as 1/abs(X)
> I guess. Note that it may be simpler to generate a call to hypot (C99).
>
> A related remark would be: with the precision of double, for x>=cst (about
> 2^53), atan(x) is constant, within .5 ulp of pi/2 if the math library is
> super precise (which it probably isn't). Returning 0 for its cos (what
> happens if x*x gives +Inf) is thus completely fine unless you are using
> crlibm, but then you wouldn't use flag_unsafe_math_optimizations. The main
> issue is that if we have -ffast-math, we have -ffinite-math-only, and we
> are possibly introducing an infinity as intermediate result...
>
> --
> Marc Glisse
Jeff Law Oct. 10, 2018, 6:06 p.m. UTC | #7
On 10/5/18 6:09 AM, Giuliano Augusto Faulin Belinassi wrote:
> Thank you for the review. I will address all these issues :-).
> 
>> Imagine a pause here while I lookup isolation of radicals....  It's been
>> a long time...   OK.  Sure.  I see what you're doing here...
> 
> Sorry, but I really did not understand your comment. Should I write a
> shorter comment for that function?
Not at all -- I was just trying to be funny.  I was struggling to
understand the comment  so I tried to recreate the steps necessary to
transform the equation myself.  And quickly realized that it's been 30
years since I've had to do that kind of algebra  :-)

> 
>> Not  sure what you mean for safety reasons.  The calculations to produce
>> "c" then convert it into a REAL_VALUE_TYPE all make sense.  Just not
>> sure what this line is really meant to do.
> 
> Imagine the following case:
> Let "c" be the real constant such that it is certain that for every x
>> "c",  1/sqrt(x*x + 1) = 1.
> Suppose now that our calculation leads us to a c' < "c" due to a minor
> imprecision.
> The logic here is that 10 * c' > "c" and everything will work, thus it is safer.
> Note however that I cannot prove that 10 * c' > "c", but I would be
> really surprised
> if this does not holds.
Ah.  I  understand.  This probably warrants a slightly better comment.

Jeff
diff mbox series

Patch

Index: gcc/match.pd
===================================================================
--- gcc/match.pd	(revisão 264058)
+++ gcc/match.pd	(cópia de trabalho)
@@ -4169,6 +4169,39 @@ 
    (tans (atans @0))
    @0)))
 
+ /* Simplify sin(atan(x)) -> x / sqrt(x*x + 1). */
+ (for sins (SIN)
+      atans (ATAN)
+      sqrts (SQRT)
+  (simplify
+   (sins (atans:s @0))
+   (if (SCALAR_FLOAT_TYPE_P (type))
+    (switch
+     (if (types_match (type, float_type_node))
+      (cond (le (abs @0) { build_sinatan_cst (float_type_node); })
+       (rdiv @0 (sqrts (plus (mult @0 @0) 
+           {build_one_cst (float_type_node);})))
+       (BUILT_IN_COPYSIGNF { build_one_cst (float_type_node); } @0)))
+     (if (types_match (type, double_type_node))
+      (cond (le (abs @0) { build_sinatan_cst (double_type_node); })
+       (rdiv @0 (sqrts (plus (mult @0 @0) 
+           {build_one_cst (double_type_node);})))
+       (BUILT_IN_COPYSIGN  { build_one_cst (double_type_node); } @0)))
+     (if (types_match (type, long_double_type_node))
+      (cond (le (abs @0) { build_sinatan_cst (long_double_type_node); })
+       (rdiv @0 (sqrts (plus (mult @0 @0) 
+           {build_one_cst (long_double_type_node);})))
+       (BUILT_IN_COPYSIGNL { build_one_cst (long_double_type_node); } @0)))))))
+
+/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
+ (for coss (COS)
+      atans (ATAN)
+      sqrts (SQRT)
+  (simplify
+   (coss (atans:s @0))
+   (rdiv {build_one_cst (type);} 
+       (sqrts (plus (mult @0 @0) {build_one_cst (type);})))))
+
 /* cabs(x+0i) or cabs(0+xi) -> abs(x).  */
 (simplify
  (CABS (complex:C @0 real_zerop@1))
Index: gcc/real.c
===================================================================
--- gcc/real.c	(revisão 264058)
+++ gcc/real.c	(cópia de trabalho)
@@ -5279,3 +5279,46 @@ 
 {
   return HONOR_SIGN_DEPENDENT_ROUNDING (GET_MODE (x));
 }
+
+/*  Build real constant used by sin(atan(x)) optimization. 
+    The logic here is as follows: It can be mathematically 
+    shown that sin(atan(x)) = x / sqrt(1 + x*x), but notice 
+    that the second formula has an x*x, which can overflow 
+    if x is big enough. However, x / sqrt(1 + x*x) converges 
+    to 1 for large x. What must be the value of x such that 
+    when computing x / sqrt (1 + x*x) = 1?
+
+    Therefore, we must then solve x / sqrt(1 + x*x) > eps 
+    for x, where eps is the largest number smaller than 1 
+    representable by the target. Hence, solving for eps 
+    yields that x > eps / sqrt(1 - eps*eps). This eps can
+    be easily calculated by calling nextafter. Likewise for
+    the negative x.  */
+
+void
+build_sinatan_real (REAL_VALUE_TYPE * r, tree type)
+{
+  REAL_VALUE_TYPE eps;
+  mpfr_t mpfr_eps, mpfr_const1, c, divisor;
+  const struct real_format * fmt = NULL;
+  
+  fmt = type ? REAL_MODE_FORMAT (TYPE_MODE (type)) : NULL;
+  
+  mpfr_inits (divisor, mpfr_eps, mpfr_const1, c, NULL);
+  mpfr_from_real (mpfr_const1, &dconst1, GMP_RNDN);
+
+  real_nextafter (&eps, fmt, &dconst1, &dconst0);
+  
+  mpfr_from_real (mpfr_eps, &eps, GMP_RNDZ);
+  mpfr_mul (divisor, mpfr_eps, mpfr_eps, GMP_RNDU);
+  mpfr_sub (divisor, mpfr_const1, divisor, GMP_RNDZ);
+  mpfr_sqrt (divisor, divisor, GMP_RNDZ);
+  mpfr_div (c, mpfr_eps, divisor, GMP_RNDU);
+
+  real_from_mpfr (r, c, fmt, GMP_RNDU);
+  
+  /* For safety reasons.  */
+  times_pten(r, 1);
+
+  mpfr_clears (divisor, mpfr_eps, mpfr_const1, c, NULL);
+}
Index: gcc/real.h
===================================================================
--- gcc/real.h	(revisão 264058)
+++ gcc/real.h	(cópia de trabalho)
@@ -523,4 +523,8 @@ 
 			       const wide_int_ref &, signop);
 #endif
 
+/* Build a constant c such that, for every x > c, x / sqrt(1 + x*x) = 1
+   in floating point.  */
+extern void build_sinatan_real (REAL_VALUE_TYPE *, tree); 
+
 #endif /* ! GCC_REAL_H */
Index: gcc/testsuite/gcc.dg/sinatan-1.c
===================================================================
--- gcc/testsuite/gcc.dg/sinatan-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sinatan-1.c	(cópia de trabalho)
@@ -0,0 +1,136 @@ 
+/* { dg-do run } */
+
+extern float sinf (float);
+extern float cosf (float);
+extern float atanf (float);
+extern float sqrtf (float);
+extern float nextafterf (float, float);
+extern double sin (double);
+extern double cos (double);
+extern double atan (double);
+extern double sqrt (double);
+extern double nextafter (double, double);
+extern long double sinl (long double);
+extern long double cosl (long double);
+extern long double atanl (long double);
+extern long double sqrtl (long double);
+extern long double nextafterl (long double, long double);
+
+extern void abort ();
+
+double __attribute__ ((noinline, optimize("Ofast")))
+sinatan (double x)
+{
+    return sin (atan (x));
+}
+
+double __attribute__ ((noinline, optimize("Ofast")))
+cosatan (double x)
+{
+    return cos (atan (x));
+}
+
+double __attribute__ ((noinline))
+sinatan_ (double x)
+{
+    return sin (atan (x));
+}
+
+float __attribute__ ((noinline, optimize("Ofast")))
+sinatanf(float x)
+{
+    return sinf (atanf (x));
+}
+
+float __attribute__ ((noinline, optimize("Ofast")))
+cosatanf(float x)
+{
+    return cosf (atanf (x));
+}
+
+float __attribute__ ((noinline))
+sinatanf_ (float x)
+{
+    return sinf (atanf (x));
+}
+
+long double __attribute__ ((noinline, optimize("Ofast")))
+sinatanl (long double x)
+{
+    return sinl (atanl (x));
+}
+
+long double __attribute__ ((noinline, optimize("Ofast")))
+cosatanl (long double x)
+{
+    return cosl (atanl (x));
+}
+
+long double __attribute__ ((noinline))
+sinatanl_ (long double x)
+{
+    return sinl (atanl (x));
+}
+
+/*  Same logic as implemented in build_sinatan_cst.  */
+float
+sinatanf_inv (float x)
+{
+    return (x / sqrtf (1 - x*x))*10;
+}
+
+double
+sinatan_inv (double x)
+{
+    return (x / sqrt (1 - x*x))*10;
+}
+
+long double
+sinatanl_inv (long double x)
+{
+    return (x / sqrtl (1 - x*x))*10;
+}
+
+int
+main()
+{
+    float fc, feps;
+    double c, eps;
+    long double lc, leps;
+
+    /*  Force move from FPU to memory, otherwise comparison may
+        fail due to possible more accurate registers (see 387)  */
+    volatile float fy1, fy2;
+    volatile double y1, y2;
+    volatile long double ly1, ly2;
+
+    feps = nextafterf (1.f, 0.f);
+    eps  = nextafter  (1. , 0. );
+    leps = nextafterl (1.L, 0.L);
+
+    fc = sinatanf_inv (feps);
+    c  = sinatan_inv (eps);
+    lc = sinatanl_inv (leps);
+
+    fy1 = sinatanf (fc);
+    fy2 = sinatanf_ (fc);
+    y1 = sinatan (c);
+    y2 = sinatan_(c);
+    ly1 = sinatanl (lc);
+    ly2 = sinatanl_ (lc);
+
+    if (fy1 != fy2 || y1 != y2 || ly1 != ly2)
+        abort ();
+
+    fy1 = sinatanf (-fc);
+    fy2 = sinatanf_ (-fc);
+    y1 = sinatan (-c);
+    y2 = sinatan_(-c);
+    ly1 = sinatanl (-lc);
+    ly2 = sinatanl_ (-lc);
+
+    if (fy1 != fy2 || y1 != y2 || ly1 != ly2)
+        abort ();
+
+    return 0;
+}
Index: gcc/testsuite/gcc.dg/sinatan-2.c
===================================================================
--- gcc/testsuite/gcc.dg/sinatan-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sinatan-2.c	(cópia de trabalho)
@@ -0,0 +1,59 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Ofast -fdump-tree-optimized" } */
+
+extern float sinf (float);
+extern float cosf (float);
+extern float atanf (float);
+extern double sin (double);
+extern double cos (double);
+extern double atan (double);
+extern long double sinl (long double);
+extern long double cosl (long double);
+extern long double atanl (long double);
+
+double __attribute__ ((noinline))
+sinatan_ (double x)
+{
+    return sin (atan (x));
+}
+
+double __attribute__ ((noinline))
+cosatan_ (double x)
+{
+    return cos (atan (x));
+}
+
+float __attribute__ ((noinline))
+sinatanf_(float x)
+{
+    return sinf (atanf (x));
+}
+
+float __attribute__ ((noinline))
+cosatanf_(float x)
+{
+    return cosf (atanf (x));
+}
+
+long double __attribute__ ((noinline))
+sinatanl_ (long double x)
+{
+    return sinl (atanl (x));
+}
+
+long double __attribute__ ((noinline))
+cosatanl_ (long double x)
+{
+    return cosl (atanl (x));
+}
+
+/* There must be no calls to sin, cos, or atan */
+/* {dg-final { scan-tree-dump-not "sin " "optimized" } } */
+/* {dg-final { scan-tree-dump-not "cos " "optimized" } } */
+/* {dg-final { scan-tree-dump-not "atan " "optimized" }} */
+/* {dg-final { scan-tree-dump-not "sinf " "optimized" } } */
+/* {dg-final { scan-tree-dump-not "cosf " "optimized" } } */
+/* {dg-final { scan-tree-dump-not "atanf " "optimized" }} */
+/* {dg-final { scan-tree-dump-not "sinl " "optimized" } } */
+/* {dg-final { scan-tree-dump-not "cosl " "optimized" } } */
+/* {dg-final { scan-tree-dump-not "atanl " "optimized" }} */
Index: gcc/testsuite/gcc.dg/sinatan-3.c
===================================================================
--- gcc/testsuite/gcc.dg/sinatan-3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sinatan-3.c	(cópia de trabalho)
@@ -0,0 +1,65 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Ofast -fdump-tree-optimized" } */
+
+extern float sinf (float);
+extern float cosf (float);
+extern float atanf (float);
+extern double sin (double);
+extern double cos (double);
+extern double atan (double);
+extern long double sinl (long double);
+extern long double cosl (long double);
+extern long double atanl (long double);
+
+float __attribute__ ((noinline)) 
+cosatanf_(float x)
+{
+    float atg = atanf(x);
+    return cosf(atg) + atg;
+}
+
+double __attribute__ ((noinline)) 
+cosatan_(double x)
+{
+    double atg = atan(x);
+    return cos(atg) + atg;
+}
+
+long double __attribute__ ((noinline)) 
+cosatanl_(long double x)
+{
+    long double atg = atanl(x);
+    return cosl(atg) + atg;
+}
+
+float __attribute__ ((noinline)) 
+sinatanf_(float x)
+{
+    float atg = atanf(x);
+    return sinf(atg) + atg;
+}
+
+double __attribute__ ((noinline)) 
+sinatan_(double x)
+{
+    double atg = atan(x);
+    return sin(atg) + atg;
+}
+
+long double __attribute__ ((noinline)) 
+sinatanl_(long double x)
+{
+    long double atg = atanl(x);
+    return sinl(atg) + atg;
+}
+
+/* There should be calls to both sin and atan */
+/* { dg-final { scan-tree-dump "cos " "optimized" } } */
+/* { dg-final { scan-tree-dump "sin " "optimized" } } */
+/* { dg-final { scan-tree-dump "atan " "optimized" } } */
+/* { dg-final { scan-tree-dump "cosf " "optimized" } } */
+/* { dg-final { scan-tree-dump "sinf " "optimized" } } */
+/* { dg-final { scan-tree-dump "atanf " "optimized" } } */
+/* { dg-final { scan-tree-dump "cosl " "optimized" } } */
+/* { dg-final { scan-tree-dump "sinl " "optimized" } } */
+/* { dg-final { scan-tree-dump "atanl " "optimized" } } */
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	(revisão 264058)
+++ gcc/tree.c	(cópia de trabalho)
@@ -2359,7 +2359,16 @@ 
     }
 }
 
+tree
+build_sinatan_cst (tree type)
+{
+  REAL_VALUE_TYPE cst;
 
+  build_sinatan_real (&cst, type);
+  return build_real (type, cst);
+}
+
+
 /* Build a BINFO with LEN language slots.  */
 
 tree
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revisão 264058)
+++ gcc/tree.h	(cópia de trabalho)
@@ -4158,6 +4158,7 @@ 
 extern tree build_minus_one_cst (tree);
 extern tree build_all_ones_cst (tree);
 extern tree build_zero_cst (tree);
+extern tree build_sinatan_cst (tree);
 extern tree build_string (int, const char *);
 extern tree build_poly_int_cst (tree, const poly_wide_int_ref &);
 extern tree build_tree_list (tree, tree CXX_MEM_STAT_INFO);