From d4a43a03641af1a48c5bae7f323614d7d69e0819 Mon Sep 17 00:00:00 2001
From: ghazi <ghazi@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Sat, 18 Nov 2006 20:38:40 +0000
Subject: [PATCH] 	* builtins.c (integer_valued_real_p): Handle
 fmin/fmax. 	(fold_builtin_fmin_fmax): New. 	(fold_builtin_1): Use it.

testsuite:
	* gcc.dg/builtins-20.c: Add fmin/fmax cases.
	* gcc.dg/torture/builtin-minmax-1.c: New.



git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@118976 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog                                 |   4 +
 gcc/builtins.c                                |  49 +++++++--
 gcc/testsuite/ChangeLog                       |   3 +
 gcc/testsuite/gcc.dg/builtins-20.c            |  78 ++++++++++++++
 .../gcc.dg/torture/builtin-minmax-1.c         | 100 ++++++++++++++++++
 5 files changed, 224 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/torture/builtin-minmax-1.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 0df21ccca998..86b2a3724eb2 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,9 @@
 2006-11-18  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
+	* builtins.c (integer_valued_real_p): Handle fmin/fmax.
+	(fold_builtin_fmin_fmax): New.
+	(fold_builtin_1): Use it.
+
 	* fold-const.c (fold_strip_sign_ops): Handle copysign.
 
 2006-11-18  Richard Guenther  <rguenther@suse.de>
diff --git a/gcc/builtins.c b/gcc/builtins.c
index b9005b48799c..4e4ea969cd78 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -6708,6 +6708,11 @@ integer_valued_real_p (tree t)
 	CASE_FLT_FN (BUILT_IN_TRUNC):
 	  return true;
 
+	CASE_FLT_FN (BUILT_IN_FMIN):
+	CASE_FLT_FN (BUILT_IN_FMAX):
+	  return integer_valued_real_p (TREE_VALUE (TREE_OPERAND (t, 1)))
+	    && integer_valued_real_p (TREE_VALUE (TREE_CHAIN (TREE_OPERAND (t, 1))));
+
 	default:
 	  break;
 	}
@@ -8722,6 +8727,38 @@ fold_builtin_abs (tree arglist, tree type)
   return fold_build1 (ABS_EXPR, type, arg);
 }
 
+/* Fold a call to builtin fmin or fmax.  */
+
+static tree
+fold_builtin_fmin_fmax (tree arglist, tree type, bool max)
+{
+  if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+    {
+      tree arg0 = TREE_VALUE (arglist);
+      tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+      /* Calculate the result when the argument is a constant.  */
+      tree res = do_mpfr_arg2 (arg0, arg1, type, (max ? mpfr_max : mpfr_min));
+
+      if (res)
+	return res;
+
+      /* Transform fmin/fmax(x,x) -> x.  */
+      if (operand_equal_p (arg0, arg1, OEP_PURE_SAME))
+	return omit_one_operand (type, arg0, arg1);
+      
+      /* Convert fmin/fmax to MIN_EXPR/MAX_EXPR.  C99 requires these
+	 functions to return the numeric arg if the other one is NaN.
+	 These tree codes don't honor that, so only transform if
+	 -ffinite-math-only is set.  C99 doesn't require -0.0 to be
+	 handled, so we don't have to worry about it either.  */
+      if (flag_finite_math_only)
+	return fold_build2 ((max ? MAX_EXPR : MIN_EXPR), type,
+			    fold_convert (type, arg0),
+			    fold_convert (type, arg1));
+    }
+  return NULL_TREE;
+}
+
 /* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
    EXP is the CALL_EXPR for the call.  */
 
@@ -9143,18 +9180,10 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore)
     break;
 
     CASE_FLT_FN (BUILT_IN_FMIN):
-      if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
-	return do_mpfr_arg2 (TREE_VALUE (arglist),
-			     TREE_VALUE (TREE_CHAIN (arglist)),
-			     type, mpfr_min);
-    break;
+      return fold_builtin_fmin_fmax (arglist, type, /*max=*/false);
 
     CASE_FLT_FN (BUILT_IN_FMAX):
-      if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
-	return do_mpfr_arg2 (TREE_VALUE (arglist),
-			     TREE_VALUE (TREE_CHAIN (arglist)),
-			     type, mpfr_max);
-    break;
+      return fold_builtin_fmin_fmax (arglist, type, /*max=*/true);
 
     CASE_FLT_FN (BUILT_IN_HYPOT):
       return fold_builtin_hypot (fndecl, arglist, type);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index c23dae9fb653..25c827e54673 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,8 @@
 2006-11-18  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
+	* gcc.dg/builtins-20.c: Add fmin/fmax cases.
+	* gcc.dg/torture/builtin-minmax-1.c: New.
+
 	* gcc.dg/builtins-20.c: Add cases for copysign.
 
 2006-11-18  Francois-Xavier Coudert  <coudert@clipper.ens.fr>
diff --git a/gcc/testsuite/gcc.dg/builtins-20.c b/gcc/testsuite/gcc.dg/builtins-20.c
index bb90aec9672a..1211cf0b6cd4 100644
--- a/gcc/testsuite/gcc.dg/builtins-20.c
+++ b/gcc/testsuite/gcc.dg/builtins-20.c
@@ -17,6 +17,8 @@ extern double sin (double);
 extern double tan (double);
 extern double fabs (double);
 extern double copysign (double, double);
+extern double fmin (double, double);
+extern double fmax (double, double);
 extern double hypot (double, double);
 extern double pure (double) __attribute__ ((__pure__));
 extern float cosf (float);
@@ -24,6 +26,8 @@ extern float sinf (float);
 extern float tanf (float);
 extern float fabsf (float);
 extern float copysignf (float, float);
+extern float fminf (float, float);
+extern float fmaxf (float, float);
 extern float hypotf (float, float);
 extern float puref (float) __attribute__ ((__pure__));
 extern long double cosl (long double);
@@ -31,6 +35,8 @@ extern long double sinl (long double);
 extern long double tanl (long double);
 extern long double fabsl (long double);
 extern long double copysignl (long double, long double);
+extern long double fminl (long double, long double);
+extern long double fmaxl (long double, long double);
 extern long double hypotl (long double, long double);
 extern long double purel (long double) __attribute__ ((__pure__));
 
@@ -155,6 +161,30 @@ void test2(double x, double y)
 
   if (hypot (tan(-x), tan(-fabs(y))) != hypot (tan(x), tan(y)))
     link_error ();
+
+  if (fmin (fmax(x,y),y) != y)
+    link_error ();
+
+  if (fmin (fmax(y,x),y) != y)
+    link_error ();
+
+  if (fmin (x,fmax(x,y)) != x)
+    link_error ();
+  
+  if (fmin (x,fmax(y,x)) != x)
+    link_error ();
+  
+  if (fmax (fmin(x,y),y) != y)
+    link_error ();
+
+  if (fmax (fmin(y,x),y) != y)
+    link_error ();
+
+  if (fmax (x,fmin(x,y)) != x)
+    link_error ();
+  
+  if (fmax (x,fmin(y,x)) != x)
+    link_error ();
 }
 
 void test1f(float x)
@@ -278,6 +308,30 @@ void test2f(float x, float y)
 
   if (hypotf (tanf(-x), tanf(-fabsf(y))) != hypotf (tanf(x), tanf(y)))
     link_error ();
+
+  if (fminf (fmaxf(x,y),y) != y)
+    link_error ();
+
+  if (fminf (fmaxf(y,x),y) != y)
+    link_error ();
+
+  if (fminf (x,fmaxf(x,y)) != x)
+    link_error ();
+  
+  if (fminf (x,fmaxf(y,x)) != x)
+    link_error ();
+  
+  if (fmaxf (fminf(x,y),y) != y)
+    link_error ();
+
+  if (fmaxf (fminf(y,x),y) != y)
+    link_error ();
+
+  if (fmaxf (x,fminf(x,y)) != x)
+    link_error ();
+  
+  if (fmaxf (x,fminf(y,x)) != x)
+    link_error ();
 }
 
 
@@ -402,6 +456,30 @@ void test2l(long double x, long double y)
 
   if (hypotl (tanl(-x), tanl(-fabsl(y))) != hypotl (tanl(x), tanl(y)))
     link_error ();
+
+  if (fminl (fmaxl(x,y),y) != y)
+    link_error ();
+
+  if (fminl (fmaxl(y,x),y) != y)
+    link_error ();
+
+  if (fminl (x,fmaxl(x,y)) != x)
+    link_error ();
+  
+  if (fminl (x,fmaxl(y,x)) != x)
+    link_error ();
+  
+  if (fmaxl (fminl(x,y),y) != y)
+    link_error ();
+
+  if (fmaxl (fminl(y,x),y) != y)
+    link_error ();
+
+  if (fmaxl (x,fminl(x,y)) != x)
+    link_error ();
+  
+  if (fmaxl (x,fminl(y,x)) != x)
+    link_error ();
 }
 
 int main()
diff --git a/gcc/testsuite/gcc.dg/torture/builtin-minmax-1.c b/gcc/testsuite/gcc.dg/torture/builtin-minmax-1.c
new file mode 100644
index 000000000000..280d3564e373
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/builtin-minmax-1.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2006  Free Software Foundation.
+
+   Verify that built-in math function folding of fmin/fmax is
+   correctly performed by the compiler.
+
+   Origin: Kaveh R. Ghazi,  November 13, 2006.  */
+
+/* { dg-do link } */
+/* { dg-options "-fno-math-errno" } */
+
+/* All references to link_error should go away at compile-time.  */
+extern void link_error(int);
+
+#define DECLARE(FUNC) \
+  extern float FUNC##f (float); \
+  extern double FUNC (double); \
+  extern long double FUNC##l (long double)
+#define DECLARE_L(FUNC) \
+  extern long FUNC##f (float); \
+  extern long FUNC (double); \
+  extern long FUNC##l (long double)
+#define DECLARE2(FUNC) \
+  extern float FUNC##f (float, float); \
+  extern double FUNC (double, double); \
+  extern long double FUNC##l (long double, long double)
+
+DECLARE2(fmin);
+DECLARE2(fmax);
+DECLARE_L(lround);
+DECLARE_L(lrint);
+DECLARE(sqrt);
+DECLARE(fabs);
+extern int pure(int) __attribute__ ((__pure__));
+
+/* Test that FUNC(x,x) == x.  We cast to (long) so "!=" folds.  */
+#define TEST_EQ(FUNC) do { \
+  if ((long)FUNC##f(xf,xf) != (long)xf) \
+    link_error(__LINE__); \
+  if ((long)FUNC(x,x) != (long)x) \
+    link_error(__LINE__); \
+  if ((long)FUNC##l(xl,xl) != (long)xl) \
+    link_error(__LINE__); \
+  } while (0)
+
+/* Test that FUNC(purefn,purefn) == purefn.  We cast to (long) so "!=" folds.  */
+#define TEST_EQ_PURE(FUNC) do { \
+  if ((long)FUNC##f(pure(i),pure(i)) != (long)FUNC##f(pure(i),pure(i))) \
+    link_error(__LINE__); \
+  if ((long)FUNC(pure(i),pure(i)) != (long)FUNC(pure(i),pure(i))) \
+    link_error(__LINE__); \
+  if ((long)FUNC##l(pure(i),pure(i)) != (long)FUNC##l(pure(i),pure(i))) \
+    link_error(__LINE__); \
+  } while (0)
+
+/* Test that lround(FUNC(int,int)) == lrint(FUNC(int,int)), i.e. both
+   lround() and lrint() should be folded away.  */
+#define TEST_NONNEG(FUNC) do { \
+  if (lroundf(FUNC##f(i,j)) != lrintf(FUNC##f(i,j))) \
+    link_error(__LINE__); \
+  if (lround(FUNC(i,j)) != lrint(FUNC(i,j))) \
+    link_error(__LINE__); \
+  if (lroundl(FUNC##l(i,j)) != lrintl(FUNC##l(i,j))) \
+    link_error(__LINE__); \
+  } while (0)
+
+/* Test that (long)fabs(FUNC(fabs(x),fabs(y))) ==
+   (long)FUNC(fabs(x),fabs(y)).  We cast to (long) so "!=" folds.  */
+#define TEST_INT(FUNC) do { \
+  if ((long)fabsf(FUNC##f(fabsf(xf),fabsf(yf))) != (long)FUNC##f(fabsf(xf),fabsf(yf))) \
+    link_error(__LINE__); \
+  if ((long)fabs(FUNC(fabs(x),fabs(y))) != (long)FUNC(fabs(x),fabs(y))) \
+    link_error(__LINE__); \
+  if ((long)fabsl(FUNC##l(fabsl(xl),fabsl(yl))) != (long)FUNC##l(fabsl(xl),fabsl(yl))) \
+    link_error(__LINE__); \
+  } while (0)
+
+void foo (float xf, double x, long double xl,
+	  float yf, double y, long double yl,
+	  int i, int j)
+{
+  TEST_EQ(fmin);
+  TEST_EQ(fmax);
+
+#ifdef __OPTIMIZE__
+  TEST_EQ_PURE(fmin);
+  TEST_EQ_PURE(fmax);
+#endif
+
+  TEST_INT(fmin);
+  TEST_INT(fmax);
+  
+  TEST_NONNEG(fmin);
+  TEST_NONNEG(fmax);
+}
+
+int main()
+{
+  foo (1,1,1,1,1,1,1,1);
+  return 0;
+}
-- 
GitLab