For the vectorization of non-contiguous memory accesses such as the
vectorization of loads from a particular struct member, specifically
when vectorizing with unknown bounds (thus using a pointer and not an
array) it is observed that inadequate alignment checking allows for
the crossing of a page boundary within a single vectorized loop
iteration. This leads to potential segmentation faults in the
resulting binaries.
For example, for the given datatype:
typedef struct {
uint64_t a;
uint64_t b;
uint32_t flag;
uint32_t pad;
} Data;
and a loop such as:
int
foo (Data *ptr) {
if (ptr == NULL)
return -1;
int cnt;
for (cnt = 0; cnt < MAX; cnt++) {
if (ptr->flag == 0)
break;
ptr++;
}
return cnt;
}
the vectorizer yields the following loop logic on armhf:
<bb 1>:
_41 = ptr_4(D) + 16;
<bb 2>:
_44 = MEM[(unsigned int *)ivtmp_42];
ivtmp_45 = ivtmp_42 + 24;
_46 = MEM[(unsigned int *)ivtmp_45];
ivtmp_47 = ivtmp_45 + 24;
_48 = MEM[(unsigned int *)ivtmp_47];
ivtmp_49 = ivtmp_47 + 24;
_50 = MEM[(unsigned int *)ivtmp_49];
vect_cst__51 = {_44, _46, _48, _50};
mask_patt_6.17_52 = vect_cst__51 == { 0, 0, 0, 0 };
if (mask_patt_6.17_52 != { 0, 0, 0, 0 })
goto <bb 4>;
else
ivtmp_43 = ivtmp_42 + 96;
goto <bb 2>;
<bb4>
...
without any proper address alignment checks on the starting address
or on whether alignment is preserved across iterations.
Reject the vectorization of such cases.
gcc/ChangeLog:
PR tree-optimization/124037
* tree-vect-stmts.cc (get_load_store_type): Add check for use
context of VMAT_ELEMENTWISE loads.
gcc/testsuite/ChangeLog:
* gcc.dg/vect/pr123588.c: New.
---
gcc/testsuite/gcc.dg/vect/pr124037.c | 57 ++++++++++++++++++++++++++++
gcc/tree-vect-stmts.cc | 13 +++++++
2 files changed, 70 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/vect/pr124037.c
diff --git a/gcc/testsuite/gcc.dg/vect/pr124037.c
b/gcc/testsuite/gcc.dg/vect/pr124037.c
new file mode 100644
index 00000000000..3121380454b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/pr124037.c
@@ -0,0 +1,57 @@
+/* PR tree-optimization/124037 */
+/* { dg-options "-O3" } */
+/* { dg-require-effective-target mmap } */
+/* { dg-require-effective-target vect_early_break } */
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define MAX 65536
+
+typedef struct {
+ uint64_t a;
+ uint64_t b;
+ uint32_t flag;
+ uint32_t pad;
+} Data;
+
+int __attribute__ ((noinline))
+foo (Data *ptr) {
+ if (ptr == NULL)
+ return -1;
+
+ int cnt;
+ for (cnt = 0; cnt < MAX; cnt++) {
+ if (ptr->flag == 0)
+ break;
+ ptr++;
+ }
+ return cnt;
+}
+
+int main() {
+ long pgsz = sysconf (_SC_PAGESIZE);
+ if (pgsz == -1) {
+ fprintf (stderr, "sysconf failed\n");
+ return 0;
+ }
+
+ /* Allocate 2 consecutive pages. */
+ void *mem = mmap (NULL, pgsz * 2, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mem == MAP_FAILED) {
+ fprintf (stderr, "mmap failed\n");
+ return 0;
+ }
+
+ memset (mem, 1, pgsz);
+ uint8_t *ptr = (uint8_t*) mem + pgsz;
+ memset (ptr - 16, 0, 16);
+
+ mprotect (ptr, pgsz, PROT_NONE);
+ Data *data = (Data *) (ptr - 80);
+ __builtin_printf("%d\n", foo(data));
+}
+
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index ee98e72d1e5..7434d934463 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -2581,6 +2581,19 @@ get_load_store_type (vec_info *vinfo, stmt_vec_info
stmt_info,
return false;
}
+ if (loop_vinfo
+ && !inbounds
+ && dr_safe_speculative_read_required (stmt_info)
+ && *memory_access_type == VMAT_ELEMENTWISE
+ && !STMT_VINFO_GATHER_SCATTER_P (stmt_info))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "unsafe use of elementwise access for buffer with"
+ "unknown bounds\n");
+ return false;
+ }
+
/* If this DR needs alignment for correctness, we must ensure the target
alignment is a constant power-of-two multiple of the amount read per
vector iteration or force masking. */
--
2.43.0