From e337595266ee9be3d0b805c522d1abeead8bc7ff Mon Sep 17 00:00:00 2001
From: Yuao Ma <c8ef@outlook.com>
Date: Sun, 1 Jun 2025 15:46:59 +0800
Subject: [PATCH] gcc: middle-end opt for trigonometric pi-based functions
 builtins

This patch partially handled PR118592.

This patch builds upon r16-710-g591d3d02664c7b and r16-711-g89935d56f768b4. It
introduces middle-end optimizations, such as constant folding, for our
trigonometric pi-based function built-ins.

For MPFR versions older than 4.2.0, we've included our own folding functions.
I've also added tests to ensure everything works as expected.

gcc/ChangeLog:

	* fold-const-call.cc (mpfr_acospi, mpfr_asinpi, mpfr_atanpi,
	mpfr_atan2pi, mpfr_cospi, mpfr_sinpi, mpfr_tanpi): Workaround
	for mpfr < 4.2.0.
	(fold_const_call_ss): Constant fold for single arg.
	(fold_const_call_sss): Constant fold for double args.
	* fold-const.cc (negate_mathfn_p): asinpi/atanpi is odd func.
	(tree_call_nonnegative_warnv_p): acospi always non-neg,
	asinpi/atanpi non-neg iff arg non-neg.
	* tree-call-cdce.cc (can_test_argument_range): Add acospi/asinpi.
	(edom_only_function): Add acospi/asinpi/cospi/sinpi. Remove atan
	because atan NEVER produce EDOM.
	(get_no_error_domain): Add acospi/asinpi.

gcc/testsuite/ChangeLog:

	* gcc.dg/torture/builtin-math-1.c: Add tests for builtin math fold.
	* gcc.dg/torture/floatn-builtin.h: Add tests for float-nx.

Signed-off-by: Yuao Ma <c8ef@outlook.com>
---
 gcc/fold-const-call.cc                        | 173 ++++++++++++++++++
 gcc/fold-const.cc                             |  14 ++
 gcc/testsuite/gcc.dg/torture/builtin-math-1.c |  35 ++++
 gcc/testsuite/gcc.dg/torture/floatn-builtin.h |  24 +++
 gcc/tree-call-cdce.cc                         |  18 +-
 5 files changed, 262 insertions(+), 2 deletions(-)

diff --git a/gcc/fold-const-call.cc b/gcc/fold-const-call.cc
index 8230b83b929..b9bb6350e14 100644
--- a/gcc/fold-const-call.cc
+++ b/gcc/fold-const-call.cc
@@ -171,6 +171,145 @@ do_mpfr_sincos (real_value *result_sin, real_value *result_cos,
   return ok;
 }
 
+#if MPFR_VERSION < MPFR_VERSION_NUM(4, 2, 0)
+static int
+mpfr_acospi (mpfr_ptr rop, mpfr_srcptr op, mpfr_rnd_t rnd)
+{
+  mpfr_t pi, tmp;
+  mpfr_inits2 (2 * mpfr_get_prec (op), pi, tmp, NULL);
+  mpfr_const_pi (pi, rnd);
+  mpfr_acos (tmp, op, rnd);
+  mpfr_div (rop, tmp, pi, rnd);
+  mpfr_clears (pi, tmp, NULL);
+  return 0;
+}
+
+static int
+mpfr_asinpi (mpfr_ptr rop, mpfr_srcptr op, mpfr_rnd_t rnd)
+{
+  mpfr_t pi, tmp;
+  mpfr_inits2 (2 * mpfr_get_prec (op), pi, tmp, NULL);
+  mpfr_const_pi (pi, rnd);
+  mpfr_asin (tmp, op, rnd);
+  mpfr_div (rop, tmp, pi, rnd);
+  mpfr_clears (pi, tmp, NULL);
+  return 0;
+}
+
+static int
+mpfr_atanpi (mpfr_ptr rop, mpfr_srcptr op, mpfr_rnd_t rnd)
+{
+  mpfr_t pi, tmp;
+  mpfr_inits2 (2 * mpfr_get_prec (op), pi, tmp, NULL);
+  mpfr_const_pi (pi, rnd);
+  mpfr_atan (tmp, op, rnd);
+  mpfr_div (rop, tmp, pi, rnd);
+  mpfr_clears (pi, tmp, NULL);
+  return 0;
+}
+
+static int
+mpfr_atan2pi (mpfr_ptr rop, mpfr_srcptr y, mpfr_srcptr x, mpfr_rnd_t rnd)
+{
+  mpfr_t pi, tmp;
+  mpfr_inits2 (2 * mpfr_get_prec (x), pi, tmp, NULL);
+  mpfr_const_pi (pi, rnd);
+  mpfr_atan2 (tmp, y, x, rnd);
+  mpfr_div (rop, tmp, pi, rnd);
+  mpfr_clears (pi, tmp, NULL);
+  return 0;
+}
+
+static int
+mpfr_cospi (mpfr_ptr rop, mpfr_srcptr op, mpfr_rnd_t rnd)
+{
+  mpfr_t cs, n, r, two;
+  int s;
+
+  mpfr_inits2 (2 * mpfr_get_prec (op), cs, n, r, two, NULL);
+
+  mpfr_abs (r, op, rnd);
+  mpfr_modf (n, r, r, rnd);
+
+  if (mpfr_cmp_d (r, 0.5) == 0)
+    {
+      mpfr_set_ui (rop, 0, rnd);
+      return 0;
+    }
+
+  mpfr_set_ui (two, 2, rnd);
+  mpfr_fmod (cs, n, two, rnd);
+  s = mpfr_cmp_ui (cs, 0) == 0 ? 1 : -1;
+
+  mpfr_const_pi (cs, rnd);
+  mpfr_mul (cs, cs, r, rnd);
+  mpfr_cos (cs, cs, rnd);
+  mpfr_mul_si (rop, cs, s, rnd);
+
+  mpfr_clears (cs, n, r, two, NULL);
+  return 0;
+}
+
+static int
+mpfr_sinpi (mpfr_ptr rop, mpfr_srcptr op, mpfr_rnd_t rnd)
+{
+  mpfr_t sn, n, r, two;
+  int s;
+
+  mpfr_inits2 (2 * mpfr_get_prec (op), sn, n, r, two, NULL);
+
+  mpfr_abs (r, op, rnd);
+  mpfr_modf (n, r, r, rnd);
+
+  if (mpfr_cmp_d (r, 0.0) == 0)
+    {
+      mpfr_set_ui (rop, 0, rnd);
+      return 0;
+    }
+
+  mpfr_set_ui (two, 2, rnd);
+  mpfr_fmod (sn, n, two, rnd);
+  s = mpfr_cmp_si (op, 0) < 0 ? -1 : 1;
+  s *= mpfr_cmp_ui (sn, 0) == 0 ? 1 : -1;
+
+  mpfr_const_pi (sn, rnd);
+  mpfr_mul (sn, sn, r, rnd);
+  mpfr_sin (sn, sn, rnd);
+  mpfr_mul_si (rop, sn, s, rnd);
+
+  mpfr_clears (sn, n, r, two, NULL);
+  return 0;
+}
+
+static int
+mpfr_tanpi (mpfr_ptr rop, mpfr_srcptr op, mpfr_rnd_t rnd)
+{
+  mpfr_t tn, n, r;
+  int s;
+
+  mpfr_inits2 (2 * mpfr_get_prec (op), tn, n, r, NULL);
+
+  mpfr_abs (r, op, rnd);
+  mpfr_modf (n, r, r, rnd);
+
+  if (mpfr_cmp_d (r, 0.0) == 0)
+    {
+      mpfr_set_ui (rop, 0, rnd);
+      return 0;
+    }
+
+  s = mpfr_cmp_si (op, 0) < 0 ? -1 : 1;
+
+  mpfr_const_pi (tn, rnd);
+  mpfr_mul (tn, tn, r, rnd);
+  mpfr_tan (tn, tn, rnd);
+  mpfr_mul_si (rop, tn, s, rnd);
+
+  mpfr_clears (tn, n, r, NULL);
+  return 0;
+}
+#endif
+
 /* Try to evaluate:
 
       *RESULT = f (*ARG0, *ARG1)
@@ -788,6 +927,34 @@ fold_const_call_ss (real_value *result, combined_fn fn,
     CASE_CFN_TANH_FN:
       return do_mpfr_arg1 (result, mpfr_tanh, arg, format);
 
+    CASE_CFN_ACOSPI:
+    CASE_CFN_ACOSPI_FN:
+      return (real_compare (GE_EXPR, arg, &dconstm1)
+	      && real_compare (LE_EXPR, arg, &dconst1)
+	      && do_mpfr_arg1 (result, mpfr_acospi, arg, format));
+
+    CASE_CFN_ASINPI:
+    CASE_CFN_ASINPI_FN:
+      return (real_compare (GE_EXPR, arg, &dconstm1)
+	      && real_compare (LE_EXPR, arg, &dconst1)
+	      && do_mpfr_arg1 (result, mpfr_asinpi, arg, format));
+
+    CASE_CFN_ATANPI:
+    CASE_CFN_ATANPI_FN:
+      return do_mpfr_arg1 (result, mpfr_atanpi, arg, format);
+
+    CASE_CFN_COSPI:
+    CASE_CFN_COSPI_FN:
+      return do_mpfr_arg1 (result, mpfr_cospi, arg, format);
+
+    CASE_CFN_SINPI:
+    CASE_CFN_SINPI_FN:
+      return do_mpfr_arg1 (result, mpfr_sinpi, arg, format);
+
+    CASE_CFN_TANPI:
+    CASE_CFN_TANPI_FN:
+      return do_mpfr_arg1 (result, mpfr_tanpi, arg, format);
+
     CASE_CFN_ERF:
     CASE_CFN_ERF_FN:
       return do_mpfr_arg1 (result, mpfr_erf, arg, format);
@@ -1432,6 +1599,12 @@ fold_const_call_sss (real_value *result, combined_fn fn,
     CASE_CFN_ATAN2_FN:
       return do_mpfr_arg2 (result, mpfr_atan2, arg0, arg1, format);
 
+    CASE_CFN_ATAN2PI:
+    CASE_CFN_ATAN2PI_FN:
+      return (!(real_compare (EQ_EXPR, arg0, &dconst0)
+		&& real_compare (EQ_EXPR, arg1, &dconst0))
+	      && do_mpfr_arg2 (result, mpfr_atan2pi, arg0, arg1, format));
+
     CASE_CFN_FDIM:
     CASE_CFN_FDIM_FN:
       return do_mpfr_arg2 (result, mpfr_dim, arg0, arg1, format);
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 014f4218793..47492575d00 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -395,10 +395,14 @@ negate_mathfn_p (combined_fn fn)
     CASE_CFN_ASIN_FN:
     CASE_CFN_ASINH:
     CASE_CFN_ASINH_FN:
+    CASE_CFN_ASINPI:
+    CASE_CFN_ASINPI_FN:
     CASE_CFN_ATAN:
     CASE_CFN_ATAN_FN:
     CASE_CFN_ATANH:
     CASE_CFN_ATANH_FN:
+    CASE_CFN_ATANPI:
+    CASE_CFN_ATANPI_FN:
     CASE_CFN_CASIN:
     CASE_CFN_CASIN_FN:
     CASE_CFN_CASINH:
@@ -432,10 +436,14 @@ negate_mathfn_p (combined_fn fn)
     CASE_CFN_SIN_FN:
     CASE_CFN_SINH:
     CASE_CFN_SINH_FN:
+    CASE_CFN_SINPI:
+    CASE_CFN_SINPI_FN:
     CASE_CFN_TAN:
     CASE_CFN_TAN_FN:
     CASE_CFN_TANH:
     CASE_CFN_TANH_FN:
+    CASE_CFN_TANPI:
+    CASE_CFN_TANPI_FN:
     CASE_CFN_TRUNC:
     CASE_CFN_TRUNC_FN:
       return true;
@@ -14962,6 +14970,8 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1,
     CASE_CFN_ACOS_FN:
     CASE_CFN_ACOSH:
     CASE_CFN_ACOSH_FN:
+    CASE_CFN_ACOSPI:
+    CASE_CFN_ACOSPI_FN:
     CASE_CFN_CABS:
     CASE_CFN_CABS_FN:
     CASE_CFN_COSH:
@@ -15006,10 +15016,14 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1,
 
     CASE_CFN_ASINH:
     CASE_CFN_ASINH_FN:
+    CASE_CFN_ASINPI:
+    CASE_CFN_ASINPI_FN:
     CASE_CFN_ATAN:
     CASE_CFN_ATAN_FN:
     CASE_CFN_ATANH:
     CASE_CFN_ATANH_FN:
+    CASE_CFN_ATANPI:
+    CASE_CFN_ATANPI_FN:
     CASE_CFN_CBRT:
     CASE_CFN_CBRT_FN:
     CASE_CFN_CEIL:
diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-1.c b/gcc/testsuite/gcc.dg/torture/builtin-math-1.c
index 4ecc98d5f5c..94ae69e7531 100644
--- a/gcc/testsuite/gcc.dg/torture/builtin-math-1.c
+++ b/gcc/testsuite/gcc.dg/torture/builtin-math-1.c
@@ -36,6 +36,14 @@ extern double tan (double);
 extern float tanf (float);
 extern long double tanl (long double);
 
+extern double acospi (double);
+extern double asinpi (double);
+extern double atanpi (double);
+extern double atan2pi (double, double);
+extern double cospi (double);
+extern double sinpi (double);
+extern double tanpi (double);
+
 /* All references to link_error should go away at compile-time.  */
 extern void link_error(void);
 
@@ -177,6 +185,33 @@ void test (float f, double d, long double ld)
 
   if (powl (1.0L, ld) != 1.0L)
     link_error ();
+
+  if (acospi (0.5) < 0.3333 || acospi (0.5) > 0.3334 || acospi (-0.5) < 0.6666
+      || acospi (-0.5) > 0.6667)
+    link_error ();
+
+  if (asinpi (0.5) < 0.1666 || asinpi (0.5) > 0.1667 || asinpi (-0.5) < -0.1667
+      || asinpi (-0.5) > -0.1666)
+    link_error ();
+
+  if (atanpi (1.0) != 0.25 || atanpi (-1.0) > -0.25)
+    link_error ();
+
+  if (atan2pi (1.0, 1.0) > 0.2501 || atan2pi (1.0, 1.0) < 0.2499
+      || atan2pi (1.0, -1.0) < 0.7499 || atan2pi (1.0, -1.0) > 0.7501)
+    link_error ();
+
+  if (cospi (1.0 / 3) > 0.5001 || cospi (-1.0 / 3) < 0.4999
+      || cospi (4.0 / 3) > -0.4999 || cospi (4.0 / 3) < -0.5001)
+    link_error ();
+
+  if (sinpi (1.0 / 6) > 0.5001 || sinpi (-1.0 / 6) > -0.4999
+      || sinpi (5.0 / 6) < 0.4999 || sinpi (-7.0 / 6) > 0.5001)
+    link_error ();
+
+  if (tanpi (0.25) != 1.0000 || tanpi (-0.25) != -1.0000
+      || tanpi (1.25) != 1.0000 || tanpi (-1.25) != -1.0000)
+    link_error ();
 }
 
 int main()
diff --git a/gcc/testsuite/gcc.dg/torture/floatn-builtin.h b/gcc/testsuite/gcc.dg/torture/floatn-builtin.h
index 304a459ee0b..bd72e7cdfb2 100644
--- a/gcc/testsuite/gcc.dg/torture/floatn-builtin.h
+++ b/gcc/testsuite/gcc.dg/torture/floatn-builtin.h
@@ -73,6 +73,12 @@ extern __typeof (FN (__builtin_remainder) (0, 0)) test_type;
 extern __typeof (FN (__builtin_remquo) (0, 0, &test_i)) test_type;
 extern __typeof (FN (__builtin_scalbln) (0, 0L)) test_type;
 extern __typeof (FN (__builtin_scalbn) (0, 0)) test_type;
+extern __typeof (FN (__builtin_acospi) (0)) test_type;
+extern __typeof (FN (__builtin_asinpi) (0)) test_type;
+extern __typeof (FN (__builtin_atanpi) (0)) test_type;
+extern __typeof (FN (__builtin_cospi) (0)) test_type;
+extern __typeof (FN (__builtin_sinpi) (0)) test_type;
+extern __typeof (FN (__builtin_tanpi) (0)) test_type;
 
 volatile TYPE inf_cst = FN (__builtin_inf) ();
 volatile TYPE huge_val_cst = FN (__builtin_huge_val) ();
@@ -115,6 +121,12 @@ volatile TYPE t33 = FN (__builtin_pow) (CST (1.0), CST (2.0));
 volatile TYPE t34 = FN (__builtin_remainder) (CST (7.0), CST (4.0));
 volatile TYPE t35 = FN (__builtin_scalbln) (CST (1.0), 1L);
 volatile TYPE t36 = FN (__builtin_scalbn) (CST (1.0), 1);
+volatile TYPE t37 = FN (__builtin_acospi) (CST (1.0));
+volatile TYPE t38 = FN (__builtin_asinpi) (CST (0.0));
+volatile TYPE t39 = FN (__builtin_atanpi) (CST (0.0));
+volatile TYPE t40 = FN (__builtin_cospi) (CST (0.0));
+volatile TYPE t41 = FN (__builtin_sinpi) (CST (0.0));
+volatile TYPE t42 = FN (__builtin_tanpi) (CST (0.0));
 
 int
 main (void)
@@ -214,5 +226,17 @@ main (void)
     abort ();
   if (t36 != CST (2.0))
     abort ();
+  if (t37 != CST (0.0))
+    abort ();
+  if (t38 != CST (0.0))
+    abort ();
+  if (t39 != CST (0.0))
+    abort ();
+  if (t40 != CST (1.0))
+    abort ();
+  if (t41 != CST (0.0))
+    abort ();
+  if (t42 != CST (0.0))
+    abort ();
   exit (0);
 }
diff --git a/gcc/tree-call-cdce.cc b/gcc/tree-call-cdce.cc
index 9ca5fda5126..649c1e2b9f9 100644
--- a/gcc/tree-call-cdce.cc
+++ b/gcc/tree-call-cdce.cc
@@ -296,8 +296,12 @@ can_test_argument_range (gcall *call)
     /* Trig functions.  */
     CASE_FLT_FN (BUILT_IN_ACOS):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOS):
+    CASE_FLT_FN (BUILT_IN_ACOSPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOSPI):
     CASE_FLT_FN (BUILT_IN_ASIN):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ASIN):
+    CASE_FLT_FN (BUILT_IN_ASINPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ASINPI):
     /* Hyperbolic functions.  */
     CASE_FLT_FN (BUILT_IN_ACOSH):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOSH):
@@ -351,15 +355,21 @@ edom_only_function (gcall *call)
     {
     CASE_FLT_FN (BUILT_IN_ACOS):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOS):
+    CASE_FLT_FN (BUILT_IN_ACOSPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOSPI):
     CASE_FLT_FN (BUILT_IN_ASIN):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ASIN):
-    CASE_FLT_FN (BUILT_IN_ATAN):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ATAN):
+    CASE_FLT_FN (BUILT_IN_ASINPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ASINPI):
     CASE_FLT_FN (BUILT_IN_COS):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_COS):
+    CASE_FLT_FN (BUILT_IN_COSPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_COSPI):
     CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
     CASE_FLT_FN (BUILT_IN_SIN):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_SIN):
+    CASE_FLT_FN (BUILT_IN_SINPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_SINPI):
     CASE_FLT_FN (BUILT_IN_SQRT):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_SQRT):
     CASE_FLT_FN (BUILT_IN_FMOD):
@@ -694,8 +704,12 @@ get_no_error_domain (enum built_in_function fnc)
     /* Trig functions: return [-1, +1]  */
     CASE_FLT_FN (BUILT_IN_ACOS):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOS):
+    CASE_FLT_FN (BUILT_IN_ACOSPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ACOSPI):
     CASE_FLT_FN (BUILT_IN_ASIN):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_ASIN):
+    CASE_FLT_FN (BUILT_IN_ASINPI):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_ASINPI):
       return get_domain (-1, true, true,
                          1, true, true);
     /* Hyperbolic functions.  */
-- 
2.43.0

