Skip to content

libs/libm: Add fmaf, using fmadd.s on RISC-V targets with an FPU. - #20553

Merged
xiaoxiang781216 merged 1 commit into
apache:masterfrom
anatol:master
Oct 12, 2026
Merged

xiaoxiang781216 merged 1 commit into
apache:masterfrom
anatol:master

Conversation

@anatol

@anatol anatol commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a portable fmaf() implementation for targets where double has 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.
  • Add an optional RISC-V implementation using the fmadd.s instruction. Enable it in the canmv230 (K230) defconfigs.
  • This fixes link failures in K230 builds when GCC emits calls to fmaf().
  • The portable implementation is adapted from musl, with its license and upstream revision recorded in the source.
  • Related NuttX or NuttX Apps issue/PR: N/A.

Impact

  • New feature: YES. Adds fmaf() to libm on supported targets and an optional RISC-V FMA implementation.
  • User impact: YES. Applications can link against fmaf() where double has a 53-bit mantissa. The RISC-V optimization is enabled through RISCV_LIBM.
  • Build impact: YES. Adds the portable source to Make and CMake builds, with an architecture-specific source selected when LIBM_ARCH_FMAF is enabled.
  • Hardware impact: YES. Adds an optional RISC-V FPU optimization and enables it in six canmv230 defconfigs.
  • Documentation impact: YES. This PR adds libm functionality; confirm whether a user documentation update is needed.
  • Security impact: NO known impact.
  • Compatibility impact: NO intended impact. The portable implementation is omitted when double does not have a 53-bit mantissa.
  • Other: The RISC-V implementation uses fmadd.s and 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-gcc 16.2.0 for NuttX builds.

  • Portable implementation regression: testing/fmaf/test_fmaf.c from NuttX Apps commit 298e3887a. Compared result bits and invalid, overflow, underflow, and inexact flags against host libm in all four rounding modes. All 4,072,672 cases
    matched across the listed GCC and Clang optimization and sanitizer builds.

  • RISC-V comparison: portable and fmadd.s implementations matched in 1,072,672 cases across four rounding modes in a bare-metal QEMU harness.

  • NuttX builds: canmv230:nsh with RISCV_LIBM=y selected lib_fmaf_riscv.o; portable implementation builds were checked with RISCV_LIBM disabled and on rv-virt:nsh64, rv-virt:nsh with ARCH_FPU disabled, esp32c3-legacy- devkit:elf, and linum-stm32h753bi:buzzer. No warnings reported.

  • nxstyle passed 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

  • This PR introduces only one functional change.
  • I have updated all required description fields above.
  • My PR adheres to the Contributing Guidelines and Documentation.
  • My PR is still work in progress (not ready for review).
  • My PR is ready for review and can be safely merged into a codebase.

Comment thread tools/test_fmaf.c Outdated
Comment thread libs/libm/libm/Kconfig Outdated
Comment thread libs/libm/libm/Make.defs Outdated
Comment thread libs/libm/libm/Make.defs Outdated
@github-actions github-actions Bot added Area: OS Components OS Components issues Size: M The size of the change in this PR is medium labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report

qemu-armv8a

  • Code: .text.init_builtin_run +60 B (+0.0%, 347,480 B)

qemu-intel64

Comment thread arch/risc-v/Kconfig Outdated
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@anatol please squash your patch into one.

@cederom cederom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @anatol please update PR description according to https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md :-)

@anatol

anatol commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

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.

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@anatol please fix cmake style error:


cmake-format check failed, run following command to update the style:
  $ cmake-format <src> -o <dst>

ERROR __main__.py:618: Check failed: /home/runner/work/nuttx/nuttx/nuttx/libs/libm/libm/risc-v/CMakeLists.txt
Some checks failed. For contributing guidelines, see:
  https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md

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>
@anatol

anatol commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor Author

@xiaoxiang781216 Given that LIBM_ARCH_FMAF is disabled by default - should i at least enable it for k230 targets?

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@xiaoxiang781216 Given that LIBM_ARCH_FMAF is disabled by default - should i at least enable it for k230 targets?

yes, you can.

@anatol

anatol commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

@xiaoxiang781216 Given that LIBM_ARCH_FMAF is disabled by default - should i at least enable it for k230 targets?

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 y. No need to go with my suggestion.

@cederom cederom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @anatol :-)

@xiaoxiang781216
xiaoxiang781216 merged commit f5999d9 into apache:master Oct 12, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: OS Components OS Components issues Board: risc-v Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants