https://github.com/chinmayingle updated https://github.com/llvm/llvm-project/pull/177588
>From 895043dfc49105b57694eba589d047c75530ebb3 Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Mon, 16 Feb 2026 14:25:43 +0100 Subject: [PATCH 1/2] [IR] Add ConstantExpr::getInBoundsPtrAdd() (#181639) As a followup to https://github.com/llvm/llvm-project/pull/181365, this adds the `getInBoundsPtrAdd()` variant and updates code to use it. --- clang/lib/CodeGen/ItaniumCXXABI.cpp | 3 +- libc/shared/math.h | 1 + libc/shared/math/hypot.h | 14 ++ libc/src/__support/math/CMakeLists.txt | 15 ++ libc/src/__support/math/hypot.h | 219 ++++++++++++++++++ libc/src/math/generic/CMakeLists.txt | 3 +- libc/src/math/generic/hypot.cpp | 7 +- libc/test/shared/CMakeLists.txt | 1 + libc/test/shared/shared_math_test.cpp | 22 ++ llvm/include/llvm/IR/Constants.h | 5 + llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 7 +- .../Instrumentation/IndirectCallPromotion.cpp | 4 +- .../Utils/CallPromotionUtilsTest.cpp | 4 +- .../llvm-project-overlay/libc/BUILD.bazel | 15 ++ 14 files changed, 303 insertions(+), 17 deletions(-) create mode 100644 libc/shared/math/hypot.h create mode 100644 libc/src/__support/math/hypot.h diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 759e512ed0719..c62bc4998c324 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -4074,8 +4074,7 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty, // The vtable address point is 8 bytes after its start: // 4 for the offset to top + 4 for the relative offset to rtti. llvm::Constant *Eight = llvm::ConstantInt::get(CGM.Int32Ty, 8); - VTable = - llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8Ty, VTable, Eight); + VTable = llvm::ConstantExpr::getInBoundsPtrAdd(VTable, Eight); } else { llvm::Constant *Two = llvm::ConstantInt::get(PtrDiffTy, 2); VTable = llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.GlobalsInt8PtrTy, diff --git a/libc/shared/math.h b/libc/shared/math.h index 93f7d6a8156cf..f4bdc670838c5 100644 --- a/libc/shared/math.h +++ b/libc/shared/math.h @@ -82,6 +82,7 @@ #include "math/fsqrt.h" #include "math/fsqrtf128.h" #include "math/fsqrtl.h" +#include "math/hypot.h" #include "math/hypotf.h" #include "math/hypotf16.h" #include "math/ilogb.h" diff --git a/libc/shared/math/hypot.h b/libc/shared/math/hypot.h new file mode 100644 index 0000000000000..383420fa3efd5 --- /dev/null +++ b/libc/shared/math/hypot.h @@ -0,0 +1,14 @@ +// libc/src/math/hypot.h +#ifndef LLVM_LIBC_SRC_MATH_HYPOT_H +#define LLVM_LIBC_SRC_MATH_HYPOT_H + +// #include "src/__support/macros/config.h" +#include "src/__support/math/hypot.h" + +namespace LIBC_NAMESPACE_DECL { + +// double hypot(double x, double y); +using math::hypot; +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_HYPOT_H \ No newline at end of file diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt index aaab78c01a891..14f6c3a821826 100644 --- a/libc/src/__support/math/CMakeLists.txt +++ b/libc/src/__support/math/CMakeLists.txt @@ -1218,6 +1218,21 @@ add_header_library( libc.src.__support.macros.properties.types ) +add_header_library( + hypot + HDRS + hypot.h + DEPENDS + libc.src.__support.common + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.rounding_mode + libc.src.__support.FPUtil.basic_operations + libc.src.__support.CPP.type_traits + libc.src.__support.CPP.bit + libc.src.__support.uint128 +) + add_header_library( hypotf HDRS diff --git a/libc/src/__support/math/hypot.h b/libc/src/__support/math/hypot.h new file mode 100644 index 0000000000000..355ee0a29f8d6 --- /dev/null +++ b/libc/src/__support/math/hypot.h @@ -0,0 +1,219 @@ +//===-- Implementation header for hypot -------------------------*- 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_SUPPORT_MATH_HYPOT_H +#define LLVM_LIBC_SRC_SUPPORT_MATH_HYPOT_H + +#include "src/__support/CPP/bit.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/FPUtil/BasicOperations.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/rounding_mode.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" +#include "src/__support/uint128.h" + +namespace LIBC_NAMESPACE_DECL { +namespace math { + +namespace internal { +// Helper to find the leading one in a mantissa. +template <typename T> +LIBC_INLINE constexpr T find_leading_one(T mant, int &shift_length) { + shift_length = 0; + if (mant > 0) { + shift_length = (sizeof(mant) * 8) - 1 - cpp::countl_zero(mant); + } + return static_cast<T>((T(1) << shift_length)); +} + +// DoubleLength structure mapping +template <typename T> struct DoubleLength; +template <> struct DoubleLength<uint16_t> { using Type = uint32_t; }; +template <> struct DoubleLength<uint32_t> { using Type = uint64_t; }; +template <> struct DoubleLength<uint64_t> { using Type = UInt128; }; + +} // namespace internal + +// Correctly rounded IEEE 754 HYPOT(x, y) with round to nearest, ties to even. +LIBC_INLINE constexpr double hypot(double x, double y) { + using FPBits_t = fputil::FPBits<double>; + using StorageType = FPBits_t::StorageType; + using DStorageType = internal::DoubleLength<StorageType>::Type; + + FPBits_t x_bits(x); + FPBits_t y_bits(y); + + FPBits_t x_abs = x_bits.abs(); + FPBits_t y_abs = y_bits.abs(); + + bool x_abs_larger = x_abs.uintval() >= y_abs.uintval(); + + FPBits_t a_bits = x_abs_larger ? x_abs : y_abs; + FPBits_t b_bits = x_abs_larger ? y_abs : x_abs; + + // 1. Handle Special Cases (Inf / NaN) + if (LIBC_UNLIKELY(a_bits.is_inf_or_nan())) { + if (x_abs.is_signaling_nan() || y_abs.is_signaling_nan()) { + if (!cpp::is_constant_evaluated()) + fputil::raise_except_if_required(FE_INVALID); + return FPBits_t::quiet_nan().get_val(); + } + if (x_abs.is_inf() || y_abs.is_inf()) + return FPBits_t::inf().get_val(); + if (x_abs.is_nan()) + return x; + return y; // y is nan + } + + uint16_t a_exp = a_bits.get_biased_exponent(); + uint16_t b_exp = b_bits.get_biased_exponent(); + + // 2. Trivial Case + if ((a_exp - b_exp >= FPBits_t::FRACTION_LEN + 2) || (x == 0) || (y == 0)) { + return x_abs.get_val() + y_abs.get_val(); + } + + // 3. Setup Soft-Float Arithmetic + uint64_t out_exp = a_exp; + StorageType a_mant = a_bits.get_mantissa(); + StorageType b_mant = b_bits.get_mantissa(); + + // FIXED: Initialized variables for constexpr + DStorageType a_mant_sq = 0; + DStorageType b_mant_sq = 0; + bool sticky_bits = false; + + constexpr StorageType ONE = StorageType(1) << (FPBits_t::FRACTION_LEN + 1); + + a_mant <<= 1; + b_mant <<= 1; + + // FIXED: Initialized variables for constexpr + StorageType leading_one = 0; + int y_mant_width = 0; + + if (a_exp != 0) { + leading_one = ONE; + a_mant |= ONE; + y_mant_width = FPBits_t::FRACTION_LEN + 1; + } else { + leading_one = internal::find_leading_one(a_mant, y_mant_width); + a_exp = 1; + } + + if (b_exp != 0) + b_mant |= ONE; + else + b_exp = 1; + + a_mant_sq = static_cast<DStorageType>(a_mant) * a_mant; + b_mant_sq = static_cast<DStorageType>(b_mant) * b_mant; + + // 4. Align and Add + uint16_t shift_length = static_cast<uint16_t>(2 * (a_exp - b_exp)); + sticky_bits = + ((b_mant_sq & ((DStorageType(1) << shift_length) - DStorageType(1))) != + DStorageType(0)); + b_mant_sq >>= shift_length; + + DStorageType sum = a_mant_sq + b_mant_sq; + + // 5. Normalize Sum + if (sum >= (DStorageType(1) << (2 * y_mant_width + 2))) { + if (leading_one == ONE) { + sticky_bits = sticky_bits || ((sum & 0x3U) != 0); + sum >>= 2; + ++out_exp; + + if (out_exp >= FPBits_t::MAX_BIASED_EXPONENT) { + if (!cpp::is_constant_evaluated()) { + int round_mode = fputil::quick_get_round(); + if (round_mode == FE_TONEAREST || round_mode == FE_UPWARD) + return FPBits_t::inf().get_val(); + } else { + return FPBits_t::inf().get_val(); + } + return FPBits_t::max_normal().get_val(); + } + } else { + leading_one <<= 1; + ++y_mant_width; + } + } + + // 6. Digit-by-Digit Sqrt + StorageType y_new = leading_one; + StorageType r = static_cast<StorageType>(sum >> y_mant_width) - leading_one; + StorageType tail_bits = static_cast<StorageType>(sum) & (leading_one - 1); + + for (StorageType current_bit = leading_one >> 1; current_bit; + current_bit >>= 1) { + r = static_cast<StorageType>((r << 1)) + + ((tail_bits & current_bit) ? 1 : 0); + StorageType tmp = static_cast<StorageType>((y_new << 1)) + + current_bit; + if (r >= tmp) { + r -= tmp; + y_new += current_bit; + } + } + + bool round_bit = y_new & StorageType(1); + bool lsb = y_new & StorageType(2); + + if (y_new >= ONE) { + y_new -= ONE; + if (out_exp == 0) out_exp = 1; + } + + y_new >>= 1; + + // 7. Rounding + int round_mode = FE_TONEAREST; + if (!cpp::is_constant_evaluated()) { + round_mode = fputil::quick_get_round(); + } + + switch (round_mode) { + case FE_TONEAREST: + if (round_bit && (lsb || sticky_bits || (r != 0))) + ++y_new; + break; + case FE_UPWARD: + if (round_bit || sticky_bits || (r != 0)) + ++y_new; + break; + } + + if (y_new >= (ONE >> 1)) { + y_new -= ONE >> 1; + ++out_exp; + if (out_exp >= FPBits_t::MAX_BIASED_EXPONENT) { + if (round_mode == FE_TONEAREST || round_mode == FE_UPWARD) + return FPBits_t::inf().get_val(); + return FPBits_t::max_normal().get_val(); + } + } + + y_new |= static_cast<StorageType>(out_exp) << FPBits_t::FRACTION_LEN; + + if (!cpp::is_constant_evaluated()) { + if (!(round_bit || sticky_bits || (r != 0))) + fputil::clear_except_if_required(FE_INEXACT); + } + + return cpp::bit_cast<double>(y_new); +} + +} // namespace math +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SUPPORT_MATH_HYPOT_H \ No newline at end of file diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 75b51b4587bea..b964f19bd1f53 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -3250,7 +3250,8 @@ add_entrypoint_object( HDRS ../hypot.h DEPENDS - libc.src.__support.FPUtil.hypot + libc.src.__support.math.hypot + libc.src.__support.FPUtil.fp_bits ) add_entrypoint_object( diff --git a/libc/src/math/generic/hypot.cpp b/libc/src/math/generic/hypot.cpp index 0dfe4360bafe0..e6e5b02abcca9 100644 --- a/libc/src/math/generic/hypot.cpp +++ b/libc/src/math/generic/hypot.cpp @@ -5,16 +5,13 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// - #include "src/math/hypot.h" -#include "src/__support/FPUtil/Hypot.h" -#include "src/__support/common.h" -#include "src/__support/macros/config.h" +#include "src/__support/math/hypot.h" namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(double, hypot, (double x, double y)) { - return LIBC_NAMESPACE::fputil::hypot(x, y); + return math::hypot(x, y); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt index 9dfbedee66a33..06f73d5c55ea5 100644 --- a/libc/test/shared/CMakeLists.txt +++ b/libc/test/shared/CMakeLists.txt @@ -78,6 +78,7 @@ add_fp_unittest( libc.src.__support.math.fsqrt libc.src.__support.math.fsqrtf128 libc.src.__support.math.fsqrtl + libc.src.__support.math.hypot libc.src.__support.math.hypotf libc.src.__support.math.hypotf16 libc.src.__support.math.ilogb diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp index 2d7ee388f754d..dced0c88da6e8 100644 --- a/libc/test/shared/shared_math_test.cpp +++ b/libc/test/shared/shared_math_test.cpp @@ -144,6 +144,28 @@ TEST(LlvmLibcSharedMathTest, AllFloat) { EXPECT_FP_EQ(0x0p+0f, canonicalizef_cx); } +// Add this AFTER the AllFloat test block +TEST(LlvmLibcSharedMathTest, Hypot) { + // 1. Basic Pythagorean triples (Runtime check) + // We use EXPECT_FP_EQ for safe floating-point comparison + EXPECT_FP_EQ(5.0, LIBC_NAMESPACE::math::hypot(3.0, 4.0)); + EXPECT_FP_EQ(13.0, LIBC_NAMESPACE::math::hypot(5.0, 12.0)); + + // 2. Compile-time check (Constexpr) + // This verifies your "header-only" logic works at compile time + constexpr double result = LIBC_NAMESPACE::math::hypot(3.0, 4.0); + static_assert(result == 5.0, "Constexpr hypot failed"); + + // 3. Special values (Inf/NaN) + constexpr double inf = __builtin_inf(); + constexpr double nan = __builtin_nan(""); + + // Use EXPECT_NE(..., 0) because __builtin functions return int, not bool + EXPECT_NE(__builtin_isinf(LIBC_NAMESPACE::math::hypot(inf, 1.0)), 0); + EXPECT_NE(__builtin_isinf(LIBC_NAMESPACE::math::hypot(1.0, inf)), 0); + EXPECT_NE(__builtin_isnan(LIBC_NAMESPACE::math::hypot(nan, 1.0)), 0); +} + TEST(LlvmLibcSharedMathTest, AllDouble) { EXPECT_FP_EQ(0x1.921fb54442d18p+0, LIBC_NAMESPACE::shared::acos(0.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::asin(0.0)); diff --git a/llvm/include/llvm/IR/Constants.h b/llvm/include/llvm/IR/Constants.h index b31490f9ca660..a1f667353b26e 100644 --- a/llvm/include/llvm/IR/Constants.h +++ b/llvm/include/llvm/IR/Constants.h @@ -1334,6 +1334,11 @@ class ConstantExpr : public Constant { return getGetElementPtr(Ty, C, IdxList, GEPNoWrapFlags::inBounds()); } + /// Create a getelementptr inbounds i8, ptr, offset constant expression. + static Constant *getInBoundsPtrAdd(Constant *Ptr, Constant *Offset) { + return getPtrAdd(Ptr, Offset, GEPNoWrapFlags::inBounds()); + } + LLVM_ABI static Constant *getExtractElement(Constant *Vec, Constant *Idx, Type *OnlyIfReducedTy = nullptr); LLVM_ABI static Constant *getInsertElement(Constant *Vec, Constant *Elt, diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp index c2b39754ba5df..518e91628a478 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -644,11 +644,8 @@ void LowerTypeTestsModule::allocateByteArrays() { for (unsigned I = 0; I != ByteArrayInfos.size(); ++I) { ByteArrayInfo *BAI = &ByteArrayInfos[I]; - - Constant *Idxs[] = {ConstantInt::get(IntPtrTy, 0), - ConstantInt::get(IntPtrTy, ByteArrayOffsets[I])}; - Constant *GEP = ConstantExpr::getInBoundsGetElementPtr( - ByteArrayConst->getType(), ByteArray, Idxs); + Constant *GEP = ConstantExpr::getInBoundsPtrAdd( + ByteArray, ConstantInt::get(IntPtrTy, ByteArrayOffsets[I])); // Create an alias instead of RAUW'ing the gep directly. On x86 this ensures // that the pc-relative displacement is folded into the lea instead of the diff --git a/llvm/lib/Transforms/Instrumentation/IndirectCallPromotion.cpp b/llvm/lib/Transforms/Instrumentation/IndirectCallPromotion.cpp index 48131955836b3..f3cdb3518ddcb 100644 --- a/llvm/lib/Transforms/Instrumentation/IndirectCallPromotion.cpp +++ b/llvm/lib/Transforms/Instrumentation/IndirectCallPromotion.cpp @@ -216,8 +216,8 @@ static Constant *getVTableAddressPointOffset(GlobalVariable *VTable, assert(AddressPointOffset < VTable->getGlobalSize(M.getDataLayout()) && "Out-of-bound access"); - return ConstantExpr::getInBoundsGetElementPtr( - Type::getInt8Ty(Context), VTable, + return ConstantExpr::getInBoundsPtrAdd( + VTable, llvm::ConstantInt::get(Type::getInt32Ty(Context), AddressPointOffset)); } diff --git a/llvm/unittests/Transforms/Utils/CallPromotionUtilsTest.cpp b/llvm/unittests/Transforms/Utils/CallPromotionUtilsTest.cpp index 59c047afd84df..c859b0c799f08 100644 --- a/llvm/unittests/Transforms/Utils/CallPromotionUtilsTest.cpp +++ b/llvm/unittests/Transforms/Utils/CallPromotionUtilsTest.cpp @@ -43,8 +43,8 @@ static Constant *getVTableAddressPointOffset(GlobalVariable *VTable, M.getDataLayout().getTypeAllocSize(VTable->getValueType()) && "Out-of-bound access"); - return ConstantExpr::getInBoundsGetElementPtr( - Type::getInt8Ty(Context), VTable, + return ConstantExpr::getInBoundsPtrAdd( + VTable, llvm::ConstantInt::get(Type::getInt32Ty(Context), AddressPointOffset)); } diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 608180b90e87b..f482bddacfa3d 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -4059,6 +4059,21 @@ libc_support_library( ], ) +libc_support_library( + name = "src___support_math_hypot", + hdrs = ["src/__support/math/hypot.h"], + deps = [ + ":__support_common", + ":__support_cpp_bit", + ":__support_cpp_type_traits", + ":__support_fp_util_basic_operations", + ":__support_fp_util_fenv_impl", + ":__support_fp_util_fp_bits", + ":__support_fp_util_rounding_mode", + ":__support_uint128", + ], +) + ############################### complex targets ################################ libc_function( >From 3ac475768e7e3b3d6caa111a1fc3135ab4ba8197 Mon Sep 17 00:00:00 2001 From: chinmayingle <[email protected]> Date: Tue, 27 Jan 2026 13:00:55 +0530 Subject: [PATCH 2/2] Address review comments --- libc/shared/math/hypot.h | 20 +- libc/src/__support/math/CMakeLists.txt | 11 +- libc/src/__support/math/hypot.h | 209 +----------------- libc/src/math/generic/CMakeLists.txt | 1 - libc/test/shared/shared_math_test.cpp | 23 +- .../llvm-project-overlay/libc/BUILD.bazel | 9 + 6 files changed, 35 insertions(+), 238 deletions(-) diff --git a/libc/shared/math/hypot.h b/libc/shared/math/hypot.h index 383420fa3efd5..51fd006397c02 100644 --- a/libc/shared/math/hypot.h +++ b/libc/shared/math/hypot.h @@ -1,14 +1,22 @@ -// libc/src/math/hypot.h -#ifndef LLVM_LIBC_SRC_MATH_HYPOT_H -#define LLVM_LIBC_SRC_MATH_HYPOT_H +//===-- Shared hypot 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_SHARED_MATH_HYPOT_H +#define LLVM_LIBC_SHARED_MATH_HYPOT_H -// #include "src/__support/macros/config.h" #include "src/__support/math/hypot.h" namespace LIBC_NAMESPACE_DECL { +namespace shared { -// double hypot(double x, double y); using math::hypot; + +} // namespace shared } // namespace LIBC_NAMESPACE_DECL -#endif // LLVM_LIBC_SRC_MATH_HYPOT_H \ No newline at end of file +#endif // LLVM_LIBC_SHARED_MATH_HYPOT_H diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt index 14f6c3a821826..d610fac920401 100644 --- a/libc/src/__support/math/CMakeLists.txt +++ b/libc/src/__support/math/CMakeLists.txt @@ -1251,14 +1251,9 @@ add_header_library( HDRS hypotf16.h DEPENDS - libc.src.__support.FPUtil.fenv_impl - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.cast - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.sqrt - libc.src.__support.macros.optimization - libc.src.__support.macros.properties.types - libc.include.llvm-libc-macros.float16_macros + libc.src.__support.common + libc.src.__support.macros.config + libc.src.__support.FPUtil.hypot ) add_header_library( diff --git a/libc/src/__support/math/hypot.h b/libc/src/__support/math/hypot.h index 355ee0a29f8d6..6d388345e0bf0 100644 --- a/libc/src/__support/math/hypot.h +++ b/libc/src/__support/math/hypot.h @@ -1,4 +1,5 @@ -//===-- Implementation header for hypot -------------------------*- C++ -*-===// +//===-- Implementation header for hypot --------------------------*- C++ +//-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,214 +7,20 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIBC_SRC_SUPPORT_MATH_HYPOT_H -#define LLVM_LIBC_SRC_SUPPORT_MATH_HYPOT_H +#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_HYPOT_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_HYPOT_H -#include "src/__support/CPP/bit.h" -#include "src/__support/CPP/type_traits.h" -#include "src/__support/FPUtil/BasicOperations.h" -#include "src/__support/FPUtil/FEnvImpl.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/rounding_mode.h" +#include "src/__support/FPUtil/Hypot.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/__support/macros/optimization.h" -#include "src/__support/uint128.h" namespace LIBC_NAMESPACE_DECL { -namespace math { - -namespace internal { -// Helper to find the leading one in a mantissa. -template <typename T> -LIBC_INLINE constexpr T find_leading_one(T mant, int &shift_length) { - shift_length = 0; - if (mant > 0) { - shift_length = (sizeof(mant) * 8) - 1 - cpp::countl_zero(mant); - } - return static_cast<T>((T(1) << shift_length)); -} - -// DoubleLength structure mapping -template <typename T> struct DoubleLength; -template <> struct DoubleLength<uint16_t> { using Type = uint32_t; }; -template <> struct DoubleLength<uint32_t> { using Type = uint64_t; }; -template <> struct DoubleLength<uint64_t> { using Type = UInt128; }; - -} // namespace internal - -// Correctly rounded IEEE 754 HYPOT(x, y) with round to nearest, ties to even. -LIBC_INLINE constexpr double hypot(double x, double y) { - using FPBits_t = fputil::FPBits<double>; - using StorageType = FPBits_t::StorageType; - using DStorageType = internal::DoubleLength<StorageType>::Type; - - FPBits_t x_bits(x); - FPBits_t y_bits(y); - - FPBits_t x_abs = x_bits.abs(); - FPBits_t y_abs = y_bits.abs(); - - bool x_abs_larger = x_abs.uintval() >= y_abs.uintval(); - - FPBits_t a_bits = x_abs_larger ? x_abs : y_abs; - FPBits_t b_bits = x_abs_larger ? y_abs : x_abs; - - // 1. Handle Special Cases (Inf / NaN) - if (LIBC_UNLIKELY(a_bits.is_inf_or_nan())) { - if (x_abs.is_signaling_nan() || y_abs.is_signaling_nan()) { - if (!cpp::is_constant_evaluated()) - fputil::raise_except_if_required(FE_INVALID); - return FPBits_t::quiet_nan().get_val(); - } - if (x_abs.is_inf() || y_abs.is_inf()) - return FPBits_t::inf().get_val(); - if (x_abs.is_nan()) - return x; - return y; // y is nan - } - - uint16_t a_exp = a_bits.get_biased_exponent(); - uint16_t b_exp = b_bits.get_biased_exponent(); - - // 2. Trivial Case - if ((a_exp - b_exp >= FPBits_t::FRACTION_LEN + 2) || (x == 0) || (y == 0)) { - return x_abs.get_val() + y_abs.get_val(); - } - - // 3. Setup Soft-Float Arithmetic - uint64_t out_exp = a_exp; - StorageType a_mant = a_bits.get_mantissa(); - StorageType b_mant = b_bits.get_mantissa(); - - // FIXED: Initialized variables for constexpr - DStorageType a_mant_sq = 0; - DStorageType b_mant_sq = 0; - bool sticky_bits = false; - - constexpr StorageType ONE = StorageType(1) << (FPBits_t::FRACTION_LEN + 1); - - a_mant <<= 1; - b_mant <<= 1; - - // FIXED: Initialized variables for constexpr - StorageType leading_one = 0; - int y_mant_width = 0; - - if (a_exp != 0) { - leading_one = ONE; - a_mant |= ONE; - y_mant_width = FPBits_t::FRACTION_LEN + 1; - } else { - leading_one = internal::find_leading_one(a_mant, y_mant_width); - a_exp = 1; - } - if (b_exp != 0) - b_mant |= ONE; - else - b_exp = 1; - - a_mant_sq = static_cast<DStorageType>(a_mant) * a_mant; - b_mant_sq = static_cast<DStorageType>(b_mant) * b_mant; - - // 4. Align and Add - uint16_t shift_length = static_cast<uint16_t>(2 * (a_exp - b_exp)); - sticky_bits = - ((b_mant_sq & ((DStorageType(1) << shift_length) - DStorageType(1))) != - DStorageType(0)); - b_mant_sq >>= shift_length; - - DStorageType sum = a_mant_sq + b_mant_sq; - - // 5. Normalize Sum - if (sum >= (DStorageType(1) << (2 * y_mant_width + 2))) { - if (leading_one == ONE) { - sticky_bits = sticky_bits || ((sum & 0x3U) != 0); - sum >>= 2; - ++out_exp; - - if (out_exp >= FPBits_t::MAX_BIASED_EXPONENT) { - if (!cpp::is_constant_evaluated()) { - int round_mode = fputil::quick_get_round(); - if (round_mode == FE_TONEAREST || round_mode == FE_UPWARD) - return FPBits_t::inf().get_val(); - } else { - return FPBits_t::inf().get_val(); - } - return FPBits_t::max_normal().get_val(); - } - } else { - leading_one <<= 1; - ++y_mant_width; - } - } - - // 6. Digit-by-Digit Sqrt - StorageType y_new = leading_one; - StorageType r = static_cast<StorageType>(sum >> y_mant_width) - leading_one; - StorageType tail_bits = static_cast<StorageType>(sum) & (leading_one - 1); - - for (StorageType current_bit = leading_one >> 1; current_bit; - current_bit >>= 1) { - r = static_cast<StorageType>((r << 1)) + - ((tail_bits & current_bit) ? 1 : 0); - StorageType tmp = static_cast<StorageType>((y_new << 1)) + - current_bit; - if (r >= tmp) { - r -= tmp; - y_new += current_bit; - } - } - - bool round_bit = y_new & StorageType(1); - bool lsb = y_new & StorageType(2); - - if (y_new >= ONE) { - y_new -= ONE; - if (out_exp == 0) out_exp = 1; - } - - y_new >>= 1; - - // 7. Rounding - int round_mode = FE_TONEAREST; - if (!cpp::is_constant_evaluated()) { - round_mode = fputil::quick_get_round(); - } - - switch (round_mode) { - case FE_TONEAREST: - if (round_bit && (lsb || sticky_bits || (r != 0))) - ++y_new; - break; - case FE_UPWARD: - if (round_bit || sticky_bits || (r != 0)) - ++y_new; - break; - } - - if (y_new >= (ONE >> 1)) { - y_new -= ONE >> 1; - ++out_exp; - if (out_exp >= FPBits_t::MAX_BIASED_EXPONENT) { - if (round_mode == FE_TONEAREST || round_mode == FE_UPWARD) - return FPBits_t::inf().get_val(); - return FPBits_t::max_normal().get_val(); - } - } - - y_new |= static_cast<StorageType>(out_exp) << FPBits_t::FRACTION_LEN; - - if (!cpp::is_constant_evaluated()) { - if (!(round_bit || sticky_bits || (r != 0))) - fputil::clear_except_if_required(FE_INEXACT); - } +namespace math { - return cpp::bit_cast<double>(y_new); -} +LIBC_INLINE double hypot(double x, double y) { return fputil::hypot(x, y); } } // namespace math } // namespace LIBC_NAMESPACE_DECL -#endif // LLVM_LIBC_SRC_SUPPORT_MATH_HYPOT_H \ No newline at end of file +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_HYPOT_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index b964f19bd1f53..c6af9de44840a 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -3251,7 +3251,6 @@ add_entrypoint_object( ../hypot.h DEPENDS libc.src.__support.math.hypot - libc.src.__support.FPUtil.fp_bits ) add_entrypoint_object( diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp index dced0c88da6e8..6dddb749eac12 100644 --- a/libc/test/shared/shared_math_test.cpp +++ b/libc/test/shared/shared_math_test.cpp @@ -144,28 +144,6 @@ TEST(LlvmLibcSharedMathTest, AllFloat) { EXPECT_FP_EQ(0x0p+0f, canonicalizef_cx); } -// Add this AFTER the AllFloat test block -TEST(LlvmLibcSharedMathTest, Hypot) { - // 1. Basic Pythagorean triples (Runtime check) - // We use EXPECT_FP_EQ for safe floating-point comparison - EXPECT_FP_EQ(5.0, LIBC_NAMESPACE::math::hypot(3.0, 4.0)); - EXPECT_FP_EQ(13.0, LIBC_NAMESPACE::math::hypot(5.0, 12.0)); - - // 2. Compile-time check (Constexpr) - // This verifies your "header-only" logic works at compile time - constexpr double result = LIBC_NAMESPACE::math::hypot(3.0, 4.0); - static_assert(result == 5.0, "Constexpr hypot failed"); - - // 3. Special values (Inf/NaN) - constexpr double inf = __builtin_inf(); - constexpr double nan = __builtin_nan(""); - - // Use EXPECT_NE(..., 0) because __builtin functions return int, not bool - EXPECT_NE(__builtin_isinf(LIBC_NAMESPACE::math::hypot(inf, 1.0)), 0); - EXPECT_NE(__builtin_isinf(LIBC_NAMESPACE::math::hypot(1.0, inf)), 0); - EXPECT_NE(__builtin_isnan(LIBC_NAMESPACE::math::hypot(nan, 1.0)), 0); -} - TEST(LlvmLibcSharedMathTest, AllDouble) { EXPECT_FP_EQ(0x1.921fb54442d18p+0, LIBC_NAMESPACE::shared::acos(0.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::asin(0.0)); @@ -179,6 +157,7 @@ TEST(LlvmLibcSharedMathTest, AllDouble) { EXPECT_FP_EQ(0x1p+0, LIBC_NAMESPACE::shared::exp10(0.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::expm1(0.0)); EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::ffma(0.0, 0.0, 0.0)); + EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::shared::hypot(0.0, 0.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::fsqrt(0.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::log(1.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::log10(1.0)); diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index f482bddacfa3d..7724dc6a0e4a8 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -3919,6 +3919,15 @@ libc_support_library( ], ) +libc_support_library( + name = "___support_math_hypot", + hdrs = ["src/__support/math/hypot.h"], + deps = [ + ":__support_common", + ":__support_fputil_hypot", + ], +) + libc_support_library( name = "__support_math_hypotf", hdrs = ["src/__support/math/hypotf.h"], _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
