[libc-commits] [clang] [libc] [llvm] [libc][math] Move hypot to shared/math and make it constexpr (PR #177588)

Chinmay Ingle via libc-commits libc-commits at lists.llvm.org
Mon Feb 16 06:14:32 PST 2026


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 <npopov at redhat.com>
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 <chinmay.r.ingle at gmail.com>
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"],



More information about the libc-commits mailing list