Skip to content

x86/avx512/dpbf16: avoid GCC's broken 512-bit masked intrinsics#1419

Open
fo40225 wants to merge 1 commit into
simd-everywhere:masterfrom
fo40225:fix_dpbf16
Open

x86/avx512/dpbf16: avoid GCC's broken 512-bit masked intrinsics#1419
fo40225 wants to merge 1 commit into
simd-everywhere:masterfrom
fo40225:fix_dpbf16

Conversation

@fo40225

@fo40225 fo40225 commented Jul 19, 2026

Copy link
Copy Markdown

Problem

x86/avx512/dpbf16/native/{c,cpp} fails on CI runners that support AVX512-BF16 (GitHub's runner fleet now includes Zen4 / Emerald Rapids / Granite Rapids machines, so -march=native builds exercise the real intrinsics).

The failures are always in the mm512_mask_/mm512_maskz_ subtests, always in lanes 8-15, and the wrong values are exactly src-passthrough (mask form) or zero (maskz form):

test/x86/avx512/dpbf16.c:1003: assertion failed: r[10] ~= ... (-2.320000 ~= 102.099998)
test/x86/avx512/dpbf16.c:1192: assertion failed: r[9]  ~= ... (0.000000 ~= -1506.540039)

Root cause

A GCC code-generation bug, not a SIMDe emulation bug.

GCC's insn patterns avx512f_dpbf16ps_<mode>_mask{,z} declare the mask operand with <avx512fmaskhalfmode>, which is QImode for V16SF, so _mm512_{mask,maskz}_dpbf16_ps load their __mmask16 argument with kmovb and silently drop the upper 8 mask bits:

f_mask:                                # _mm512_mask_dpbf16_ps
    kmovb   %edi, %k1                  # <-- should be kmovw
    vdpbf16ps  %zmm2, %zmm1, %zmm0{%k1}

The bug is present in every GCC release with AVX512BF16 support (GCC through at least GCC 16.1, still present on trunk).

clang emits kmovd and is unaffected.

This is also the actual root cause of issue 1095 : the emulated implementation was never wrong, so no test-vector regeneration is needed (vectors regenerated on hardware through GCC's masked intrinsics would bake this GCC bug into the expected values of the masked tests).

Fix

For GCC, implement the two 512-bit masked variants as the unmasked native vdpbf16ps plus mask_mov/maskz_mov.

GCC compiles this to a correct, still fully native kmovw + vdpbf16ps + masked vmovaps sequence, so the cost is a single blend instruction.

The 128/256-bit variants take a __mmask8, for which kmovb is correct, and are left untouched.

Verification

  • Real hardware: on an AMD EPYC 9474F (zen4, avx512_bf16) with GCC 16.1.0, a minimal reproducer shows lanes 8-15 wrong for both masked forms before the fix; the SIMDe test built from master reproduces the CI failures byte for byte, and the test built from this branch passes all 9 dpbf16 subtests.
  • Intel SDE (-spr): lane-identical behavior to the AMD hardware, confirming the bug lives in GCC's generated code rather than in either vendor's silicon.
  • No emulation regressions: dpbf16 native/emul x C/C++ pass locally, and the emulated paths were regression-swept with GCC and clang across aarch64, armhf, ppc64le, ppc32, riscv64, s390x and mips64el at -O0/-O1/-O2/-O3.

Follow-up

This should be reported to GCC bugzilla (no existing report found); the likely fix there is changing the mask operand of the avx512f_dpbf16ps_<mode>_mask{,z} patterns in gcc/config/i386 from <avx512fmaskhalfmode> to <avx512fmaskmode>.

Once a fixed GCC release exists, the guard here can become version-gated.

GCC's insn pattern for the masked/maskz forms of vdpbf16ps declares the
mask operand with the half-width mask mode (<avx512fmaskhalfmode>), so
_mm512_{mask,maskz}_dpbf16_ps load the __mmask16 argument with kmovb
and silently drop the upper 8 mask bits.  Lanes 8-15 then behave as if
their mask bit were clear: the mask form passes src through and the
maskz form zeroes the lane.  The bug is present in every GCC release
with AVX512BF16 support, GCC through at least GCC 16.1; clang is
unaffected.

This went unnoticed until CI runners gained AVX512-BF16 hardware
(GitHub's runner fleet now includes Zen4/Zen5), where -march=native
builds exercise the real intrinsics:

  test/x86/avx512/dpbf16.c:1003: assertion failed:
    r[10] ~= ... (-2.320000 ~= 102.099998)   [mask: src passthrough]
  test/x86/avx512/dpbf16.c:1192: assertion failed:
    r[9] ~= ... (0.000000 ~= -1506.540039)   [maskz: zeroed]

Route GCC around the buggy intrinsics via the unmasked native
vdpbf16ps plus mask_mov/maskz_mov, which GCC compiles to a correct
kmovw + vdpbf16ps + masked vmovaps sequence.  The 128/256-bit variants
take a __mmask8 and are unaffected.

Verified on an AMD EPYC 9474F (zen4, avx512_bf16) with GCC 16.1.0:
before this change the masked intrinsics compute lanes 8-15
incorrectly, matching the CI failures byte for byte; after it all
dpbf16 tests pass.  Intel SDE (-spr) shows lane-identical behavior,
confirming the bug is in GCC's codegen, not the hardware.

@mr-c mr-c left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for the investigation and fix @fo40225 !

We'll need a GCC bug number so a !defined(SIMDE_BUG_GCC_126NNN guard can be used instead of !defined(HEDLEY_GCC_VERSION).

The guard needs to be added to simde/simde-common.h alongside the others; that way, after fixes for various GCC versions are released, that can be implemented in one place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants