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.