With this patch the float -> __bf16 conversion still gives a different
result with and without AVX512BF16 for the same float input:

/* { dg-do run } */
/* { dg-options "-O2 -ffast-math -march=x86-64" } */

#include <stdio.h>
#include <stdint.h>
#include <string.h>

/* Both functions are just "return a;".  FOO is compiled for plain
   x86-64, so it uses the inline expansion; BAR uses vcvtneps2bf16.  */

__attribute__ ((noipa, noinline))
static __bf16
foo (float a)
{
  return a;
}

__attribute__ ((noipa, noinline, target ("avx512vl,avx512bf16")))
static __bf16
bar (float a)
{
  return a;
}

static uint16_t
bits16 (__bf16 x)
{
  uint16_t r;
  memcpy (&r, &x, sizeof (r));
  return r;
}

int
main (void)
{
  uint32_t v[] = { 0x007f8000, 0x807f8000, 0x007fffff, 0x807fffff,
                   0x007f7fff, 0x00367000, 0x00800000 };

  for (unsigned i = 0; i < sizeof v / sizeof v[0]; i++)
    {
      float f;
      memcpy (&f, &v[i], sizeof (f));
      uint16_t a = bits16 (foo (f));
      uint16_t b = bits16 (bar (f));
      printf ("0x%08x  foo=0x%04x  bar=0x%04x  %s\n",
              v[i], a, b, a == b ? "same" : "DIFFER");
    }
  return 0;
}

On AVX512BF16 hardware this prints:

0x007f8000  foo=0x0080  bar=0x0000  DIFFER
0x807f8000  foo=0x8080  bar=0x8000  DIFFER
0x007fffff  foo=0x0080  bar=0x0000  DIFFER
0x807fffff  foo=0x8080  bar=0x8000  DIFFER
0x007f7fff  foo=0x0000  bar=0x0000  same
0x00367000  foo=0x0000  bar=0x0000  same
0x00800000  foo=0x0080  bar=0x0080  same

These are SFmode denormals: vcvtneps2bf16 returns a sign preserving zero,
the inline expansion returns +-0x0080, i.e. +-2^-126.  Sweeping the whole
SFmode denormal range, 65536 inputs differ, the 32768 mantissas in
[0x7f8000, 0x7fffff] for each sign.

The reason is that the flush test is applied to the rounded result rather
than to the input:

> +  /* TMP1 is zero or denormal if (TMP1 & 0x7f80) == 0.  */
> +  tmp0 = expand_simple_binop (SImode, AND, tmp1, GEN_INT(0x7f80),
> +                              nullptr, 0, OPTAB_DIRECT);

Testing REG1_SI & 0x7f800000 instead makes the two agree for all 2^32
non-NaN inputs.

________________________________________
From: H.J. Lu <[email protected]>
Sent: Sunday, 23 August 2026 23:20:50
To: GCC Patches; Uros Bizjak; Liu, Hongtao
Subject: [v2 PATCH] x86: Expand the default truncsfbf2 like vcvtneps2bf16

On Wed, Aug 19, 2026 at 9:51 PM H.J. Lu <[email protected]> wrote:
>
> Expand the default truncsfbf2 like vcvtneps2bf16, which doesn't honor
> SNAN, turns sNAN into qNAN quietly, it always rounds to nearest even
> and flushes denormals to zero, with
>
> (fromi + 0x7fff + ((fromi >> 16) & 1)) >> 16
>
> and flush denormals to zero.
>
> gcc/
>
> PR target/126933
> * config/i386/i386-expand.cc (ix86_expand_truncsfbf2): New.
> * config/i386/i386-protos.h (ix86_expand_truncsfbf2): Likewise.
> * config/i386/i386.md (truncsfbf2): Changed to define_expand.
> (truncsfbf2_vcvtneps2bf16): New.
>
> gcc/testsuite/
>
> PR target/126933
> * gcc.target/i386/truncsfbf-1.c (dg-options): Add
> -mno-avxneconvert -mno-avx512bf16 -fno-asynchronous-unwind-tables.
> Use check-function-bodies to check updated codegen.
> * gcc.target/i386/truncsfbf-2.c (dg-options): Add
> -mno-avxneconvert -mno-avx512bf16.
> (foo): Make it static with __attribute__ ((noipa, noinline)).
> (CALC): Add __attribute__ ((noipa, noinline)).  Flush denormal
> to zero.
> * gcc.target/i386/truncsfbf-3.c: New test.
> * gcc.target/i386/truncsfbf-4.c: Likewise.
> * gcc.target/i386/truncsfbf-5.c: Likewise.
> * gcc.target/i386/truncsfbf-6.c: Likewise.
>

truncsfbf2 should behave the same with -funsafe-math-optimizations
regardless if AVXNECONVERT or AVX512BF16 are available or not.

Changes in v2:

1. Remove duplicated codes in ix86_expand_truncsfbf2
2. Scan cmov, instead of branch, in gcc.target/i386/truncsfbf-1.c.


--
H.J.

Reply via email to