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

            Bug ID: 128561
           Summary: rs6000: _mm_mullo_epi16 (x, 3) / 3 is folded to x
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: yuki.tanaka at defaultcase dot de
  Target Milestone: ---

#define NO_WARN_X86_INTRINSICS 1
#include <emmintrin.h>
#include <stdint.h>

typedef int16_t v8hi __attribute__((vector_size(16)));

__attribute__((noipa)) v8hi a(__m128i b)
{
  return (v8hi)_mm_mullo_epi16(b, _mm_set1_epi16(3)) / 3;
}

int main(void)
{
  int16_t in[8] = {0x5555, 0x7fff, 0x2aab, 1, 0, -1, -0x5555, 100};
  v8hi r = a(_mm_loadu_si128((__m128i *)in));

  for (int i = 0; i < 8; i++)
    {
      int16_t p = (int16_t)(uint16_t)(in[i] * 3);

      if (r[i] != p / 3)
        __builtin_abort();
    }

  return 0;
}

_mm_mullo_epi16 returns the low 16 bits of each product, so the multiplication
wraps. For the lane 0x5555, 0x5555 * 3 = 0xFFFF, which is -1 as int16_t, and -1
/ 3 = 0. The program must return normally.

With -O2 the function a is compiled to a bare blr: the multiplication and the
division are gone. The argument is returned unchanged, so lane 0 is 0x5555
instead of 0, and the check in main would abort. This happens on GCC 15.2.0,
16.1.0 and trunk. I do not have a PowerPC machine, so I could not run the
program, the result is derived from the generated code.

This is specific to PowerPC (powerpc64le, config/rs6000). The same program
without the NO_WARN_X86_INTRINSICS define passes on x86-64 with -O2.

The PowerPC header multiplies signed vectors: gcc/config/rs6000/emmintrin.h has
_mm_mullo_epi16 return (__m128i) ((__v8hi)__A * (__v8hi)__B). Signed overflow
of a vector element is undefined, so (x * 3) / 3 is folded to x. The x86 header
uses __v8hu here.

Compiler Explorer:
https://godbolt.org/z/1o9Pf6Woo

Reply via email to