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.
From a01127e463072439c21e102063c6d2596860fc52 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <[email protected]> Date: Wed, 19 Aug 2026 11:42:38 +0800 Subject: [PATCH v2] x86: Expand the default truncsfbf2 like vcvtneps2bf16 Expand 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 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 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. Signed-off-by: H.J. Lu <[email protected]> --- gcc/config/i386/i386-expand.cc | 87 +++++++++++++++++++ gcc/config/i386/i386-protos.h | 1 + gcc/config/i386/i386.md | 38 +++++---- gcc/testsuite/gcc.target/i386/truncsfbf-1.c | 44 +++++++++- gcc/testsuite/gcc.target/i386/truncsfbf-2.c | 27 +++++- gcc/testsuite/gcc.target/i386/truncsfbf-3.c | 26 ++++++ gcc/testsuite/gcc.target/i386/truncsfbf-4.c | 26 ++++++ gcc/testsuite/gcc.target/i386/truncsfbf-5.c | 92 ++++++++++++++++++++ gcc/testsuite/gcc.target/i386/truncsfbf-6.c | 93 +++++++++++++++++++++ 9 files changed, 413 insertions(+), 21 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/truncsfbf-3.c create mode 100644 gcc/testsuite/gcc.target/i386/truncsfbf-4.c create mode 100644 gcc/testsuite/gcc.target/i386/truncsfbf-5.c create mode 100644 gcc/testsuite/gcc.target/i386/truncsfbf-6.c diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc index 34c9599f928..0d86992ee40 100644 --- a/gcc/config/i386/i386-expand.cc +++ b/gcc/config/i386/i386-expand.cc @@ -22554,6 +22554,93 @@ ix86_expand_truncdf_32 (rtx operand0, rtx operand1) emit_move_insn (operand0, res); } +/* Expand truncsfbf2 like vcvtneps2bf16, which doesn't honor SNAN, + turns sNAN into qNAN quietly, it always rounds to nearest even + and flushes denormals to zero. We can expand the conversion + inline as (fromi + 0x7fff + ((fromi >> 16) & 1)) >> 16, flushing + denormals to zero. */ + +void +ix86_expand_truncsfbf2 (rtx op0, rtx op1) +{ + rtx set; + + if (TARGET_AVXNECONVERT + || (TARGET_AVX512BF16 && TARGET_AVX512VL)) + { + set = gen_truncsfbf2_vcvtneps2bf16 (op0, op1); + emit_insn (set); + return; + } + + /* Convert OP1 to REG1_SI in SImode. */ + rtx reg1_si = gen_reg_rtx (SImode); + rtx op1_si = gen_lowpart (SImode, op1); + set = gen_rtx_SET (reg1_si, op1_si); + emit_insn (set); + + /* Set TMP0 to REG1_SI >> 16. */ + rtx tmp0 = expand_simple_binop (SImode, LSHIFTRT, reg1_si, + GEN_INT(16), nullptr, 0, + OPTAB_DIRECT); + /* Set TMP1 to TMP0 & 1. */ + rtx tmp1 = expand_simple_binop (SImode, AND, tmp0, const1_rtx, + nullptr, 0, OPTAB_DIRECT); + + /* Set TMP2 to REG1_SI + 0x7fff. */ + rtx tmp2 = expand_simple_binop (SImode, PLUS, reg1_si, + GEN_INT (0x7fff), nullptr, 0, + OPTAB_DIRECT); + /* Set TMP1 to TMP2 + TMP1. */ + tmp1 = expand_simple_binop (SImode, PLUS, tmp2, tmp1, nullptr, 0, + OPTAB_DIRECT); + + /* Set TMP1 to TMP1 >> 16. */ + tmp1 = expand_simple_binop (SImode, LSHIFTRT, tmp1, GEN_INT(16), + nullptr, 0, OPTAB_DIRECT); + + rtx_code_label *zero_label = gen_label_rtx (); + rtx_code_label *cast_label = gen_label_rtx (); + + rtx result = gen_reg_rtx (SImode); + + /* TMP1 is zero or denormal if (TMP1 & 0x7f80) == 0. */ + tmp0 = expand_simple_binop (SImode, AND, tmp1, GEN_INT(0x7f80), + nullptr, 0, OPTAB_DIRECT); + + emit_cmp_and_jump_insns (tmp0, const0_rtx, EQ, nullptr, SImode, + true, zero_label); + + /* Move TMP1 to RESULT. */ + emit_move_insn (result, tmp1); + + emit_jump_insn (gen_jump (cast_label)); + emit_barrier (); + + emit_label (zero_label); + + /* Flush TMP1 to zero while keeping the sign bit for zero and + denormal. */ + tmp1 = expand_simple_binop (SImode, AND, tmp1, GEN_INT(0x8000), + nullptr, 0, OPTAB_DIRECT); + + /* Move TMP1 to RESULT. */ + emit_move_insn (result, tmp1); + + emit_label (cast_label); + + /* Cast RESULT to TMP2 in HImode. */ + tmp2 = gen_reg_rtx (HImode); + tmp0 = gen_lowpart (HImode, result); + set = gen_rtx_SET (tmp2, tmp0); + emit_insn (set); + + /* Convert TMP2 to OP0. */ + tmp0 = gen_lowpart (BFmode, tmp2); + set = gen_rtx_SET (op0, tmp0); + emit_insn (set); +} + /* Expand SSE sequence for computing round from OPERAND1 storing into OPERAND0. */ void diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 21994d3d498..2687de646b0 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -233,6 +233,7 @@ extern void ix86_expand_floorceil (rtx, rtx, bool); extern void ix86_expand_floorceildf_32 (rtx, rtx, bool); extern void ix86_expand_trunc (rtx, rtx); extern void ix86_expand_truncdf_32 (rtx, rtx); +extern void ix86_expand_truncsfbf2 (rtx, rtx); extern void ix86_expand_round (rtx, rtx); extern void ix86_expand_rounddf_32 (rtx, rtx); extern void ix86_expand_round_sse4 (rtx, rtx); diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 4e18797743f..f057a59dc46 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -5817,28 +5817,36 @@ (define_insn "*trunc<mode>hf2" (set_attr "prefix" "evex") (set_attr "mode" "HF")]) -/* vcvtneps2bf16 doesn't honor SNAN, and turn sNAN into qNAN quietly, - and it always round to even. - flag_unsafe_math_optimization is needed for psrld. - If we don't expect qNaNs nor sNaNs and can assume rounding - to nearest, we can expand the conversion inline as - (fromi + 0x7fff + ((fromi >> 16) & 1)) >> 16. */ -(define_insn "truncsfbf2" - [(set (match_operand:BF 0 "register_operand" "=x,x,v,Yv") +/* vcvtneps2bf16 doesn't honor SNAN, turns sNAN into qNAN quietly, it + always rounds to nearest even and flushes denormals to zero. + flag_unsafe_math_optimization is needed for vcvtneps2bf16 emulation + which doesn't support sNAN and qNAN. */ +(define_expand "truncsfbf2" + [(set (match_operand:BF 0 "register_operand" "") (float_truncate:BF - (match_operand:SF 1 "register_operand" "0,x,v,Yv")))] + (match_operand:SF 1 "register_operand" "")))] "TARGET_SSE2 && !HONOR_NANS (BFmode) && !flag_rounding_math && (flag_unsafe_math_optimizations || TARGET_AVXNECONVERT || (TARGET_AVX512BF16 && TARGET_AVX512VL))" +{ + ix86_expand_truncsfbf2 (operands[0], operands[1]); + DONE; +}) + +(define_insn "truncsfbf2_vcvtneps2bf16" + [(set (match_operand:BF 0 "register_operand" "=x,v") + (float_truncate:BF + (match_operand:SF 1 "register_operand" "x,v")))] + "(TARGET_AVXNECONVERT || (TARGET_AVX512BF16 && TARGET_AVX512VL)) + && !HONOR_NANS (BFmode) + && !flag_rounding_math" "@ - psrld\t{$16, %0|%0, 16} %{vex%} vcvtneps2bf16\t{%1, %0|%0, %1} - vcvtneps2bf16\t{%1, %0|%0, %1} - vpsrld\t{$16, %1, %0|%0, %1, 16}" - [(set_attr "isa" "noavx,avxneconvert,avx512bf16vl,avx") - (set_attr "prefix" "orig,vex,evex,vex") - (set_attr "type" "sseishft1,ssecvt,ssecvt,sseishft1")]) + vcvtneps2bf16\t{%1, %0|%0, %1}" + [(set_attr "isa" "avxneconvert,avx512bf16vl") + (set_attr "prefix" "vex,evex") + (set_attr "type" "ssecvt,ssecvt")]) ;; Signed conversion to DImode. diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-1.c b/gcc/testsuite/gcc.target/i386/truncsfbf-1.c index dd3ff8a50b4..debc29276d9 100644 --- a/gcc/testsuite/gcc.target/i386/truncsfbf-1.c +++ b/gcc/testsuite/gcc.target/i386/truncsfbf-1.c @@ -1,6 +1,46 @@ /* { dg-do compile } */ -/* { dg-options "-msse2 -O2 -ffast-math" } */ -/* { dg-final { scan-assembler-times "psrld" 1 } } */ +/* { dg-options "-msse2 -O2 -ffast-math -mno-avxneconvert -mno-avx512bf16" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "ia32" "*ia32" "" { target ia32 } {^\t?\.} } } */ +/* { dg-final { check-function-bodies "x64" "*x64" "" { target { ! ia32 } } {^\t?\.} } } */ + +/* +ia32foo: +ia32.LFB0: +ia32 .cfi_startproc +ia32 movl 4\(%esp\), %edx +ia32 movl %edx, %eax +ia32 shrl \$16, %eax +ia32 andl \$1, %eax +ia32 leal 32767\(%edx,%eax\), %eax +ia32 shrl \$16, %eax +ia32 movl %eax, %edx +ia32 andl \$32768, %edx +ia32 testl \$32640, %eax +ia32 cmove %edx, %eax +ia32 (v|)movd %eax, %xmm0 +ia32 ret +ia32... +*ia32 + +x64foo: +x64.LFB0: +x64 .cfi_startproc +x64 (v|)movd %xmm0, %eax +x64 movl %eax, %edx +x64 shrl \$16, %edx +x64 andl \$1, %edx +x64 leal 32767\(%(e|r)ax,%(e|r)dx\), %eax +x64 shrl \$16, %eax +x64 movl %eax, %edx +x64 andl \$32768, %edx +x64 testl \$32640, %eax +x64 cmove %edx, %eax +x64 (v|)movd %eax, %xmm0 +x64 ret +x64... +*x64 +*/ __bf16 foo (float a) diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-2.c b/gcc/testsuite/gcc.target/i386/truncsfbf-2.c index f4952f88fc9..36b311a16e5 100644 --- a/gcc/testsuite/gcc.target/i386/truncsfbf-2.c +++ b/gcc/testsuite/gcc.target/i386/truncsfbf-2.c @@ -1,23 +1,31 @@ /* { dg-do run } */ -/* { dg-options "-msse2 -O2 -ffast-math" } */ +/* { dg-options "-msse2 -O2 -ffast-math -mno-avxneconvert -mno-avx512bf16" } */ +#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <math.h> -__bf16 +__attribute__ ((noipa, noinline)) +static __bf16 foo (float a) { return a; } +__attribute__ ((noipa, noinline)) static __bf16 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; __bf16 bf16; memcpy (&bf16, &bfloat16_bits, sizeof (bf16)); @@ -31,6 +39,7 @@ main (void) 3.1415926f, -3.1415926f, 1e-8f, -1e-8f, 1.0e+38f, -1.0e+38f, 1.0e-38f, -1.0e-38f }; size_t num_values = sizeof (test_values) / sizeof (test_values[0]); + bool failed = false; for (size_t i = 0; i < num_values; ++i) { @@ -40,7 +49,10 @@ main (void) /* Verify psrld $16, %0 == %0 >> 16 */ if (memcmp (&hw_bf16, &sw_bf16, sizeof (__bf16)) != 0) - abort (); + { + printf ("float -> bf16 failed: %d, %e\n", i, original); + failed = true; + } /* Reconstruct the float value from the __bf16 bits */ uint16_t bf16_bits; @@ -59,7 +71,14 @@ main (void) ? ldexpf (1.0f, -126 - 7) : ldexpf (1.0f, exponent - 7); if (diff > expected_loss) - abort (); + { + printf ("bf16 -> float failed: %d, %e\n", i, original); + failed = true; + } } + + if (failed) + abort (); + return 0; } diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-3.c b/gcc/testsuite/gcc.target/i386/truncsfbf-3.c new file mode 100644 index 00000000000..afabacbacb6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/truncsfbf-3.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-msse2 -O2 -ffast-math -mavxneconvert -mno-avx512bf16 -fno-asynchronous-unwind-tables" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "ia32" "*ia32" "" { target ia32 } {^\t?\.} } } */ +/* { dg-final { check-function-bodies "x64" "*x64" "" { target { ! ia32 } } {^\t?\.} } } */ + +/* +ia32foo: +ia32 vmovss 4\(%esp\), %xmm0 +ia32 {vex} vcvtneps2bf16 %xmm0, %xmm0 +ia32 ret +ia32... +*ia32 + +x64foo: +x64 {vex} vcvtneps2bf16 %xmm0, %xmm0 +x64 ret +x64... +*x64 +*/ + +__bf16 +foo (float a) +{ + return a; +} diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-4.c b/gcc/testsuite/gcc.target/i386/truncsfbf-4.c new file mode 100644 index 00000000000..ea2653f3e90 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/truncsfbf-4.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-msse2 -O2 -ffast-math -mno-avxneconvert -mavx512vl -mavx512bf16 -fno-asynchronous-unwind-tables" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "ia32" "*ia32" "" { target ia32 } {^\t?\.} } } */ +/* { dg-final { check-function-bodies "x64" "*x64" "" { target { ! ia32 } } {^\t?\.} } } */ + +/* +ia32foo: +ia32 vmovss 4\(%esp\), %xmm0 +ia32 vcvtneps2bf16 %xmm0, %xmm0 +ia32 ret +ia32... +*ia32 + +x64foo: +x64 vcvtneps2bf16 %xmm0, %xmm0 +x64 ret +x64... +*x64 +*/ + +__bf16 +foo (float a) +{ + return a; +} diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-5.c b/gcc/testsuite/gcc.target/i386/truncsfbf-5.c new file mode 100644 index 00000000000..cd59a997bea --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/truncsfbf-5.c @@ -0,0 +1,92 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -ffast-math -march=x86-64" } */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <math.h> + +__attribute__ ((noipa, noinline, target("avxneconvert"))) +static __bf16 +foo (float a) +{ + return a; +} + +__attribute__ ((noipa, noinline)) +static __bf16 +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; + __bf16 bf16; + memcpy (&bf16, &bfloat16_bits, sizeof (bf16)); + return bf16; +} + +__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 }; + size_t num_values = sizeof (test_values) / sizeof (test_values[0]); + bool failed = false; + + for (size_t i = 0; i < num_values; ++i) + { + float original = test_values[i]; + __bf16 hw_bf16 = foo (original); + __bf16 sw_bf16 = CALC (&original); + + /* Verify psrld $16, %0 == %0 >> 16 */ + if (memcmp (&hw_bf16, &sw_bf16, sizeof (__bf16)) != 0) + { + printf ("float -> bf16 failed: %d, %e\n", i, original); + failed = true; + } + + /* Reconstruct the float value from the __bf16 bits */ + uint16_t bf16_bits; + memcpy (&bf16_bits, &hw_bf16, sizeof (bf16_bits)); + uint32_t reconstructed_bits = ((uint32_t) bf16_bits) << 16; + float converted; + memcpy (&converted, &reconstructed_bits, sizeof (converted)); + + float diff = fabsf (original - converted); + + /* Expected Maximum Precision Loss */ + uint32_t orig_bits; + memcpy (&orig_bits, &original, sizeof (orig_bits)); + int exponent = ((orig_bits >> 23) & 0xFF) - 127; + float expected_loss = (exponent == -127) + ? ldexpf (1.0f, -126 - 7) + : ldexpf (1.0f, exponent - 7); + if (diff > expected_loss) + { + printf ("bf16 -> float failed: %d, %e\n", i, original); + failed = true; + } + } + + if (failed) + abort (); +} + +int +main (void) +{ + if (__builtin_cpu_supports ("avxneconvert")) + do_test (); + + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/truncsfbf-6.c b/gcc/testsuite/gcc.target/i386/truncsfbf-6.c new file mode 100644 index 00000000000..ed53d48b0e2 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/truncsfbf-6.c @@ -0,0 +1,93 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -ffast-math -march=x86-64" } */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <math.h> + +__attribute__ ((noipa, noinline, target("avx512vl,avx512bf16"))) +static __bf16 +foo (float a) +{ + return a; +} + +__attribute__ ((noipa, noinline)) +static __bf16 +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; + __bf16 bf16; + memcpy (&bf16, &bfloat16_bits, sizeof (bf16)); + return bf16; +} + +__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 }; + size_t num_values = sizeof (test_values) / sizeof (test_values[0]); + bool failed = false; + + for (size_t i = 0; i < num_values; ++i) + { + float original = test_values[i]; + __bf16 hw_bf16 = foo (original); + __bf16 sw_bf16 = CALC (&original); + + /* Verify psrld $16, %0 == %0 >> 16 */ + if (memcmp (&hw_bf16, &sw_bf16, sizeof (__bf16)) != 0) + { + printf ("float -> bf16 failed: %d, %e\n", i, original); + failed = true; + } + + /* Reconstruct the float value from the __bf16 bits */ + uint16_t bf16_bits; + memcpy (&bf16_bits, &hw_bf16, sizeof (bf16_bits)); + uint32_t reconstructed_bits = ((uint32_t) bf16_bits) << 16; + float converted; + memcpy (&converted, &reconstructed_bits, sizeof (converted)); + + float diff = fabsf (original - converted); + + /* Expected Maximum Precision Loss */ + uint32_t orig_bits; + memcpy (&orig_bits, &original, sizeof (orig_bits)); + int exponent = ((orig_bits >> 23) & 0xFF) - 127; + float expected_loss = (exponent == -127) + ? ldexpf (1.0f, -126 - 7) + : ldexpf (1.0f, exponent - 7); + if (diff > expected_loss) + { + printf ("bf16 -> float failed: %d, %e\n", i, original); + failed = true; + } + } + + if (failed) + abort (); +} + +int +main (void) +{ + if (__builtin_cpu_supports ("avx512vl") + && __builtin_cpu_supports ("avx512bf16")) + do_test (); + + return 0; +} -- 2.55.0
