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.