Repository navigation
libs/libm: Add fmaf, using fmadd.s on RISC-V targets with an FPU. - #20553
Conversation
|
|
@anatol please squash your patch into one. |
cederom
left a comment
There was a problem hiding this comment.
Thank you @anatol please update PR description according to https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md :-)
Hi @cederom thank you for the feedback. i updated the PR description as well. PTAL. |
|
@anatol please fix cmake style error: |
The public math header declares fmaf(), but the NuttX libm does not
provide it. An unoptimized K230 Sigmoid build therefore fails to
link when GCC leaves the fused operations as calls to the standard
function.
Add a portable, correctly rounded fmaf() adapted from musl (MIT). The
license header and the pinned upstream revision are kept. It adds in
binary64, where the binary32 product is exact, and rounds that sum to
odd if it is a binary32 halfway or subnormal value, so that the second
rounding to binary32 neither picks the wrong neighbour nor misses the
underflow and inexact flags. It needs a binary64 double: it is not built
where DBL_MANT_DIG is not 53, instead of rounding wrongly.
On RISC-V targets with an FPU, add the LIBM_ARCH_FMAF option, selected
by the new RISCV_LIBM ("Architecture specific FPU optimizations", which
depends on ARCH_FPU and LIBM), to use the fmadd.s instruction instead,
in both the Make and CMake builds. This follows ARMV7M_LIBM: it is a
user option, enabled here in the canmv230 (K230) defconfigs.
The host regression for the portable fmaf() is
testing/fmaf/test_fmaf.c in nuttx-apps (298e3887a, "testing: Add host
fmaf regression program"). It compares the portable fmaf() with the host
libm: result bits and the invalid, overflow, underflow and inexact
flags, in all four rounding modes. Besides boundary, tie and random
inputs it has the double rounding traps, whose exact sum is just below
or above a binary32 halfway value but rounds to it in binary64. Random
inputs hit those with a probability near 2^-29.
Testing
Host, Linux x86_64 with glibc as the reference: test_fmaf.c built
with gcc 16.2.1 (-O0, -O2, -O3) and clang 23.1.1 (-O0, -O3), and with
both at -O2 with ASan and UBSan. All 4,072,672 cases match in the four
rounding modes (boundary, tie and double rounding trap cases, and
1,000,000 random cases per mode). It fails for each of four broken
fmaf() variants: the plain (float)((double)x * y + z), no correction at
all, no halfway correction, and no subnormal correction.
RISC-V, bare metal on qemu-system-riscv64 11.1.2 (virt, rv64imafdc,
lp64d), with a throwaway harness that is not part of this commit: the
fmadd.s version and the portable version return the same bits and
exception flags in 1,072,672 cases, in the four rounding modes. The
comparison also fails with the portable version without the halfway
correction.
Build, Make with riscv64-elf-gcc 16.2.0: canmv230:nsh (K230, with
RISCV_LIBM=y) builds lib_fmaf_riscv.o, which is a single fmadd.s, and
not lib_fmaf.o. The portable lib_fmaf.o is used when RISCV_LIBM is
disabled and on rv-virt:nsh64 and rv-virt:nsh with ARCH_FPU disabled,
esp32c3-legacy-devkit:elf and linum-stm32h753bi:buzzer, with no
warnings. nxstyle passes on the new C files.
Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Anatol Pomozov <anatol.pomozov@gmail.com>
|
@xiaoxiang781216 Given that |
yes, you can. |
i doublechecked the changes and i see that LIBM_ARCH_FMAF is automaticlly enabled if LIBM and FPU enabled. So for k230 it already |
Summary
fmaf()implementation for targets wheredoublehas a 53-bit mantissa. It uses a correction step to handle halfway and subnormal cases that a simple binary64 multiply and add can round incorrectly.fmadd.sinstruction. Enable it in the canmv230 (K230) defconfigs.fmaf().Impact
fmaf()to libm on supported targets and an optional RISC-V FMA implementation.fmaf()wheredoublehas a 53-bit mantissa. The RISC-V optimization is enabled throughRISCV_LIBM.LIBM_ARCH_FMAFis enabled.doubledoes not have a 53-bit mantissa.fmadd.sand relies on the hardware to apply the active rounding mode and update exception flags.Testing
Host: Linux x86_64, glibc reference.
Target: bare-metal RISC-V under
qemu-system-riscv64 11.1.2(virt,rv64imafdc,lp64d).Build host/compiler: Linux x86_64; GCC 16.2.1 and Clang 23.1.1 for host regression;
riscv64-elf-gcc16.2.0 for NuttX builds.Portable implementation regression:
testing/fmaf/test_fmaf.cfrom NuttX Apps commit298e3887a. Compared result bits and invalid, overflow, underflow, and inexact flags against host libm in all four rounding modes. All 4,072,672 casesmatched across the listed GCC and Clang optimization and sanitizer builds.
RISC-V comparison: portable and
fmadd.simplementations matched in 1,072,672 cases across four rounding modes in a bare-metal QEMU harness.NuttX builds:
canmv230:nshwithRISCV_LIBM=yselectedlib_fmaf_riscv.o; portable implementation builds were checked withRISCV_LIBMdisabled and onrv-virt:nsh64,rv-virt:nshwithARCH_FPUdisabled,esp32c3-legacy- devkit:elf, andlinum-stm32h753bi:buzzer. No warnings reported.nxstylepassed on the new C files.Testing logs before change:
Not available.
Testing logs after change:
Regression and build results are summarized above. Real-world hardware build and runtime logs still need to be added; the RISC-V runtime comparison used QEMU.
PR verification Self-Check