https://gcc.gnu.org/g:cf40537f3afc4903a5278305db78b7675be4f97a
commit r13-9614-gcf40537f3afc4903a5278305db78b7675be4f97a Author: Richard Sandiford <richard.sandif...@arm.com> Date: Thu Apr 24 14:21:31 2025 +0100 vect: Enforce dr_with_seg_len::align precondition [PR116125] tree-data-refs.cc uses alignment information to try to optimise the code generated for alias checks. The assumption for "normal" non-grouped, full-width scalar accesses was that the access size would be a multiple of the alignment. As Richi notes in the PR, this is a documented precondition of dr_with_seg_len: /* The minimum common alignment of DR's start address, SEG_LEN and ACCESS_SIZE. */ unsigned int align; PR115192 was a case in which this assumption didn't hold. The access was part of an aligned 4-element group, but only the first 2 elements of the group were accessed. The alignment was therefore double the access size. In r15-820-ga0fe4fb1c8d78045 I'd "fixed" that by capping the alignment in one of the output routines. But I think that was misconceived. The precondition means that we should cap the alignment at source instead. Failure to do that caused a similar wrong code bug in this PR, where the alignment comes from a short bitfield access rather than from a group access. gcc/ PR tree-optimization/116125 * tree-vect-data-refs.cc (vect_prune_runtime_alias_test_list): Make the dr_with_seg_len alignment fields describe tha access sizes as well as the pointer alignment. * tree-data-ref.cc (create_intersect_range_checks): Don't compensate for invalid alignment fields here. gcc/testsuite/ PR tree-optimization/116125 * gcc.dg/vect/pr116125.c: New test. (cherry picked from commit e8651b80aeb86da935035e218747a6b41b611497) Diff: --- gcc/testsuite/gcc.dg/vect/pr116125.c | 30 ++++++++++++++++++++++++++++++ gcc/tree-data-ref.cc | 2 -- gcc/tree-vect-data-refs.cc | 10 +++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/gcc/testsuite/gcc.dg/vect/pr116125.c b/gcc/testsuite/gcc.dg/vect/pr116125.c new file mode 100644 index 000000000000..eab9efdc061b --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/pr116125.c @@ -0,0 +1,30 @@ +#include "tree-vect.h" + +struct st +{ + unsigned int num : 8; +}; + +void __attribute__((noipa)) +mem_overlap (struct st *a, struct st *b) +{ + for (int i = 0; i < 9; i++) + a[i].num = b[i].num + 1; +} + +int +main (void) +{ + check_vect (); + + struct st a[9] = {}; + + // input a = 0, 0, 0, 0, 0, 0, 0, 0, 0 + mem_overlap (&a[1], a); + + // output a = 0, 1, 2, 3, 4, 5, 6, 7, 8 + if (a[2].num == 2) + return 0; + else + __builtin_abort (); +} diff --git a/gcc/tree-data-ref.cc b/gcc/tree-data-ref.cc index 1a8edf8f9ab2..b6823d2bb16c 100644 --- a/gcc/tree-data-ref.cc +++ b/gcc/tree-data-ref.cc @@ -2636,8 +2636,6 @@ create_intersect_range_checks (class loop *loop, tree *cond_expr, if the maximum value of one segment is equal to the minimum value of the other. */ min_align = std::min (dr_a.align, dr_b.align); - min_align = std::min (min_align, known_alignment (dr_a.access_size)); - min_align = std::min (min_align, known_alignment (dr_b.access_size)); cmp_code = LT_EXPR; } diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc index a8b2c96a6679..a268a27b8c8d 100644 --- a/gcc/tree-vect-data-refs.cc +++ b/gcc/tree-vect-data-refs.cc @@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ +#define INCLUDE_ALGORITHM #include "config.h" #include "system.h" #include "coretypes.h" @@ -3662,7 +3663,7 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo) poly_uint64 lower_bound; tree segment_length_a, segment_length_b; unsigned HOST_WIDE_INT access_size_a, access_size_b; - unsigned int align_a, align_b; + unsigned HOST_WIDE_INT align_a, align_b; /* Ignore the alias if the VF we chose ended up being no greater than the dependence distance. */ @@ -3819,6 +3820,13 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo) stmt_info_b->stmt); } + /* dr_with_seg_len requires the alignment to apply to the segment length + and access size, not just the start address. The access size can be + smaller than the pointer alignment for grouped accesses and bitfield + references; see PR115192 and PR116125 respectively. */ + align_a = std::min (align_a, least_bit_hwi (access_size_a)); + align_b = std::min (align_b, least_bit_hwi (access_size_b)); + dr_with_seg_len dr_a (dr_info_a->dr, segment_length_a, access_size_a, align_a); dr_with_seg_len dr_b (dr_info_b->dr, segment_length_b,