Project import generated by Copybara. am: 76a5866889

Original change: https://android-review.googlesource.com/c/platform/external/llvm-libc/+/3495325

Change-Id: Ib44c0295c3524baf4271d9586484e6f67fef6b77
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/include/__llvm-libc-common.h b/include/__llvm-libc-common.h
index d3f8a4e..a0fa506 100644
--- a/include/__llvm-libc-common.h
+++ b/include/__llvm-libc-common.h
@@ -9,6 +9,8 @@
 #ifndef LLVM_LIBC_COMMON_H
 #define LLVM_LIBC_COMMON_H
 
+#define __LLVM_LIBC__ 1
+
 #ifdef __cplusplus
 
 #undef __BEGIN_C_DECLS
diff --git a/include/llvm-libc-macros/endian-macros.h b/include/llvm-libc-macros/endian-macros.h
index 94e1d60..e1e105d 100644
--- a/include/llvm-libc-macros/endian-macros.h
+++ b/include/llvm-libc-macros/endian-macros.h
@@ -9,8 +9,42 @@
 #ifndef LLVM_LIBC_MACROS_ENDIAN_MACROS_H
 #define LLVM_LIBC_MACROS_ENDIAN_MACROS_H
 
+#include "stdint-macros.h"
+
 #define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
 #define BIG_ENDIAN __ORDER_BIG_ENDIAN__
 #define BYTE_ORDER __BYTE_ORDER__
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define htobe16(x) __builtin_bswap16((x))
+#define htobe32(x) __builtin_bswap32((x))
+#define htobe64(x) __builtin_bswap64((x))
+#define htole16(x) ((uint16_t)(x))
+#define htole32(x) ((uint32_t)(x))
+#define htole64(x) ((uint64_t)(x))
+#define be16toh(x) __builtin_bswap16((x))
+#define be32toh(x) __builtin_bswap32((x))
+#define be64toh(x) __builtin_bswap64((x))
+#define le16toh(x) ((uint16_t)(x))
+#define le32toh(x) ((uint32_t)(x))
+#define le64toh(x) ((uint64_t)(x))
+
+#else
+
+#define htobe16(x) ((uint16_t)(x))
+#define htobe32(x) ((uint32_t)(x))
+#define htobe64(x) ((uint64_t)(x))
+#define htole16(x) __builtin_bswap16((x))
+#define htole32(x) __builtin_bswap32((x))
+#define htole64(x) __builtin_bswap64((x))
+#define be16toh(x) ((uint16_t)(x))
+#define be32toh(x) ((uint32_t)(x))
+#define be64toh(x) ((uint64_t)(x))
+#define le16toh(x) __builtin_bswap16((x))
+#define le32toh(x) __builtin_bswap32((x))
+#define le64toh(x) __builtin_bswap64((x))
+
+#endif
+
 #endif // LLVM_LIBC_MACROS_ENDIAN_MACROS_H
diff --git a/include/llvm-libc-macros/features-macros.h b/include/llvm-libc-macros/features-macros.h
index 5bc87a6..f87ae4a 100644
--- a/include/llvm-libc-macros/features-macros.h
+++ b/include/llvm-libc-macros/features-macros.h
@@ -9,6 +9,4 @@
 #ifndef LLVM_LIBC_MACROS_FEATURES_MACROS_H
 #define LLVM_LIBC_MACROS_FEATURES_MACROS_H
 
-#define __LLVM_LIBC__ 1
-
 #endif // LLVM_LIBC_MACROS_FEATURES_MACROS_H
diff --git a/include/llvm-libc-macros/pthread-macros.h b/include/llvm-libc-macros/pthread-macros.h
index 8a144db..fcc6ef9 100644
--- a/include/llvm-libc-macros/pthread-macros.h
+++ b/include/llvm-libc-macros/pthread-macros.h
@@ -9,6 +9,8 @@
 #ifndef LLVM_LIBC_MACROS_PTHREAD_MACRO_H
 #define LLVM_LIBC_MACROS_PTHREAD_MACRO_H
 
+#include "null-macro.h"
+
 #define PTHREAD_CREATE_JOINABLE 0
 #define PTHREAD_CREATE_DETACHED 1
 
@@ -25,8 +27,34 @@
 #define PTHREAD_PROCESS_PRIVATE 0
 #define PTHREAD_PROCESS_SHARED 1
 
-#define PTHREAD_MUTEX_INITIALIZER {0}
-#define PTHREAD_RWLOCK_INITIALIZER {0}
+#ifdef __linux__
+#define PTHREAD_MUTEX_INITIALIZER                                              \
+  {                                                                            \
+      /* .__timed = */ 0,      /* .__recursive = */ 0,                         \
+      /* .__robust = */ 0,     /* .__owner = */ NULL,                          \
+      /* .__lock_count = */ 0, /* .__futex_word = */ {0},                      \
+  }
+#else
+#define PTHREAD_MUTEX_INITIALIZER                                              \
+  {                                                                            \
+      /* .__timed = */ 0,      /* .__recursive = */ 0,                         \
+      /* .__robust = */ 0,     /* .__owner = */ NULL,                          \
+      /* .__lock_count = */ 0,                                                 \
+  }
+#endif
+
+#define PTHREAD_RWLOCK_INITIALIZER                                             \
+  {                                                                            \
+      /* .__is_pshared = */ 0,                                                 \
+      /* .__preference = */ 0,                                                 \
+      /* .__state = */ 0,                                                      \
+      /* .__write_tid = */ 0,                                                  \
+      /* .__wait_queue_mutex = */ {0},                                         \
+      /* .__pending_readers = */ {0},                                          \
+      /* .__pending_writers = */ {0},                                          \
+      /* .__reader_serialization = */ {0},                                     \
+      /* .__writer_serialization = */ {0},                                     \
+  }
 
 // glibc extensions
 #define PTHREAD_STACK_MIN (1 << 14) // 16KB
diff --git a/include/llvm-libc-types/struct_tm.h b/include/llvm-libc-types/struct_tm.h
index 9fef7c5..2ec74ec 100644
--- a/include/llvm-libc-types/struct_tm.h
+++ b/include/llvm-libc-types/struct_tm.h
@@ -19,6 +19,7 @@
   int tm_wday;  // days since Sunday
   int tm_yday;  // days since January
   int tm_isdst; // Daylight Saving Time flag
+  // TODO: add tm_gmtoff and tm_zone? (posix extensions)
 };
 
 #endif // LLVM_LIBC_TYPES_STRUCT_TM_H
diff --git a/src/__support/CPP/bit.h b/src/__support/CPP/bit.h
index adcd047..82b9eb5 100644
--- a/src/__support/CPP/bit.h
+++ b/src/__support/CPP/bit.h
@@ -232,7 +232,7 @@
     return value;
   if (rotate < 0)
     return cpp::rotr<T>(value, -rotate);
-  return (value << rotate) | (value >> (N - rotate));
+  return static_cast<T>((value << rotate) | (value >> (N - rotate)));
 }
 
 template <typename T>
@@ -244,7 +244,7 @@
     return value;
   if (rotate < 0)
     return cpp::rotl<T>(value, -rotate);
-  return (value >> rotate) | (value << (N - rotate));
+  return static_cast<T>((value >> rotate) | (value << (N - rotate)));
 }
 
 // TODO: Do we need this function at all? How is it different from
diff --git a/src/__support/FPUtil/FMA.h b/src/__support/FPUtil/FMA.h
index 807d685..1e40d06 100644
--- a/src/__support/FPUtil/FMA.h
+++ b/src/__support/FPUtil/FMA.h
@@ -25,11 +25,19 @@
 
 #ifdef LIBC_TARGET_CPU_HAS_FMA
 template <> LIBC_INLINE float fma(float x, float y, float z) {
+#if __has_builtin(__builtin_elementwise_fma)
+  return __builtin_elementwise_fma(x, y, z);
+#else
   return __builtin_fmaf(x, y, z);
+#endif
 }
 
 template <> LIBC_INLINE double fma(double x, double y, double z) {
+#if __has_builtin(__builtin_elementwise_fma)
+  return __builtin_elementwise_fma(x, y, z);
+#else
   return __builtin_fma(x, y, z);
+#endif
 }
 #endif // LIBC_TARGET_CPU_HAS_FMA
 
diff --git a/src/__support/FPUtil/double_double.h b/src/__support/FPUtil/double_double.h
index db3c2c8..b24ffd4 100644
--- a/src/__support/FPUtil/double_double.h
+++ b/src/__support/FPUtil/double_double.h
@@ -18,43 +18,52 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace fputil {
 
-#define DEFAULT_DOUBLE_SPLIT 27
+template <typename T> struct DefaultSplit;
+template <> struct DefaultSplit<float> {
+  static constexpr size_t VALUE = 12;
+};
+template <> struct DefaultSplit<double> {
+  static constexpr size_t VALUE = 27;
+};
 
-using DoubleDouble = LIBC_NAMESPACE::NumberPair<double>;
+using DoubleDouble = NumberPair<double>;
+using FloatFloat = NumberPair<float>;
 
 // The output of Dekker's FastTwoSum algorithm is correct, i.e.:
 //   r.hi + r.lo = a + b exactly
 //   and |r.lo| < eps(r.lo)
 // Assumption: |a| >= |b|, or a = 0.
-template <bool FAST2SUM = true>
-LIBC_INLINE constexpr DoubleDouble exact_add(double a, double b) {
-  DoubleDouble r{0.0, 0.0};
+template <bool FAST2SUM = true, typename T = double>
+LIBC_INLINE constexpr NumberPair<T> exact_add(T a, T b) {
+  NumberPair<T> r{0.0, 0.0};
   if constexpr (FAST2SUM) {
     r.hi = a + b;
-    double t = r.hi - a;
+    T t = r.hi - a;
     r.lo = b - t;
   } else {
     r.hi = a + b;
-    double t1 = r.hi - a;
-    double t2 = r.hi - t1;
-    double t3 = b - t1;
-    double t4 = a - t2;
+    T t1 = r.hi - a;
+    T t2 = r.hi - t1;
+    T t3 = b - t1;
+    T t4 = a - t2;
     r.lo = t3 + t4;
   }
   return r;
 }
 
 // Assumption: |a.hi| >= |b.hi|
-LIBC_INLINE constexpr DoubleDouble add(const DoubleDouble &a,
-                                       const DoubleDouble &b) {
-  DoubleDouble r = exact_add(a.hi, b.hi);
-  double lo = a.lo + b.lo;
+template <typename T>
+LIBC_INLINE constexpr NumberPair<T> add(const NumberPair<T> &a,
+                                        const NumberPair<T> &b) {
+  NumberPair<T> r = exact_add(a.hi, b.hi);
+  T lo = a.lo + b.lo;
   return exact_add(r.hi, r.lo + lo);
 }
 
 // Assumption: |a.hi| >= |b|
-LIBC_INLINE constexpr DoubleDouble add(const DoubleDouble &a, double b) {
-  DoubleDouble r = exact_add<false>(a.hi, b);
+template <typename T>
+LIBC_INLINE constexpr NumberPair<T> add(const NumberPair<T> &a, T b) {
+  NumberPair<T> r = exact_add<false>(a.hi, b);
   return exact_add(r.hi, r.lo + a.lo);
 }
 
@@ -63,12 +72,12 @@
 //   Zimmermann, P., "Note on the Veltkamp/Dekker Algorithms with Directed
 //   Roundings," https://inria.hal.science/hal-04480440.
 // Default splitting constant = 2^ceil(prec(double)/2) + 1 = 2^27 + 1.
-template <size_t N = DEFAULT_DOUBLE_SPLIT>
-LIBC_INLINE constexpr DoubleDouble split(double a) {
-  DoubleDouble r{0.0, 0.0};
+template <typename T = double, size_t N = DefaultSplit<T>::VALUE>
+LIBC_INLINE constexpr NumberPair<T> split(T a) {
+  NumberPair<T> r{0.0, 0.0};
   // CN = 2^N.
-  constexpr double CN = static_cast<double>(1 << N);
-  constexpr double C = CN + 1.0;
+  constexpr T CN = static_cast<T>(1 << N);
+  constexpr T C = CN + 1.0;
   double t1 = C * a;
   double t2 = a - t1;
   r.hi = t1 + t2;
@@ -77,16 +86,15 @@
 }
 
 // Helper for non-fma exact mult where the first number is already split.
-template <size_t SPLIT_B = DEFAULT_DOUBLE_SPLIT>
-LIBC_INLINE DoubleDouble exact_mult(const DoubleDouble &as, double a,
-                                    double b) {
-  DoubleDouble bs = split<SPLIT_B>(b);
-  DoubleDouble r{0.0, 0.0};
+template <typename T = double, size_t SPLIT_B = DefaultSplit<T>::VALUE>
+LIBC_INLINE NumberPair<T> exact_mult(const NumberPair<T> &as, T a, T b) {
+  NumberPair<T> bs = split<T, SPLIT_B>(b);
+  NumberPair<T> r{0.0, 0.0};
 
   r.hi = a * b;
-  double t1 = as.hi * bs.hi - r.hi;
-  double t2 = as.hi * bs.lo + t1;
-  double t3 = as.lo * bs.hi + t2;
+  T t1 = as.hi * bs.hi - r.hi;
+  T t2 = as.hi * bs.lo + t1;
+  T t3 = as.lo * bs.hi + t2;
   r.lo = as.lo * bs.lo + t3;
 
   return r;
@@ -99,18 +107,18 @@
 // Using Theorem 1 in the paper above, without FMA instruction, if we restrict
 // the generated constants to precision <= 51, and splitting it by 2^28 + 1,
 // then a * b = r.hi + r.lo is exact for all rounding modes.
-template <size_t SPLIT_B = 27>
-LIBC_INLINE DoubleDouble exact_mult(double a, double b) {
-  DoubleDouble r{0.0, 0.0};
+template <typename T = double, size_t SPLIT_B = DefaultSplit<T>::VALUE>
+LIBC_INLINE NumberPair<T> exact_mult(T a, T b) {
+  NumberPair<T> r{0.0, 0.0};
 
 #ifdef LIBC_TARGET_CPU_HAS_FMA
   r.hi = a * b;
   r.lo = fputil::multiply_add(a, b, -r.hi);
 #else
   // Dekker's Product.
-  DoubleDouble as = split(a);
+  NumberPair<T> as = split(a);
 
-  r = exact_mult<SPLIT_B>(as, a, b);
+  r = exact_mult<T, SPLIT_B>(as, a, b);
 #endif // LIBC_TARGET_CPU_HAS_FMA
 
   return r;
@@ -125,7 +133,7 @@
 template <size_t SPLIT_B = 27>
 LIBC_INLINE DoubleDouble quick_mult(const DoubleDouble &a,
                                     const DoubleDouble &b) {
-  DoubleDouble r = exact_mult<SPLIT_B>(a.hi, b.hi);
+  DoubleDouble r = exact_mult<double, SPLIT_B>(a.hi, b.hi);
   double t1 = multiply_add(a.hi, b.lo, r.lo);
   double t2 = multiply_add(a.lo, b.hi, t1);
   r.lo = t2;
@@ -157,19 +165,20 @@
 //   rl = q * (ah - bh * rh) + q * (al - bl * rh)
 // as accurate as possible, then the error is bounded by:
 //   |(ah + al) / (bh + bl) - (rh + rl)| < O(bl/bh) * (2^-52 + al/ah + bl/bh)
-LIBC_INLINE DoubleDouble div(const DoubleDouble &a, const DoubleDouble &b) {
-  DoubleDouble r;
-  double q = 1.0 / b.hi;
+template <typename T>
+LIBC_INLINE NumberPair<T> div(const NumberPair<T> &a, const NumberPair<T> &b) {
+  NumberPair<T> r;
+  T q = T(1) / b.hi;
   r.hi = a.hi * q;
 
 #ifdef LIBC_TARGET_CPU_HAS_FMA
-  double e_hi = fputil::multiply_add(b.hi, -r.hi, a.hi);
-  double e_lo = fputil::multiply_add(b.lo, -r.hi, a.lo);
+  T e_hi = fputil::multiply_add(b.hi, -r.hi, a.hi);
+  T e_lo = fputil::multiply_add(b.lo, -r.hi, a.lo);
 #else
-  DoubleDouble b_hi_r_hi = fputil::exact_mult(b.hi, -r.hi);
-  DoubleDouble b_lo_r_hi = fputil::exact_mult(b.lo, -r.hi);
-  double e_hi = (a.hi + b_hi_r_hi.hi) + b_hi_r_hi.lo;
-  double e_lo = (a.lo + b_lo_r_hi.hi) + b_lo_r_hi.lo;
+  NumberPair<T> b_hi_r_hi = fputil::exact_mult(b.hi, -r.hi);
+  NumberPair<T> b_lo_r_hi = fputil::exact_mult(b.lo, -r.hi);
+  T e_hi = (a.hi + b_hi_r_hi.hi) + b_hi_r_hi.lo;
+  T e_lo = (a.lo + b_lo_r_hi.hi) + b_lo_r_hi.lo;
 #endif // LIBC_TARGET_CPU_HAS_FMA
 
   r.lo = q * (e_hi + e_lo);
diff --git a/src/__support/FPUtil/multiply_add.h b/src/__support/FPUtil/multiply_add.h
index a86067c..ae00e08 100644
--- a/src/__support/FPUtil/multiply_add.h
+++ b/src/__support/FPUtil/multiply_add.h
@@ -47,11 +47,19 @@
 namespace fputil {
 
 LIBC_INLINE float multiply_add(float x, float y, float z) {
+#if __has_builtin(__builtin_elementwise_fma)
+  return __builtin_elementwise_fma(x, y, z);
+#else
   return __builtin_fmaf(x, y, z);
+#endif
 }
 
 LIBC_INLINE double multiply_add(double x, double y, double z) {
+#if __has_builtin(__builtin_elementwise_fma)
+  return __builtin_elementwise_fma(x, y, z);
+#else
   return __builtin_fma(x, y, z);
+#endif
 }
 
 } // namespace fputil
diff --git a/src/__support/big_int.h b/src/__support/big_int.h
index f591b41..fb5ad99 100644
--- a/src/__support/big_int.h
+++ b/src/__support/big_int.h
@@ -241,7 +241,7 @@
 }
 
 template <typename word, size_t N>
-LIBC_INLINE constexpr bool is_negative(cpp::array<word, N> &array) {
+LIBC_INLINE constexpr bool is_negative(const cpp::array<word, N> &array) {
   using signed_word = cpp::make_signed_t<word>;
   return cpp::bit_cast<signed_word>(array.back()) < 0;
 }
diff --git a/src/__support/fixed_point/fx_bits.h b/src/__support/fixed_point/fx_bits.h
index 225ea41..7509419 100644
--- a/src/__support/fixed_point/fx_bits.h
+++ b/src/__support/fixed_point/fx_bits.h
@@ -11,9 +11,10 @@
 
 #include "include/llvm-libc-macros/stdfix-macros.h"
 #include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/limits.h" // numeric_limits
 #include "src/__support/CPP/type_traits.h"
-#include "src/__support/macros/attributes.h" // LIBC_INLINE
-#include "src/__support/macros/config.h"
+#include "src/__support/macros/attributes.h"   // LIBC_INLINE
+#include "src/__support/macros/config.h"       // LIBC_NAMESPACE_DECL
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 #include "src/__support/math_extras.h"
 
@@ -50,6 +51,12 @@
   static constexpr StorageType SIGN_MASK =
       (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET);
 
+  // mask for <integral | fraction>
+  static constexpr StorageType VALUE_MASK = INTEGRAL_MASK | FRACTION_MASK;
+
+  // mask for <sign | integral | fraction>
+  static constexpr StorageType TOTAL_MASK = SIGN_MASK | VALUE_MASK;
+
 public:
   LIBC_INLINE constexpr FXBits() = default;
 
@@ -74,6 +81,12 @@
     return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
   }
 
+  // returns complete bitstring representation the fixed point number
+  // the bitstring is of the form: padding | sign | integral | fraction
+  LIBC_INLINE constexpr StorageType get_bits() {
+    return (value & TOTAL_MASK) >> FRACTION_OFFSET;
+  }
+
   // TODO: replace bool with Sign
   LIBC_INLINE constexpr bool get_sign() {
     return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET);
@@ -163,6 +176,24 @@
   return bit_and((x + round_bit), rounding_mask);
 }
 
+// count leading sign bits
+// TODO: support fixed_point_padding
+template <typename T>
+LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, int>
+countls(T f) {
+  using FXRep = FXRep<T>;
+  using BitType = typename FXRep::StorageType;
+  using FXBits = FXBits<T>;
+
+  if constexpr (FXRep::SIGN_LEN > 0) {
+    if (f < 0)
+      f = bit_not(f);
+  }
+
+  BitType value_bits = FXBits(f).get_bits();
+  return cpp::countl_zero(value_bits) - FXRep::SIGN_LEN;
+}
+
 } // namespace fixed_point
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/src/__support/fixed_point/fx_rep.h b/src/__support/fixed_point/fx_rep.h
index 1869389..7227fff 100644
--- a/src/__support/fixed_point/fx_rep.h
+++ b/src/__support/fixed_point/fx_rep.h
@@ -43,8 +43,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = SFRACT_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return SFRACT_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return SFRACT_MAX; }
@@ -63,8 +63,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = USFRACT_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return USFRACT_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return USFRACT_MAX; }
@@ -83,8 +83,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = FRACT_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return FRACT_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return FRACT_MAX; }
@@ -103,8 +103,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = UFRACT_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return UFRACT_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return UFRACT_MAX; }
@@ -123,8 +123,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = LFRACT_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return LFRACT_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return LFRACT_MAX; }
@@ -143,8 +143,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = ULFRACT_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return ULFRACT_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return ULFRACT_MAX; }
@@ -163,8 +163,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = SACCUM_IBIT;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = SACCUM_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return SACCUM_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return SACCUM_MAX; }
@@ -183,8 +183,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = USACCUM_IBIT;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = USACCUM_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return USACCUM_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return USACCUM_MAX; }
@@ -203,8 +203,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = ACCUM_IBIT;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = ACCUM_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return ACCUM_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return ACCUM_MAX; }
@@ -223,8 +223,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = UACCUM_IBIT;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = UACCUM_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return UACCUM_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return UACCUM_MAX; }
@@ -243,8 +243,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = LACCUM_IBIT;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = LACCUM_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return LACCUM_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return LACCUM_MAX; }
@@ -263,8 +263,8 @@
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0;
   LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = ULACCUM_IBIT;
   LIBC_INLINE_VAR static constexpr int FRACTION_LEN = ULACCUM_FBIT;
-  LIBC_INLINE_VAR static constexpr int TOTAL_LEN =
-      SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN;
+  LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN;
 
   LIBC_INLINE static constexpr Type MIN() { return ULACCUM_MIN; }
   LIBC_INLINE static constexpr Type MAX() { return ULACCUM_MAX; }
diff --git a/src/__support/macros/optimization.h b/src/__support/macros/optimization.h
index a263495..253843e 100644
--- a/src/__support/macros/optimization.h
+++ b/src/__support/macros/optimization.h
@@ -45,6 +45,7 @@
 #define LIBC_MATH_FAST                                                         \
   (LIBC_MATH_SKIP_ACCURATE_PASS | LIBC_MATH_SMALL_TABLES |                     \
    LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT)
+#define LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT 0x10
 
 #ifndef LIBC_MATH
 #define LIBC_MATH 0
@@ -58,4 +59,8 @@
 #define LIBC_MATH_HAS_SMALL_TABLES
 #endif
 
+#if (LIBC_MATH & LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT)
+#define LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_OPTIMIZATION_H
diff --git a/src/math/asinf16.h b/src/math/asinf16.h
new file mode 100644
index 0000000..f16647e
--- /dev/null
+++ b/src/math/asinf16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for asinf16 -----------------------*- 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_MATH_ASINF16_H
+#define LLVM_LIBC_SRC_MATH_ASINF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 asinf16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_ASINF16_H
diff --git a/src/math/generic/asinf16.cpp b/src/math/generic/asinf16.cpp
new file mode 100644
index 0000000..518c384
--- /dev/null
+++ b/src/math/generic/asinf16.cpp
@@ -0,0 +1,133 @@
+//===-- Half-precision asinf16(x) 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 "src/math/asinf16.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/sqrt.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Generated by Sollya using the following command:
+// > round(pi/2, D, RN);
+static constexpr float PI_2 = 0x1.921fb54442d18p0f;
+
+LLVM_LIBC_FUNCTION(float16, asinf16, (float16 x)) {
+  using FPBits = fputil::FPBits<float16>;
+  FPBits xbits(x);
+
+  uint16_t x_u = xbits.uintval();
+  uint16_t x_abs = x_u & 0x7fff;
+  float xf = x;
+
+  // |x| > 0x1p0, |x| > 1, or x is NaN.
+  if (LIBC_UNLIKELY(x_abs > 0x3c00)) {
+    // asinf16(NaN) = NaN
+    if (xbits.is_nan()) {
+      if (xbits.is_signaling_nan()) {
+        fputil::raise_except_if_required(FE_INVALID);
+        return FPBits::quiet_nan().get_val();
+      }
+
+      return x;
+    }
+
+    // 1 < |x| <= +/-inf
+    fputil::raise_except_if_required(FE_INVALID);
+    fputil::set_errno_if_required(EDOM);
+
+    return FPBits::quiet_nan().get_val();
+  }
+
+  float xsq = xf * xf;
+
+  // |x| <= 0x1p-1, |x| <= 0.5
+  if (x_abs <= 0x3800) {
+    // asinf16(+/-0) = +/-0
+    if (LIBC_UNLIKELY(x_abs == 0))
+      return x;
+
+    // Exhaustive tests show that,
+    // for |x| <= 0x1.878p-9, when:
+    // x > 0, and rounding upward, or
+    // x < 0, and rounding downward, then,
+    // asin(x) = x * 2^-11 + x
+    // else, in other rounding modes,
+    // asin(x) = x
+    if (LIBC_UNLIKELY(x_abs <= 0x1a1e)) {
+      int rounding = fputil::quick_get_round();
+
+      if ((xbits.is_pos() && rounding == FE_UPWARD) ||
+          (xbits.is_neg() && rounding == FE_DOWNWARD))
+        return fputil::cast<float16>(fputil::multiply_add(xf, 0x1.0p-11f, xf));
+      return x;
+    }
+
+    // Degree-6 minimax odd polynomial of asin(x) generated by Sollya with:
+    // > P = fpminimax(asin(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 0.5]);
+    float result =
+        fputil::polyeval(xsq, 0x1.000002p0f, 0x1.554c2ap-3f, 0x1.3541ccp-4f,
+                         0x1.43b2d6p-5f, 0x1.a0d73ep-5f);
+    return fputil::cast<float16>(xf * result);
+  }
+
+  // When |x| > 0.5, assume that 0.5 < |x| <= 1,
+  //
+  // Step-by-step range-reduction proof:
+  // 1:  Let y = asin(x), such that, x = sin(y)
+  // 2:  From complimentary angle identity:
+  //       x = sin(y) = cos(pi/2 - y)
+  // 3:  Let z = pi/2 - y, such that x = cos(z)
+  // 4:  From double angle formula; cos(2A) = 1 - sin^2(A):
+  //       z = 2A, z/2 = A
+  //       cos(z) = 1 - 2 * sin^2(z/2)
+  // 5:  Make sin(z/2) subject of the formula:
+  //       sin(z/2) = sqrt((1 - cos(z))/2)
+  // 6:  Recall [3]; x = cos(z). Therefore:
+  //       sin(z/2) = sqrt((1 - x)/2)
+  // 7:  Let u = (1 - x)/2
+  // 8:  Therefore:
+  //       asin(sqrt(u)) = z/2
+  //       2 * asin(sqrt(u)) = z
+  // 9:  Recall [3], z = pi/2 - y. Therefore:
+  //       y = pi/2 - z
+  //       y = pi/2 - 2 * asin(sqrt(u))
+  // 10: Recall [1], y = asin(x). Therefore:
+  //       asin(x) = pi/2 - 2 * asin(sqrt(u))
+  //
+  // WHY?
+  // 11: Recall [7], u = (1 - x)/2
+  // 12: Since 0.5 < x <= 1, therefore:
+  //       0 <= u <= 0.25 and 0 <= sqrt(u) <= 0.5
+  //
+  // Hence, we can reuse the same [0, 0.5] domain polynomial approximation for
+  // Step [10] as `sqrt(u)` is in range.
+
+  // 0x1p-1 < |x| <= 0x1p0, 0.5 < |x| <= 1.0
+  float xf_abs = (xf < 0 ? -xf : xf);
+  float sign = (xbits.uintval() >> 15 == 1 ? -1.0 : 1.0);
+  float u = fputil::multiply_add(-0.5f, xf_abs, 0.5f);
+  float u_sqrt = fputil::sqrt<float>(u);
+
+  // Degree-6 minimax odd polynomial of asin(x) generated by Sollya with:
+  // > P = fpminimax(asin(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 0.5]);
+  float asin_sqrt_u =
+      u_sqrt * fputil::polyeval(u, 0x1.000002p0f, 0x1.554c2ap-3f,
+                                0x1.3541ccp-4f, 0x1.43b2d6p-5f, 0x1.a0d73ep-5f);
+
+  return fputil::cast<float16>(sign *
+                               fputil::multiply_add(-2.0f, asin_sqrt_u, PI_2));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/math/generic/atan2f.cpp b/src/math/generic/atan2f.cpp
index db76393..5ac2b29 100644
--- a/src/math/generic/atan2f.cpp
+++ b/src/math/generic/atan2f.cpp
@@ -17,6 +17,14 @@
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 
+#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) &&                               \
+    defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT)
+
+// We use float-float implementation to reduce size.
+#include "src/math/generic/atan2f_float.h"
+
+#else
+
 namespace LIBC_NAMESPACE_DECL {
 
 namespace {
@@ -324,3 +332,5 @@
 }
 
 } // namespace LIBC_NAMESPACE_DECL
+
+#endif
diff --git a/src/math/generic/atan2f_float.h b/src/math/generic/atan2f_float.h
new file mode 100644
index 0000000..1fd853d
--- /dev/null
+++ b/src/math/generic/atan2f_float.h
@@ -0,0 +1,237 @@
+//===-- Single-precision atan2f 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 "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/double_double.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/math/atan2f.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace {
+
+using FloatFloat = fputil::FloatFloat;
+
+// atan(i/64) with i = 0..16, generated by Sollya with:
+// > for i from 0 to 16 do {
+//     a = round(atan(i/16), SG, RN);
+//     b = round(atan(i/16) - a, SG, RN);
+//     print("{", b, ",", a, "},");
+//   };
+constexpr FloatFloat ATAN_I[17] = {
+    {0.0f, 0.0f},
+    {-0x1.1a6042p-30f, 0x1.ff55bcp-5f},
+    {-0x1.54f424p-30f, 0x1.fd5baap-4f},
+    {0x1.79cb6p-28f, 0x1.7b97b4p-3f},
+    {-0x1.b4dfc8p-29f, 0x1.f5b76p-3f},
+    {-0x1.1f0286p-27f, 0x1.362774p-2f},
+    {0x1.e4defp-30f, 0x1.6f6194p-2f},
+    {0x1.e611fep-29f, 0x1.a64eecp-2f},
+    {0x1.586ed4p-28f, 0x1.dac67p-2f},
+    {-0x1.6499e6p-26f, 0x1.0657eap-1f},
+    {0x1.7bdfd6p-26f, 0x1.1e00bap-1f},
+    {-0x1.98e422p-28f, 0x1.345f02p-1f},
+    {0x1.934f7p-28f, 0x1.4978fap-1f},
+    {0x1.c5a6c6p-27f, 0x1.5d5898p-1f},
+    {0x1.5e118cp-27f, 0x1.700a7cp-1f},
+    {-0x1.1d4eb6p-26f, 0x1.819d0cp-1f},
+    {-0x1.777a5cp-26f, 0x1.921fb6p-1f},
+};
+
+// Approximate atan(x) for |x| <= 2^-5.
+// Using degree-3 Taylor polynomial:
+//  P = x - x^3/3
+// Then the absolute error is bounded by:
+//   |atan(x) - P(x)| < |x|^5/5 < 2^(-5*5) / 5 < 2^-27.
+// And the relative error is bounded by:
+//   |(atan(x) - P(x))/atan(x)| < |x|^4 / 4 < 2^-22.
+// For x = x_hi + x_lo, fully expand the polynomial and drop any terms less than
+//   ulp(x_hi^3 / 3) gives us:
+// P(x) ~ x_hi - x_hi^3/3 + x_lo * (1 - x_hi^2)
+FloatFloat atan_eval(const FloatFloat &x) {
+  FloatFloat p;
+  p.hi = x.hi;
+  float x_hi_sq = x.hi * x.hi;
+  // c0 ~ - x_hi^2 / 3
+  float c0 = -0x1.555556p-2f * x_hi_sq;
+  // c1 ~ x_lo * (1 - x_hi^2)
+  float c1 = fputil::multiply_add(x_hi_sq, -x.lo, x.lo);
+  // p.lo ~ - x_hi^3 / 3 + x_lo * (1 - x_hi*2)
+  p.lo = fputil::multiply_add(x.hi, c0, c1);
+  return p;
+}
+
+} // anonymous namespace
+
+// There are several range reduction steps we can take for atan2(y, x) as
+// follow:
+
+// * Range reduction 1: signness
+// atan2(y, x) will return a number between -PI and PI representing the angle
+// forming by the 0x axis and the vector (x, y) on the 0xy-plane.
+// In particular, we have that:
+//   atan2(y, x) = atan( y/x )         if x >= 0 and y >= 0 (I-quadrant)
+//               = pi + atan( y/x )    if x < 0 and y >= 0  (II-quadrant)
+//               = -pi + atan( y/x )   if x < 0 and y < 0   (III-quadrant)
+//               = atan( y/x )         if x >= 0 and y < 0  (IV-quadrant)
+// Since atan function is odd, we can use the formula:
+//   atan(-u) = -atan(u)
+// to adjust the above conditions a bit further:
+//   atan2(y, x) = atan( |y|/|x| )         if x >= 0 and y >= 0 (I-quadrant)
+//               = pi - atan( |y|/|x| )    if x < 0 and y >= 0  (II-quadrant)
+//               = -pi + atan( |y|/|x| )   if x < 0 and y < 0   (III-quadrant)
+//               = -atan( |y|/|x| )        if x >= 0 and y < 0  (IV-quadrant)
+// Which can be simplified to:
+//   atan2(y, x) = sign(y) * atan( |y|/|x| )             if x >= 0
+//               = sign(y) * (pi - atan( |y|/|x| ))      if x < 0
+
+// * Range reduction 2: reciprocal
+// Now that the argument inside atan is positive, we can use the formula:
+//   atan(1/x) = pi/2 - atan(x)
+// to make the argument inside atan <= 1 as follow:
+//   atan2(y, x) = sign(y) * atan( |y|/|x|)            if 0 <= |y| <= x
+//               = sign(y) * (pi/2 - atan( |x|/|y| )   if 0 <= x < |y|
+//               = sign(y) * (pi - atan( |y|/|x| ))    if 0 <= |y| <= -x
+//               = sign(y) * (pi/2 + atan( |x|/|y| ))  if 0 <= -x < |y|
+
+// * Range reduction 3: look up table.
+// After the previous two range reduction steps, we reduce the problem to
+// compute atan(u) with 0 <= u <= 1, or to be precise:
+//   atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|).
+// An accurate polynomial approximation for the whole [0, 1] input range will
+// require a very large degree.  To make it more efficient, we reduce the input
+// range further by finding an integer idx such that:
+//   | n/d - idx/16 | <= 1/32.
+// In particular,
+//   idx := 2^-4 * round(2^4 * n/d)
+// Then for the fast pass, we find a polynomial approximation for:
+//   atan( n/d ) ~ atan( idx/16 ) + (n/d - idx/16) * Q(n/d - idx/16)
+// with Q(x) = x - x^3/3 be the cubic Taylor polynomial of atan(x).
+// It's error in float-float precision is estimated in Sollya to be:
+// > P = x - x^3/3;
+// > dirtyinfnorm(atan(x) - P, [-2^-5, 2^-5]);
+// 0x1.995...p-28.
+
+LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
+  using FPBits = typename fputil::FPBits<float>;
+  constexpr float IS_NEG[2] = {1.0f, -1.0f};
+  constexpr FloatFloat ZERO = {0.0f, 0.0f};
+  constexpr FloatFloat MZERO = {-0.0f, -0.0f};
+  constexpr FloatFloat PI = {-0x1.777a5cp-24f, 0x1.921fb6p1f};
+  constexpr FloatFloat MPI = {0x1.777a5cp-24f, -0x1.921fb6p1f};
+  constexpr FloatFloat PI_OVER_4 = {-0x1.777a5cp-26f, 0x1.921fb6p-1f};
+  constexpr FloatFloat PI_OVER_2 = {-0x1.777a5cp-25f, 0x1.921fb6p0f};
+  constexpr FloatFloat MPI_OVER_2 = {-0x1.777a5cp-25f, 0x1.921fb6p0f};
+  constexpr FloatFloat THREE_PI_OVER_4 = {-0x1.99bc5cp-28f, 0x1.2d97c8p1f};
+  // Adjustment for constant term:
+  //   CONST_ADJ[x_sign][y_sign][recip]
+  constexpr FloatFloat CONST_ADJ[2][2][2] = {
+      {{ZERO, MPI_OVER_2}, {MZERO, MPI_OVER_2}},
+      {{MPI, PI_OVER_2}, {MPI, PI_OVER_2}}};
+
+  FPBits x_bits(x), y_bits(y);
+  bool x_sign = x_bits.sign().is_neg();
+  bool y_sign = y_bits.sign().is_neg();
+  x_bits = x_bits.abs();
+  y_bits = y_bits.abs();
+  uint32_t x_abs = x_bits.uintval();
+  uint32_t y_abs = y_bits.uintval();
+  bool recip = x_abs < y_abs;
+  uint32_t min_abs = recip ? x_abs : y_abs;
+  uint32_t max_abs = !recip ? x_abs : y_abs;
+  auto min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN);
+  auto max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN);
+
+  float num = FPBits(min_abs).get_val();
+  float den = FPBits(max_abs).get_val();
+
+  // Check for exceptional cases, whether inputs are 0, inf, nan, or close to
+  // overflow, or close to underflow.
+  if (LIBC_UNLIKELY(max_exp > 0xffU - 64U || min_exp < 64U)) {
+    if (x_bits.is_nan() || y_bits.is_nan())
+      return FPBits::quiet_nan().get_val();
+    unsigned x_except = x == 0.0f ? 0 : (FPBits(x_abs).is_inf() ? 2 : 1);
+    unsigned y_except = y == 0.0f ? 0 : (FPBits(y_abs).is_inf() ? 2 : 1);
+
+    // Exceptional cases:
+    //   EXCEPT[y_except][x_except][x_is_neg]
+    // with x_except & y_except:
+    //   0: zero
+    //   1: finite, non-zero
+    //   2: infinity
+    constexpr FloatFloat EXCEPTS[3][3][2] = {
+        {{ZERO, PI}, {ZERO, PI}, {ZERO, PI}},
+        {{PI_OVER_2, PI_OVER_2}, {ZERO, ZERO}, {ZERO, PI}},
+        {{PI_OVER_2, PI_OVER_2},
+         {PI_OVER_2, PI_OVER_2},
+         {PI_OVER_4, THREE_PI_OVER_4}},
+    };
+
+    if ((x_except != 1) || (y_except != 1)) {
+      FloatFloat r = EXCEPTS[y_except][x_except][x_sign];
+      return fputil::multiply_add(IS_NEG[y_sign], r.hi, IS_NEG[y_sign] * r.lo);
+    }
+    bool scale_up = min_exp < 64U;
+    bool scale_down = max_exp > 0xffU - 64U;
+    // At least one input is denormal, multiply both numerator and denominator
+    // by some large enough power of 2 to normalize denormal inputs.
+    if (scale_up) {
+      num *= 0x1.0p32f;
+      if (!scale_down)
+        den *= 0x1.0p32f;
+    } else if (scale_down) {
+      den *= 0x1.0p-32f;
+      num *= 0x1.0p-32f;
+    }
+
+    min_abs = FPBits(num).uintval();
+    max_abs = FPBits(den).uintval();
+    min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN);
+    max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN);
+  }
+
+  float final_sign = IS_NEG[(x_sign != y_sign) != recip];
+  FloatFloat const_term = CONST_ADJ[x_sign][y_sign][recip];
+  unsigned exp_diff = max_exp - min_exp;
+  // We have the following bound for normalized n and d:
+  //   2^(-exp_diff - 1) < n/d < 2^(-exp_diff + 1).
+  if (LIBC_UNLIKELY(exp_diff > 25))
+    return fputil::multiply_add(final_sign, const_term.hi,
+                                final_sign * (const_term.lo + num / den));
+
+  float k = fputil::nearest_integer(16.0f * num / den);
+  unsigned idx = static_cast<unsigned>(k);
+  // k = idx / 16
+  k *= 0x1.0p-4f;
+
+  // Range reduction:
+  // atan(n/d) - atan(k/64) = atan((n/d - k/16) / (1 + (n/d) * (k/16)))
+  //                        = atan((n - d * k/16)) / (d + n * k/16))
+  FloatFloat num_k = fputil::exact_mult(num, k);
+  FloatFloat den_k = fputil::exact_mult(den, k);
+
+  // num_dd = n - d * k
+  FloatFloat num_ff = fputil::exact_add(num - den_k.hi, -den_k.lo);
+  // den_dd = d + n * k
+  FloatFloat den_ff = fputil::exact_add(den, num_k.hi);
+  den_ff.lo += num_k.lo;
+
+  // q = (n - d * k) / (d + n * k)
+  FloatFloat q = fputil::div(num_ff, den_ff);
+  // p ~ atan(q)
+  FloatFloat p = atan_eval(q);
+
+  FloatFloat r = fputil::add(const_term, fputil::add(ATAN_I[idx], p));
+  return final_sign * r.hi;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/math/generic/pow.cpp b/src/math/generic/pow.cpp
index 213dbd9..a2a0bb6 100644
--- a/src/math/generic/pow.cpp
+++ b/src/math/generic/pow.cpp
@@ -400,7 +400,7 @@
 #else
   double c = FPBits(m_x.uintval() & 0x3fff'e000'0000'0000).get_val();
   dx = fputil::multiply_add(RD[idx_x], m_x.get_val() - c, CD[idx_x]); // Exact
-  dx_c0 = fputil::exact_mult<28>(dx, COEFFS[0]);                      // Exact
+  dx_c0 = fputil::exact_mult<double, 28>(dx, COEFFS[0]);              // Exact
 #endif // LIBC_TARGET_CPU_HAS_FMA
 
   double dx2 = dx * dx;
diff --git a/src/math/generic/range_reduction_double_common.h b/src/math/generic/range_reduction_double_common.h
index 06aeb49..711a122 100644
--- a/src/math/generic/range_reduction_double_common.h
+++ b/src/math/generic/range_reduction_double_common.h
@@ -21,7 +21,7 @@
 namespace LIBC_NAMESPACE_DECL {
 
 #ifdef LIBC_TARGET_CPU_HAS_FMA
-static constexpr unsigned SPLIT = DEFAULT_DOUBLE_SPLIT;
+static constexpr unsigned SPLIT = fputil::DefaultSplit<double>::VALUE;
 #else
 // When there is no-FMA instructions, in order to have exact product of 2 double
 // precision with directional roundings, we need to lower the precision of the
diff --git a/src/math/generic/range_reduction_double_fma.h b/src/math/generic/range_reduction_double_fma.h
index cab031c..8e0bc3a 100644
--- a/src/math/generic/range_reduction_double_fma.h
+++ b/src/math/generic/range_reduction_double_fma.h
@@ -33,14 +33,14 @@
   // 2^62 <= |x_reduced| < 2^(62 + 16) = 2^78
   x_reduced = xbits.get_val();
   // x * c_hi = ph.hi + ph.lo exactly.
-  DoubleDouble ph =
-      fputil::exact_mult<SPLIT>(x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][0]);
+  DoubleDouble ph = fputil::exact_mult<double, SPLIT>(
+      x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][0]);
   // x * c_mid = pm.hi + pm.lo exactly.
-  DoubleDouble pm =
-      fputil::exact_mult<SPLIT>(x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][1]);
+  DoubleDouble pm = fputil::exact_mult<double, SPLIT>(
+      x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][1]);
   // x * c_lo = pl.hi + pl.lo exactly.
-  DoubleDouble pl =
-      fputil::exact_mult<SPLIT>(x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][2]);
+  DoubleDouble pl = fputil::exact_mult<double, SPLIT>(
+      x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][2]);
   // Extract integral parts and fractional parts of (ph.lo + pm.hi).
   double sum_hi = ph.lo + pm.hi;
   double kd = fputil::nearest_integer(sum_hi);
diff --git a/src/math/generic/range_reduction_double_nofma.h b/src/math/generic/range_reduction_double_nofma.h
index 5640732..606c3f8 100644
--- a/src/math/generic/range_reduction_double_nofma.h
+++ b/src/math/generic/range_reduction_double_nofma.h
@@ -34,14 +34,14 @@
   x_reduced = xbits.get_val();
   // x * c_hi = ph.hi + ph.lo exactly.
   DoubleDouble x_split = fputil::split(x_reduced);
-  DoubleDouble ph = fputil::exact_mult<SPLIT>(x_split, x_reduced,
-                                              ONE_TWENTY_EIGHT_OVER_PI[idx][0]);
+  DoubleDouble ph = fputil::exact_mult<double, SPLIT>(
+      x_split, x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][0]);
   // x * c_mid = pm.hi + pm.lo exactly.
-  DoubleDouble pm = fputil::exact_mult<SPLIT>(x_split, x_reduced,
-                                              ONE_TWENTY_EIGHT_OVER_PI[idx][1]);
+  DoubleDouble pm = fputil::exact_mult<double, SPLIT>(
+      x_split, x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][1]);
   // x * c_lo = pl.hi + pl.lo exactly.
-  DoubleDouble pl = fputil::exact_mult<SPLIT>(x_split, x_reduced,
-                                              ONE_TWENTY_EIGHT_OVER_PI[idx][2]);
+  DoubleDouble pl = fputil::exact_mult<double, SPLIT>(
+      x_split, x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][2]);
   // Extract integral parts and fractional parts of (ph.lo + pm.hi).
   double sum_hi = ph.lo + pm.hi;
   double kd = fputil::nearest_integer(sum_hi);
diff --git a/src/math/generic/sqrtf128.cpp b/src/math/generic/sqrtf128.cpp
index f87066b..c844d3a 100644
--- a/src/math/generic/sqrtf128.cpp
+++ b/src/math/generic/sqrtf128.cpp
@@ -7,14 +7,431 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/math/sqrtf128.h"
-#include "src/__support/FPUtil/sqrt.h"
+#include "src/__support/CPP/bit.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"
+
+// Compute sqrtf128 with correct rounding for all rounding modes using integer
+// arithmetic by Alexei Sibidanov (sibid@uvic.ca):
+//   https://github.com/sibidanov/llvm-project/tree/as_sqrt_v2
+//   https://github.com/sibidanov/llvm-project/tree/as_sqrt_v3
+// TODO: Update the reference once Alexei's implementation is in the CORE-MATH
+// project. https://github.com/llvm/llvm-project/issues/126794
+
+// Let the input be expressed as x = 2^e * m_x,
+// - Step 1: Range reduction
+//   Let x_reduced = 2^(e % 2) * m_x,
+//   Then sqrt(x) = 2^(e / 2) * sqrt(x_reduced), with
+//     1 <= x_reduced < 4.
+// - Step 2: Polynomial approximation
+//   Approximate 1/sqrt(x_reduced) using polynomial approximation with the
+//   result errors bounded by:
+//     |r0 - 1/sqrt(x_reduced)| < 2^-32.
+//   The computations are done in uint64_t.
+// - Step 3: First Newton iteration
+//   Let the scaled error defined by:
+//     h0 = r0^2 * x_reduced - 1.
+//   Then we compute the first Newton iteration:
+//     r1 = r0 - r0 * h0 / 2.
+//   The result is then bounded by:
+//     |r1 - 1 / sqrt(x_reduced)| < 2^-62.
+// - Step 4: Second Newton iteration
+//   We calculate the scaled error from Step 3:
+//     h1 = r1^2 * x_reduced - 1.
+//   Then the second Newton iteration is computed by:
+//     r2 = x_reduced * (r1 - r1 * h0 / 2)
+//        ~ x_reduced * (1/sqrt(x_reduced)) = sqrt(x_reduced)
+// - Step 5: Perform rounding test and correction if needed.
+//     Rounding correction is done by computing the exact rounding errors:
+//       x_reduced - r2^2.
 
 namespace LIBC_NAMESPACE_DECL {
 
+using FPBits = fputil::FPBits<float128>;
+
+namespace {
+
+template <typename T, typename U = T> static inline constexpr T prod_hi(T, U);
+
+// Get high part of integer multiplications.
+// Use template to prevent implicit conversion.
+template <>
+inline constexpr uint64_t prod_hi<uint64_t>(uint64_t x, uint64_t y) {
+  return static_cast<uint64_t>(
+      (static_cast<UInt128>(x) * static_cast<UInt128>(y)) >> 64);
+}
+
+// Get high part of unsigned 128x64 bit multiplication.
+template <>
+inline constexpr UInt128 prod_hi<UInt128, uint64_t>(UInt128 x, uint64_t y) {
+  uint64_t x_lo = static_cast<uint64_t>(x);
+  uint64_t x_hi = static_cast<uint64_t>(x >> 64);
+  UInt128 xyl = static_cast<UInt128>(x_lo) * static_cast<UInt128>(y);
+  UInt128 xyh = static_cast<UInt128>(x_hi) * static_cast<UInt128>(y);
+  return xyh + (xyl >> 64);
+}
+
+// Get high part of signed 64x64 bit multiplication.
+template <> inline constexpr int64_t prod_hi<int64_t>(int64_t x, int64_t y) {
+  return static_cast<int64_t>(
+      (static_cast<Int128>(x) * static_cast<Int128>(y)) >> 64);
+}
+
+// Get high 128-bit part of unsigned 128x128 bit multiplication.
+template <> inline constexpr UInt128 prod_hi<UInt128>(UInt128 x, UInt128 y) {
+  uint64_t x_lo = static_cast<uint64_t>(x);
+  uint64_t x_hi = static_cast<uint64_t>(x >> 64);
+  uint64_t y_lo = static_cast<uint64_t>(y);
+  uint64_t y_hi = static_cast<uint64_t>(y >> 64);
+
+  UInt128 xh_yh = static_cast<UInt128>(x_hi) * static_cast<UInt128>(y_hi);
+  UInt128 xh_yl = static_cast<UInt128>(x_hi) * static_cast<UInt128>(y_lo);
+  UInt128 xl_yh = static_cast<UInt128>(x_lo) * static_cast<UInt128>(y_hi);
+
+  xh_yh += xh_yl >> 64;
+
+  return xh_yh + (xl_yh >> 64);
+}
+
+// Get high 128-bit part of mixed sign 128x128 bit multiplication.
+template <>
+inline constexpr Int128 prod_hi<Int128, UInt128>(Int128 x, UInt128 y) {
+  UInt128 mask = static_cast<UInt128>(x >> 127);
+  UInt128 negative_part = y & mask;
+  UInt128 prod = prod_hi(static_cast<UInt128>(x), y);
+  return static_cast<Int128>(prod - negative_part);
+}
+
+// Newton-Raphson first order step to improve accuracy of the result.
+// For the initial approximation r0 ~ 1/sqrt(x), let
+//   h = r0^2 * x - 1
+// be its scaled error.  Then the first-order Newton-Raphson iteration is:
+//   r1 = r0 - r0 * h / 2
+// which has error bounded by:
+//   |r1 - 1/sqrt(x)| < h^2 / 2.
+LIBC_INLINE uint64_t rsqrt_newton_raphson(uint64_t m, uint64_t r) {
+  uint64_t r2 = prod_hi(r, r);
+  // h = r0^2*x - 1.
+  int64_t h = static_cast<int64_t>(prod_hi(m, r2) + r2);
+  // hr = r * h / 2
+  int64_t hr = prod_hi(h, static_cast<int64_t>(r >> 1));
+  return r - hr;
+}
+
+#ifdef LIBC_MATH_HAS_SMALL_TABLES
+// Degree-12 minimax polynomials for 1/sqrt(x) on [1, 2].
+constexpr uint32_t RSQRT_COEFFS[12] = {
+    0xb5947a4a, 0x2d651e32, 0x9ad50532, 0x2d28d093, 0x0d8be653, 0x04239014,
+    0x01492449, 0x0066ff7d, 0x001e74a1, 0x000984cc, 0x00049abc, 0x00018340,
+};
+
+LIBC_INLINE uint64_t rsqrt_approx(uint64_t m) {
+  int64_t x = static_cast<uint64_t>(m) ^ (uint64_t(1) << 63);
+  int64_t x_26 = x >> 2;
+  int64_t z = x >> 31;
+
+  if (LIBC_UNLIKELY(z <= -4294967296))
+    return ~(m >> 1);
+
+  uint64_t x2 = static_cast<uint64_t>(z) * static_cast<uint64_t>(z);
+  uint64_t x2_26 = x2 >> 5;
+  x2 >>= 32;
+  // Calculate the odd part of the polynomial using Horner's method.
+  uint64_t c0 = RSQRT_COEFFS[8] + ((x2 * RSQRT_COEFFS[10]) >> 32);
+  uint64_t c1 = RSQRT_COEFFS[6] + ((x2 * c0) >> 32);
+  uint64_t c2 = RSQRT_COEFFS[4] + ((x2 * c1) >> 32);
+  uint64_t c3 = RSQRT_COEFFS[2] + ((x2 * c2) >> 32);
+  uint64_t c4 = RSQRT_COEFFS[0] + ((x2 * c3) >> 32);
+  uint64_t odd =
+      static_cast<uint64_t>((x >> 34) * static_cast<int64_t>(c4 >> 3)) + x_26;
+  // Calculate the even part of the polynomial using Horner's method.
+  uint64_t d0 = RSQRT_COEFFS[9] + ((x2 * RSQRT_COEFFS[11]) >> 32);
+  uint64_t d1 = RSQRT_COEFFS[7] + ((x2 * d0) >> 32);
+  uint64_t d2 = RSQRT_COEFFS[5] + ((x2 * d1) >> 32);
+  uint64_t d3 = RSQRT_COEFFS[3] + ((x2 * d2) >> 32);
+  uint64_t d4 = RSQRT_COEFFS[1] + ((x2 * d3) >> 32);
+  uint64_t even = 0xd105eb806655d608ul + ((x2 * d4) >> 6) + x2_26;
+
+  uint64_t r = even - odd; // error < 1.5e-10
+  // Newton-Raphson first order step to improve accuracy of the result to almost
+  // 64 bits.
+  return rsqrt_newton_raphson(m, r);
+}
+
+#else
+// Cubic minimax polynomials for 1/sqrt(x) on [1 + k/64, 1 + (k + 1)/64]
+// for k = 0..63.
+constexpr uint32_t RSQRT_COEFFS[64][4] = {
+    {0xffffffff, 0xfffff780, 0xbff55815, 0x9bb5b6e7},
+    {0xfc0bd889, 0xfa1d6e7d, 0xb8a95a89, 0x938bf8f0},
+    {0xf82ec882, 0xf473bea9, 0xb1bf4705, 0x8bed0079},
+    {0xf467f280, 0xeefff2a1, 0xab309d4a, 0x84cdb431},
+    {0xf0b6848c, 0xe9bf46f4, 0xa4f76232, 0x7e24037b},
+    {0xed19b75e, 0xe4af2628, 0x9f0e1340, 0x77e6ca62},
+    {0xe990cdad, 0xdfcd2521, 0x996f9b96, 0x720db8df},
+    {0xe61b138e, 0xdb16ffde, 0x94174a00, 0x6c913cff},
+    {0xe2b7dddf, 0xd68a967b, 0x8f00c812, 0x676a6f92},
+    {0xdf6689b7, 0xd225ea80, 0x8a281226, 0x62930308},
+    {0xdc267bea, 0xcde71c63, 0x8589702c, 0x5e05343e},
+    {0xd8f7208e, 0xc9cc6948, 0x81216f2e, 0x59bbbcf8},
+    {0xd5d7ea91, 0xc5d428ee, 0x7cecdb76, 0x55b1c7d6},
+    {0xd2c8534e, 0xc1fccbc9, 0x78e8bb45, 0x51e2e592},
+    {0xcfc7da32, 0xbe44d94a, 0x75124a0a, 0x4e4b0369},
+    {0xccd6045f, 0xbaaaee41, 0x7166f40f, 0x4ae66284},
+    {0xc9f25c5c, 0xb72dbb69, 0x6de45288, 0x47b19045},
+    {0xc71c71c7, 0xb3cc040f, 0x6a882804, 0x44a95f5f},
+    {0xc453d90f, 0xb0849cd4, 0x67505d2a, 0x41cae1a0},
+    {0xc1982b2e, 0xad566a85, 0x643afdc8, 0x3f13625c},
+    {0xbee9056f, 0xaa406113, 0x6146361f, 0x3c806169},
+    {0xbc46092e, 0xa7418293, 0x5e70506d, 0x3a0f8e8e},
+    {0xb9aedba5, 0xa458de58, 0x5bb7b2b1, 0x37bec572},
+    {0xb72325b7, 0xa1859022, 0x591adc9a, 0x358c09e2},
+    {0xb4a293c2, 0x9ec6bf52, 0x569865a7, 0x33758476},
+    {0xb22cd56d, 0x9c1b9e36, 0x542efb6a, 0x31797f8a},
+    {0xafc19d86, 0x9983695c, 0x51dd5ffb, 0x2f96647a},
+    {0xad60a1d1, 0x96fd66f7, 0x4fa2687c, 0x2dcab91f},
+    {0xab099ae9, 0x9488e64b, 0x4d7cfbc9, 0x2c151d8a},
+    {0xa8bc441a, 0x92253f20, 0x4b6c1139, 0x2a7449ef},
+    {0xa6785b42, 0x8fd1d14a, 0x496eaf82, 0x28e70cc3},
+    {0xa43da0ae, 0x8d8e042a, 0x4783eba7, 0x276c4900},
+    {0xa20bd701, 0x8b594648, 0x45aae80a, 0x2602f493},
+    {0x9fe2c315, 0x89330ce4, 0x43e2d382, 0x24aa16ec},
+    {0x9dc22be4, 0x871ad399, 0x422ae88c, 0x2360c7af},
+    {0x9ba9da6c, 0x85101c05, 0x40826c88, 0x22262d7b},
+    {0x99999999, 0x83126d70, 0x3ee8af07, 0x20f97cd2},
+    {0x97913630, 0x81215480, 0x3d5d0922, 0x1fd9f714},
+    {0x95907eb8, 0x7f3c62ef, 0x3bdedce0, 0x1ec6e994},
+    {0x93974369, 0x7d632f45, 0x3a6d94a9, 0x1dbfacbb},
+    {0x91a55615, 0x7b955498, 0x3908a2be, 0x1cc3a33b},
+    {0x8fba8a1c, 0x79d2724e, 0x37af80bf, 0x1bd23960},
+    {0x8dd6b456, 0x781a2be4, 0x3661af39, 0x1aeae458},
+    {0x8bf9ab07, 0x766c28ba, 0x351eb539, 0x1a0d21a2},
+    {0x8a2345cc, 0x74c813dd, 0x33e61feb, 0x19387676},
+    {0x88535d90, 0x732d9bdc, 0x32b7823a, 0x186c6f3e},
+    {0x8689cc7e, 0x719c7297, 0x3192747d, 0x17a89f21},
+    {0x84c66df1, 0x70144d19, 0x30769424, 0x16ec9f89},
+    {0x83091e6a, 0x6e94e36c, 0x2f63836f, 0x16380fbf},
+    {0x8151bb87, 0x6d1df079, 0x2e58e925, 0x158a9484},
+    {0x7fa023f1, 0x6baf31de, 0x2d567053, 0x14e3d7ba},
+    {0x7df43758, 0x6a4867d3, 0x2c5bc811, 0x1443880e},
+    {0x7c4dd664, 0x68e95508, 0x2b68a346, 0x13a958ab},
+    {0x7aace2b0, 0x6791be86, 0x2a7cb871, 0x131500ee},
+    {0x79113ebc, 0x66416b95, 0x2997c17a, 0x12863c29},
+    {0x777acde8, 0x64f825a1, 0x28b97b82, 0x11fcc95c},
+    {0x75e9746a, 0x63b5b822, 0x27e1a6b4, 0x11786b03},
+    {0x745d1746, 0x6279f081, 0x2710061d, 0x10f8e6da},
+    {0x72d59c46, 0x61449e06, 0x26445f86, 0x107e05ac},
+    {0x7152e9f4, 0x601591be, 0x257e7b4d, 0x10079327},
+    {0x6fd4e793, 0x5eec9e6b, 0x24be2445, 0x0f955da9},
+    {0x6e5b7d16, 0x5dc9986e, 0x24032795, 0x0f273620},
+    {0x6ce6931d, 0x5cac55b7, 0x234d5496, 0x0ebcefdb},
+    {0x6b7612ec, 0x5b94adb2, 0x229c7cbc, 0x0e56606e},
+};
+
+// Approximate rsqrt with cubic polynomials.
+// The range [1,2] is splitted into 64 equal sub-ranges and the reciprocal
+// square root is approximated by a cubic polynomial by the minimax method in
+// each subrange. The approximation accuracy fits into 32-33 bits and thus it is
+// natural to round coefficients into 32 bit. The constant coefficient can be
+// rounded to 33 bits since the most significant bit is always 1 and implicitly
+// assumed in the table.
+LIBC_INLINE uint64_t rsqrt_approx(uint64_t m) {
+  // ULP(m) = 2^-64.
+  // Use the top 6 bits as index for looking up polynomial coeffs.
+  uint64_t indx = m >> 58;
+
+  uint64_t c0 = static_cast<uint64_t>(RSQRT_COEFFS[indx][0]);
+  c0 <<= 31;        // to 64 bit with the space for the implicit bit
+  c0 |= 1ull << 63; // add implicit bit
+
+  uint64_t c1 = static_cast<uint64_t>(RSQRT_COEFFS[indx][1]);
+  c1 <<= 25; // to 64 bit format
+
+  uint64_t c2 = static_cast<uint64_t>(RSQRT_COEFFS[indx][2]);
+  uint64_t c3 = static_cast<uint64_t>(RSQRT_COEFFS[indx][3]);
+
+  uint64_t d = (m << 6) >> 32; // local coordinate in the subrange [0, 2^32]
+  uint64_t d2 = (d * d) >> 32; // square of the local coordinate
+  uint64_t re = c0 + (d2 * c2 >> 13); // even part of the polynomial (positive)
+  uint64_t ro = d * ((c1 + ((d2 * c3) >> 19)) >> 26) >>
+                6;      // odd part of the polynomial (negative)
+  uint64_t r = re - ro; // maximal error < 1.55e-10 and it is less than 2^-32
+  // Newton-Raphson first order step to improve accuracy of the result to almost
+  // 64 bits.
+  r = rsqrt_newton_raphson(m, r);
+  // Adjust in the unlucky case x~1;
+  if (LIBC_UNLIKELY(!r))
+    --r;
+  return r;
+}
+#endif // LIBC_MATH_HAS_SMALL_TABLES
+
+} // anonymous namespace
+
 LLVM_LIBC_FUNCTION(float128, sqrtf128, (float128 x)) {
-  return fputil::sqrt<float128>(x);
+  using FPBits = fputil::FPBits<float128>;
+  // Get rounding mode.
+  uint32_t rm = fputil::get_round();
+
+  FPBits xbits(x);
+  UInt128 x_u = xbits.uintval();
+  // Bring leading bit of the mantissa to the highest bit.
+  //   ulp(x_frac) = 2^-128.
+  UInt128 x_frac = xbits.get_mantissa() << (FPBits::EXP_LEN + 1);
+
+  int sign_exp = static_cast<int>(x_u >> FPBits::FRACTION_LEN);
+
+  if (LIBC_UNLIKELY(sign_exp == 0 || sign_exp >= 0x7fff)) {
+    // Special cases: NAN, inf, negative numbers
+    if (sign_exp >= 0x7fff) {
+      // x = -0 or x = inf
+      if (xbits.is_zero() || xbits == xbits.inf())
+        return x;
+      // x is nan
+      if (xbits.is_nan()) {
+        // pass through quiet nan
+        if (xbits.is_quiet_nan())
+          return x;
+        // transform signaling nan to quiet and return
+        return xbits.quiet_nan().get_val();
+      }
+      // x < 0 or x = -inf
+      fputil::set_errno_if_required(EDOM);
+      fputil::raise_except_if_required(FE_INVALID);
+      return xbits.quiet_nan().get_val();
+    }
+    // Now x is subnormal or x = +0.
+
+    // x is +0.
+    if (x_frac == 0)
+      return x;
+
+    // Normalize subnormal inputs.
+    sign_exp = -cpp::countl_zero(x_frac);
+    int normal_shifts = 1 - sign_exp;
+    x_frac <<= normal_shifts;
+  }
+
+  // For sign_exp = biased exponent of x = real_exponent + 16383,
+  // let f be the real exponent of the output:
+  //   f = floor(real_exponent / 2)
+  // Then:
+  //   floor((sign_exp + 1) / 2) = f + 8192
+  // Hence, the biased exponent of the final result is:
+  //   f + 16383 = floor((sign_exp + 1) / 2) + 8191.
+  // Since the output mantissa will include the hidden bit, we can define the
+  // output exponent part:
+  //   e2 = floor((sign_exp + 1) / 2) + 8190
+  unsigned i = static_cast<unsigned>(1 - (sign_exp & 1));
+  uint32_t q2 = (sign_exp + 1) >> 1;
+  // Exponent of the final result
+  uint32_t e2 = q2 + 8190;
+
+  constexpr uint64_t RSQRT_2[2] = {~0ull,
+                                   0xb504f333f9de6484 /* 2^64/sqrt(2) */};
+
+  // Approximate 1/sqrt(1 + x_frac)
+  // Error: |r_1 - 1/sqrt(x)| < 2^-62.
+  uint64_t r1 = rsqrt_approx(static_cast<uint64_t>(x_frac >> 64));
+  // Adjust for the even/odd exponent.
+  uint64_t r2 = prod_hi(r1, RSQRT_2[i]);
+  unsigned shift = 2 - i;
+
+  // Normalized input:
+  //   1 <= x_reduced < 4
+  UInt128 x_reduced = (x_frac >> shift) | (UInt128(1) << (126 + i));
+  // With r2 ~ 1/sqrt(x) up to 2^-63, we perform another round of Newton-Raphson
+  // iteration:
+  //   r3 = r2 - r2 * h / 2,
+  // for h = r2^2 * x - 1.
+  // Then:
+  //   sqrt(x) = x * (1 / sqrt(x))
+  //           ~ x * r3
+  //           = x * (r2 - r2 * h / 2)
+  //           = (x * r2) - (x * r2) * h / 2
+  UInt128 sx = prod_hi(x_reduced, r2);
+  UInt128 h = prod_hi(sx, r2) << 2;
+  UInt128 ds = static_cast<UInt128>(prod_hi(static_cast<Int128>(h), sx));
+  UInt128 v = (sx << 1) - ds;
+
+  uint32_t nrst = rm == FE_TONEAREST;
+  // The result lies within (-2,5) of true square root so we now
+  // test that we can correctly round the result taking into account
+  // the rounding mode.
+  // Check the lowest 14 bits (by clearing and sign-extending the top
+  // 32 - 14 = 18 bits).
+  int dd = (static_cast<int>(v) << 18) >> 18;
+
+  if (LIBC_UNLIKELY(dd < 4 && dd >= -8)) { // can round correctly?
+    // m is almost the final result it can be only 1 ulp off so we
+    // just need to test both possibilities. We square it and
+    // compare with the initial argument.
+    UInt128 m = v >> 15;
+    UInt128 m2 = m * m;
+    // The difference of the squared result and the argument
+    Int128 t0 = static_cast<Int128>(m2 - (x_reduced << 98));
+    if (t0 == 0) {
+      // the square root is exact
+      v = m << 15;
+    } else {
+      // Add +-1 ulp to m depend on the sign of the difference. Here
+      // we do not need to square again since (m+1)^2 = m^2 + 2*m +
+      // 1 so just need to add shifted m and 1.
+      Int128 t1 = t0;
+      Int128 sgn = t0 >> 127; // sign of the difference
+      t1 -= (m << 1) ^ sgn;
+      t1 += 1 + sgn;
+
+      Int128 sgn1 = t1 >> 127;
+      if (LIBC_UNLIKELY(sgn == sgn1)) {
+        t0 = t1;
+        v -= sgn << 15;
+        t1 -= (m << 1) ^ sgn;
+        t1 += 1 + sgn;
+      }
+
+      if (t1 == 0) {
+        // 1 ulp offset brings again an exact root
+        v = (m - (2 * sgn + 1)) << 15;
+      } else {
+        t1 += t0;
+        Int128 side = t1 >> 127; // select what is closer m or m+-1
+        v &= ~UInt128(0) << 15;  // wipe the fractional bits
+        v -= ((sgn & side) | (~sgn & 1)) << (15 + side);
+        v |= 1; // add sticky bit since we cannot have an exact mid-point
+                // situation
+      }
+    }
+  }
+
+  unsigned frac = static_cast<unsigned>(v) & 0x7fff; // fractional part
+  unsigned rnd;                                      // round bit
+  if (LIBC_LIKELY(nrst != 0)) {
+    rnd = frac >> 14; // round to nearest tie to even
+  } else if (rm == FE_UPWARD) {
+    rnd = !!frac; // round up
+  } else {
+    rnd = 0; // round down or round to zero
+  }
+
+  v >>= 15; // position mantissa
+  v += rnd; // round
+
+  // Set inexact flag only if square root is inexact
+  // TODO: We will have to raise FE_INEXACT most of the time, but this
+  // operation is very costly, especially in x86-64, since technically, it
+  // needs to synchronize both SSE and x87 flags.  Need to investigate
+  // further to see how we can make this performant.
+  // https://github.com/llvm/llvm-project/issues/126753
+
+  // if(frac) fputil::raise_except_if_required(FE_INEXACT);
+
+  v += static_cast<UInt128>(e2) << FPBits::FRACTION_LEN; // place exponent
+  return cpp::bit_cast<float128>(v);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlshk.cpp b/src/stdfix/countlshk.cpp
new file mode 100644
index 0000000..f94728b
--- /dev/null
+++ b/src/stdfix/countlshk.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlshk 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 "countlshk.h"
+#include "src/__support/common.h"
+#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, countlshk, (short accum f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlshk.h b/src/stdfix/countlshk.h
new file mode 100644
index 0000000..ab33424
--- /dev/null
+++ b/src/stdfix/countlshk.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlshk 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_COUNTLSHK_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSHK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlshk(short accum f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSHK_H
diff --git a/src/stdfix/countlshr.cpp b/src/stdfix/countlshr.cpp
new file mode 100644
index 0000000..d77d3e9
--- /dev/null
+++ b/src/stdfix/countlshr.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlshr 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 "countlshr.h"
+#include "src/__support/common.h"
+#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, countlshr, (short fract f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlshr.h b/src/stdfix/countlshr.h
new file mode 100644
index 0000000..579b7b6
--- /dev/null
+++ b/src/stdfix/countlshr.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlshr 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_COUNTLSHR_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSHR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlshr(short fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSHR_H
diff --git a/src/stdfix/countlsk.cpp b/src/stdfix/countlsk.cpp
new file mode 100644
index 0000000..b6f56ad
--- /dev/null
+++ b/src/stdfix/countlsk.cpp
@@ -0,0 +1,18 @@
+//===-- Implementation for countlsk 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 "countlsk.h"
+#include "src/__support/common.h"
+#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, countlsk, (accum f)) { return fixed_point::countls(f); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsk.h b/src/stdfix/countlsk.h
new file mode 100644
index 0000000..d0c893b
--- /dev/null
+++ b/src/stdfix/countlsk.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsk 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_COUNTLSK_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsk(accum f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSK_H
diff --git a/src/stdfix/countlslk.cpp b/src/stdfix/countlslk.cpp
new file mode 100644
index 0000000..9bf30ff
--- /dev/null
+++ b/src/stdfix/countlslk.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlslk 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 "countlslk.h"
+#include "src/__support/common.h"
+#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, countlslk, (long accum f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlslk.h b/src/stdfix/countlslk.h
new file mode 100644
index 0000000..60fa469
--- /dev/null
+++ b/src/stdfix/countlslk.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlslk 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_COUNTLSLK_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSLK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlslk(long accum f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSLK_H
diff --git a/src/stdfix/countlslr.cpp b/src/stdfix/countlslr.cpp
new file mode 100644
index 0000000..774023c
--- /dev/null
+++ b/src/stdfix/countlslr.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlslr 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 "countlslr.h"
+#include "src/__support/common.h"
+#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, countlslr, (long fract f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlslr.h b/src/stdfix/countlslr.h
new file mode 100644
index 0000000..c909551
--- /dev/null
+++ b/src/stdfix/countlslr.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlslr 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_COUNTLSLR_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSLR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlslr(long fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSLR_H
diff --git a/src/stdfix/countlsr.cpp b/src/stdfix/countlsr.cpp
new file mode 100644
index 0000000..1456312
--- /dev/null
+++ b/src/stdfix/countlsr.cpp
@@ -0,0 +1,18 @@
+//===-- Implementation for countlsr 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 "countlsr.h"
+#include "src/__support/common.h"
+#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, countlsr, (fract f)) { return fixed_point::countls(f); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsr.h b/src/stdfix/countlsr.h
new file mode 100644
index 0000000..75dcf4a
--- /dev/null
+++ b/src/stdfix/countlsr.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsr 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_COUNTLSR_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsr(fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSR_H
diff --git a/src/stdfix/countlsuhk.cpp b/src/stdfix/countlsuhk.cpp
new file mode 100644
index 0000000..2cc266f
--- /dev/null
+++ b/src/stdfix/countlsuhk.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlsuhk 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 "countlsuhk.h"
+#include "src/__support/common.h"
+#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, countlsuhk, (unsigned short accum f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsuhk.h b/src/stdfix/countlsuhk.h
new file mode 100644
index 0000000..fcb2fec
--- /dev/null
+++ b/src/stdfix/countlsuhk.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsuhk 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_COUNTLSUHK_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSUHK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsuhk(unsigned short accum f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUHK_H
diff --git a/src/stdfix/countlsuhr.cpp b/src/stdfix/countlsuhr.cpp
new file mode 100644
index 0000000..f30b0dd
--- /dev/null
+++ b/src/stdfix/countlsuhr.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlsuhr 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 "countlsuhr.h"
+#include "src/__support/common.h"
+#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, countlsuhr, (unsigned short fract f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsuhr.h b/src/stdfix/countlsuhr.h
new file mode 100644
index 0000000..b60132d
--- /dev/null
+++ b/src/stdfix/countlsuhr.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsuhr 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_COUNTLSUHR_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSUHR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsuhr(unsigned short fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUHR_H
diff --git a/src/stdfix/countlsuk.cpp b/src/stdfix/countlsuk.cpp
new file mode 100644
index 0000000..90617cf
--- /dev/null
+++ b/src/stdfix/countlsuk.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlsuk 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 "countlsuk.h"
+#include "src/__support/common.h"
+#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, countlsuk, (unsigned accum f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsuk.h b/src/stdfix/countlsuk.h
new file mode 100644
index 0000000..7ad0e70
--- /dev/null
+++ b/src/stdfix/countlsuk.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsuk 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_COUNTLSUK_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSUK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsuk(unsigned accum f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUK_H
diff --git a/src/stdfix/countlsulk.cpp b/src/stdfix/countlsulk.cpp
new file mode 100644
index 0000000..04090dd
--- /dev/null
+++ b/src/stdfix/countlsulk.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlsulk 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 "countlsulk.h"
+#include "src/__support/common.h"
+#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, countlsulk, (unsigned long accum f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsulk.h b/src/stdfix/countlsulk.h
new file mode 100644
index 0000000..55ca9d2
--- /dev/null
+++ b/src/stdfix/countlsulk.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsulk 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_COUNTLSULK_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSULK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsulk(unsigned long accum f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSULK_H
diff --git a/src/stdfix/countlsulr.cpp b/src/stdfix/countlsulr.cpp
new file mode 100644
index 0000000..d9d6ff4
--- /dev/null
+++ b/src/stdfix/countlsulr.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlsulr 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 "countlsulr.h"
+#include "src/__support/common.h"
+#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, countlsulr, (unsigned long fract f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsulr.h b/src/stdfix/countlsulr.h
new file mode 100644
index 0000000..59e7d72
--- /dev/null
+++ b/src/stdfix/countlsulr.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsulr 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_COUNTLSULR_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSULR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsulr(unsigned long fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSULR_H
diff --git a/src/stdfix/countlsur.cpp b/src/stdfix/countlsur.cpp
new file mode 100644
index 0000000..777e5f3
--- /dev/null
+++ b/src/stdfix/countlsur.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation for countlsur 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 "countlsur.h"
+#include "src/__support/common.h"
+#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, countlsur, (unsigned fract f)) {
+  return fixed_point::countls(f);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsur.h b/src/stdfix/countlsur.h
new file mode 100644
index 0000000..1d34e97
--- /dev/null
+++ b/src/stdfix/countlsur.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for countlsur 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_COUNTLSUR_H
+#define LLVM_LIBC_SRC_STDFIX_COUNTLSUR_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+
+int countlsur(unsigned fract f);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUR_H
diff --git a/src/time/mktime.cpp b/src/time/mktime.cpp
index 3874cad..fc05ff2 100644
--- a/src/time/mktime.cpp
+++ b/src/time/mktime.cpp
@@ -14,100 +14,8 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// Returns number of years from (1, year).
-static constexpr int64_t get_num_of_leap_years_before(int64_t year) {
-  return (year / 4) - (year / 100) + (year / 400);
-}
-
-// Returns True if year is a leap year.
-static constexpr bool is_leap_year(const int64_t year) {
-  return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
-}
-
 LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) {
-  // Unlike most C Library functions, mktime doesn't just die on bad input.
-  // TODO(rtenneti); Handle leap seconds.
-  int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE;
-
-  // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038.
-  if (sizeof(time_t) == 4 &&
-      tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) {
-    if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR)
-      return time_utils::out_of_range();
-    if (tm_out->tm_mon > 0)
-      return time_utils::out_of_range();
-    if (tm_out->tm_mday > 19)
-      return time_utils::out_of_range();
-    else if (tm_out->tm_mday == 19) {
-      if (tm_out->tm_hour > 3)
-        return time_utils::out_of_range();
-      else if (tm_out->tm_hour == 3) {
-        if (tm_out->tm_min > 14)
-          return time_utils::out_of_range();
-        else if (tm_out->tm_min == 14) {
-          if (tm_out->tm_sec > 7)
-            return time_utils::out_of_range();
-        }
-      }
-    }
-  }
-
-  // Years are ints.  A 32-bit year will fit into a 64-bit time_t.
-  // A 64-bit year will not.
-  static_assert(
-      sizeof(int) == 4,
-      "ILP64 is unimplemented. This implementation requires 32-bit integers.");
-
-  // Calculate number of months and years from tm_mon.
-  int64_t month = tm_out->tm_mon;
-  if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) {
-    int64_t years = month / 12;
-    month %= 12;
-    if (month < 0) {
-      years--;
-      month += 12;
-    }
-    tm_year_from_base += years;
-  }
-  bool tm_year_is_leap = is_leap_year(tm_year_from_base);
-
-  // Calculate total number of days based on the month and the day (tm_mday).
-  int64_t total_days = tm_out->tm_mday - 1;
-  for (int64_t i = 0; i < month; ++i)
-    total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i];
-  // Add one day if it is a leap year and the month is after February.
-  if (tm_year_is_leap && month > 1)
-    total_days++;
-
-  // Calculate total numbers of days based on the year.
-  total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) *
-                time_constants::DAYS_PER_NON_LEAP_YEAR;
-  if (tm_year_from_base >= time_constants::EPOCH_YEAR) {
-    total_days += get_num_of_leap_years_before(tm_year_from_base - 1) -
-                  get_num_of_leap_years_before(time_constants::EPOCH_YEAR);
-  } else if (tm_year_from_base >= 1) {
-    total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
-                  get_num_of_leap_years_before(tm_year_from_base - 1);
-  } else {
-    // Calculate number of leap years until 0th year.
-    total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
-                  get_num_of_leap_years_before(0);
-    if (tm_year_from_base <= 0) {
-      total_days -= 1; // Subtract 1 for 0th year.
-      // Calculate number of leap years until -1 year
-      if (tm_year_from_base < 0) {
-        total_days -= get_num_of_leap_years_before(-tm_year_from_base) -
-                      get_num_of_leap_years_before(1);
-      }
-    }
-  }
-
-  // TODO: https://github.com/llvm/llvm-project/issues/121962
-  // Need to handle timezone and update of tm_isdst.
-  int64_t seconds = tm_out->tm_sec +
-                    tm_out->tm_min * time_constants::SECONDS_PER_MIN +
-                    tm_out->tm_hour * time_constants::SECONDS_PER_HOUR +
-                    total_days * time_constants::SECONDS_PER_DAY;
+  int64_t seconds = time_utils::mktime_internal(tm_out);
 
   // Update the tm structure's year, month, day, etc. from seconds.
   if (time_utils::update_from_seconds(seconds, tm_out) < 0)
diff --git a/src/time/time_constants.h b/src/time/time_constants.h
index 3e25f74..bcf19ff 100644
--- a/src/time/time_constants.h
+++ b/src/time/time_constants.h
@@ -18,7 +18,7 @@
 namespace time_constants {
 
 enum Month : int {
-  JANUARY,
+  JANUARY = 0,
   FEBRUARY,
   MARCH,
   APRIL,
@@ -32,14 +32,28 @@
   DECEMBER
 };
 
+enum WeekDay : int {
+  SUNDAY = 0,
+  MONDAY,
+  TUESDAY,
+  WEDNESDAY,
+  THURSDAY,
+  FRIDAY,
+  SATURDAY
+};
+
 constexpr int SECONDS_PER_MIN = 60;
 constexpr int MINUTES_PER_HOUR = 60;
 constexpr int HOURS_PER_DAY = 24;
 constexpr int DAYS_PER_WEEK = 7;
+constexpr int WEEKS_PER_YEAR = 52;
 constexpr int MONTHS_PER_YEAR = 12;
 constexpr int DAYS_PER_NON_LEAP_YEAR = 365;
 constexpr int DAYS_PER_LEAP_YEAR = 366;
 
+constexpr int LAST_DAY_OF_NON_LEAP_YEAR = DAYS_PER_NON_LEAP_YEAR - 1;
+constexpr int LAST_DAY_OF_LEAP_YEAR = DAYS_PER_LEAP_YEAR - 1;
+
 constexpr int SECONDS_PER_HOUR = SECONDS_PER_MIN * MINUTES_PER_HOUR;
 constexpr int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
 constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR =
@@ -49,6 +63,8 @@
 constexpr int EPOCH_YEAR = 1970;
 constexpr int EPOCH_WEEK_DAY = 4;
 
+constexpr int ISO_FIRST_DAY_OF_YEAR = 3; // the 4th day of the year, 0-indexed.
+
 // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are
 // not within the normal ranges as defined in <time.h>, or if struct tm's
 // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
diff --git a/src/time/time_utils.cpp b/src/time/time_utils.cpp
index abc93b8..3ccb2dd9 100644
--- a/src/time/time_utils.cpp
+++ b/src/time/time_utils.cpp
@@ -12,9 +12,103 @@
 #include "src/__support/macros/config.h"
 #include "src/time/time_constants.h"
 
+#include <stdint.h>
+
 namespace LIBC_NAMESPACE_DECL {
 namespace time_utils {
 
+// TODO: clean this up in a followup patch
+int64_t mktime_internal(const tm *tm_out) {
+  // Unlike most C Library functions, mktime doesn't just die on bad input.
+  // TODO(rtenneti); Handle leap seconds.
+  int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE;
+
+  // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038.
+  if (sizeof(time_t) == 4 &&
+      tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) {
+    if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR)
+      return time_utils::out_of_range();
+    if (tm_out->tm_mon > 0)
+      return time_utils::out_of_range();
+    if (tm_out->tm_mday > 19)
+      return time_utils::out_of_range();
+    else if (tm_out->tm_mday == 19) {
+      if (tm_out->tm_hour > 3)
+        return time_utils::out_of_range();
+      else if (tm_out->tm_hour == 3) {
+        if (tm_out->tm_min > 14)
+          return time_utils::out_of_range();
+        else if (tm_out->tm_min == 14) {
+          if (tm_out->tm_sec > 7)
+            return time_utils::out_of_range();
+        }
+      }
+    }
+  }
+
+  // Years are ints.  A 32-bit year will fit into a 64-bit time_t.
+  // A 64-bit year will not.
+  static_assert(
+      sizeof(int) == 4,
+      "ILP64 is unimplemented. This implementation requires 32-bit integers.");
+
+  // Calculate number of months and years from tm_mon.
+  int64_t month = tm_out->tm_mon;
+  if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) {
+    int64_t years = month / 12;
+    month %= 12;
+    if (month < 0) {
+      years--;
+      month += 12;
+    }
+    tm_year_from_base += years;
+  }
+  bool tm_year_is_leap = time_utils::is_leap_year(tm_year_from_base);
+
+  // Calculate total number of days based on the month and the day (tm_mday).
+  int64_t total_days = tm_out->tm_mday - 1;
+  for (int64_t i = 0; i < month; ++i)
+    total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i];
+  // Add one day if it is a leap year and the month is after February.
+  if (tm_year_is_leap && month > 1)
+    total_days++;
+
+  // Calculate total numbers of days based on the year.
+  total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) *
+                time_constants::DAYS_PER_NON_LEAP_YEAR;
+  if (tm_year_from_base >= time_constants::EPOCH_YEAR) {
+    total_days +=
+        time_utils::get_num_of_leap_years_before(tm_year_from_base - 1) -
+        time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR);
+  } else if (tm_year_from_base >= 1) {
+    total_days -=
+        time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
+        time_utils::get_num_of_leap_years_before(tm_year_from_base - 1);
+  } else {
+    // Calculate number of leap years until 0th year.
+    total_days -=
+        time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
+        time_utils::get_num_of_leap_years_before(0);
+    if (tm_year_from_base <= 0) {
+      total_days -= 1; // Subtract 1 for 0th year.
+      // Calculate number of leap years until -1 year
+      if (tm_year_from_base < 0) {
+        total_days -=
+            time_utils::get_num_of_leap_years_before(-tm_year_from_base) -
+            time_utils::get_num_of_leap_years_before(1);
+      }
+    }
+  }
+
+  // TODO: https://github.com/llvm/llvm-project/issues/121962
+  // Need to handle timezone and update of tm_isdst.
+  int64_t seconds = tm_out->tm_sec +
+                    tm_out->tm_min * time_constants::SECONDS_PER_MIN +
+                    tm_out->tm_hour * time_constants::SECONDS_PER_HOUR +
+                    total_days * time_constants::SECONDS_PER_DAY;
+  return seconds;
+}
+
 static int64_t computeRemainingYears(int64_t daysPerYears,
                                      int64_t quotientYears,
                                      int64_t *remainingDays) {
@@ -42,7 +136,7 @@
 //
 // Compute the number of months from the remaining days. Finally, adjust years
 // to be 1900 and months to be from January.
-int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
+int64_t update_from_seconds(int64_t total_seconds, tm *tm) {
   // Days in month starting from March in the year 2000.
   static const char daysInMonth[] = {31 /* Mar */, 30, 31, 30, 31, 31,
                                      30,           31, 30, 31, 31, 29};
diff --git a/src/time/time_utils.h b/src/time/time_utils.h
index 5e0a692..68eaac8 100644
--- a/src/time/time_utils.h
+++ b/src/time/time_utils.h
@@ -12,6 +12,8 @@
 #include "hdr/types/size_t.h"
 #include "hdr/types/struct_tm.h"
 #include "hdr/types/time_t.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/errno/libc_errno.h"
@@ -22,9 +24,13 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace time_utils {
 
+// calculates the seconds from the epoch for tm_in. Does not update the struct,
+// you must call update_from_seconds for that.
+int64_t mktime_internal(const tm *tm_out);
+
 // Update the "tm" structure's year, month, etc. members from seconds.
 // "total_seconds" is the number of seconds since January 1st, 1970.
-extern int64_t update_from_seconds(int64_t total_seconds, struct tm *tm);
+int64_t update_from_seconds(int64_t total_seconds, tm *tm);
 
 // TODO(michaelrj): move these functions to use ErrorOr instead of setting
 // errno. They always accompany a specific return value so we only need the one
@@ -43,7 +49,7 @@
 
 LIBC_INLINE void invalid_value() { libc_errno = EINVAL; }
 
-LIBC_INLINE char *asctime(const struct tm *timeptr, char *buffer,
+LIBC_INLINE char *asctime(const tm *timeptr, char *buffer,
                           size_t bufferLength) {
   if (timeptr == nullptr || buffer == nullptr) {
     invalid_value();
@@ -61,6 +67,7 @@
   }
 
   // TODO(michaelr): move this to use the strftime machinery
+  // equivalent to strftime(buffer, bufferLength, "%a %b %T %Y\n", timeptr)
   int written_size = __builtin_snprintf(
       buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
       time_constants::WEEK_DAY_NAMES[timeptr->tm_wday].data(),
@@ -76,7 +83,7 @@
   return buffer;
 }
 
-LIBC_INLINE struct tm *gmtime_internal(const time_t *timer, struct tm *result) {
+LIBC_INLINE tm *gmtime_internal(const time_t *timer, tm *result) {
   int64_t seconds = *timer;
   // Update the tm structure's year, month, day, etc. from seconds.
   if (update_from_seconds(seconds, result) < 0) {
@@ -89,11 +96,252 @@
 
 // TODO: localtime is not yet implemented and a temporary solution is to
 //       use gmtime, https://github.com/llvm/llvm-project/issues/107597
-LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
-  static struct tm result;
+LIBC_INLINE tm *localtime(const time_t *t_ptr) {
+  static tm result;
   return time_utils::gmtime_internal(t_ptr, &result);
 }
 
+// Returns number of years from (1, year).
+LIBC_INLINE constexpr int64_t get_num_of_leap_years_before(int64_t year) {
+  return (year / 4) - (year / 100) + (year / 400);
+}
+
+// Returns True if year is a leap year.
+LIBC_INLINE constexpr bool is_leap_year(const int64_t year) {
+  return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
+}
+
+LIBC_INLINE constexpr int get_days_in_year(const int year) {
+  return is_leap_year(year) ? time_constants::DAYS_PER_LEAP_YEAR
+                            : time_constants::DAYS_PER_NON_LEAP_YEAR;
+}
+
+// This is a helper class that takes a struct tm and lets you inspect its
+// values. Where relevant, results are bounds checked and returned as optionals.
+// This class does not, however, do data normalization except where necessary.
+// It will faithfully return a date of 9999-99-99, even though that makes no
+// sense.
+class TMReader final {
+  const tm *timeptr;
+
+  template <size_t N>
+  LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+  bounds_check(const cpp::array<cpp::string_view, N> &arr, int index) const {
+    if (index >= 0 && index < static_cast<int>(arr.size()))
+      return arr[index];
+    return cpp::nullopt;
+  }
+
+public:
+  LIBC_INLINE constexpr explicit TMReader(const tm *tmptr) : timeptr(tmptr) {}
+
+  // Strings
+  LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+  get_weekday_short_name() const {
+    return bounds_check(time_constants::WEEK_DAY_NAMES, timeptr->tm_wday);
+  }
+
+  LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+  get_weekday_full_name() const {
+    return bounds_check(time_constants::WEEK_DAY_FULL_NAMES, timeptr->tm_wday);
+  }
+
+  LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+  get_month_short_name() const {
+    return bounds_check(time_constants::MONTH_NAMES, timeptr->tm_mon);
+  }
+
+  LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+  get_month_full_name() const {
+    return bounds_check(time_constants::MONTH_FULL_NAMES, timeptr->tm_mon);
+  }
+
+  LIBC_INLINE constexpr cpp::string_view get_am_pm() const {
+    if (timeptr->tm_hour < 12)
+      return "AM";
+    return "PM";
+  }
+
+  LIBC_INLINE constexpr cpp::string_view get_timezone_name() const {
+    // TODO: timezone support
+    return "UTC";
+  }
+
+  // Numbers
+  LIBC_INLINE constexpr int get_sec() const { return timeptr->tm_sec; }
+  LIBC_INLINE constexpr int get_min() const { return timeptr->tm_min; }
+  LIBC_INLINE constexpr int get_hour() const { return timeptr->tm_hour; }
+  LIBC_INLINE constexpr int get_mday() const { return timeptr->tm_mday; }
+  LIBC_INLINE constexpr int get_mon() const { return timeptr->tm_mon; }
+  LIBC_INLINE constexpr int get_yday() const { return timeptr->tm_yday; }
+  LIBC_INLINE constexpr int get_wday() const { return timeptr->tm_wday; }
+  LIBC_INLINE constexpr int get_isdst() const { return timeptr->tm_isdst; }
+
+  // returns the year, counting from 1900
+  LIBC_INLINE constexpr int get_year_raw() const { return timeptr->tm_year; }
+  // returns the year, counting from 0
+  LIBC_INLINE constexpr int get_year() const {
+    return timeptr->tm_year + time_constants::TIME_YEAR_BASE;
+  }
+
+  LIBC_INLINE constexpr int is_leap_year() const {
+    return time_utils::is_leap_year(get_year());
+  }
+
+  LIBC_INLINE constexpr int get_iso_wday() const {
+    using time_constants::DAYS_PER_WEEK;
+    using time_constants::MONDAY;
+    // ISO uses a week that starts on Monday, but struct tm starts its week on
+    // Sunday. This function normalizes the weekday so that it always returns a
+    // value 0-6
+    const int NORMALIZED_WDAY = timeptr->tm_wday % DAYS_PER_WEEK;
+    return (NORMALIZED_WDAY + (DAYS_PER_WEEK - MONDAY)) % DAYS_PER_WEEK;
+  }
+
+  // returns the week of the current year, with weeks starting on start_day.
+  LIBC_INLINE constexpr int get_week(time_constants::WeekDay start_day) const {
+    using time_constants::DAYS_PER_WEEK;
+    // The most recent start_day. The rest of the days into the current week
+    // don't count, so ignore them.
+    // Also add 7 to handle start_day > tm_wday
+    const int start_of_cur_week =
+        timeptr->tm_yday -
+        ((timeptr->tm_wday + DAYS_PER_WEEK - start_day) % DAYS_PER_WEEK);
+
+    // The original formula is ceil((start_of_cur_week + 1) / DAYS_PER_WEEK)
+    // That becomes (start_of_cur_week + 1 + DAYS_PER_WEEK - 1) / DAYS_PER_WEEK)
+    // Which simplifies to (start_of_cur_week + DAYS_PER_WEEK) / DAYS_PER_WEEK
+    const int ceil_weeks_since_start =
+        (start_of_cur_week + DAYS_PER_WEEK) / DAYS_PER_WEEK;
+
+    return ceil_weeks_since_start;
+  }
+
+  LIBC_INLINE constexpr int get_iso_week() const {
+    using time_constants::DAYS_PER_WEEK;
+    using time_constants::ISO_FIRST_DAY_OF_YEAR;
+    using time_constants::MONDAY;
+    using time_constants::WeekDay;
+    using time_constants::WEEKS_PER_YEAR;
+
+    constexpr WeekDay START_DAY = MONDAY;
+
+    // The most recent start_day. The rest of the days into the current week
+    // don't count, so ignore them.
+    // Also add 7 to handle start_day > tm_wday
+    const int start_of_cur_week =
+        timeptr->tm_yday -
+        ((timeptr->tm_wday + DAYS_PER_WEEK - START_DAY) % DAYS_PER_WEEK);
+
+    // if the week starts in the previous year, and also if the 4th of this year
+    // is not in this week.
+    if (start_of_cur_week < -3) {
+      const int days_into_prev_year =
+          get_days_in_year(get_year() - 1) + start_of_cur_week;
+      // Each year has at least 52 weeks, but a year's last week will be 53 if
+      // its first week starts in the previous year and its last week ends
+      // in the next year. We know get_year() - 1 must extend into get_year(),
+      // so here we check if it also extended into get_year() - 2 and add 1 week
+      // if it does.
+      return WEEKS_PER_YEAR +
+             ((days_into_prev_year % DAYS_PER_WEEK) > ISO_FIRST_DAY_OF_YEAR);
+    }
+
+    // subtract 1 to account for yday being 0 indexed
+    const int days_until_end_of_year =
+        get_days_in_year(get_year()) - start_of_cur_week - 1;
+
+    // if there are less than 3 days from the start of this week to the end of
+    // the year, then there must be 4 days in this week in the next year, which
+    // means that this week is the first week of that year.
+    if (days_until_end_of_year < 3)
+      return 1;
+
+    // else just calculate the current week like normal.
+    const int ceil_weeks_since_start =
+        (start_of_cur_week + DAYS_PER_WEEK) / DAYS_PER_WEEK;
+
+    // add 1 if this year's first week starts in the previous year.
+    const int WEEK_STARTS_IN_PREV_YEAR =
+        ((start_of_cur_week + time_constants::DAYS_PER_WEEK) %
+         time_constants::DAYS_PER_WEEK) > time_constants::ISO_FIRST_DAY_OF_YEAR;
+    return ceil_weeks_since_start + WEEK_STARTS_IN_PREV_YEAR;
+  }
+
+  LIBC_INLINE constexpr int get_iso_year() const {
+    const int BASE_YEAR = get_year();
+    // The ISO year is the same as a standard year for all dates after the start
+    // of the first week and before the last week. Since the first ISO week of a
+    // year starts on the 4th, anything after that is in this year.
+    if (timeptr->tm_yday >= time_constants::ISO_FIRST_DAY_OF_YEAR &&
+        timeptr->tm_yday < time_constants::DAYS_PER_NON_LEAP_YEAR -
+                               time_constants::DAYS_PER_WEEK)
+      return BASE_YEAR;
+
+    const int ISO_WDAY = get_iso_wday();
+    // The first week of the ISO year is defined as the week containing the
+    // 4th day of January.
+
+    // first week
+    if (timeptr->tm_yday < time_constants::ISO_FIRST_DAY_OF_YEAR) {
+      /*
+      If jan 4 is in this week, then we're in BASE_YEAR, else we're in the
+      previous year. The formula's been rearranged so here's the derivation:
+
+              +--------+-- days until jan 4
+              |        |
+       wday + (4 - yday) < 7
+       |               |
+       +---------------+-- weekday of jan 4
+
+       rearranged to get all the constants on one side:
+
+       wday - yday < 7 - 4
+      */
+      const int IS_CUR_YEAR = (ISO_WDAY - timeptr->tm_yday <
+                               time_constants::DAYS_PER_WEEK -
+                                   time_constants::ISO_FIRST_DAY_OF_YEAR);
+      return BASE_YEAR - !IS_CUR_YEAR;
+    }
+
+    // last week
+    const int DAYS_LEFT_IN_YEAR =
+        get_days_in_year(get_year()) - timeptr->tm_yday;
+    /*
+    Similar to above, we're checking if jan 4 (of next year) is in this week. If
+    it is, this is in the next year. Note that this also handles the case of
+    yday > days in year gracefully.
+
+           +------------------+-- days until jan 4 (of next year)
+           |                  |
+    wday + (4 + remaining days) < 7
+    |                         |
+    +-------------------------+-- weekday of jan 4
+
+    rearranging we get:
+
+    wday + remaining days < 7 - 4
+    */
+    const int IS_NEXT_YEAR =
+        (ISO_WDAY + DAYS_LEFT_IN_YEAR <
+         time_constants::DAYS_PER_WEEK - time_constants::ISO_FIRST_DAY_OF_YEAR);
+    return BASE_YEAR + IS_NEXT_YEAR;
+  }
+
+  LIBC_INLINE time_t get_epoch() const {
+    return static_cast<time_t>(mktime_internal(timeptr));
+  }
+
+  // returns the timezone offset in microwave time:
+  // return (hours * 100) + minutes;
+  // This means that a shift of -4:30 is returned as -430, simplifying
+  // conversion.
+  LIBC_INLINE constexpr int get_timezone_offset() const {
+    // TODO: timezone support
+    return 0;
+  }
+};
+
 } // namespace time_utils
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/test/UnitTest/FPMatcher.h b/test/UnitTest/FPMatcher.h
index 53e0c16..21b8a45 100644
--- a/test/UnitTest/FPMatcher.h
+++ b/test/UnitTest/FPMatcher.h
@@ -330,27 +330,6 @@
     EXPECT_FP_EXCEPTION(expected_except);                                      \
   } while (0)
 
-#define EXPECT_FP_EQ_ALL_ROUNDING(expected, actual)                            \
-  do {                                                                         \
-    using namespace LIBC_NAMESPACE::fputil::testing;                           \
-    ForceRoundingMode __r1(RoundingMode::Nearest);                             \
-    if (__r1.success) {                                                        \
-      EXPECT_FP_EQ((expected), (actual));                                      \
-    }                                                                          \
-    ForceRoundingMode __r2(RoundingMode::Upward);                              \
-    if (__r2.success) {                                                        \
-      EXPECT_FP_EQ((expected), (actual));                                      \
-    }                                                                          \
-    ForceRoundingMode __r3(RoundingMode::Downward);                            \
-    if (__r3.success) {                                                        \
-      EXPECT_FP_EQ((expected), (actual));                                      \
-    }                                                                          \
-    ForceRoundingMode __r4(RoundingMode::TowardZero);                          \
-    if (__r4.success) {                                                        \
-      EXPECT_FP_EQ((expected), (actual));                                      \
-    }                                                                          \
-  } while (0)
-
 #define EXPECT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode)            \
   do {                                                                         \
     using namespace LIBC_NAMESPACE::fputil::testing;                           \
@@ -372,6 +351,61 @@
 #define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual)                    \
   EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
 
+#define EXPECT_FP_EQ_ALL_ROUNDING_1(expected, actual)                          \
+  do {                                                                         \
+    EXPECT_FP_EQ_ROUNDING_NEAREST((expected), (actual));                       \
+    EXPECT_FP_EQ_ROUNDING_UPWARD((expected), (actual));                        \
+    EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected), (actual));                      \
+    EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected), (actual));                   \
+  } while (0)
+
+#define EXPECT_FP_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward,         \
+                                    expected_downward, expected_toward_zero,   \
+                                    actual)                                    \
+  do {                                                                         \
+    EXPECT_FP_EQ_ROUNDING_NEAREST((expected_nearest), (actual));               \
+    EXPECT_FP_EQ_ROUNDING_UPWARD((expected_upward), (actual));                 \
+    EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected_downward), (actual));             \
+    EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual));       \
+  } while (0)
+
+#define EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED(...)                             \
+  static_assert(false, "Unsupported number of arguments")
+
+#define EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5,    \
+                                              ARG6, ...)                       \
+  ARG6
+
+#define EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(...)                               \
+  EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(                                       \
+      __VA_ARGS__, EXPECT_FP_EQ_ALL_ROUNDING_4,                                \
+      EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED,                                   \
+      EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, EXPECT_FP_EQ_ALL_ROUNDING_1)
+
+#define EXPECT_FP_EQ_ALL_ROUNDING(...)                                         \
+  EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)
+
+#define ASSERT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode)            \
+  do {                                                                         \
+    using namespace LIBC_NAMESPACE::fputil::testing;                           \
+    ForceRoundingMode __r((rounding_mode));                                    \
+    if (__r.success) {                                                         \
+      ASSERT_FP_EQ((expected), (actual));                                      \
+    }                                                                          \
+  } while (0)
+
+#define ASSERT_FP_EQ_ROUNDING_NEAREST(expected, actual)                        \
+  ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
+
+#define ASSERT_FP_EQ_ROUNDING_UPWARD(expected, actual)                         \
+  ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
+
+#define ASSERT_FP_EQ_ROUNDING_DOWNWARD(expected, actual)                       \
+  ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)
+
+#define ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual)                    \
+  ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
+
 #define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE(                             \
     expected, actual, expected_except, rounding_mode)                          \
   do {                                                                         \
diff --git a/test/UnitTest/LibcTest.h b/test/UnitTest/LibcTest.h
index b4e3819..fbeafd0 100644
--- a/test/UnitTest/LibcTest.h
+++ b/test/UnitTest/LibcTest.h
@@ -400,6 +400,14 @@
   SuiteClass##_##TestName SuiteClass##_##TestName##_Instance;                  \
   void SuiteClass##_##TestName::Run()
 
+// Helper to trick the compiler into ignoring lack of braces on the else
+// branch.  We cannot introduce braces at this point, since it would prevent
+// using `<< ...` after the test macro for additional failure output.
+#define LIBC_TEST_DISABLE_DANGLING_ELSE                                        \
+  switch (0)                                                                   \
+  case 0:                                                                      \
+  default: // NOLINT
+
 // If RET_OR_EMPTY is the 'return' keyword we perform an early return which
 // corresponds to an assert. If it is empty the execution continues, this
 // corresponds to an expect.
@@ -411,6 +419,7 @@
 // returning a boolean. This expression is responsible for logging the
 // diagnostic in case of failure.
 #define LIBC_TEST_SCAFFOLDING_(TEST, RET_OR_EMPTY)                             \
+  LIBC_TEST_DISABLE_DANGLING_ELSE                                              \
   if (TEST)                                                                    \
     ;                                                                          \
   else                                                                         \
diff --git a/test/integration/src/pthread/pthread_mutex_test.cpp b/test/integration/src/pthread/pthread_mutex_test.cpp
index ce2a353..137daed 100644
--- a/test/integration/src/pthread/pthread_mutex_test.cpp
+++ b/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -186,6 +186,10 @@
   LIBC_NAMESPACE::pthread_mutex_destroy(&counter_lock);
 }
 
+// Test the initializer
+[[maybe_unused]]
+static pthread_mutex_t test_initializer = PTHREAD_MUTEX_INITIALIZER;
+
 TEST_MAIN() {
   relay_counter();
   wait_and_step();
diff --git a/test/src/__support/CPP/bit_test.cpp b/test/src/__support/CPP/bit_test.cpp
index 9429b66..1f23152 100644
--- a/test/src/__support/CPP/bit_test.cpp
+++ b/test/src/__support/CPP/bit_test.cpp
@@ -41,7 +41,8 @@
   constexpr auto LSB = T(1);
   constexpr auto MSB = T(~(ALL_ONES >> 1));
   for (T value = 1; value; value <<= 1) {
-    auto two_bits_value = value | ((value <= MIDPOINT) ? MSB : LSB);
+    T two_bits_value =
+        static_cast<T>(value | ((value <= MIDPOINT) ? MSB : LSB));
     EXPECT_FALSE(has_single_bit<T>(two_bits_value));
   }
 }
diff --git a/test/src/math/SqrtTest.h b/test/src/math/SqrtTest.h
index 770cc94..fdfc4f9 100644
--- a/test/src/math/SqrtTest.h
+++ b/test/src/math/SqrtTest.h
@@ -29,14 +29,14 @@
       FPBits denormal(zero);
       denormal.set_mantissa(mant);
       InType x = denormal.get_val();
-      EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5);
+      ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5);
     }
 
     constexpr StorageType COUNT = 200'001;
     constexpr StorageType STEP = HIDDEN_BIT / COUNT;
     for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
       InType x = FPBits(i).get_val();
-      EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5);
+      ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5);
     }
   }
 
@@ -48,7 +48,7 @@
       InType x = x_bits.get_val();
       if (x_bits.is_nan() || (x < 0))
         continue;
-      EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5);
+      ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5);
     }
   }
 };
diff --git a/test/src/math/asinf16_test.cpp b/test/src/math/asinf16_test.cpp
new file mode 100644
index 0000000..9593cad
--- /dev/null
+++ b/test/src/math/asinf16_test.cpp
@@ -0,0 +1,42 @@
+//===-- Exhaustive test for asinf16 ---------------------------------------===//
+//
+// 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 "src/math/asinf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcAsinf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+// Range: [0, Inf]
+static constexpr uint16_t POS_START = 0x0000U;
+static constexpr uint16_t POS_STOP = 0x7c00U;
+
+// Range: [-Inf, 0]
+static constexpr uint16_t NEG_START = 0x8000U;
+static constexpr uint16_t NEG_STOP = 0xfc00U;
+
+TEST_F(LlvmLibcAsinf16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Asin, x,
+                                   LIBC_NAMESPACE::asinf16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcAsinf16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Asin, x,
+                                   LIBC_NAMESPACE::asinf16(x), 0.5);
+  }
+}
diff --git a/test/src/math/performance_testing/sqrtf128_perf.cpp b/test/src/math/performance_testing/sqrtf128_perf.cpp
new file mode 100644
index 0000000..bc04e69
--- /dev/null
+++ b/test/src/math/performance_testing/sqrtf128_perf.cpp
@@ -0,0 +1,20 @@
+//===-- Differential test for sqrtf128
+//----------------------------------------===//
+//
+// 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 "SingleInputSingleOutputPerf.h"
+
+#include "src/__support/FPUtil/sqrt.h"
+#include "src/math/sqrtf128.h"
+
+float128 sqrtf128_placeholder(float128 x) {
+  return LIBC_NAMESPACE::fputil::sqrt<float128>(x);
+}
+
+SINGLE_INPUT_SINGLE_OUTPUT_PERF(float128, LIBC_NAMESPACE::sqrtf128,
+                                ::sqrtf128_placeholder, "sqrtf128_perf.log")
diff --git a/test/src/math/smoke/SqrtTest.h b/test/src/math/smoke/SqrtTest.h
index b5eaee2..29666ad 100644
--- a/test/src/math/smoke/SqrtTest.h
+++ b/test/src/math/smoke/SqrtTest.h
@@ -39,7 +39,8 @@
 
 #define LIST_SQRT_TESTS(T, func)                                               \
   using LlvmLibcSqrtTest = SqrtTest<T, T>;                                     \
-  TEST_F(LlvmLibcSqrtTest, SpecialNumbers) { test_special_numbers(&func); }
+  TEST_F(LlvmLibcSqrtTest, SpecialNumbers) { test_special_numbers(&func); }    \
+  static_assert(true, "Require semicolon.")
 
 #define LIST_NARROWING_SQRT_TESTS(OutType, InType, func)                       \
   using LlvmLibcSqrtTest = SqrtTest<OutType, InType>;                          \
diff --git a/test/src/math/smoke/asinf16_test.cpp b/test/src/math/smoke/asinf16_test.cpp
new file mode 100644
index 0000000..9f675b0
--- /dev/null
+++ b/test/src/math/smoke/asinf16_test.cpp
@@ -0,0 +1,42 @@
+//===-- Unittests for asinf16 ---------------------------------------------===//
+//
+//
+// 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 "src/errno/libc_errno.h"
+#include "src/math/asinf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcAsinf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcAsinf16Test, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::asinf16(sNaN), FE_INVALID);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(zero, LIBC_NAMESPACE::asinf16(zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::asinf16(neg_zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(neg_inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(2.0f));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(-2.0f));
+  EXPECT_MATH_ERRNO(EDOM);
+}
diff --git a/test/src/math/smoke/generic_sqrt_test.cpp b/test/src/math/smoke/generic_sqrt_test.cpp
index d0ab31f..4451e5e 100644
--- a/test/src/math/smoke/generic_sqrt_test.cpp
+++ b/test/src/math/smoke/generic_sqrt_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/__support/FPUtil/generic/sqrt.h"
 
-LIST_SQRT_TESTS(double, LIBC_NAMESPACE::fputil::sqrt<double>)
+LIST_SQRT_TESTS(double, LIBC_NAMESPACE::fputil::sqrt<double>);
diff --git a/test/src/math/smoke/generic_sqrtf128_test.cpp b/test/src/math/smoke/generic_sqrtf128_test.cpp
index edba114..790ff0a 100644
--- a/test/src/math/smoke/generic_sqrtf128_test.cpp
+++ b/test/src/math/smoke/generic_sqrtf128_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/__support/FPUtil/generic/sqrt.h"
 
-LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::fputil::sqrt<float128>)
+LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::fputil::sqrt<float128>);
diff --git a/test/src/math/smoke/generic_sqrtf_test.cpp b/test/src/math/smoke/generic_sqrtf_test.cpp
index f22ac88..e04d4c4 100644
--- a/test/src/math/smoke/generic_sqrtf_test.cpp
+++ b/test/src/math/smoke/generic_sqrtf_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/__support/FPUtil/generic/sqrt.h"
 
-LIST_SQRT_TESTS(float, LIBC_NAMESPACE::fputil::sqrt<float>)
+LIST_SQRT_TESTS(float, LIBC_NAMESPACE::fputil::sqrt<float>);
diff --git a/test/src/math/smoke/generic_sqrtl_test.cpp b/test/src/math/smoke/generic_sqrtl_test.cpp
index ddc6a23..ccb5054 100644
--- a/test/src/math/smoke/generic_sqrtl_test.cpp
+++ b/test/src/math/smoke/generic_sqrtl_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/__support/FPUtil/generic/sqrt.h"
 
-LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::fputil::sqrt<long double>)
+LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::fputil::sqrt<long double>);
diff --git a/test/src/math/smoke/sqrt_test.cpp b/test/src/math/smoke/sqrt_test.cpp
index 1551b31..b41e06da 100644
--- a/test/src/math/smoke/sqrt_test.cpp
+++ b/test/src/math/smoke/sqrt_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/math/sqrt.h"
 
-LIST_SQRT_TESTS(double, LIBC_NAMESPACE::sqrt)
+LIST_SQRT_TESTS(double, LIBC_NAMESPACE::sqrt);
diff --git a/test/src/math/smoke/sqrtf128_test.cpp b/test/src/math/smoke/sqrtf128_test.cpp
index 23397b0..3b9686c 100644
--- a/test/src/math/smoke/sqrtf128_test.cpp
+++ b/test/src/math/smoke/sqrtf128_test.cpp
@@ -8,6 +8,130 @@
 
 #include "SqrtTest.h"
 
+#include "src/__support/uint128.h"
 #include "src/math/sqrtf128.h"
 
-LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::sqrtf128)
+LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::sqrtf128);
+
+TEST_F(LlvmLibcSqrtTest, HardToRound) {
+  using LIBC_NAMESPACE::fputil::testing::RoundingMode;
+  using FPBits = LIBC_NAMESPACE::fputil::FPBits<float128>;
+
+  // Since there is no exact half cases for square root I encode the
+  // round direction in the sign of the result. E.g. if the number is
+  // negative it means that the exact root is below the rounded value
+  // (the absolute value). Thus I can test not only hard to round
+  // cases for the round to nearest mode but also the directional
+  // modes.
+  float128 HARD_TO_ROUND[][2] = {
+      {0x0.000000dee2f5b6a26c8f07f05442p-16382q,
+       -0x1.ddbd8763a617cff753e2a31083p-8204q},
+      {0x0.000000c86d174c5ad8ae54a548e7p-16382q,
+       0x1.c507bb538940719890851ec1ca88p-8204q},
+      {0x0.000020ab15cfe0b8e488e128f535p-16382q,
+       -0x1.6dccb402560213bc0d62d62e910bp-8201q},
+      {0x0.0000219e97732a9970f2511989bap-16382q,
+       0x1.73163d28be706f4b5052791e28a5p-8201q},
+      {0x0.000026e477546ae99ef57066f9fdp-16382q,
+       -0x1.8f20dd0d0c570a23ea59bc2bf009p-8201q},
+      {0x0.00002d0f88d27a496b3e533f5067p-16382q,
+       0x1.ad9d4abe9f047225a7352bcc52c1p-8201q},
+      {0x1.0000000000000000000000000001p+0q, 0x1p+0q},
+      {0x1.0000000000000000000000000002p+0q,
+       -0x1.0000000000000000000000000001p+0q},
+      {0x1.0000000000000000000000000003p+0q,
+       0x1.0000000000000000000000000001p+0q},
+      {0x1.0000000000000000000000000005p+0q,
+       0x1.0000000000000000000000000002p+0q},
+      {0x1.0000000000000000000000000006p+0q,
+       -0x1.0000000000000000000000000003p+0q},
+      {0x1.1d4c381cbf3a0aa15b9aee344892p+0q,
+       0x1.0e408c3fadc5e64b449c63673f4bp+0q},
+      {0x1.2af17a4ae6f93d11310c49c11b59p+0q,
+       -0x1.14a3bdf0ea5231f12d421a5dbe33p+0q},
+      {0x1.96f893bf29fb91e0fbe19a46d0c8p+0q,
+       0x1.42c6bf6202e66f2295807dee44d9p+0q},
+      {0x1.97fb3839925b66804c429289cce8p+0q,
+       -0x1.432d4049ac1c85a241f333d326e9p+0q},
+      {0x1.be1d900eaeb1533f0f19cc15c7e6p+0q,
+       0x1.51f1715154da44f3bf11f3d96c2dp+0q},
+      {0x1.c4f5074269525063a26051a0ad27p+0q,
+       0x1.54864e9b1daa4d9135ff00663366p+0q},
+      {0x1.035cb5f298a801dc4be9b1f8cd97p+1q,
+       -0x1.6c688775bffcb3f507ba11d0abb9p+0q},
+      {0x1.274be02380427e709beab4dedeb4p+1q,
+       -0x1.84d5763281f2318422392e506b1cp+0q},
+      {0x1.64e797cfdbaa3f7e2f33279dbc6p+1q,
+       0x1.ab79b164e255b26eca00ff99cc99p+0q},
+      {0x1.693a741358c9dac44a570a7e9f6cp+1q,
+       0x1.ae0e8eaeab25bb0c40ee0c2693d3p+0q},
+      {0x1.8275db3fc4d822596047adcb71b9p+1q,
+       -0x1.bcd2bfb653e37a5dbe0ccc2cd917p+0q},
+      {0x1.83280bb98c4a7b88bd6f535899d9p+1q,
+       0x1.bd39409dfd1990dd6a7f8211bb27p+0q},
+      {0x1.d78d8352b48608b510bfd5c75315p+1q,
+       -0x1.eb5c420f15adce0ed2bde5a241cep+0q},
+      {0x1.e3e4774f564b526edff84ce46668p+1q,
+       0x1.f1bf73c0523a19b4bb639c98c0b5p+0q},
+      {0x1.fffffffffffffffffffffffffffap+1q,
+       -0x1.fffffffffffffffffffffffffffdp+0q},
+      {0x1.fffffffffffffffffffffffffffbp+1q,
+       0x1.fffffffffffffffffffffffffffdp+0q},
+      {0x1.fffffffffffffffffffffffffffdp+1q,
+       0x1.fffffffffffffffffffffffffffep+0q},
+      {0x1.fffffffffffffffffffffffffffep+1q,
+       -0x1.ffffffffffffffffffffffffffffp+0q},
+      {0x1.ffffffffffffffffffffffffffffp+1q,
+       0x1.ffffffffffffffffffffffffffffp+0q},
+  };
+
+  auto rnd = [](float128 x, RoundingMode rm) -> float128 {
+    bool is_neg = x < 0;
+    float128 y = is_neg ? -x : x;
+    FPBits ybits(y);
+
+    if (is_neg &&
+        (rm == RoundingMode::Downward || rm == RoundingMode::TowardZero))
+      return FPBits(ybits.uintval() - 1).get_val();
+    if (!is_neg && (rm == RoundingMode::Upward))
+      return FPBits(ybits.uintval() + 1).get_val();
+
+    return y;
+  };
+
+  for (auto &t : HARD_TO_ROUND) {
+    EXPECT_FP_EQ_ALL_ROUNDING(
+        rnd(t[1], RoundingMode::Nearest), rnd(t[1], RoundingMode::Upward),
+        rnd(t[1], RoundingMode::Downward), rnd(t[1], RoundingMode::TowardZero),
+        LIBC_NAMESPACE::sqrtf128(t[0]));
+  }
+
+  // Exact results for subnormal arguments
+  float128 EXACT_SUBNORMAL[][2] = {
+      {0x0.0000000000000000000000000001p-16382q, 0x1p-8247q},
+      {0x0.0000000000000000000000000004p-16382q, 0x1p-8246q},
+      {0x0.0000000000001000000000000000p-16382q, 0x1p-8217q},
+      {0x0.0000000000010000000000000000p-16382q, 0x1p-8215q},
+      {0x0.0000000000100000000000000000p-16382q, 0x1p-8213q},
+  };
+
+  for (auto t : EXACT_SUBNORMAL)
+    EXPECT_FP_EQ_ALL_ROUNDING(t[1], LIBC_NAMESPACE::sqrtf128(t[0]));
+
+  // Check exact cases starting from small numbers
+  for (unsigned k = 1; k < 100 * 100; ++k) {
+    unsigned k2 = k * k;
+    float128 x = static_cast<float128>(k2);
+    float128 y = static_cast<float128>(k);
+    EXPECT_FP_EQ_ALL_ROUNDING(y, LIBC_NAMESPACE::sqrtf128(x));
+  };
+
+  // Then from the largest number.
+  uint64_t k0 = 101904826760412362ULL;
+  for (uint64_t k = k0; k > k0 - 10000; --k) {
+    UInt128 k2 = static_cast<UInt128>(k) * static_cast<UInt128>(k);
+    float128 x = static_cast<float128>(k2);
+    float128 y = static_cast<float128>(k);
+    EXPECT_FP_EQ_ALL_ROUNDING(y, LIBC_NAMESPACE::sqrtf128(x));
+  }
+}
diff --git a/test/src/math/smoke/sqrtf16_test.cpp b/test/src/math/smoke/sqrtf16_test.cpp
index d620496..950abd2 100644
--- a/test/src/math/smoke/sqrtf16_test.cpp
+++ b/test/src/math/smoke/sqrtf16_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/math/sqrtf16.h"
 
-LIST_SQRT_TESTS(float16, LIBC_NAMESPACE::sqrtf16)
+LIST_SQRT_TESTS(float16, LIBC_NAMESPACE::sqrtf16);
diff --git a/test/src/math/smoke/sqrtf_test.cpp b/test/src/math/smoke/sqrtf_test.cpp
index 3f2e973..888b6cb 100644
--- a/test/src/math/smoke/sqrtf_test.cpp
+++ b/test/src/math/smoke/sqrtf_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/math/sqrtf.h"
 
-LIST_SQRT_TESTS(float, LIBC_NAMESPACE::sqrtf)
+LIST_SQRT_TESTS(float, LIBC_NAMESPACE::sqrtf);
diff --git a/test/src/math/smoke/sqrtl_test.cpp b/test/src/math/smoke/sqrtl_test.cpp
index f80bcfb..4f4a64f 100644
--- a/test/src/math/smoke/sqrtl_test.cpp
+++ b/test/src/math/smoke/sqrtl_test.cpp
@@ -10,4 +10,4 @@
 
 #include "src/math/sqrtl.h"
 
-LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::sqrtl)
+LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::sqrtl);
diff --git a/test/src/stdbit/stdc_bit_ceil_uc_test.cpp b/test/src/stdbit/stdc_bit_ceil_uc_test.cpp
index 1ef87b0..6915859 100644
--- a/test/src/stdbit/stdc_bit_ceil_uc_test.cpp
+++ b/test/src/stdbit/stdc_bit_ceil_uc_test.cpp
@@ -17,18 +17,21 @@
 
 TEST(LlvmLibcStdcBitceilUcTest, Ones) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc(1U << i),
-              static_cast<unsigned char>(1U << i));
+    EXPECT_EQ(
+        LIBC_NAMESPACE::stdc_bit_ceil_uc(static_cast<unsigned char>(1U << i)),
+        static_cast<unsigned char>(1U << i));
 }
 
 TEST(LlvmLibcStdcBitceilUcTest, OneLessThanPowsTwo) {
   for (unsigned i = 2U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc((1U << i) - 1),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc(
+                  static_cast<unsigned char>((1U << i) - 1)),
               static_cast<unsigned char>(1U << i));
 }
 
 TEST(LlvmLibcStdcBitceilUcTest, OneMoreThanPowsTwo) {
   for (unsigned i = 1U; i != UCHAR_WIDTH - 1; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc((1U << i) + 1),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc(
+                  static_cast<unsigned char>((1U << i) + 1)),
               static_cast<unsigned char>(1U << (i + 1)));
 }
diff --git a/test/src/stdbit/stdc_bit_ceil_us_test.cpp b/test/src/stdbit/stdc_bit_ceil_us_test.cpp
index 56873c5..9a8b46f 100644
--- a/test/src/stdbit/stdc_bit_ceil_us_test.cpp
+++ b/test/src/stdbit/stdc_bit_ceil_us_test.cpp
@@ -17,18 +17,21 @@
 
 TEST(LlvmLibcStdcBitceilUsTest, Ones) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us(1U << i),
-              static_cast<unsigned short>(1U << i));
+    EXPECT_EQ(
+        LIBC_NAMESPACE::stdc_bit_ceil_us(static_cast<unsigned short>(1U << i)),
+        static_cast<unsigned short>(1U << i));
 }
 
 TEST(LlvmLibcStdcBitceilUsTest, OneLessThanPowsTwo) {
   for (unsigned i = 2U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us((1U << i) - 1),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us(
+                  static_cast<unsigned short>((1U << i) - 1)),
               static_cast<unsigned short>(1U << i));
 }
 
 TEST(LlvmLibcStdcBitceilUsTest, OneMoreThanPowsTwo) {
   for (unsigned i = 1U; i != USHRT_WIDTH - 1; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us((1U << i) + 1),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us(
+                  static_cast<unsigned short>((1U << i) + 1)),
               static_cast<unsigned short>(1U << (i + 1)));
 }
diff --git a/test/src/stdbit/stdc_first_leading_one_uc_test.cpp b/test/src/stdbit/stdc_first_leading_one_uc_test.cpp
index b8c8db5..2ab8397 100644
--- a/test/src/stdbit/stdc_first_leading_one_uc_test.cpp
+++ b/test/src/stdbit/stdc_first_leading_one_uc_test.cpp
@@ -16,6 +16,7 @@
 
 TEST(LlvmLibcStdcFirstLeadingOneUcTest, OneHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_uc(1U << i),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_uc(
+                  static_cast<unsigned char>(1U << i)),
               UCHAR_WIDTH - i);
 }
diff --git a/test/src/stdbit/stdc_first_leading_one_us_test.cpp b/test/src/stdbit/stdc_first_leading_one_us_test.cpp
index e948833..de81275 100644
--- a/test/src/stdbit/stdc_first_leading_one_us_test.cpp
+++ b/test/src/stdbit/stdc_first_leading_one_us_test.cpp
@@ -16,6 +16,7 @@
 
 TEST(LlvmLibcStdcFirstLeadingOneUsTest, OneHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_us(1U << i),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_us(
+                  static_cast<unsigned short>(1U << i)),
               USHRT_WIDTH - i);
 }
diff --git a/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp b/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp
index ac7e8c7..a19d0ab 100644
--- a/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp
+++ b/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp
@@ -16,6 +16,7 @@
 
 TEST(LlvmLibcStdcFirstLeadingZeroUcTest, ZeroHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_uc(~(1U << i)),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_uc(
+                  static_cast<unsigned char>(~(1U << i))),
               UCHAR_WIDTH - i);
 }
diff --git a/test/src/stdbit/stdc_first_leading_zero_us_test.cpp b/test/src/stdbit/stdc_first_leading_zero_us_test.cpp
index 37f8612..2971267 100644
--- a/test/src/stdbit/stdc_first_leading_zero_us_test.cpp
+++ b/test/src/stdbit/stdc_first_leading_zero_us_test.cpp
@@ -16,6 +16,7 @@
 
 TEST(LlvmLibcStdcFirstLeadingZeroUsTest, ZeroHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_us(~(1U << i)),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_us(
+                  static_cast<unsigned short>(~(1U << i))),
               USHRT_WIDTH - i);
 }
diff --git a/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp b/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp
index ed2b492..5ca4cfc 100644
--- a/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp
+++ b/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp
@@ -16,5 +16,7 @@
 
 TEST(LlvmLibcStdcFirstTrailingOneUcTest, OneHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_uc(1U << i), i + 1);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_uc(
+                  static_cast<unsigned char>(1U << i)),
+              i + 1);
 }
diff --git a/test/src/stdbit/stdc_first_trailing_one_us_test.cpp b/test/src/stdbit/stdc_first_trailing_one_us_test.cpp
index 6002155..46c69ac 100644
--- a/test/src/stdbit/stdc_first_trailing_one_us_test.cpp
+++ b/test/src/stdbit/stdc_first_trailing_one_us_test.cpp
@@ -16,5 +16,7 @@
 
 TEST(LlvmLibcStdcFirstTrailingOneUsTest, OneHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_us(1U << i), i + 1);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_us(
+                  static_cast<unsigned short>(1U << i)),
+              i + 1);
 }
diff --git a/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp b/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp
index 2b17aa6..9535ad9 100644
--- a/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp
+++ b/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp
@@ -16,5 +16,7 @@
 
 TEST(LlvmLibcStdcFirstTrailingZeroUcTest, ZeroHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_uc(~(1U << i)), i + 1);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_uc(
+                  static_cast<unsigned char>(~(1U << i))),
+              i + 1);
 }
diff --git a/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp b/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp
index e370379..e0dc34f 100644
--- a/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp
+++ b/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp
@@ -16,5 +16,7 @@
 
 TEST(LlvmLibcStdcFirstTrailingZeroUsTest, ZeroHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_us(~(1U << i)), i + 1);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_us(
+                  static_cast<unsigned short>(~(1U << i))),
+              i + 1);
 }
diff --git a/test/src/stdbit/stdc_has_single_bit_uc_test.cpp b/test/src/stdbit/stdc_has_single_bit_uc_test.cpp
index 1bc189c..9dd2bdc 100644
--- a/test/src/stdbit/stdc_has_single_bit_uc_test.cpp
+++ b/test/src/stdbit/stdc_has_single_bit_uc_test.cpp
@@ -16,5 +16,7 @@
 
 TEST(LlvmLibcStdcHasSingleBitUcTest, OneHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_uc(1U << i), true);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_uc(
+                  static_cast<unsigned char>(1U << i)),
+              true);
 }
diff --git a/test/src/stdbit/stdc_has_single_bit_us_test.cpp b/test/src/stdbit/stdc_has_single_bit_us_test.cpp
index a038f6f..3ff0b83 100644
--- a/test/src/stdbit/stdc_has_single_bit_us_test.cpp
+++ b/test/src/stdbit/stdc_has_single_bit_us_test.cpp
@@ -16,5 +16,7 @@
 
 TEST(LlvmLibcStdcHasSingleBitUsTest, OneHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_us(1U << i), true);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_us(
+                  static_cast<unsigned short>(1U << i)),
+              true);
 }
diff --git a/test/src/stdbit/stdc_leading_ones_uc_test.cpp b/test/src/stdbit/stdc_leading_ones_uc_test.cpp
index 5d32d92..4ba240f 100644
--- a/test/src/stdbit/stdc_leading_ones_uc_test.cpp
+++ b/test/src/stdbit/stdc_leading_ones_uc_test.cpp
@@ -17,6 +17,7 @@
 
 TEST(LlvmLibcStdcLeadingOnesUcTest, ZeroHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_uc(~(1U << i)),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_uc(
+                  static_cast<unsigned char>(~(1U << i))),
               UCHAR_WIDTH - i - 1U);
 }
diff --git a/test/src/stdbit/stdc_leading_ones_us_test.cpp b/test/src/stdbit/stdc_leading_ones_us_test.cpp
index 91a1253..0f93eed 100644
--- a/test/src/stdbit/stdc_leading_ones_us_test.cpp
+++ b/test/src/stdbit/stdc_leading_ones_us_test.cpp
@@ -17,6 +17,7 @@
 
 TEST(LlvmLibcStdcLeadingOnesUsTest, ZeroHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_us(~(1U << i)),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_us(
+                  static_cast<unsigned short>(~(1U << i))),
               USHRT_WIDTH - i - 1U);
 }
diff --git a/test/src/stdbit/stdc_leading_zeros_uc_test.cpp b/test/src/stdbit/stdc_leading_zeros_uc_test.cpp
index 3d55507..42f78c2 100644
--- a/test/src/stdbit/stdc_leading_zeros_uc_test.cpp
+++ b/test/src/stdbit/stdc_leading_zeros_uc_test.cpp
@@ -17,6 +17,7 @@
 
 TEST(LlvmLibcStdcLeadingZerosUcTest, OneHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_uc(1U << i),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_uc(
+                  static_cast<unsigned char>(1U << i)),
               UCHAR_WIDTH - i - 1U);
 }
diff --git a/test/src/stdbit/stdc_leading_zeros_us_test.cpp b/test/src/stdbit/stdc_leading_zeros_us_test.cpp
index afb418a..967ceb1 100644
--- a/test/src/stdbit/stdc_leading_zeros_us_test.cpp
+++ b/test/src/stdbit/stdc_leading_zeros_us_test.cpp
@@ -17,6 +17,7 @@
 
 TEST(LlvmLibcStdcLeadingZerosUsTest, OneHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_us(1U << i),
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_us(
+                  static_cast<unsigned short>(1U << i)),
               USHRT_WIDTH - i - 1U);
 }
diff --git a/test/src/stdbit/stdc_trailing_ones_uc_test.cpp b/test/src/stdbit/stdc_trailing_ones_uc_test.cpp
index 79d4e5b..0036408 100644
--- a/test/src/stdbit/stdc_trailing_ones_uc_test.cpp
+++ b/test/src/stdbit/stdc_trailing_ones_uc_test.cpp
@@ -17,5 +17,7 @@
 
 TEST(LlvmLibcStdcTrailingOnesUcTest, ZeroHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_uc(~(1U << i)), i);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_uc(
+                  static_cast<unsigned char>(~(1U << i))),
+              i);
 }
diff --git a/test/src/stdbit/stdc_trailing_ones_us_test.cpp b/test/src/stdbit/stdc_trailing_ones_us_test.cpp
index 7ab1574..5ebacc8 100644
--- a/test/src/stdbit/stdc_trailing_ones_us_test.cpp
+++ b/test/src/stdbit/stdc_trailing_ones_us_test.cpp
@@ -17,5 +17,7 @@
 
 TEST(LlvmLibcStdcTrailingOnesUsTest, ZeroHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_us(~(1U << i)), i);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_us(
+                  static_cast<unsigned short>(~(1U << i))),
+              i);
 }
diff --git a/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp b/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp
index c02b518..129ab38 100644
--- a/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp
+++ b/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp
@@ -17,5 +17,7 @@
 
 TEST(LlvmLibcStdcTrailingZerosUcTest, OneHot) {
   for (unsigned i = 0U; i != UCHAR_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_uc(1U << i), i);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_uc(
+                  static_cast<unsigned char>(1U << i)),
+              i);
 }
diff --git a/test/src/stdbit/stdc_trailing_zeros_us_test.cpp b/test/src/stdbit/stdc_trailing_zeros_us_test.cpp
index a9f8327..e1171f2 100644
--- a/test/src/stdbit/stdc_trailing_zeros_us_test.cpp
+++ b/test/src/stdbit/stdc_trailing_zeros_us_test.cpp
@@ -17,5 +17,7 @@
 
 TEST(LlvmLibcStdcTrailingZerosUsTest, OneHot) {
   for (unsigned i = 0U; i != USHRT_WIDTH; ++i)
-    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_us(1U << i), i);
+    EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_us(
+                  static_cast<unsigned short>(1U << i)),
+              i);
 }
diff --git a/test/src/stdfix/CountlsTest.h b/test/src/stdfix/CountlsTest.h
new file mode 100644
index 0000000..a8201ac
--- /dev/null
+++ b/test/src/stdfix/CountlsTest.h
@@ -0,0 +1,58 @@
+//===-- Utility class to test countls -------------------*- 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"
+
+template <typename T> class CountlsTest : 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 int (*CountlsFunc)(T);
+
+  void testSpecialNumbers(CountlsFunc func) {
+    constexpr bool is_signed = (FXRep::SIGN_LEN > 0);
+
+    EXPECT_EQ(FXRep::INTEGRAL_LEN, func(one_half));
+    EXPECT_EQ(FXRep::INTEGRAL_LEN + 1, func(one_fourth));
+    EXPECT_EQ(FXRep::VALUE_LEN, func(zero));
+    EXPECT_EQ(FXRep::VALUE_LEN - 1, func(eps));
+    EXPECT_EQ(0, func(max));
+    // If signed, left shifting the minimum value will overflow, so countls = 0.
+    // If unsigned, the minimum value is zero, so countls is the number of value
+    // bits according to ISO/IEC TR 18037.
+    EXPECT_EQ(is_signed ? 0 : FXRep::VALUE_LEN, func(min));
+
+    if (10 <= static_cast<int>(max))
+      EXPECT_EQ(FXRep::INTEGRAL_LEN - 4, func(10));
+
+    if (static_cast<int>(min) <= -10)
+      EXPECT_EQ(FXRep::INTEGRAL_LEN - 4, func(-10));
+
+    if constexpr (is_signed) {
+      EXPECT_EQ(FXRep::VALUE_LEN, func(-zero));
+      EXPECT_EQ(FXRep::VALUE_LEN, func(-eps));
+      EXPECT_EQ(FXRep::INTEGRAL_LEN + 1, func(-one_half));
+      if (FXRep::FRACTION_LEN >= 2)
+        EXPECT_EQ(FXRep::INTEGRAL_LEN + 2, func(-one_fourth));
+    }
+  }
+};
+
+#define LIST_COUNTLS_TESTS(T, func)                                            \
+  using LlvmLibcCountlsTest = CountlsTest<T>;                                  \
+  TEST_F(LlvmLibcCountlsTest, SpecialNumbers) { testSpecialNumbers(&func); }   \
+  static_assert(true, "Require semicolon.")
diff --git a/test/src/stdfix/countlshk_test.cpp b/test/src/stdfix/countlshk_test.cpp
new file mode 100644
index 0000000..659f869
--- /dev/null
+++ b/test/src/stdfix/countlshk_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlshk -------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlshk.h"
+
+LIST_COUNTLS_TESTS(short accum, LIBC_NAMESPACE::countlshk);
diff --git a/test/src/stdfix/countlshr_test.cpp b/test/src/stdfix/countlshr_test.cpp
new file mode 100644
index 0000000..361d4ac
--- /dev/null
+++ b/test/src/stdfix/countlshr_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlshr -------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlshr.h"
+
+LIST_COUNTLS_TESTS(short fract, LIBC_NAMESPACE::countlshr);
diff --git a/test/src/stdfix/countlsk_test.cpp b/test/src/stdfix/countlsk_test.cpp
new file mode 100644
index 0000000..74cb519
--- /dev/null
+++ b/test/src/stdfix/countlsk_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsk --------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsk.h"
+
+LIST_COUNTLS_TESTS(accum, LIBC_NAMESPACE::countlsk);
diff --git a/test/src/stdfix/countlslk_test.cpp b/test/src/stdfix/countlslk_test.cpp
new file mode 100644
index 0000000..006939d
--- /dev/null
+++ b/test/src/stdfix/countlslk_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlslk -------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlslk.h"
+
+LIST_COUNTLS_TESTS(long accum, LIBC_NAMESPACE::countlslk);
diff --git a/test/src/stdfix/countlslr_test.cpp b/test/src/stdfix/countlslr_test.cpp
new file mode 100644
index 0000000..896cf92
--- /dev/null
+++ b/test/src/stdfix/countlslr_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlslr -------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlslr.h"
+
+LIST_COUNTLS_TESTS(long fract, LIBC_NAMESPACE::countlslr);
diff --git a/test/src/stdfix/countlsr_test.cpp b/test/src/stdfix/countlsr_test.cpp
new file mode 100644
index 0000000..d7ae91c
--- /dev/null
+++ b/test/src/stdfix/countlsr_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsr --------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsr.h"
+
+LIST_COUNTLS_TESTS(fract, LIBC_NAMESPACE::countlsr);
diff --git a/test/src/stdfix/countlsuhk_test.cpp b/test/src/stdfix/countlsuhk_test.cpp
new file mode 100644
index 0000000..d8e68d6
--- /dev/null
+++ b/test/src/stdfix/countlsuhk_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsuhk ------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsuhk.h"
+
+LIST_COUNTLS_TESTS(unsigned short accum, LIBC_NAMESPACE::countlsuhk);
diff --git a/test/src/stdfix/countlsuhr_test.cpp b/test/src/stdfix/countlsuhr_test.cpp
new file mode 100644
index 0000000..7dbc590
--- /dev/null
+++ b/test/src/stdfix/countlsuhr_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsuhr ------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsuhr.h"
+
+LIST_COUNTLS_TESTS(unsigned short fract, LIBC_NAMESPACE::countlsuhr);
diff --git a/test/src/stdfix/countlsuk_test.cpp b/test/src/stdfix/countlsuk_test.cpp
new file mode 100644
index 0000000..20f78d8
--- /dev/null
+++ b/test/src/stdfix/countlsuk_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsuk -------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsuk.h"
+
+LIST_COUNTLS_TESTS(unsigned accum, LIBC_NAMESPACE::countlsuk);
diff --git a/test/src/stdfix/countlsulk_test.cpp b/test/src/stdfix/countlsulk_test.cpp
new file mode 100644
index 0000000..81ae208
--- /dev/null
+++ b/test/src/stdfix/countlsulk_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsulk ------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsulk.h"
+
+LIST_COUNTLS_TESTS(unsigned long accum, LIBC_NAMESPACE::countlsulk);
diff --git a/test/src/stdfix/countlsulr_test.cpp b/test/src/stdfix/countlsulr_test.cpp
new file mode 100644
index 0000000..5b9b047
--- /dev/null
+++ b/test/src/stdfix/countlsulr_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsulr ------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsulr.h"
+
+LIST_COUNTLS_TESTS(unsigned long fract, LIBC_NAMESPACE::countlsulr);
diff --git a/test/src/stdfix/countlsur_test.cpp b/test/src/stdfix/countlsur_test.cpp
new file mode 100644
index 0000000..67e32d7
--- /dev/null
+++ b/test/src/stdfix/countlsur_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for countlsur -------------------------------------------===//
+//
+// 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 "CountlsTest.h"
+
+#include "src/stdfix/countlsur.h"
+
+LIST_COUNTLS_TESTS(unsigned fract, LIBC_NAMESPACE::countlsur);