[libc-commits] [libc] [wip][libc] Implemented fixed point divifx functions in llvm-libc (PR #138238)
Abhinav Kumar via libc-commits
libc-commits at lists.llvm.org
Fri May 2 01:00:09 PDT 2025
https://github.com/kr-2003 created https://github.com/llvm/llvm-project/pull/138238
## Currently work in progress
Fixing #129124
>From 7737e793aacb836bc5c0f8170194aaae9e3b29a0 Mon Sep 17 00:00:00 2001
From: kr-2003 <kumar.kr.abhinav at gmail.com>
Date: Fri, 2 May 2025 13:24:19 +0530
Subject: [PATCH 1/2] added divir function
---
libc/config/darwin/arm/entrypoints.txt | 1 +
libc/include/stdfix.yaml | 8 ++
libc/src/__support/fixed_point/divifx.h | 132 ++++++++++++++++++++++++
libc/src/stdfix/CMakeLists.txt | 14 +++
libc/src/stdfix/divir.cpp | 13 +++
libc/src/stdfix/divir.h | 21 ++++
libc/test/src/stdfix/CMakeLists.txt | 17 +++
libc/test/src/stdfix/DiviFxTest.h | 40 +++++++
libc/test/src/stdfix/divir_test.cpp | 13 +++
9 files changed, 259 insertions(+)
create mode 100644 libc/src/__support/fixed_point/divifx.h
create mode 100644 libc/src/stdfix/divir.cpp
create mode 100644 libc/src/stdfix/divir.h
create mode 100644 libc/test/src/stdfix/DiviFxTest.h
create mode 100644 libc/test/src/stdfix/divir_test.cpp
diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index 70c888aec064c..1759da59a7e5a 100644
--- a/libc/config/darwin/arm/entrypoints.txt
+++ b/libc/config/darwin/arm/entrypoints.txt
@@ -581,6 +581,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.abslk
libc.src.stdfix.abslr
libc.src.stdfix.absr
+ libc.src.stdfix.divir
libc.src.stdfix.exphk
libc.src.stdfix.expk
libc.src.stdfix.roundhk
diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml
index 5b385124eb63d..16d7650884e3c 100644
--- a/libc/include/stdfix.yaml
+++ b/libc/include/stdfix.yaml
@@ -238,6 +238,14 @@ functions:
arguments:
- type: unsigned long accum
guard: LIBC_COMPILER_HAS_FIXED_POINT
+ - name: divir
+ standards:
+ - stdc_ext
+ return_type: int
+ arguments:
+ - type: int
+ - type: fract
+ guard: LIBC_COMPILER_HAS_FIXED_POINT
- name: idivr
standards:
- stdc_ext
diff --git a/libc/src/__support/fixed_point/divifx.h b/libc/src/__support/fixed_point/divifx.h
new file mode 100644
index 0000000000000..9f619ab87647c
--- /dev/null
+++ b/libc/src/__support/fixed_point/divifx.h
@@ -0,0 +1,132 @@
+//===-- Division of integers by fixed-point numbers ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides implementations for functions that divide a standard
+// integer type by a fixed-point type, returning a standard integer type result.
+// This corresponds to the divi<fx> family (e.g., divir, divik) described
+// in ISO/IEC TR 18037:2008 Annex C.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_INT_DIV_FX_H
+#define LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_INT_DIV_FX_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h" // Fixed-point types
+#include "src/__support/CPP/bit.h" // bit_cast
+#include "src/__support/CPP/limits.h" // numeric_limits (optional)
+#include "src/__support/CPP/type_traits.h" // conditional_t, is_same_v
+#include "src/__support/macros/attributes.h" // LIBC_INLINE
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL, LIBC_COMPILER_HAS_FIXED_POINT
+#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+
+#include "fx_rep.h" // FXRep for type info (FRACTION_LEN, StorageType, IS_SIGNED)
+
+// Only define contents if the compiler supports fixed-point types
+#ifdef LIBC_COMPILER_HAS_FIXED_POINT
+
+// Check for 128-bit integer support needed for high precision intermediates
+#if defined(__SIZEOF_INT128__)
+#define LIBC_INTERNAL_HAS_INT128
+using int128_t = __int128_t;
+using uint128_t = __uint128_t;
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fixed_point {
+namespace internal {
+
+// --- Helper type traits for selecting intermediate calculation types ---
+
+template <typename IntType, int FractionalBits>
+using SelectDivIntermediateSigned =
+ cpp::conditional_t<
+ (sizeof(IntType) * 8 - 1 + FractionalBits <= 64), int64_t,
+#ifdef LIBC_INTERNAL_HAS_INT128
+ cpp::conditional_t<(sizeof(IntType) * 8 - 1 + FractionalBits <= 128),
+ int128_t,
+ void>
+#else
+ void
+#endif
+ >;
+
+template <typename IntType, int FractionalBits>
+using SelectDivIntermediateUnsigned = cpp::conditional_t<
+ (sizeof(IntType) * 8 - 1 + FractionalBits <= 64), uint64_t,
+#ifdef LIBC_INTERNAL_HAS_INT128
+ cpp::conditional_t<(sizeof(IntType) * 8 - 1 + FractionalBits <= 128),
+ uint128_t, void>
+#else
+ void
+#endif
+ >;
+
+// --- Core implementation template ---
+
+
+template <typename IntType, typename FxType>
+LIBC_INLINE IntType divifx_impl(IntType i, FxType fx) {
+ // Get metadata about the fixed-point type using FXRep helper
+ using FX = FXRep<FxType>;
+ using StorageType = typename FX::StorageType;
+ constexpr int F = FX::FRACTION_LEN; // Number of fractional bits
+ constexpr bool FxIsSigned = FX::SIGN_LEN; // Is the fx type signed?
+
+ // Extract the raw integer bits from the fixed-point divisor
+ StorageType raw_fx = cpp::bit_cast<StorageType>(fx);
+
+ volatile StorageType check_raw_fx = raw_fx;
+ if (LIBC_UNLIKELY(check_raw_fx == 0)) {
+
+ }
+
+ // Select appropriately sized intermediate types for the calculation
+ using IntermediateSigned = SelectDivIntermediateSigned<IntType, F>;
+ using IntermediateUnsigned = SelectDivIntermediateUnsigned<IntType, F>;
+
+ // Compile-time check: ensure a wide enough type was found.
+ static_assert(!cpp::is_same_v<IntermediateSigned, void>,
+ "Calculation requires intermediate precision exceeding "
+ "available types (int64_t or __int128_t).");
+
+ // Calculate the numerator: (i << F)
+ // Use the signed intermediate type for the numerator.
+ IntermediateSigned num = static_cast<IntermediateSigned>(i) << F;
+
+ // Perform the division: num / raw_fx
+ IntermediateSigned intermediate_result;
+ if constexpr (FxIsSigned) {
+ IntermediateSigned den = static_cast<IntermediateSigned>(raw_fx);
+ intermediate_result = num / den;
+ } else {
+ IntermediateUnsigned den = static_cast<IntermediateUnsigned>(raw_fx);
+ intermediate_result = num / den;
+ }
+
+ return static_cast<IntType>(intermediate_result);
+}
+
+} // namespace internal
+
+//===----------------------------------------------------------------------===//
+// Public API: divi<fx> functions
+//===----------------------------------------------------------------------===//
+
+// --- Signed Fract Types ---
+
+/** Divides int by fract, returns int. */
+LIBC_INLINE int divir(int i, fract f) {
+ return internal::divifx_impl<int, fract>(i, f);
+}
+
+} // namespace fixed_point
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_COMPILER_HAS_FIXED_POINT
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_INT_DIV_FX_H
diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt
index 843111e3f80b1..926035199c958 100644
--- a/libc/src/stdfix/CMakeLists.txt
+++ b/libc/src/stdfix/CMakeLists.txt
@@ -26,6 +26,20 @@ foreach(suffix IN ITEMS uhr ur ulr uhk uk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_entrypoint_object(
+ divi${suffix}
+ HDRS
+ divi${suffix}.h
+ SRCS
+ divi${suffix}.cpp
+ COMPILE_OPTIONS
+ ${libc_opt_high_flag}
+ DEPENDS
+ libc.src.__support.fixed_point.fx_bits
+ )
+endforeach()
+
foreach(suffix IN ITEMS hr r lr hk k lk uhr ur ulr uhk uk ulk)
add_entrypoint_object(
round${suffix}
diff --git a/libc/src/stdfix/divir.cpp b/libc/src/stdfix/divir.cpp
new file mode 100644
index 0000000000000..a952f9fba492f
--- /dev/null
+++ b/libc/src/stdfix/divir.cpp
@@ -0,0 +1,13 @@
+#include "divir.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/divifx.h" // divifx_impl
+#include "src/__support/fixed_point/fx_bits.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, divir, (int i, fract f)) {
+ return fixed_point::divir(i, f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/divir.h b/libc/src/stdfix/divir.h
new file mode 100644
index 0000000000000..ef803160ff1b5
--- /dev/null
+++ b/libc/src/stdfix/divir.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for divir function ----------------*- 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_DIVIR_H
+#define LLVM_LIBC_SRC_STDFIX_DIVIR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h" // Provides 'fract' type
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int divir(int i, fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_DIVIR_H
diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt
index e2b4bc1805f7c..d2449b628e87f 100644
--- a/libc/test/src/stdfix/CMakeLists.txt
+++ b/libc/test/src/stdfix/CMakeLists.txt
@@ -43,6 +43,23 @@ foreach(suffix IN ITEMS uhr ur ulr uhk uk)
)
endforeach()
+foreach(suffix IN ITEMS r)
+ add_libc_test(
+ divi${suffix}_test
+ SUITE
+ libc-stdfix-tests
+ HDRS
+ DiviFxTest.h
+ SRCS
+ divi${suffix}_test.cpp
+ COMPILE_OPTIONS
+ ${libc_opt_high_flag}
+ DEPENDS
+ libc.src.stdfix.divi${suffix}
+ libc.src.__support.fixed_point.fx_bits
+ )
+endforeach()
+
foreach(suffix IN ITEMS hr r lr hk k lk uhr ur ulr uhk uk ulk)
add_libc_test(
round${suffix}_test
diff --git a/libc/test/src/stdfix/DiviFxTest.h b/libc/test/src/stdfix/DiviFxTest.h
new file mode 100644
index 0000000000000..1ee2a5e7b2b48
--- /dev/null
+++ b/libc/test/src/stdfix/DiviFxTest.h
@@ -0,0 +1,40 @@
+//===-- Utility class to test bitsfx 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/fixed_point/divifx.h"
+
+template <typename T, typename XType>
+class DiviFxTest : public LIBC_NAMESPACE::testing::Test {
+
+ using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<T>;
+ static constexpr T zero = FXRep::ZERO();
+ static constexpr T max = FXRep::MAX();
+ static constexpr T min = FXRep::MIN();
+ static constexpr T one_half = FXRep::ONE_HALF();
+ static constexpr T one_fourth = FXRep::ONE_FOURTH();
+ static constexpr T eps = FXRep::EPS();
+
+public:
+ typedef XType (*DiviFxFunc)(int, T);
+
+ void testSpecialNumbers(DiviFxFunc func) {
+ EXPECT_EQ(static_cast<XType>(200), func(100, one_half));
+ EXPECT_EQ(static_cast<XType>(400), func(100, one_fourth));
+ // std::cout << one_half << " " << one_fourth << std::endl;
+ }
+};
+
+#define LIST_DIVIFX_TESTS(Name, T, XType, func) \
+ using LlvmLibcDivifx##Name##Test = DiviFxTest<T, XType>; \
+ TEST_F(LlvmLibcDivifx##Name##Test, SpecialNumbers) { \
+ testSpecialNumbers(&func); \
+ } \
+ static_assert(true, "Require semicolon.")
diff --git a/libc/test/src/stdfix/divir_test.cpp b/libc/test/src/stdfix/divir_test.cpp
new file mode 100644
index 0000000000000..1fbd0f24852f5
--- /dev/null
+++ b/libc/test/src/stdfix/divir_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for divir -------------------------------------------===//
+//
+// 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 "DiviFxTest.h"
+
+#include "src/stdfix/divir.h"
+
+LIST_DIVIFX_TESTS(r, fract, int, LIBC_NAMESPACE::divir);
>From b1cf4307b1e5644577c0b12f69ed274c5f22702d Mon Sep 17 00:00:00 2001
From: kr-2003 <kumar.kr.abhinav at gmail.com>
Date: Fri, 2 May 2025 13:25:16 +0530
Subject: [PATCH 2/2] added divir test
---
libc/src/__support/fixed_point/divifx.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/fixed_point/divifx.h b/libc/src/__support/fixed_point/divifx.h
index 9f619ab87647c..af715612c327d 100644
--- a/libc/src/__support/fixed_point/divifx.h
+++ b/libc/src/__support/fixed_point/divifx.h
@@ -24,7 +24,7 @@
#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL, LIBC_COMPILER_HAS_FIXED_POINT
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
-#include "fx_rep.h" // FXRep for type info (FRACTION_LEN, StorageType, IS_SIGNED)
+#include "fx_rep.h" // FXRep for type info (FRACTION_LEN, StorageType)
// Only define contents if the compiler supports fixed-point types
#ifdef LIBC_COMPILER_HAS_FIXED_POINT
More information about the libc-commits
mailing list