On Mon, Aug 24, 2026 at 5:50 PM Liu, Hongtao <[email protected]> wrote:
>
>
>
> > -----Original Message-----
> > From: H.J. Lu <[email protected]>
> > Sent: Monday, August 24, 2026 4:48 PM
> > To: Liu, Hongtao <[email protected]>
> > Cc: GCC Patches <[email protected]>; Uros Bizjak
> > <[email protected]>
> > Subject: [v4 PATCH] x86: Expand the default truncsfbf2 like vcvtneps2bf16
> >
> > On Mon, Aug 24, 2026 at 4:31 PM H.J. Lu <[email protected]> wrote:
> > >
> > > On Mon, Aug 24, 2026 at 3:44 PM Liu, Hongtao <[email protected]>
> > wrote:
> > > >
> > > > 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.
> > > >
> >
> > Here is the v4 patch:
> >
> > 1. Mark input denormal branch as unlikely.
> > 2. Remove -mtune=generic from truncsfbf-1.c.
> Ok.
>
> >
> > > Fixed in the v3 patch with tests.
> > >
> > > Changes in v3:
> > >
> > > 1. Test (REG1_SI & 0x7f800000) == 0 for input denormals.
> > > 2. Add tests for input denormals.
> > > 3. Update expected codegen in truncsfbf-1.c.
> > >
> > > > Changes in v2:
> > > >
> > > > 1. Remove duplicated codes in ix86_expand_truncsfbf2 2. Scan cmov,
> > > > instead of branch, in gcc.target/i386/truncsfbf-1.c.
> > > >
> > >
> > > Thanks.
> > >
> > > --
> > > H.J.
> > > ---
> > > Expand default truncsfbf2 like vcvtneps2bf16, which doesn't honor
> > > SNAN, turns sNAN into qNAN quietly, it always rounds to nearest even
> > > and flushes input denormals to zero, with
> > >
> > > (fromi + 0x7fff + ((fromi >> 16) & 1)) >> 16
> > >
> > > and flush input 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 and
> > > update comments.
> > > (truncsfbf2_vcvtneps2bf16): New.
> > >
> > > gcc/testsuite/
> > >
> > > PR target/126933
> > > * gcc.target/i386/truncsfbf-1.c (dg-options): Add -mno-avxneconvert
> > > -mno-avx512bf16.  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 input denormal
> > > to zero.
> > > (main): Add tests for float denormal inputs.
> > > * 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.
> >
> >
> >
> > --
> > H.J.

I am checking in this patch to correct vcvtneps2bf16 emulation
in truncsfbf-5.c and truncsfbf-6.c.

-- 
H.J.
---
Update vcvtneps2bf16 emulation in truncsfbf-5.c and truncsfbf-6.c to
flush input denormal to zero.

PR target/126933
* gcc.target/i386/truncsfbf-5.c (CALC): Flush input denormal to
zero.
(main): Add tests for float denormal inputs.
* gcc.target/i386/truncsfbf-6.c (CALC): Flush input denormal to
zero.
(main): Add tests for float denormal inputs.
From 0d00eeec911fff3453fb97858fdb9b133ff15342 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Mon, 24 Aug 2026 19:43:55 +0800
Subject: [PATCH] vcvtneps2bf16 emulation: Flush input denormal to zero

Update vcvtneps2bf16 emulation in truncsfbf-5.c and truncsfbf-6.c to
flush input denormal to zero.

	PR target/126933
	* gcc.target/i386/truncsfbf-5.c (CALC): Flush input denormal to
	zero.
	(main): Add tests for float denormal inputs.
	* gcc.target/i386/truncsfbf-6.c (CALC): Flush input denormal to
	zero.
	(main): Add tests for float denormal inputs.

Signed-off-by: H.J. Lu <[email protected]>
---
 gcc/testsuite/gcc.target/i386/truncsfbf-5.c | 30 ++++++++++++++-------
 gcc/testsuite/gcc.target/i386/truncsfbf-6.c | 30 ++++++++++++++-------
 2 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-5.c b/gcc/testsuite/gcc.target/i386/truncsfbf-5.c
index cd59a997bea..c6595262f81 100644
--- a/gcc/testsuite/gcc.target/i386/truncsfbf-5.c
+++ b/gcc/testsuite/gcc.target/i386/truncsfbf-5.c
@@ -20,14 +20,20 @@ CALC (float *a)
 {
   uint32_t bits;
   memcpy (&bits, a, sizeof (bits));
-  uint32_t rounding_bias = 0x7FFF + ((bits >> 16) & 1);
-  bits += rounding_bias;
-  bits >>= 16;
-  /* Flush denormal to zero like vcvtneps2bf16.  */
-  if ((bits & 0x7f80) == 0)
-    bits &= 0x8000;
-  uint16_t bfloat16_bits = (uint16_t) bits;
+  /* Flush input denormal to zero like vcvtneps2bf16.  */
+  if ((bits & 0x7f800000) == 0)
+    {
+      bits >>= 16;
+      bits &= 0x8000;
+    }
+  else
+    {
+      uint32_t rounding_bias = 0x7FFF + ((bits >> 16) & 1);
+      bits += rounding_bias;
+      bits >>= 16;
+    }
   __bf16 bf16;
+  uint16_t bfloat16_bits = (uint16_t) bits;
   memcpy (&bf16, &bfloat16_bits, sizeof (bf16));
   return bf16;
 }
@@ -36,9 +42,13 @@ __attribute__ ((noipa, noinline))
 static void
 do_test (void)
 {
-  float test_values[] = { 0.0f, -0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1000.0f, -1000.0f,
-                          3.1415926f, -3.1415926f, 1e-8f, -1e-8f,
-                          1.0e+38f, -1.0e+38f, 1.0e-38f, -1.0e-38f };
+  float test_values[] =
+    {
+      0.0f, -0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1000.0f, -1000.0f,
+      3.1415926f, -3.1415926f, 1e-8f, -1e-8f, 1.0e+38f, -1.0e+38f,
+      1.0e-38f, -1.0e-38f, 1.170903e-38, -1.170903e-38, 1.175494e-38,
+      -1.175494e-38, 1.170902e-38, 4.999295e-39, 1.175494e-38
+    };
   size_t num_values = sizeof (test_values) / sizeof (test_values[0]);
   bool failed = false;
 
diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-6.c b/gcc/testsuite/gcc.target/i386/truncsfbf-6.c
index ed53d48b0e2..3b1707b99c2 100644
--- a/gcc/testsuite/gcc.target/i386/truncsfbf-6.c
+++ b/gcc/testsuite/gcc.target/i386/truncsfbf-6.c
@@ -20,14 +20,20 @@ CALC (float *a)
 {
   uint32_t bits;
   memcpy (&bits, a, sizeof (bits));
-  uint32_t rounding_bias = 0x7FFF + ((bits >> 16) & 1);
-  bits += rounding_bias;
-  bits >>= 16;
-  /* Flush denormal to zero like vcvtneps2bf16.  */
-  if ((bits & 0x7f80) == 0)
-    bits &= 0x8000;
-  uint16_t bfloat16_bits = (uint16_t) bits;
+  /* Flush input denormal to zero like vcvtneps2bf16.  */
+  if ((bits & 0x7f800000) == 0)
+    {
+      bits >>= 16;
+      bits &= 0x8000;
+    }
+  else
+    {
+      uint32_t rounding_bias = 0x7FFF + ((bits >> 16) & 1);
+      bits += rounding_bias;
+      bits >>= 16;
+    }
   __bf16 bf16;
+  uint16_t bfloat16_bits = (uint16_t) bits;
   memcpy (&bf16, &bfloat16_bits, sizeof (bf16));
   return bf16;
 }
@@ -36,9 +42,13 @@ __attribute__ ((noipa, noinline))
 static void
 do_test (void)
 {
-  float test_values[] = { 0.0f, -0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1000.0f, -1000.0f,
-                          3.1415926f, -3.1415926f, 1e-8f, -1e-8f,
-                          1.0e+38f, -1.0e+38f, 1.0e-38f, -1.0e-38f };
+  float test_values[] =
+    {
+      0.0f, -0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1000.0f, -1000.0f,
+      3.1415926f, -3.1415926f, 1e-8f, -1e-8f, 1.0e+38f, -1.0e+38f,
+      1.0e-38f, -1.0e-38f, 1.170903e-38, -1.170903e-38, 1.175494e-38,
+      -1.175494e-38, 1.170902e-38, 4.999295e-39, 1.175494e-38
+    };
   size_t num_values = sizeof (test_values) / sizeof (test_values[0]);
   bool failed = false;
 
-- 
2.55.0

Reply via email to