This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new f5999d9556a libs/libm: Add fmaf, using fmadd.s on RISC-V targets with
an FPU.
f5999d9556a is described below
commit f5999d9556aea6f21a3b58e70db2a71aadaffbfb
Author: Anatol Pomozov <[email protected]>
AuthorDate: Sat Oct 3 00:53:10 2026 -0700
libs/libm: Add fmaf, using fmadd.s on RISC-V targets with an FPU.
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 <[email protected]>
---
boards/risc-v/k230/canmv230/configs/knsh/defconfig | 1 +
.../risc-v/k230/canmv230/configs/master/defconfig | 1 +
boards/risc-v/k230/canmv230/configs/nsbi/defconfig | 1 +
boards/risc-v/k230/canmv230/configs/nsh/defconfig | 1 +
boards/risc-v/k230/canmv230/configs/pnsh/defconfig | 1 +
.../risc-v/k230/canmv230/configs/remote/defconfig | 1 +
libs/libm/libm/CMakeLists.txt | 8 ++
libs/libm/libm/Kconfig | 9 +++
libs/libm/libm/Make.defs | 8 ++
libs/libm/libm/lib_fmaf.c | 92 ++++++++++++++++++++++
libs/libm/libm/risc-v/CMakeLists.txt | 24 ++++++
libs/libm/libm/risc-v/Kconfig | 18 +++++
libs/libm/libm/risc-v/Make.defs | 27 +++++++
libs/libm/libm/risc-v/lib_fmaf_riscv.c | 48 +++++++++++
14 files changed, 240 insertions(+)
diff --git a/boards/risc-v/k230/canmv230/configs/knsh/defconfig
b/boards/risc-v/k230/canmv230/configs/knsh/defconfig
index 691aba8d61b..7aa096c1483 100644
--- a/boards/risc-v/k230/canmv230/configs/knsh/defconfig
+++ b/boards/risc-v/k230/canmv230/configs/knsh/defconfig
@@ -72,6 +72,7 @@ CONFIG_RAM_SIZE=16777216
CONFIG_RAM_START=0x8200000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RISCV_LIBM=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_LPWORK=y
diff --git a/boards/risc-v/k230/canmv230/configs/master/defconfig
b/boards/risc-v/k230/canmv230/configs/master/defconfig
index d070f471926..ff98bb02382 100644
--- a/boards/risc-v/k230/canmv230/configs/master/defconfig
+++ b/boards/risc-v/k230/canmv230/configs/master/defconfig
@@ -84,6 +84,7 @@ CONFIG_RAM_SIZE=16777216
CONFIG_RAM_START=0x6000000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RISCV_LIBM=y
CONFIG_RPMSG_PING=y
CONFIG_RPMSG_UART=y
CONFIG_RPTUN=y
diff --git a/boards/risc-v/k230/canmv230/configs/nsbi/defconfig
b/boards/risc-v/k230/canmv230/configs/nsbi/defconfig
index c63d44daa7c..cefa88b3585 100644
--- a/boards/risc-v/k230/canmv230/configs/nsbi/defconfig
+++ b/boards/risc-v/k230/canmv230/configs/nsbi/defconfig
@@ -77,6 +77,7 @@ CONFIG_RAM_SIZE=16777216
CONFIG_RAM_START=0x6000000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RISCV_LIBM=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_LPWORK=y
diff --git a/boards/risc-v/k230/canmv230/configs/nsh/defconfig
b/boards/risc-v/k230/canmv230/configs/nsh/defconfig
index 3f084781af3..1f3b008f08a 100644
--- a/boards/risc-v/k230/canmv230/configs/nsh/defconfig
+++ b/boards/risc-v/k230/canmv230/configs/nsh/defconfig
@@ -50,6 +50,7 @@ CONFIG_RAM_SIZE=16777216
CONFIG_RAM_START=0x6000000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RISCV_LIBM=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_WAITPID=y
CONFIG_SERIAL_UART_ARCH_MMIO=y
diff --git a/boards/risc-v/k230/canmv230/configs/pnsh/defconfig
b/boards/risc-v/k230/canmv230/configs/pnsh/defconfig
index c34a6218903..c8f18d95d07 100644
--- a/boards/risc-v/k230/canmv230/configs/pnsh/defconfig
+++ b/boards/risc-v/k230/canmv230/configs/pnsh/defconfig
@@ -53,6 +53,7 @@ CONFIG_RAM_SIZE=3145728
CONFIG_RAM_START=0x6000000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RISCV_LIBM=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_WAITPID=y
CONFIG_SERIAL_UART_ARCH_MMIO=y
diff --git a/boards/risc-v/k230/canmv230/configs/remote/defconfig
b/boards/risc-v/k230/canmv230/configs/remote/defconfig
index b83c7a28b9a..cfda6b24aff 100644
--- a/boards/risc-v/k230/canmv230/configs/remote/defconfig
+++ b/boards/risc-v/k230/canmv230/configs/remote/defconfig
@@ -72,6 +72,7 @@ CONFIG_RAM_SIZE=16777216
CONFIG_RAM_START=0x7000000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RISCV_LIBM=y
CONFIG_RPMSG_PING=y
CONFIG_RPMSG_UART=y
CONFIG_RPMSG_UART_CONSOLE=y
diff --git a/libs/libm/libm/CMakeLists.txt b/libs/libm/libm/CMakeLists.txt
index f905767a98a..db8ba65cc6d 100644
--- a/libs/libm/libm/CMakeLists.txt
+++ b/libs/libm/libm/CMakeLists.txt
@@ -25,6 +25,10 @@ if(CONFIG_LIBM)
nuttx_add_subdirectory(arm)
endif()
+ if(CONFIG_ARCH_RISCV)
+ nuttx_add_subdirectory(risc-v)
+ endif()
+
# Add the floating point math C files to the build
set(SRCS
lib_acosf.c
@@ -199,5 +203,9 @@ if(CONFIG_LIBM)
list(APPEND SRCS lib_sqrtf.c)
endif()
+ if(NOT CONFIG_LIBM_ARCH_FMAF)
+ list(APPEND SRCS lib_fmaf.c)
+ endif()
+
target_sources(c PRIVATE ${SRCS})
endif()
diff --git a/libs/libm/libm/Kconfig b/libs/libm/libm/Kconfig
index ea03e447b59..d675f57fbfc 100644
--- a/libs/libm/libm/Kconfig
+++ b/libs/libm/libm/Kconfig
@@ -78,8 +78,17 @@ config LIBM_ARCH_SQRTF
bool
default n
+config LIBM_ARCH_FMAF
+ bool
+ default n
+ depends on LIBM
+
# One or more the of above may be selected by architecture specific logic
if ARCH_ARM
source "libs/libm/libm/arm/Kconfig"
endif
+
+if ARCH_RISCV
+source "libs/libm/libm/risc-v/Kconfig"
+endif
diff --git a/libs/libm/libm/Make.defs b/libs/libm/libm/Make.defs
index e18034ed75f..a0947f47487 100644
--- a/libs/libm/libm/Make.defs
+++ b/libs/libm/libm/Make.defs
@@ -109,10 +109,18 @@ ifneq ($(CONFIG_LIBM_ARCH_SQRTF),y)
CSRCS += lib_sqrtf.c
endif
+ifneq ($(CONFIG_LIBM_ARCH_FMAF),y)
+CSRCS += lib_fmaf.c
+endif
+
ifeq ($(CONFIG_ARCH_ARM),y)
include $(TOPDIR)/libs/libm/libm/arm/Make.defs
endif
+ifeq ($(CONFIG_ARCH_RISCV),y)
+include $(TOPDIR)/libs/libm/libm/risc-v/Make.defs
+endif
+
# Add the floating point math directory to the build
DEPPATH += --dep-path libm
diff --git a/libs/libm/libm/lib_fmaf.c b/libs/libm/libm/lib_fmaf.c
new file mode 100644
index 00000000000..fa14b39671c
--- /dev/null
+++ b/libs/libm/libm/lib_fmaf.c
@@ -0,0 +1,92 @@
+/****************************************************************************
+ * libs/libm/libm/lib_fmaf.c
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Adapted from musl 9b2d8a1646391d5217f9a358555aebcaab5b8aaf.
+ * Portable arithmetic rewritten by Szabolcs Nagy (48a619cad667, 2026-09-09).
+ *
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <float.h>
+#include <math.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/* Binary32 products are exact in binary64, but rounding the addition to
+ * binary64 and then binary32 can choose the wrong side of a halfway value.
+ * Recover the lost residual and round the binary64 intermediate to odd in
+ * halfway/subnormal cases. A plain (float)((double)x * y + z) is not fused:
+ * removing this correction changes ties and can miss underflow/inexact.
+ *
+ * This needs a binary64 double. Where double is narrower (or absent, as on
+ * the ez80) fmaf() is not provided, instead of returning a wrongly rounded
+ * result.
+ */
+
+#if DBL_MANT_DIG == 53
+
+float fmaf(float x, float y, float z)
+{
+ double xy = (double)x * y;
+ union
+ {
+ double r;
+ uint64_t i;
+ } u =
+ {
+ .r = xy + z
+ };
+
+ int exponent = (u.i >> 52) & 0x7ff;
+ int halfway = (u.i & UINT64_C(0x1fffffff)) == UINT64_C(0x10000000);
+ int tiny = exponent <= 0x3ff - 126 && exponent >= 0x3ff - 149;
+
+ if (!halfway && !tiny)
+ {
+ return (float)u.r;
+ }
+
+ if (exponent != 0x7ff)
+ {
+ int sign = u.i >> 63;
+ double residual = sign == (xy < z) ? xy - u.r + z : z - u.r + xy;
+
+ if (residual != 0.0)
+ {
+ u.i -= sign ^ (residual < 0.0);
+ u.i |= UINT64_C(1);
+ }
+ }
+
+ return (float)u.r;
+}
+
+#endif /* DBL_MANT_DIG == 53 */
diff --git a/libs/libm/libm/risc-v/CMakeLists.txt
b/libs/libm/libm/risc-v/CMakeLists.txt
new file mode 100644
index 00000000000..af3d478f137
--- /dev/null
+++ b/libs/libm/libm/risc-v/CMakeLists.txt
@@ -0,0 +1,24 @@
+#
##############################################################################
+# libs/libm/libm/risc-v/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
contributor
+# license agreements. See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership. The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may
not
+# use this file except in compliance with the License. You may obtain a copy
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
##############################################################################
+
+if(CONFIG_LIBM_ARCH_FMAF)
+ target_sources(c PRIVATE lib_fmaf_riscv.c)
+endif()
diff --git a/libs/libm/libm/risc-v/Kconfig b/libs/libm/libm/risc-v/Kconfig
new file mode 100644
index 00000000000..95e6642ff5c
--- /dev/null
+++ b/libs/libm/libm/risc-v/Kconfig
@@ -0,0 +1,18 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+if ARCH_RISCV
+
+config RISCV_LIBM
+ bool "Architecture specific FPU optimizations"
+ default n
+ select LIBM_ARCH_FMAF
+ depends on ARCH_FPU
+ depends on LIBM
+ ---help---
+ Enable RISC-V specific floating point optimizations
+ for fmaf() using the fmadd.s instruction.
+
+endif
diff --git a/libs/libm/libm/risc-v/Make.defs b/libs/libm/libm/risc-v/Make.defs
new file mode 100644
index 00000000000..8c6989413a0
--- /dev/null
+++ b/libs/libm/libm/risc-v/Make.defs
@@ -0,0 +1,27 @@
+############################################################################
+# libs/libm/libm/risc-v/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifeq ($(CONFIG_LIBM_ARCH_FMAF),y)
+CSRCS += lib_fmaf_riscv.c
+DEPPATH += --dep-path libm/risc-v
+VPATH += :libm/risc-v
+endif
diff --git a/libs/libm/libm/risc-v/lib_fmaf_riscv.c
b/libs/libm/libm/risc-v/lib_fmaf_riscv.c
new file mode 100644
index 00000000000..14b180a7bec
--- /dev/null
+++ b/libs/libm/libm/risc-v/lib_fmaf_riscv.c
@@ -0,0 +1,48 @@
+/****************************************************************************
+ * libs/libm/libm/risc-v/lib_fmaf_riscv.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <math.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/* Use the hardware instruction directly. Calling __builtin_fmaf from the
+ * implementation of fmaf can become a recursive library call when the
+ * compiler cannot lower it. fmadd.s rounds once in the current frm mode and
+ * sets the FPU exception flags, including cancellation and subnormal cases.
+ * The asm is volatile because the compiler does not see that it depends on
+ * the rounding mode and sets the flags, and must not cache or reorder it
+ * across fesetround() and fetestexcept() when fmaf() is inlined.
+ */
+
+float fmaf(float x, float y, float z)
+{
+ float result;
+
+ __asm__ __volatile__("fmadd.s %0, %1, %2, %3"
+ : "=f"(result) : "f"(x), "f"(y), "f"(z));
+ return result;
+}