https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80815

            Bug ID: 80815
           Summary: wrong code because of broken runtime alias check in
                    vectorizer
           Product: gcc
           Version: 8.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: amker at gcc dot gnu.org
  Target Milestone: ---

Hi,
I was suspecting that vect_prune_runtime_alias_check_list is broken, now I can
create a test case for it:
#include <stdio.h>

int arr[2048];

int
foo (int *a, int *b)
{
  int i;
  int *a1 = a;
  int *a0 = a1 - 512;
  for (i = 0; i < 500; i++)
    {
      *b = *a0 + *a1;
      b++;
      a0--;
      a1--;
    }
  return 0;
}

int main (void)
{
  int *a = &arr[1027];
  int *b = &arr[1024];

  int i;
  for (i = 0; i < 2048; i++)
    arr[i] = i;

  foo (a, b);

  for (i = 0; i < 2048; i++)
    fprintf (stdout, "%d: %d\n", i, arr[i]);

  return 0;
}

Compiled on x86_64 with below two commands:

Binary 1)
$ ../gcc -O2 -fno-inline ../x.c -o x.exe
$ ./x.exe >/tmp/x

Binary 2)
../gcc -Ofast -fno-inline ../x.c -o y.exe
$ ./y.exe >/tmp/y

We have:
$ diff /tmp/x /tmp/y
1027,1028c1027,1028
< 1026: 2053
< 1027: 2054
---
> 1026: 1538
> 1027: 1536

I think the root cause is in below code:

          /* Generally the new segment length is the maximum of the
             left segment size and the right segment size plus the distance.
             ???  We can also build tree MAX_EXPR here but it's not clear this
             is profitable.  */
          else if (tree_fits_uhwi_p (dr_a1->seg_len)
                   && tree_fits_uhwi_p (dr_a2->seg_len))
            {
              unsigned HOST_WIDE_INT seg_len_a1 = tree_to_uhwi
(dr_a1->seg_len);
              unsigned HOST_WIDE_INT seg_len_a2 = tree_to_uhwi
(dr_a2->seg_len);
              dr_a1->seg_len = size_int (MAX (seg_len_a1, diff + seg_len_a2));
              do_remove = true;
            }

Here dr_a1 is *a0, dr_a2 is *a1 and dr_b1/dr_b2 is *b, and their ranges is like

-------------------------------------------------------------->
    a0_en      a0        a1_end    b   a1       b_end

It should merge range_a0/range_a1 and result in range of a1, but because:
1) seg_len_a1 wraps to a large unsigned integer;
2) seg_len_a2 wraps to a large unsigned integer;
3) diff + seg_len_a2 wraps again to a small unsigned integer;
The program falsely merges ranges to range_a0.  As a result, loop is vectorized
because range_a0 and range_b are considered not alias to each other.  This is
wrong because *a1 and *b overlaps at the first several iterations and have
circle in dependence.

Reply via email to