https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127720

            Bug ID: 127720
           Summary: x86/F16C: scalar _Float16 to double uses __extendhfdf2
                    instead of widening through float
           Product: gcc
           Version: 16.2.1
               URL: https://godbolt.org/z/xjbvjMe7P
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: adamant.pwn at gmail dot com
  Target Milestone: ---
            Target: x86_64-pc-linux-gnu

On x86-64 with -O2 -mf16c, GCC emits a call to __extendhfdf2 for a scalar
_Float16-to-double conversion. Explicitly writing the exact float intermediate
does not help: GCC folds it away. Preserving that intermediate with
__builtin_assoc_barrier produces inline hardware conversions instead.

This reproduces with a single scalar argument, without loops or vectorization.

Complete testcase (t.cpp; no headers required):

extern "C" double direct(_Float16 x) {
    return double(x);
}
extern "C" double via_float(_Float16 x) {
    return double(float(x));
}
extern "C" double barrier(_Float16 x) {
    return double(__builtin_assoc_barrier(float(x)));
}

Command:
  g++ -std=c++23 -O2 -mf16c -S -masm=intel t.cpp -o t.s

Actual output for both direct and via_float (GCC 16.2.1 20260810):
  sub rsp, 8
  call __extendhfdf2@PLT
  add rsp, 8
  ret

Output for barrier:
  vpxor xmm1, xmm1, xmm1
  vpblendw xmm0, xmm1, xmm0, 1
  vcvtph2ps xmm0, xmm0
  vcvtss2sd xmm0, xmm0, xmm0
  ret

Expected: use an inline F16C half-to-float conversion followed by
float-to-double conversion for direct and via_float too, with appropriate
handling of unused lanes and floating-point exceptions. Every finite binary16
value is exactly representable in both binary32 and binary64; this widening
does not have the double-rounding problem of narrowing double to half through
float.

Clang 21.1 emits vcvtph2ps followed by vcvtss2sd for direct and via_float. The
GCC helper call also reproduces with GCC 14.2 and GCC trunk on Compiler
Explorer, and locally with -O3 -march=x86-64-v3 (without fast-math).

Compiler Explorer comparison (GCC trunk and Clang 21.1; the barrier function is
conditionalized for Clang):
  https://godbolt.org/z/xjbvjMe7P

Local toolchain:
  Target: x86_64-pc-linux-gnu
  g++ (GCC) 16.2.1 20260810

Additional code-generation observation: with the flags above, Clang 21.1 (and
local Clang 23.1.1) emits just vcvtph2ps + vcvtss2sd + ret, without GCC's
vpxor/vpblendw. This should be considered separately from eliminating the
libcall: with -ftrapping-math, local Clang 23.1.1 also clears unused half lanes
(vpextrw + movzx + vmovd) before the conversions. Local GCC 16.2.1 still emits
vpxor/vpblendw in barrier even with -fno-trapping-math, and direct/via_float
still call __extendhfdf2.

Related reports:
  PR102811 fixed scalar half/float conversions with F16C, but this testcase
widens to double.
  PR121688 and PR122382 concern packed conversions/vectorization; this testcase
is scalar.

The original F16C discussion also describes widening to double through float as
valid:
  https://gcc.gnu.org/pipermail/gcc-patches/2021-July/574515.html

I have not established this as a regression from an earlier GCC version.

Reply via email to