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

            Bug ID: 128142
           Summary: [16/17 Regression] Vector (x + y) >> 1 folded to
                    .AVG_FLOOR although the signed sum can wrap with
                    -fwrapv
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ishevlyakov at gcc dot gnu.org
  Target Milestone: ---

The match.pd fold of a vector (x + y) >> 1 into IFN_AVG_FLOOR (x, y),
added in r16-1112-g7bb1933c1fc, checks that each element of x and y has
a leading zero bit.  For an unsigned type that keeps x + y from
wrapping.  For a signed type whose overflow is defined (-fwrapv) it
does not: two nonnegative values can add up to a negative one, and the
shift then gives a negative result, while IFN_AVG_FLOOR adds without
truncation and gives a positive one.

typedef int v4si __attribute__ ((vector_size (16)));

__attribute__ ((noipa)) v4si
f (v4si a, v4si b)
{
  return ((a & 0x7fffffff) + (b & 0x7fffffff)) >> 1;
}

int
main ()
{
  v4si a = { 0x7fffffff, 0x7fffffff, 0x40000000, 3 };
  v4si b = { 0x7fffffff, 1, 0x40000000, 4 };
  v4si r = f (a, b);
  if (r[0] != -1 || r[1] != -0x40000000 || r[2] != -0x40000000 || r[3] != 3)
    __builtin_abort ();
  return 0;
}

With -O2 -fwrapv, trunk compiles f for aarch64 to

        bic     v1.4s, #128, lsl #24
        bic     v0.4s, #128, lsl #24
        shadd   v0.4s, v0.4s, v1.4s
        ret

so r[0] is 0x7fffffff instead of -1, and r[1] and r[2] are 0x40000000
instead of -0x40000000.  The optimized dump shows .AVG_FLOOR.  GCC 15
keeps the add and the arithmetic shift.

Without -fwrapv the fold is valid, since a wrapping signed sum would be
undefined.  With -fwrapv, a signed type needs two leading zero bits in
each operand for x + y not to wrap.

Reply via email to