Skip to content

Commit ded4ea9

Browse files
authored
[libc][stdfix] Add sqrt for fixed point types. (#83042)
1 parent 62d0c01 commit ded4ea9

23 files changed

+520
-1
lines changed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,12 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
473473
libc.src.stdfix.roundur
474474
libc.src.stdfix.roundulk
475475
libc.src.stdfix.roundulr
476+
libc.src.stdfix.sqrtuhk
477+
libc.src.stdfix.sqrtuhr
478+
libc.src.stdfix.sqrtuk
479+
libc.src.stdfix.sqrtur
480+
# libc.src.stdfix.sqrtulk
481+
libc.src.stdfix.sqrtulr
476482
)
477483
endif()
478484

libc/docs/math/stdfix.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Fixed-point Arithmetics
7878
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
7979
| round | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| |
8080
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
81-
| sqrt | | | | | | | | | | | | |
81+
| sqrt | |check| | | |check| | | |check| | | |check| | | |check| | | | |
8282
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
8383

8484
================== =========

libc/spec/stdc_ext.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ def StdcExt : StandardSpec<"stdc_ext"> {
4747
GuardedFunctionSpec<"rounduhk", RetValSpec<UnsignedShortAccumType>, [ArgSpec<UnsignedShortAccumType>, ArgSpec<IntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
4848
GuardedFunctionSpec<"rounduk", RetValSpec<UnsignedAccumType>, [ArgSpec<UnsignedAccumType>, ArgSpec<IntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
4949
GuardedFunctionSpec<"roundulk", RetValSpec<UnsignedLongAccumType>, [ArgSpec<UnsignedLongAccumType>, ArgSpec<IntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
50+
51+
GuardedFunctionSpec<"sqrtuhr", RetValSpec<UnsignedShortFractType>, [ArgSpec<UnsignedShortFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
52+
GuardedFunctionSpec<"sqrtur", RetValSpec<UnsignedFractType>, [ArgSpec<UnsignedFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
53+
GuardedFunctionSpec<"sqrtulr", RetValSpec<UnsignedLongFractType>, [ArgSpec<UnsignedLongFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
54+
55+
GuardedFunctionSpec<"sqrtuhk", RetValSpec<UnsignedShortAccumType>, [ArgSpec<UnsignedShortAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
56+
GuardedFunctionSpec<"sqrtuk", RetValSpec<UnsignedAccumType>, [ArgSpec<UnsignedAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
57+
GuardedFunctionSpec<"sqrtulk", RetValSpec<UnsignedLongAccumType>, [ArgSpec<UnsignedLongAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
5058
]
5159
>;
5260

libc/src/__support/fixed_point/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,16 @@ add_header_library(
2121
libc.src.__support.CPP.bit
2222
libc.src.__support.math_extras
2323
)
24+
25+
add_header_library(
26+
sqrt
27+
HDRS
28+
sqrt.h
29+
DEPENDS
30+
.fx_rep
31+
libc.include.llvm-libc-macros.stdfix_macros
32+
libc.src.__support.macros.attributes
33+
libc.src.__support.macros.optimization
34+
libc.src.__support.CPP.bit
35+
libc.src.__support.CPP.type_traits
36+
)

libc/src/__support/fixed_point/sqrt.h

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===-- Calculate square root of fixed point numbers. -----*- C++ -*-=========//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_SQRT_H
10+
#define LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_SQRT_H
11+
12+
#include "include/llvm-libc-macros/stdfix-macros.h"
13+
#include "src/__support/CPP/bit.h"
14+
#include "src/__support/CPP/type_traits.h"
15+
#include "src/__support/macros/attributes.h" // LIBC_INLINE
16+
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
17+
18+
#include "fx_rep.h"
19+
20+
#ifdef LIBC_COMPILER_HAS_FIXED_POINT
21+
22+
namespace LIBC_NAMESPACE::fixed_point {
23+
24+
namespace internal {
25+
26+
template <typename T> struct SqrtConfig;
27+
28+
template <> struct SqrtConfig<unsigned short fract> {
29+
using Type = unsigned short fract;
30+
static constexpr int EXTRA_STEPS = 0;
31+
};
32+
33+
template <> struct SqrtConfig<unsigned fract> {
34+
using Type = unsigned fract;
35+
static constexpr int EXTRA_STEPS = 1;
36+
};
37+
38+
template <> struct SqrtConfig<unsigned long fract> {
39+
using Type = unsigned long fract;
40+
static constexpr int EXTRA_STEPS = 2;
41+
};
42+
43+
template <>
44+
struct SqrtConfig<unsigned short accum> : SqrtConfig<unsigned fract> {};
45+
46+
template <>
47+
struct SqrtConfig<unsigned accum> : SqrtConfig<unsigned long fract> {};
48+
49+
// TODO: unsigned long accum type is 64-bit, and will need 64-bit fract type.
50+
// Probably we will use DyadicFloat<64> for intermediate computations instead.
51+
52+
// Linear approximation for the initial values, with errors bounded by:
53+
// max(1.5 * 2^-11, eps)
54+
// Generated with Sollya:
55+
// > for i from 4 to 15 do {
56+
// P = fpminimax(sqrt(x), 1, [|8, 8|], [i * 2^-4, (i + 1)*2^-4],
57+
// fixed, absolute);
58+
// print("{", coeff(P, 1), "uhr,", coeff(P, 0), "uhr},");
59+
// };
60+
static constexpr unsigned short fract SQRT_FIRST_APPROX[12][2] = {
61+
{0x1.e8p-1uhr, 0x1.0cp-2uhr}, {0x1.bap-1uhr, 0x1.28p-2uhr},
62+
{0x1.94p-1uhr, 0x1.44p-2uhr}, {0x1.74p-1uhr, 0x1.6p-2uhr},
63+
{0x1.6p-1uhr, 0x1.74p-2uhr}, {0x1.4ep-1uhr, 0x1.88p-2uhr},
64+
{0x1.3ep-1uhr, 0x1.9cp-2uhr}, {0x1.32p-1uhr, 0x1.acp-2uhr},
65+
{0x1.22p-1uhr, 0x1.c4p-2uhr}, {0x1.18p-1uhr, 0x1.d4p-2uhr},
66+
{0x1.08p-1uhr, 0x1.fp-2uhr}, {0x1.04p-1uhr, 0x1.f8p-2uhr},
67+
};
68+
69+
} // namespace internal
70+
71+
template <typename T>
72+
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> sqrt(T x) {
73+
using BitType = typename FXRep<T>::StorageType;
74+
BitType x_bit = cpp::bit_cast<BitType>(x);
75+
76+
if (LIBC_UNLIKELY(x_bit == 0))
77+
return FXRep<T>::ZERO();
78+
79+
int leading_zeros = cpp::countl_zero(x_bit);
80+
constexpr int STORAGE_LENGTH = sizeof(BitType) * CHAR_BIT;
81+
constexpr int EXP_ADJUSTMENT = STORAGE_LENGTH - FXRep<T>::FRACTION_LEN - 1;
82+
// x_exp is the real exponent of the leading bit of x.
83+
int x_exp = EXP_ADJUSTMENT - leading_zeros;
84+
int shift = EXP_ADJUSTMENT - 1 - (x_exp & (~1));
85+
// Normalize.
86+
x_bit <<= shift;
87+
using FracType = typename internal::SqrtConfig<T>::Type;
88+
FracType x_frac = cpp::bit_cast<FracType>(x_bit);
89+
90+
// Use use Newton method to approximate sqrt(a):
91+
// x_{n + 1} = 1/2 (x_n + a / x_n)
92+
// For the initial values, we choose x_0
93+
94+
// Use the leading 4 bits to do look up for sqrt(x).
95+
// After normalization, 0.25 <= x_frac < 1, so the leading 4 bits of x_frac
96+
// are between 0b0100 and 0b1111. Hence the lookup table only needs 12
97+
// entries, and we can get the index by subtracting the leading 4 bits of
98+
// x_frac by 4 = 0b0100.
99+
int index = (x_bit >> (STORAGE_LENGTH - 4)) - 4;
100+
FracType a = static_cast<FracType>(internal::SQRT_FIRST_APPROX[index][0]);
101+
FracType b = static_cast<FracType>(internal::SQRT_FIRST_APPROX[index][1]);
102+
103+
// Initial approximation step.
104+
// Estimated error bounds: | r - sqrt(x_frac) | < max(1.5 * 2^-11, eps).
105+
FracType r = a * x_frac + b;
106+
107+
// Further Newton-method iterations for square-root:
108+
// x_{n + 1} = 0.5 * (x_n + a / x_n)
109+
// We distribute and do the multiplication by 0.5 first to avoid overflow.
110+
// TODO: Investigate the performance and accuracy of using division-free
111+
// iterations from:
112+
// Blanchard, J. D. and Chamberland, M., "Newton's Method Without Division",
113+
// The American Mathematical Monthly (2023).
114+
// https://chamberland.math.grinnell.edu/papers/newton.pdf
115+
for (int i = 0; i < internal::SqrtConfig<T>::EXTRA_STEPS; ++i)
116+
r = (r >> 1) + (x_frac >> 1) / r;
117+
118+
// Re-scaling
119+
r >>= EXP_ADJUSTMENT - (x_exp >> 1);
120+
121+
// Return result.
122+
return cpp::bit_cast<T>(r);
123+
}
124+
125+
} // namespace LIBC_NAMESPACE::fixed_point
126+
127+
#endif // LIBC_COMPILER_HAS_FIXED_POINT
128+
129+
#endif // LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_SQRT_H

libc/src/stdfix/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ foreach(suffix IN ITEMS hr r lr hk k lk)
1717
)
1818
endforeach()
1919

20+
foreach(suffix IN ITEMS uhr ur ulr uhk uk)
21+
add_entrypoint_object(
22+
sqrt${suffix}
23+
HDRS
24+
sqrt${suffix}.h
25+
SRCS
26+
sqrt${suffix}.cpp
27+
COMPILE_OPTIONS
28+
-O3
29+
-ffixed-point
30+
DEPENDS
31+
libc.src.__support.fixed_point.sqrt
32+
)
33+
endforeach()
34+
2035
foreach(suffix IN ITEMS hr r lr hk k lk uhr ur ulr uhk uk ulk)
2136
add_entrypoint_object(
2237
round${suffix}

libc/src/stdfix/sqrtuhk.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation of sqrtuhk function --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "sqrtuhk.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/fixed_point/sqrt.h"
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
LLVM_LIBC_FUNCTION(unsigned short accum, sqrtuhk, (unsigned short accum x)) {
16+
return fixed_point::sqrt(x);
17+
}
18+
19+
} // namespace LIBC_NAMESPACE

libc/src/stdfix/sqrtuhk.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for sqrtuhk -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDFIX_SQRTUHK_H
10+
#define LLVM_LIBC_SRC_STDFIX_SQRTUHK_H
11+
12+
#include "include/llvm-libc-macros/stdfix-macros.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
unsigned short accum sqrtuhk(unsigned short accum x);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_STDFIX_SQRTUHK_H

libc/src/stdfix/sqrtuhr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation of sqrtuhr function --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "sqrtuhr.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/fixed_point/sqrt.h"
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
LLVM_LIBC_FUNCTION(unsigned short fract, sqrtuhr, (unsigned short fract x)) {
16+
return fixed_point::sqrt(x);
17+
}
18+
19+
} // namespace LIBC_NAMESPACE

libc/src/stdfix/sqrtuhr.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for sqrtuhr -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDFIX_SQRTUHR_H
10+
#define LLVM_LIBC_SRC_STDFIX_SQRTUHR_H
11+
12+
#include "include/llvm-libc-macros/stdfix-macros.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
unsigned short fract sqrtuhr(unsigned short fract x);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_STDFIX_SQRTUHR_H

libc/src/stdfix/sqrtuk.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation of sqrtuk function ---------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "sqrtuk.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/fixed_point/sqrt.h"
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
LLVM_LIBC_FUNCTION(unsigned accum, sqrtuk, (unsigned accum x)) {
16+
return fixed_point::sqrt(x);
17+
}
18+
19+
} // namespace LIBC_NAMESPACE

libc/src/stdfix/sqrtuk.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for sqrtuk ------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDFIX_SQRTUK_H
10+
#define LLVM_LIBC_SRC_STDFIX_SQRTUK_H
11+
12+
#include "include/llvm-libc-macros/stdfix-macros.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
unsigned accum sqrtuk(unsigned accum x);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_STDFIX_SQRTUK_H

libc/src/stdfix/sqrtulr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation of sqrtulr function -------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "sqrtulr.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/fixed_point/sqrt.h"
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
LLVM_LIBC_FUNCTION(unsigned long fract, sqrtulr, (unsigned long fract x)) {
16+
return fixed_point::sqrt(x);
17+
}
18+
19+
} // namespace LIBC_NAMESPACE

libc/src/stdfix/sqrtulr.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for sqrtulr -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDFIX_SQRTULR_H
10+
#define LLVM_LIBC_SRC_STDFIX_SQRTULR_H
11+
12+
#include "include/llvm-libc-macros/stdfix-macros.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
unsigned long fract sqrtulr(unsigned long fract x);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_STDFIX_SQRTULR_H

libc/src/stdfix/sqrtur.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation of sqrtur function ---------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "sqrtur.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/fixed_point/sqrt.h"
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
LLVM_LIBC_FUNCTION(unsigned fract, sqrtur, (unsigned fract x)) {
16+
return fixed_point::sqrt(x);
17+
}
18+
19+
} // namespace LIBC_NAMESPACE

0 commit comments

Comments
 (0)