re PR tree-optimization/86829 (Missing sin(atan(x)) and cos(atan(x)) optimizations)
authorGiuliano Belinassi <giuliano.belinassi@usp.br>
Thu, 11 Oct 2018 21:06:12 +0000 (21:06 +0000)
committerJeff Law <law@gcc.gnu.org>
Thu, 11 Oct 2018 21:06:12 +0000 (15:06 -0600)
PR tree-optimization/86829
* match.pd (sin (atan (x))): New simplification rules.
(cos (atan (x))): Likewise.
* real.c (build_sinatan_real): New function.
* real.h (build_sinatan_real): Prototype.

PR tree-optimization/86829
* gcc.dg/sinatan-1.c: New test.
* gcc.dg/sinatan-2.c: New test.
* gcc.dg/sinatan-3.c: New test.

From-SVN: r265064

gcc/ChangeLog
gcc/match.pd
gcc/real.c
gcc/real.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/sinatan-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/sinatan-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/sinatan-3.c [new file with mode: 0644]

index d6a4cade483f4c99d054903e431838448741a554..307d64ac88135ee338073bcab73cd07e79a47127 100644 (file)
@@ -1,3 +1,11 @@
+2018-10-11  Giuliano Belinassi  <giuliano.belinassi@usp.br>
+
+       PR tree-optimization/86829
+       * match.pd (sin (atan (x))): New simplification rules.
+       (cos (atan (x))): Likewise.
+       * real.c (build_sinatan_real): New function.
+       * real.h (build_sinatan_real): Prototype.
+
 2018-10-11  Will Schmidt <will_schmidt@vnet.ibm.com>
 
        * config/rs6000/rs6000.c (map_to_integral_tree_type): New helper
index 7cc2374ffeb2c87d30cd3957ce1f65c3680b3cfd..94fbab841f5e36bd33fda849a686fd80886ee1ff 100644 (file)
@@ -4223,6 +4223,45 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    (tans (atans @0))
    @0)))
 
+ /* Simplify sin(atan(x)) -> x / sqrt(x*x + 1). */
+ (for sins (SIN)
+      atans (ATAN)
+      sqrts (SQRT)
+      copysigns (COPYSIGN)
+  (simplify
+   (sins (atans:s @0))
+   (with
+     {
+      REAL_VALUE_TYPE r_cst;
+      build_sinatan_real (&r_cst, type);
+      tree t_cst = build_real (type, r_cst);
+      tree t_one = build_one_cst (type);
+     }
+    (if (SCALAR_FLOAT_TYPE_P (type))
+     (cond (le (abs @0) { t_cst; })
+      (rdiv @0 (sqrts (plus (mult @0 @0) { t_one; })))
+      (copysigns { t_one; } @0))))))
+
+/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
+ (for coss (COS)
+      atans (ATAN)
+      sqrts (SQRT)
+      copysigns (COPYSIGN)
+  (simplify
+   (coss (atans:s @0))
+   (with
+     {
+      REAL_VALUE_TYPE r_cst;
+      build_sinatan_real (&r_cst, type);
+      tree t_cst = build_real (type, r_cst);
+      tree t_one = build_one_cst (type);
+      tree t_zero = build_zero_cst (type);
+     }
+    (if (SCALAR_FLOAT_TYPE_P (type))
+     (cond (le (abs @0) { t_cst; })
+      (rdiv { t_one; } (sqrts (plus (mult @0 @0) { t_one; })))
+      (copysigns { t_zero; } @0))))))
+
 /* cabs(x+0i) or cabs(0+xi) -> abs(x).  */
 (simplify
  (CABS (complex:C @0 real_zerop@1))
index f822ae82d6148d3efaf4533175caf9bb9cf58ccd..51f8fd5b3aeb02ce2a996ce730721f9402031c84 100644 (file)
@@ -5279,3 +5279,29 @@ HONOR_SIGN_DEPENDENT_ROUNDING (const_rtx x)
 {
   return HONOR_SIGN_DEPENDENT_ROUNDING (GET_MODE (x));
 }
+
+/* Fills r with the largest value such that 1 + r*r won't overflow.
+   This is used in both sin (atan (x)) and cos (atan(x)) optimizations. */
+
+void
+build_sinatan_real (REAL_VALUE_TYPE * r, tree type)
+{
+  REAL_VALUE_TYPE maxval;
+  mpfr_t mpfr_const1, mpfr_c, mpfr_maxval;
+  machine_mode mode = TYPE_MODE (type);
+  const struct real_format * fmt = REAL_MODE_FORMAT (mode);
+
+  real_maxval (&maxval, 0, mode);
+
+  mpfr_inits (mpfr_const1, mpfr_c, mpfr_maxval, NULL);
+
+  mpfr_from_real (mpfr_const1, &dconst1, GMP_RNDN);
+  mpfr_from_real (mpfr_maxval, &maxval,  GMP_RNDN);
+
+  mpfr_sub (mpfr_c, mpfr_maxval, mpfr_const1, GMP_RNDN);
+  mpfr_sqrt (mpfr_c, mpfr_c, GMP_RNDZ);
+
+  real_from_mpfr (r, mpfr_c, fmt, GMP_RNDZ);
+  
+  mpfr_clears (mpfr_const1, mpfr_c, mpfr_maxval, NULL);
+}
index 0ce4256570819c3f18054373c4c1615cb645b505..561d0fda129d01db9c64b2c2d8af4f882227cab8 100644 (file)
@@ -523,4 +523,8 @@ extern void real_from_integer (REAL_VALUE_TYPE *, format_helper,
                               const wide_int_ref &, signop);
 #endif
 
+/* Fills r with the largest value such that 1 + r*r won't overflow.
+   This is used in both sin (atan (x)) and cos (atan(x)) optimizations. */
+extern void build_sinatan_real (REAL_VALUE_TYPE *, tree); 
+
 #endif /* ! GCC_REAL_H */
index 3b19068fcd59db72a094e190f794494436e9dbbc..c8d0d3fbd356ce840c87b0bddb71f15cf915ec02 100644 (file)
@@ -1,3 +1,10 @@
+2018-10-09  Giuliano Belinassi  <giuliano.belinassi@usp.br>
+
+       PR tree-optimization/86829
+       * gcc.dg/sinatan-1.c: New test.
+       * gcc.dg/sinatan-2.c: New test.
+       * gcc.dg/sinatan-3.c: New test.
+
 2018-10-11  Will Schmidt  <will_schmidt@vnet.ibm.com>
 
        * gcc.target/powerpc/fold-vec-mergeeo-floatdouble.c: New.
diff --git a/gcc/testsuite/gcc.dg/sinatan-1.c b/gcc/testsuite/gcc.dg/sinatan-1.c
new file mode 100644 (file)
index 0000000..194926c
--- /dev/null
@@ -0,0 +1,101 @@
+/* { dg-do run } */
+/* { dg-options "-Ofast" } */
+
+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));
+}
+
+float __attribute__ ((noinline, optimize("Ofast")))
+sinatanf(float x)
+{
+    return sinf (atanf (x));
+}
+
+float __attribute__ ((noinline, optimize("Ofast")))
+cosatanf(float x)
+{
+    return cosf (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));
+}
+
+int
+main()
+{
+    /* Get first x such that 1 + x*x will overflow */
+    float fc = nextafterf (sqrtf (__FLT_MAX__ - 1), __FLT_MAX__);
+    double c = nextafter (sqrt (__DBL_MAX__ - 1), __DBL_MAX__);
+    long double lc = nextafter (sqrtl (__LDBL_MAX__ - 1), __LDBL_MAX__);
+
+    /*  Force move from FPU to memory, otherwise comparison may
+        fail due to possible more accurate registers (see 387)  */
+    volatile float fy;
+    volatile double y;
+    volatile long double ly;
+
+    fy = sinatanf (fc);
+    y = sinatan (c);
+    ly = sinatanl (lc);
+
+    if (fy != 1.f || y != 1 || ly != 1.L)
+        abort ();
+
+    fy = cosatanf (fc);
+    y = cosatan (c);
+    ly = cosatanl (lc);
+
+    if (fy != 0.f || y != 0. || ly != 0.L)
+        abort ();
+
+    fy = sinatanf (-fc);
+    y = sinatan (-c);
+    ly = sinatanl (-lc);
+
+    if (fy != -1.f || y != -1. || ly != -1.L)
+        abort ();
+    
+    fy = cosatanf (-fc);
+    y = cosatan (-c);
+    ly = cosatanl (-lc);
+
+    if (fy != 0.f || y != 0. || ly != 0.L)
+        abort ();
+
+    return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/sinatan-2.c b/gcc/testsuite/gcc.dg/sinatan-2.c
new file mode 100644 (file)
index 0000000..07a1e1e
--- /dev/null
@@ -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" }} */
diff --git a/gcc/testsuite/gcc.dg/sinatan-3.c b/gcc/testsuite/gcc.dg/sinatan-3.c
new file mode 100644 (file)
index 0000000..600d475
--- /dev/null
@@ -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" } } */