Dear all,

while looking at details of a related but slightly different PR, I found
that we did evaluate the arguments to MINLOC/MAXLOC too often in the
inlined version.

The attached patch creates temporaries for array elements where needed,
and ensures that each array element is only touched once.  This required
a minor adjustment for the rank-1 algorithm, which is documented.

Regtested on x86_64-pc-linux-gnu.  OK for mainline?

Thanks,
Harald

From d2a52256b3e4817e16a5d222c2fecd7bc66e5613 Mon Sep 17 00:00:00 2001
From: Harald Anlauf <anl...@gmx.de>
Date: Wed, 22 Jan 2025 22:44:39 +0100
Subject: [PATCH] Fortran: do not evaluate arguments of MAXVAL/MINVAL too often
 [PR118613]

	PR fortran/118613

gcc/fortran/ChangeLog:

	* trans-intrinsic.cc (gfc_conv_intrinsic_minmaxval): Adjust algorithm
	for inlined version of MINLOC and MAXLOC so that arguments are only
	evaluted once, and create temporaries where necessary.  Document
	change of algorithm.

gcc/testsuite/ChangeLog:

	* gfortran.dg/maxval_arg_eval_count.f90: New test.
---
 gcc/fortran/trans-intrinsic.cc                | 37 +++++++-
 .../gfortran.dg/maxval_arg_eval_count.f90     | 88 +++++++++++++++++++
 2 files changed, 121 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/maxval_arg_eval_count.f90

diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index afbec5b2752..51237d0d3be 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -6409,8 +6409,16 @@ gfc_conv_intrinsic_findloc (gfc_se *se, gfc_expr *expr)
       nonempty = false;
       S = from;
       while (S <= to) {
-	if (mask[S]) { nonempty = true; if (a[S] <= limit) goto lab; }
-	S++;
+	if (mask[S]) {
+	  nonempty = true;
+	  if (a[S] <= limit) {
+	    limit = a[S];
+	    S++;
+	    goto lab;
+	  }
+	else
+	  S++;
+	}
       }
       limit = nonempty ? NaN : huge (limit);
       lab:
@@ -6419,7 +6427,15 @@ gfc_conv_intrinsic_findloc (gfc_se *se, gfc_expr *expr)
       at runtime whether array is nonempty or not, rank 1:
       limit = Infinity;
       S = from;
-      while (S <= to) { if (a[S] <= limit) goto lab; S++; }
+      while (S <= to) {
+	if (a[S] <= limit) {
+	  limit = a[S];
+	  S++;
+	  goto lab;
+	  }
+	else
+	  S++;
+      }
       limit = (from <= to) ? NaN : huge (limit);
       lab:
       while (S <= to) { limit = min (a[S], limit); S++; }
@@ -6710,6 +6726,7 @@ gfc_conv_intrinsic_minmaxval (gfc_se * se, gfc_expr * expr, enum tree_code op)
   gfc_copy_loopinfo_to_se (&arrayse, &loop);
   arrayse.ss = arrayss;
   gfc_conv_expr_val (&arrayse, arrayexpr);
+  arrayse.expr = gfc_evaluate_now (arrayse.expr, &arrayse.pre);
   gfc_add_block_to_block (&block, &arrayse.pre);
 
   gfc_init_block (&block2);
@@ -6722,7 +6739,18 @@ gfc_conv_intrinsic_minmaxval (gfc_se * se, gfc_expr * expr, enum tree_code op)
       tmp = fold_build2_loc (input_location, op == GT_EXPR ? GE_EXPR : LE_EXPR,
 			     logical_type_node, arrayse.expr, limit);
       if (lab)
-	ifbody = build1_v (GOTO_EXPR, lab);
+	{
+	  stmtblock_t ifblock;
+	  tree inc_loop;
+	  inc_loop = fold_build2_loc (input_location, PLUS_EXPR,
+				      TREE_TYPE (loop.loopvar[0]),
+				      loop.loopvar[0], gfc_index_one_node);
+	  gfc_init_block (&ifblock);
+	  gfc_add_modify (&ifblock, limit, arrayse.expr);
+	  gfc_add_modify (&ifblock, loop.loopvar[0], inc_loop);
+	  gfc_add_expr_to_block (&ifblock, build1_v (GOTO_EXPR, lab));
+	  ifbody = gfc_finish_block (&ifblock);
+	}
       else
 	{
 	  stmtblock_t ifblock;
@@ -6816,6 +6844,7 @@ gfc_conv_intrinsic_minmaxval (gfc_se * se, gfc_expr * expr, enum tree_code op)
       gfc_copy_loopinfo_to_se (&arrayse, &loop);
       arrayse.ss = arrayss;
       gfc_conv_expr_val (&arrayse, arrayexpr);
+      arrayse.expr = gfc_evaluate_now (arrayse.expr, &arrayse.pre);
       gfc_add_block_to_block (&block, &arrayse.pre);
 
       /* MIN_EXPR/MAX_EXPR has unspecified behavior with NaNs or
diff --git a/gcc/testsuite/gfortran.dg/maxval_arg_eval_count.f90 b/gcc/testsuite/gfortran.dg/maxval_arg_eval_count.f90
new file mode 100644
index 00000000000..1c1a04819a0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/maxval_arg_eval_count.f90
@@ -0,0 +1,88 @@
+! { dg-do run }
+!
+! PR fortran/118613 - check argument evaluation count of MAXVAL
+
+program p
+  implicit none
+  integer, parameter :: k = 2
+  integer :: n
+  integer :: i1(k*k), i2(k,k), mm
+  real    :: a1(k*k), a2(k,k), mx
+  complex :: c1(k*k), c2(k,k)
+  logical :: m1(k*k), m2(k,k)
+
+  ! prepare mask for masked variants
+  m1 = .true.
+  m2 = .true.
+  i1 = 0
+  i2 = 0
+  a1 = 0.
+  a2 = 0.
+  c1 = 0.
+  c2 = 0.
+
+  ! integer
+  n = 0
+  mm = maxval (h(i1))
+  if (n /= k*k .or. mm /= 0) stop 1
+  n = 0
+  mm = maxval (h(i2))
+  if (n /= k*k .or. mm /= 0) stop 2
+  n = 0
+  mm = maxval (h(i1),m1)
+  if (n /= k*k .or. mm /= 0) stop 3
+  n = 0
+  mm = maxval (h(i2),m2)
+  if (n /= k*k .or. mm /= 0) stop 4
+
+  ! real
+  n = 0
+  mx = maxval (f(a1))
+  if (n /= k*k .or. mx /= 0) stop 5
+  n = 0
+  mx = maxval (f(a2))
+  if (n /= k*k .or. mx /= 0) stop 6
+  n = 0
+  mx = maxval (f(a1),m1)
+  if (n /= k*k .or. mx /= 0) stop 7
+  n = 0
+  mx = maxval (f(a2),m2)
+  if (n /= k*k .or. mx /= 0) stop 8
+
+  ! complex
+  n = 0
+  mx = maxval (g(c1))
+  if (n /= k*k .or. mx /= 0) stop 9
+  n = 0
+  mx = maxval (g(c2))
+  if (n /= k*k .or. mx /= 0) stop 10
+  n = 0
+  mx = maxval (g(c1),m1)
+  if (n /= k*k .or. mx /= 0) stop 11
+  n = 0
+  mx = maxval (g(c2),m2)
+  if (n /= k*k .or. mx /= 0) stop 12
+
+contains
+
+  impure elemental function h (x)
+    integer, intent(in) :: x
+    integer             :: h
+    h = abs (x)
+    n = n + 1   ! Count number of function evaluations
+  end
+
+  impure elemental function f (x)
+    real, intent(in) :: x
+    real             :: f
+    f = abs (x)
+    n = n + 1   ! Count number of function evaluations
+  end
+
+  impure elemental function g (x)
+    complex, intent(in) :: x
+    real                :: g
+    g = abs (x)
+    n = n + 1   ! Count number of function evaluations
+  end
+end
-- 
2.43.0

Reply via email to