While looking at the reported issue, it appeared that the Fortran frontend
mishandled the conversion of functions of the MIN/MAX variety to inline code.
At the same time, the simplification of expressions using a common and GNU
extension (but non-standard) could result in inconsistent results.  The patch
below addresses that.

Regtested on x86_64-pc-linux-gnu.

OK for master?

Thanks,
Harald


PR fortran/96613 - Fix type/kind of temporaries evaluating MIN/MAX

When evaluating functions of the MIN/MAX variety inline, use a temporary
of appropriate type and kind, and convert to the result type at the end.
In the case of allowing for the GNU extensions to MIN/MAX, derive the
result kind consistently during simplificaton.

gcc/fortran/ChangeLog:

        * simplify.c (min_max_choose): The simplification result shall
        have the highest kind value of the arguments.
        * trans-intrinsic.c (gfc_conv_intrinsic_minmax): Choose type and
        kind of intermediate by looking at all arguments, not the result.

gcc/testsuite/ChangeLog:

        * gfortran.dg/min_max_kind.f90: New test.
        * gfortran.dg/pr96613.f90: New test.

diff --git a/gcc/fortran/simplify.c b/gcc/fortran/simplify.c
index eb8b2afeb29..074b50c2e68 100644
--- a/gcc/fortran/simplify.c
+++ b/gcc/fortran/simplify.c
@@ -4924,6 +4924,8 @@ min_max_choose (gfc_expr *arg, gfc_expr *extremum, int sign, bool back_val)
   switch (arg->ts.type)
     {
       case BT_INTEGER:
+	if (extremum->ts.kind < arg->ts.kind)
+	  extremum->ts.kind = arg->ts.kind;
 	ret = mpz_cmp (arg->value.integer,
 		       extremum->value.integer) * sign;
 	if (ret > 0)
@@ -4931,6 +4933,8 @@ min_max_choose (gfc_expr *arg, gfc_expr *extremum, int sign, bool back_val)
 	break;

       case BT_REAL:
+	if (extremum->ts.kind < arg->ts.kind)
+	  extremum->ts.kind = arg->ts.kind;
 	if (mpfr_nan_p (extremum->value.real))
 	  {
 	    ret = 1;
diff --git a/gcc/fortran/trans-intrinsic.c b/gcc/fortran/trans-intrinsic.c
index fd8809902b7..2483f016d8e 100644
--- a/gcc/fortran/trans-intrinsic.c
+++ b/gcc/fortran/trans-intrinsic.c
@@ -4073,6 +4073,7 @@ gfc_conv_intrinsic_minmax (gfc_se * se, gfc_expr * expr, enum tree_code op)
   tree val;
   tree *args;
   tree type;
+  tree argtype;
   gfc_actual_arglist *argexpr;
   unsigned int i, nargs;

@@ -4082,16 +4083,24 @@ gfc_conv_intrinsic_minmax (gfc_se * se, gfc_expr * expr, enum tree_code op)
   gfc_conv_intrinsic_function_args (se, expr, args, nargs);
   type = gfc_typenode_for_spec (&expr->ts);

-  argexpr = expr->value.function.actual;
-  if (TREE_TYPE (args[0]) != type)
-    args[0] = convert (type, args[0]);
   /* Only evaluate the argument once.  */
   if (!VAR_P (args[0]) && !TREE_CONSTANT (args[0]))
     args[0] = gfc_evaluate_now (args[0], &se->pre);

-  mvar = gfc_create_var (type, "M");
-  gfc_add_modify (&se->pre, mvar, args[0]);
+  /* Determine suitable type of temporary, as a GNU extension allows
+     different argument kinds.  */
+  argtype = TREE_TYPE (args[0]);
+  argexpr = expr->value.function.actual;
+  for (i = 1, argexpr = argexpr->next; i < nargs; i++, argexpr = argexpr->next)
+    {
+      tree tmptype = TREE_TYPE (args[i]);
+      if (TYPE_PRECISION (tmptype) > TYPE_PRECISION (argtype))
+	argtype = tmptype;
+    }
+  mvar = gfc_create_var (argtype, "M");
+  gfc_add_modify (&se->pre, mvar, convert (argtype, args[0]));

+  argexpr = expr->value.function.actual;
   for (i = 1, argexpr = argexpr->next; i < nargs; i++, argexpr = argexpr->next)
     {
       tree cond = NULL_TREE;
@@ -4119,8 +4128,8 @@ gfc_conv_intrinsic_minmax (gfc_se * se, gfc_expr * expr, enum tree_code op)
 	 Also, there is no consensus among other tested compilers.  In
 	 short, it's a mess.  So lets just do whatever is fastest.  */
       tree_code code = op == GT_EXPR ? MAX_EXPR : MIN_EXPR;
-      calc = fold_build2_loc (input_location, code, type,
-			      convert (type, val), mvar);
+      calc = fold_build2_loc (input_location, code, argtype,
+			      convert (argtype, val), mvar);
       tmp = build2_v (MODIFY_EXPR, mvar, calc);

       if (cond != NULL_TREE)
@@ -4128,7 +4137,10 @@ gfc_conv_intrinsic_minmax (gfc_se * se, gfc_expr * expr, enum tree_code op)
 			build_empty_stmt (input_location));
       gfc_add_expr_to_block (&se->pre, tmp);
     }
-  se->expr = mvar;
+  if (TREE_CODE (type) == INTEGER_TYPE)
+    se->expr = fold_build1_loc (input_location, FIX_TRUNC_EXPR, type, mvar);
+  else
+    se->expr = convert (type, mvar);
 }


diff --git a/gcc/testsuite/gfortran.dg/min_max_kind.f90 b/gcc/testsuite/gfortran.dg/min_max_kind.f90
new file mode 100644
index 00000000000..b22691e1ffe
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/min_max_kind.f90
@@ -0,0 +1,15 @@
+! { dg-do run }
+! { dg-options "-O2 -std=gnu" }
+! Verify that the GNU extensions to MIN/MAX handle mixed kinds properly.
+
+program p
+  implicit none
+  integer(1), parameter :: i1 = 1
+  integer(2), parameter :: i2 = 2
+  real(4),    parameter :: r4 = 4
+  real(8),    parameter :: r8 = 8
+  if (kind (min (i1, i2)) /= kind (i2)) stop 1
+  if (kind (min (i2, i1)) /= kind (i2)) stop 2
+  if (kind (min (r4, r8)) /= kind (r8)) stop 3
+  if (kind (min (r8, r4)) /= kind (r8)) stop 4
+end program p
diff --git a/gcc/testsuite/gfortran.dg/pr96613.f90 b/gcc/testsuite/gfortran.dg/pr96613.f90
new file mode 100644
index 00000000000..2043c25fe1c
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr96613.f90
@@ -0,0 +1,15 @@
+! { dg-do run }
+! { dg-options "-O2 -std=gnu" }
+! PR fortran/96613 - Fix type/kind of temporaries evaluating MIN/MAX
+
+program test
+  implicit none
+  real :: x = 7.7643945e+09
+  real :: y = 6000.
+  integer :: ix
+
+  ix = min1 (5000.0, x)
+  if (ix /= 5000) stop 1
+  ix = min1 (y, x, 5555.d0)
+  if (ix /= 5555) stop 2
+end program

Reply via email to