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

            Bug ID: 128614
           Summary: [14/15/16/17 Regression] combine splits a masked
                    AVX-512 add into an unmasked add and a masked store,
                    raising FP exceptions in masked-off lanes
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: lzy20000419 at outlook dot com
  Target Milestone: ---

Fails with trunk (gcc 17.0.0 20261110, 33ed38d4ee09) and with 9.1.0 through
16.2.0. 8.5.0 and older keep the mask on the add.
Target: x86_64-*-* with -mavx512f.
Godbolt (trunk vs 8.5.0): https://godbolt.org/z/r7fE4ab5o

_mm512_mask_add_pd must not compute the masked-off lanes. When the result is
stored back to the merge operand's address, combine turns the masked add into
an unmasked add followed by a masked store, so the masked-off lanes are still
computed and raise FE_OVERFLOW.

    #include <fenv.h>
    #include <immintrin.h>

    __attribute__((noipa)) void
    f (__m512d *a, const __m512d *b, __mmask8 k, double w)
    {
      *a = _mm512_mask_add_pd (*a, k, *b, _mm512_set1_pd (w));
    }

    int
    main (void)
    {
      __attribute__((aligned (64))) double a[8], b[8];
      for (int i = 0; i < 8; i++)
        {
          a[i] = 1.0;
          b[i] = (i & 1) ? 1.0 : 1e308;
        }
      feclearexcept (FE_ALL_EXCEPT);
      f ((__m512d *) a, (const __m512d *) b, (__mmask8) 0xaa, 1e308);
      if (fetestexcept (FE_OVERFLOW))
        __builtin_abort ();
      return 0;
    }

    gcc -O2 -mavx512f t.c -lm && ./a.out

Observed: abort (FE_OVERFLOW is set by the masked-off lanes). Expected: exit 0.
-O1, -O2, -O3 and -Os abort; -O0 and -O2 -fdisable-rtl-combine exit 0. This was
run on AVX-512 hardware.

Trunk -O2 code for f:

    vbroadcastsd    %xmm0, %zmm0
    vaddpd  (%rsi), %zmm0, %zmm0
    kmovw   %edx, %k1
    vmovapd %zmm0, (%rdi){%k1}

8.5.0 emits vaddpd (%rsi), %zmm0, %zmm1{%k1} followed by an unmasked store.

The same split happens on trunk for _mm256_mask_fmadd_pd and _mm_mask_add_pd
(-mavx512vl). _mm512_mask_fmadd_pd was split the same way through 15.3.0. In
16.1.0 and later that combination is rejected on cost, but the split itself is
still allowed.

Cause: the expander correctly emits (set mem (vec_merge (plus ...) mem mask)).
That insn does not match because the masked patterns need a register
destination. find_split_point then splits out the trapping PLUS as a separate
unmasked insn, and the masked store matches. The only trap check on this split
in try_combine requires cfun->can_throw_non_call_exceptions, so under default
-ftrapping-math nothing prevents it.

Possible fix (not tested): do not split a may_trap_p operation out of a
VEC_MERGE when flag_trapping_math is set.

Related: PR128221 (a different case of the same symptom, in the cond_fma
expanders).

Disclaimer: This test case was found with LLM-assisted targeted testing, and
the report was written with LLM assistance.

Reply via email to