The reuse-volume calculations can wrap to zero when their results
exceed the range of unsigned, leading to incorrect prefetch and
non-temporal store decisions. Use saturating multiplication to cap
these estimates at UINT_MAX.

gcc/ChangeLog:

        PR tree-optimization/128232
        * tree-ssa-loop-prefetch.cc (saturating_mul): New function.
        (volume_of_dist_vector): Use it.
        (determine_loop_nest_reuse): Likewise.

gcc/testsuite/ChangeLog:

        PR tree-optimization/128232
        * gcc.dg/tree-ssa/pr128232-1.c: New test.
        * gcc.dg/tree-ssa/pr128232-2.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/pr128232-1.c | 26 ++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr128232-2.c | 16 ++++++++++++++++
 gcc/tree-ssa-loop-prefetch.cc              | 18 ++++++++++++++++--
 3 files changed, 58 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr128232-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr128232-2.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr128232-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr128232-1.c
new file mode 100644
index 00000000000..ee3c6463e98
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr128232-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-require-effective-target double64 } */
+/* { dg-options "-O2 -fno-tree-vectorize -fprefetch-loop-arrays --param 
l1-cache-line-size=64 --param l2-cache-size=512 -fdump-tree-aprefetch-details" 
} */
+
+/* The initial volume estimate, 64 * N * N = 4 GiB, must not wrap to zero.
+   Self-reuse reduces the saturated estimate to 64 * N = 512 KiB.  */
+#define N 8192
+
+double a[N][N];
+
+double
+sum_columns (void)
+{
+  unsigned i, j;
+  double sum = 0;
+
+  for (i = 0; i < N; i++)
+    for (j = 0; j < N; j++)
+      sum += a[j][i];
+
+  return sum;
+}
+
+/* { dg-final { scan-tree-dump-times {reference [0-9]+:[0-9]+ distance 524288} 
1 "aprefetch" } } */
+/* { dg-final { scan-tree-dump-not {reference [0-9]+:[0-9]+ distance 0(\n|$)} 
"aprefetch" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr128232-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr128232-2.c
new file mode 100644
index 00000000000..0925c5f6928
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr128232-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-O2 -fno-tree-vectorize -fno-tree-loop-distribute-patterns 
-fprefetch-loop-arrays -fdump-tree-aprefetch-details" } */
+
+/* Each iteration reads and writes a 4-byte int.  The 4 GiB total volume
+   must not wrap to zero and suppress non-temporal stores.  */
+
+void
+store (int *__restrict b, const int *__restrict c, int scalar)
+{
+  for (unsigned long j = 0; j < 536870912UL; ++j)
+    b[j] = scalar * c[j];
+}
+
+/* { dg-final { scan-tree-dump-times {reference [0-9]+:[0-9]+ distance 
4294967295} 2 "aprefetch" } } */
+/* { dg-final { scan-tree-dump-not {reference [0-9]+:[0-9]+ distance 0(\n|$)} 
"aprefetch" } } */
diff --git a/gcc/tree-ssa-loop-prefetch.cc b/gcc/tree-ssa-loop-prefetch.cc
index c5437cfdc84..ab9824c48f3 100644
--- a/gcc/tree-ssa-loop-prefetch.cc
+++ b/gcc/tree-ssa-loop-prefetch.cc
@@ -1470,6 +1470,20 @@ volume_of_references (struct mem_ref_group *refs)
   return volume;
 }
 
+/* Return VOLUME * COUNT, saturating at the largest representable reuse
+   distance.  Distances above the L2 cache size are equivalent for
+   the reuse analysis, but wrapping them could make a streaming access look
+   cache-resident.  Check before multiplying so even a wide COUNT is safe.  */
+
+static unsigned
+saturating_mul (unsigned volume, unsigned HOST_WIDE_INT count)
+{
+  if (volume != 0 && count > UINT_MAX / volume)
+    return UINT_MAX;
+
+  return volume * count;
+}
+
 /* Returns the volume of memory references accessed across VEC iterations of
    loops, whose sizes are described in the LOOP_SIZES array.  N is the number
    of the loops in the nest (length of VEC and LOOP_SIZES vectors).  */
@@ -1490,7 +1504,7 @@ volume_of_dist_vector (lambda_vector vec, unsigned 
*loop_sizes, unsigned n)
 
   /* We ignore the parts of the distance vector in subloops, since usually
      the numbers of iterations are much smaller.  */
-  return loop_sizes[i] * vec[i];
+  return saturating_mul (loop_sizes[i], vec[i]);
 }
 
 /* Add the steps of ACCESS_FN multiplied by STRIDE to the array STRIDE
@@ -1655,7 +1669,7 @@ determine_loop_nest_reuse (class loop *loop, struct 
mem_ref_group *refs,
       vol = estimated_stmt_executions_int (aloop);
       if (vol == -1)
        vol = expected_loop_iterations (aloop);
-      volume *= vol;
+      volume = saturating_mul (volume, vol);
     }
 
   /* Prepare the references in the form suitable for data dependence
-- 
2.34.1


Reply via email to