From 7737e793aacb836bc5c0f8170194aaae9e3b29a0 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Fri, 2 May 2025 13:24:19 +0530 Subject: [PATCH 1/4] 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 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 +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 +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 +LIBC_INLINE IntType divifx_impl(IntType i, FxType fx) { + // Get metadata about the fixed-point type using FXRep helper + using FX = FXRep; + 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(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; + using IntermediateUnsigned = SelectDivIntermediateUnsigned; + + // Compile-time check: ensure a wide enough type was found. + static_assert(!cpp::is_same_v, + "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(i) << F; + + // Perform the division: num / raw_fx + IntermediateSigned intermediate_result; + if constexpr (FxIsSigned) { + IntermediateSigned den = static_cast(raw_fx); + intermediate_result = num / den; + } else { + IntermediateUnsigned den = static_cast(raw_fx); + intermediate_result = num / den; + } + + return static_cast(intermediate_result); +} + +} // namespace internal + +//===----------------------------------------------------------------------===// +// Public API: divi functions +//===----------------------------------------------------------------------===// + +// --- Signed Fract Types --- + +/** Divides int by fract, returns int. */ +LIBC_INLINE int divir(int i, fract f) { + return internal::divifx_impl(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 +class DiviFxTest : public LIBC_NAMESPACE::testing::Test { + + using FXRep = LIBC_NAMESPACE::fixed_point::FXRep; + 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(200), func(100, one_half)); + EXPECT_EQ(static_cast(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; \ + 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 Date: Fri, 2 May 2025 13:25:16 +0530 Subject: [PATCH 2/4] 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 From bdbad7114c1dc21392f22fba6188790abc9a02e3 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Sat, 3 May 2025 01:45:43 +0530 Subject: [PATCH 3/4] used uint128.h header --- libc/src/__support/fixed_point/divifx.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/libc/src/__support/fixed_point/divifx.h b/libc/src/__support/fixed_point/divifx.h index af715612c327d..6fcbbce0e544f 100644 --- a/libc/src/__support/fixed_point/divifx.h +++ b/libc/src/__support/fixed_point/divifx.h @@ -23,19 +23,13 @@ #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 "src/__support/uint128.h" // UInt128, Int128 (optional) #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 -// 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 { @@ -48,7 +42,7 @@ using SelectDivIntermediateSigned = (sizeof(IntType) * 8 - 1 + FractionalBits <= 64), int64_t, #ifdef LIBC_INTERNAL_HAS_INT128 cpp::conditional_t<(sizeof(IntType) * 8 - 1 + FractionalBits <= 128), - int128_t, + Int128, void> #else void @@ -60,7 +54,7 @@ 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> + UInt128, void> #else void #endif From 3ddd7bf9f815cbfa634201505ea2e1bd910e315b Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Sat, 3 May 2025 11:56:13 +0530 Subject: [PATCH 4/4] clang-formatted --- libc/src/__support/fixed_point/divifx.h | 20 ++++++++------------ libc/src/stdfix/divir.h | 2 +- libc/test/src/stdfix/DiviFxTest.h | 6 +++--- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/libc/src/__support/fixed_point/divifx.h b/libc/src/__support/fixed_point/divifx.h index 6fcbbce0e544f..6b751b3d12e8c 100644 --- a/libc/src/__support/fixed_point/divifx.h +++ b/libc/src/__support/fixed_point/divifx.h @@ -23,7 +23,7 @@ #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 "src/__support/uint128.h" // UInt128, Int128 (optional) +#include "src/__support/uint128.h" // UInt128, Int128 (optional) #include "fx_rep.h" // FXRep for type info (FRACTION_LEN, StorageType) @@ -37,17 +37,15 @@ namespace internal { // --- Helper type traits for selecting intermediate calculation types --- template -using SelectDivIntermediateSigned = - cpp::conditional_t< - (sizeof(IntType) * 8 - 1 + FractionalBits <= 64), int64_t, +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, - void> + cpp::conditional_t<(sizeof(IntType) * 8 - 1 + FractionalBits <= 128), + Int128, void> #else - void + void #endif - >; + >; template using SelectDivIntermediateUnsigned = cpp::conditional_t< @@ -62,13 +60,12 @@ using SelectDivIntermediateUnsigned = cpp::conditional_t< // --- Core implementation template --- - template LIBC_INLINE IntType divifx_impl(IntType i, FxType fx) { // Get metadata about the fixed-point type using FXRep helper using FX = FXRep; using StorageType = typename FX::StorageType; - constexpr int F = FX::FRACTION_LEN; // Number of fractional bits + 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 @@ -76,7 +73,6 @@ LIBC_INLINE IntType divifx_impl(IntType i, FxType fx) { volatile StorageType check_raw_fx = raw_fx; if (LIBC_UNLIKELY(check_raw_fx == 0)) { - } // Select appropriately sized intermediate types for the calculation diff --git a/libc/src/stdfix/divir.h b/libc/src/stdfix/divir.h index ef803160ff1b5..5466f5451a2b9 100644 --- a/libc/src/stdfix/divir.h +++ b/libc/src/stdfix/divir.h @@ -10,7 +10,7 @@ #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 +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL namespace LIBC_NAMESPACE_DECL { diff --git a/libc/test/src/stdfix/DiviFxTest.h b/libc/test/src/stdfix/DiviFxTest.h index 1ee2a5e7b2b48..2de13d2c261dc 100644 --- a/libc/test/src/stdfix/DiviFxTest.h +++ b/libc/test/src/stdfix/DiviFxTest.h @@ -8,8 +8,8 @@ #include "test/UnitTest/Test.h" -#include "src/__support/fixed_point/fx_rep.h" #include "src/__support/fixed_point/divifx.h" +#include "src/__support/fixed_point/fx_rep.h" template class DiviFxTest : public LIBC_NAMESPACE::testing::Test { @@ -33,8 +33,8 @@ class DiviFxTest : public LIBC_NAMESPACE::testing::Test { }; #define LIST_DIVIFX_TESTS(Name, T, XType, func) \ - using LlvmLibcDivifx##Name##Test = DiviFxTest; \ - TEST_F(LlvmLibcDivifx##Name##Test, SpecialNumbers) { \ + using LlvmLibcDivifx##Name##Test = DiviFxTest; \ + TEST_F(LlvmLibcDivifx##Name##Test, SpecialNumbers) { \ testSpecialNumbers(&func); \ } \ static_assert(true, "Require semicolon.")