[libc-commits] [libc] [libc][stdfix] Implement fxdivi functions (rdivi) (PR #154914)
Shreeyash Pandey via libc-commits
libc-commits at lists.llvm.org
Tue Aug 26 05:47:19 PDT 2025
https://github.com/bojle updated https://github.com/llvm/llvm-project/pull/154914
>From 019fc9b182d796ef5c5248c11866dc43d267370a Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Fri, 22 Aug 2025 14:16:57 +0530
Subject: [PATCH 1/2] [libc][stdfix] Implement fxdivi functions
Signed-off-by: Shreeyash Pandey <shreeyash335 at gmail.com>
---
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/include/stdfix.yaml | 8 ++++
libc/src/__support/fixed_point/fx_bits.h | 41 ++++++++++++++++++++
libc/src/stdfix/CMakeLists.txt | 14 +++++++
libc/src/stdfix/rdivi.cpp | 21 ++++++++++
libc/src/stdfix/rdivi.h | 21 ++++++++++
libc/test/src/stdfix/CMakeLists.txt | 16 ++++++++
libc/test/src/stdfix/DivITest.h | 49 ++++++++++++++++++++++++
libc/test/src/stdfix/rdivi_test.cpp | 14 +++++++
10 files changed, 186 insertions(+)
create mode 100644 libc/src/stdfix/rdivi.cpp
create mode 100644 libc/src/stdfix/rdivi.h
create mode 100644 libc/test/src/stdfix/DivITest.h
create mode 100644 libc/test/src/stdfix/rdivi_test.cpp
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 1a03683d72e61..b783dd4b04c74 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -986,6 +986,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.idivulr
libc.src.stdfix.idivuk
libc.src.stdfix.idivulk
+ libc.src.stdfix.rdivi
)
endif()
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5590f1a15ac57..f6beccc7229dd 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1019,6 +1019,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.idivulr
libc.src.stdfix.idivuk
libc.src.stdfix.idivulk
+ libc.src.stdfix.rdivi
)
endif()
diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml
index 5b385124eb63d..451330c3478d2 100644
--- a/libc/include/stdfix.yaml
+++ b/libc/include/stdfix.yaml
@@ -544,3 +544,11 @@ functions:
arguments:
- type: unsigned long accum
guard: LIBC_COMPILER_HAS_FIXED_POINT
+ - name: rdivi
+ standards:
+ - stdc_ext
+ return_type: fract
+ arguments:
+ - type: int
+ - type: int
+ guard: LIBC_COMPILER_HAS_FIXED_POINT
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 00c6119b4f353..13eecba981664 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -224,6 +224,47 @@ idiv(T x, T y) {
return static_cast<XType>(result);
}
+inline long accum nrstep(long accum d, long accum x0) {
+ auto v = x0 * (2 - (d * x0));
+ return v;
+}
+
+/* Divide the two integers and return a fixed_point value
+ *
+ * For reference, see:
+ * https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division
+ * https://stackoverflow.com/a/9231996
+ */
+template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
+ // If the value of the second operand of the / operator is zero, the
+ // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
+ LIBC_CRASH_ON_VALUE(d, 0);
+
+ unsigned int nv = static_cast<unsigned int>(n);
+ unsigned int dv = static_cast<unsigned int>(d);
+ unsigned int clz = cpp::countl_zero<unsigned int>(dv) - 1;
+ unsigned long int scaled_val = dv << clz;
+ /* Scale denominator to be in the range of [0.5,1] */
+ FXBits<long accum> d_scaled{scaled_val};
+ unsigned long int scaled_val_n = nv << clz;
+ /* Scale the numerator as much as the denominator to maintain correctness of
+ * the original equation
+ */
+ FXBits<long accum> n_scaled{scaled_val_n};
+ long accum n_scaled_val = n_scaled.get_val();
+ long accum d_scaled_val = d_scaled.get_val();
+ /* x0 = (48/17) - (32/17) * d_n */
+ long accum a = 2.8235lk; /* 48/17 */
+ long accum b = 1.8823lk; /* 32/17 */
+ long accum initial_approx = a - (b * d_scaled_val);
+ long accum val = nrstep(d_scaled_val, initial_approx);
+ val = nrstep(d_scaled_val, val);
+ val = nrstep(d_scaled_val, val);
+ val = nrstep(d_scaled_val, val);
+ long accum res = n_scaled_val * val;
+ return static_cast<XType>(res);
+}
+
} // namespace fixed_point
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt
index 843111e3f80b1..3cbabd17ad34c 100644
--- a/libc/src/stdfix/CMakeLists.txt
+++ b/libc/src/stdfix/CMakeLists.txt
@@ -89,6 +89,20 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_entrypoint_object(
+ ${suffix}divi
+ HDRS
+ ${suffix}divi.h
+ SRCS
+ ${suffix}divi.cpp
+ COMPILE_OPTIONS
+ ${libc_opt_high_flag}
+ DEPENDS
+ libc.src.__support.fixed_point.fx_bits
+ )
+endforeach()
+
add_entrypoint_object(
uhksqrtus
HDRS
diff --git a/libc/src/stdfix/rdivi.cpp b/libc/src/stdfix/rdivi.cpp
new file mode 100644
index 0000000000000..227b412879a38
--- /dev/null
+++ b/libc/src/stdfix/rdivi.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of rdivi function ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "rdivi.h"
+#include "include/llvm-libc-macros/stdfix-macros.h" // fract
+#include "src/__support/common.h" // LLVM_LIBC_FUNCTION
+#include "src/__support/fixed_point/fx_bits.h" // fixed_point
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(fract, rdivi, (int a, int b)) {
+ return fixed_point::divi<fract>(a,b);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/rdivi.h b/libc/src/stdfix/rdivi.h
new file mode 100644
index 0000000000000..aeda1ee9d40f0
--- /dev/null
+++ b/libc/src/stdfix/rdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for rdivi ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDFIX_RDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_RDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+fract rdivi(int a, int b);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_RDIVI_H
diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt
index e2b4bc1805f7c..741522276feaa 100644
--- a/libc/test/src/stdfix/CMakeLists.txt
+++ b/libc/test/src/stdfix/CMakeLists.txt
@@ -120,6 +120,22 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_libc_test(
+ ${suffix}divi_test
+ SUITE
+ libc-stdfix-tests
+ HDRS
+ DivITest.h
+ SRCS
+ ${suffix}divi_test.cpp
+ DEPENDS
+ libc.src.stdfix.${suffix}divi
+ libc.src.__support.fixed_point.fx_bits
+ libc.hdr.signal_macros
+ )
+endforeach()
+
add_libc_test(
uhksqrtus_test
SUITE
diff --git a/libc/test/src/stdfix/DivITest.h b/libc/test/src/stdfix/DivITest.h
new file mode 100644
index 0000000000000..3e93a3ca4a469
--- /dev/null
+++ b/libc/test/src/stdfix/DivITest.h
@@ -0,0 +1,49 @@
+//===-- Utility class to test fxdivi functions ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "test/UnitTest/Test.h"
+
+#include "src/__support/fixed_point/fx_rep.h"
+#include "src/__support/macros/sanitizer.h"
+
+#include "hdr/signal_macros.h"
+
+template <typename XType>
+class DivITest : public LIBC_NAMESPACE::testing::Test {
+ using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<int>;
+
+public:
+ typedef XType (*DivIFunc)(int, int);
+
+ void testBasic(DivIFunc func) {
+ auto c1 = func(2, 3);
+ auto e1 = 0.666666;
+ EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
+
+ c1 = func(3, 4);
+ e1 = 0.75;
+ EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
+
+ c1 = func(1043, 2764);
+ e1 = 0.37735;
+ EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
+
+ c1 = func(60000, 720293);
+ e1 = 0.083299;
+ EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
+
+ c1 = func(7, 3);
+ e1 = 2.33333;
+ EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
+ }
+};
+
+#define LIST_DIVI_TESTS(Name, XType, func) \
+ using LlvmLibc##Name##diviTest = DivITest<XType>; \
+ TEST_F(LlvmLibc##Name##diviTest, Basic) { testBasic(&func); } \
+ static_assert(true, "Require semicolon.")
diff --git a/libc/test/src/stdfix/rdivi_test.cpp b/libc/test/src/stdfix/rdivi_test.cpp
new file mode 100644
index 0000000000000..10ab366679a36
--- /dev/null
+++ b/libc/test/src/stdfix/rdivi_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for rdivi -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DivITest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h" // fract
+#include "src/stdfix/rdivi.h"
+
+LIST_DIVI_TESTS(r, fract, LIBC_NAMESPACE::rdivi);
>From 4d1cb9b455880450c0d9fe0cb0edcc72ec516ce9 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Tue, 26 Aug 2025 18:16:01 +0530
Subject: [PATCH 2/2] [to squash] use casts directly in tests, use LIBC_INLINE
and prefixes
Signed-off-by: Shreeyash Pandey <shreeyash335 at gmail.com>
---
libc/src/__support/fixed_point/fx_bits.h | 4 ++--
libc/test/src/stdfix/DivITest.h | 24 +++++-------------------
2 files changed, 7 insertions(+), 21 deletions(-)
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 13eecba981664..ad5b25b19d767 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -224,8 +224,8 @@ idiv(T x, T y) {
return static_cast<XType>(result);
}
-inline long accum nrstep(long accum d, long accum x0) {
- auto v = x0 * (2 - (d * x0));
+LIBC_INLINE long accum nrstep(long accum d, long accum x0) {
+ auto v = x0 * (2.lk - (d * x0));
return v;
}
diff --git a/libc/test/src/stdfix/DivITest.h b/libc/test/src/stdfix/DivITest.h
index 3e93a3ca4a469..3dc379abec07d 100644
--- a/libc/test/src/stdfix/DivITest.h
+++ b/libc/test/src/stdfix/DivITest.h
@@ -21,25 +21,11 @@ class DivITest : public LIBC_NAMESPACE::testing::Test {
typedef XType (*DivIFunc)(int, int);
void testBasic(DivIFunc func) {
- auto c1 = func(2, 3);
- auto e1 = 0.666666;
- EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
-
- c1 = func(3, 4);
- e1 = 0.75;
- EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
-
- c1 = func(1043, 2764);
- e1 = 0.37735;
- EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
-
- c1 = func(60000, 720293);
- e1 = 0.083299;
- EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
-
- c1 = func(7, 3);
- e1 = 2.33333;
- EXPECT_EQ(static_cast<XType>(c1), static_cast<XType>(e1));
+ EXPECT_EQ(static_cast<XType>(func(128,256)), static_cast<XType>(0.5));
+ EXPECT_EQ(static_cast<XType>(func(2,3)), static_cast<XType>(0.666666));
+ EXPECT_EQ(static_cast<XType>(func(3,4)), static_cast<XType>(0.75));
+ EXPECT_EQ(static_cast<XType>(func(1043, 2764)), static_cast<XType>(0.37735));
+ EXPECT_EQ(static_cast<XType>(func(60000, 720293)), static_cast<XType>(0.083299));
}
};
More information about the libc-commits
mailing list