21 changes: 17 additions & 4 deletions libc/docs/math/stdfix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ StdFix Functions

.. include:: ../check.rst

Standards
---------
Standards and Goals
-------------------

- stdfix.h is specified in the `ISO/IEC TR 18037:2008 <https://www.iso.org/standard/51126.html>`_,
C extensions to support embedded processors .

- Its `specifications <https://standards.iso.org/ittf/PubliclyAvailableStandards/c051126_ISO_IEC_TR_18037_2008.zip>`_.

- Our goal is to implement a complete set of math functions for fixed point
types, most of them are currently not included in the ISO/IEC TR
18037:2008 standard. Our math functions for fixed point types are modeled
after the C99/C23 math functions for floating point types.

---------------
Source location
---------------
Expand Down Expand Up @@ -53,6 +58,8 @@ Predefined Macros
Fixed-point Arithmetics
=======================

The following functions are included in the ISO/IEC TR 18037:2008 standard.

+---------------+------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+
| Function Name | _Fract (r) | _Accum (k) |
| +------------------------------+----------------------------+------------------------------+------------------------------+----------------------------+------------------------------+
Expand All @@ -78,8 +85,6 @@ Fixed-point Arithmetics
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| round | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| sqrt | |check| | | |check| | | |check| | | |check| | | |check| | | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+

================== =========
Type Generic Macro Available
Expand All @@ -93,6 +98,9 @@ roundfx
Higher math functions
=====================

The following math functions are modeled after C99/C23 math functions for
floating point types, but are not part of the ISO/IEC TR 18037:2008 spec.

+---------------+------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+
| Function Name | _Fract (r) | _Accum (k) |
| +------------------------------+----------------------------+------------------------------+------------------------------+----------------------------+------------------------------+
Expand All @@ -108,13 +116,18 @@ Higher math functions
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| sin | | | | | | | | | | | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| sqrt | |check| | | |check| | | |check| | | |check| | | |check| | | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| tan | | | | | | | | | | | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+


Conversion Functions
====================

The following conversion functions are included in the ISO/IEC TR 18037:2008
standard.

+---------------+------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+
| Function Name | _Fract (r) | _Accum (k) |
| +------------------------------+----------------------------+------------------------------+------------------------------+----------------------------+------------------------------+
Expand Down
24 changes: 22 additions & 2 deletions libc/spec/llvm_libc_ext.td
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,29 @@ def LLVMLibcExt : StandardSpec<"llvm_libc_ext"> {
]
>;

HeaderSpec StdFix = HeaderSpec<
"stdfix.h",
[], // macros
[], // types
[], // enums
[ // functions
GuardedFunctionSpec<"sqrtuhr", RetValSpec<UnsignedShortFractType>, [ArgSpec<UnsignedShortFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtur", RetValSpec<UnsignedFractType>, [ArgSpec<UnsignedFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtulr", RetValSpec<UnsignedLongFractType>, [ArgSpec<UnsignedLongFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,

GuardedFunctionSpec<"sqrtuhk", RetValSpec<UnsignedShortAccumType>, [ArgSpec<UnsignedShortAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtuk", RetValSpec<UnsignedAccumType>, [ArgSpec<UnsignedAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtulk", RetValSpec<UnsignedLongAccumType>, [ArgSpec<UnsignedLongAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,

GuardedFunctionSpec<"uhksqrtus", RetValSpec<UnsignedShortAccumType>, [ArgSpec<UnsignedShortType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"uksqrtui", RetValSpec<UnsignedAccumType>, [ArgSpec<UnsignedIntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
]
>;

let Headers = [
Strings,
Sched,
Assert,
Sched,
StdFix,
Strings,
];
}
8 changes: 0 additions & 8 deletions libc/spec/stdc_ext.td
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@ def StdcExt : StandardSpec<"stdc_ext"> {
GuardedFunctionSpec<"rounduhk", RetValSpec<UnsignedShortAccumType>, [ArgSpec<UnsignedShortAccumType>, ArgSpec<IntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"rounduk", RetValSpec<UnsignedAccumType>, [ArgSpec<UnsignedAccumType>, ArgSpec<IntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"roundulk", RetValSpec<UnsignedLongAccumType>, [ArgSpec<UnsignedLongAccumType>, ArgSpec<IntType>], "LIBC_COMPILER_HAS_FIXED_POINT">,

GuardedFunctionSpec<"sqrtuhr", RetValSpec<UnsignedShortFractType>, [ArgSpec<UnsignedShortFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtur", RetValSpec<UnsignedFractType>, [ArgSpec<UnsignedFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtulr", RetValSpec<UnsignedLongFractType>, [ArgSpec<UnsignedLongFractType>], "LIBC_COMPILER_HAS_FIXED_POINT">,

GuardedFunctionSpec<"sqrtuhk", RetValSpec<UnsignedShortAccumType>, [ArgSpec<UnsignedShortAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtuk", RetValSpec<UnsignedAccumType>, [ArgSpec<UnsignedAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"sqrtulk", RetValSpec<UnsignedLongAccumType>, [ArgSpec<UnsignedLongAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
]
>;

Expand Down
1 change: 1 addition & 0 deletions libc/src/__support/fixed_point/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ add_header_library(
libc.src.__support.macros.attributes
libc.src.__support.macros.optimization
libc.src.__support.CPP.bit
libc.src.__support.CPP.limits
libc.src.__support.CPP.type_traits
)
24 changes: 24 additions & 0 deletions libc/src/__support/fixed_point/fx_rep.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ template <> struct FXRep<short fract> {
LIBC_INLINE static constexpr Type MAX() { return SFRACT_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0HR; }
LIBC_INLINE static constexpr Type EPS() { return SFRACT_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5HR; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25HR; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_signed_t<StorageType>;
Expand All @@ -66,6 +68,8 @@ template <> struct FXRep<unsigned short fract> {
LIBC_INLINE static constexpr Type MAX() { return USFRACT_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0UHR; }
LIBC_INLINE static constexpr Type EPS() { return USFRACT_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5UHR; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25UHR; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_unsigned_t<StorageType>;
Expand All @@ -84,6 +88,8 @@ template <> struct FXRep<fract> {
LIBC_INLINE static constexpr Type MAX() { return FRACT_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0R; }
LIBC_INLINE static constexpr Type EPS() { return FRACT_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5R; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25R; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_signed_t<StorageType>;
Expand All @@ -102,6 +108,8 @@ template <> struct FXRep<unsigned fract> {
LIBC_INLINE static constexpr Type MAX() { return UFRACT_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0UR; }
LIBC_INLINE static constexpr Type EPS() { return UFRACT_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5UR; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25UR; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_unsigned_t<StorageType>;
Expand All @@ -120,6 +128,8 @@ template <> struct FXRep<long fract> {
LIBC_INLINE static constexpr Type MAX() { return LFRACT_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0LR; }
LIBC_INLINE static constexpr Type EPS() { return LFRACT_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5LR; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25LR; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_signed_t<StorageType>;
Expand All @@ -138,6 +148,8 @@ template <> struct FXRep<unsigned long fract> {
LIBC_INLINE static constexpr Type MAX() { return ULFRACT_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0ULR; }
LIBC_INLINE static constexpr Type EPS() { return ULFRACT_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5ULR; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25ULR; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_unsigned_t<StorageType>;
Expand All @@ -156,6 +168,8 @@ template <> struct FXRep<short accum> {
LIBC_INLINE static constexpr Type MAX() { return SACCUM_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0HK; }
LIBC_INLINE static constexpr Type EPS() { return SACCUM_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5HK; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25HK; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_signed_t<StorageType>;
Expand All @@ -174,6 +188,8 @@ template <> struct FXRep<unsigned short accum> {
LIBC_INLINE static constexpr Type MAX() { return USACCUM_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0UHK; }
LIBC_INLINE static constexpr Type EPS() { return USACCUM_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5UHK; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25UHK; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_unsigned_t<StorageType>;
Expand All @@ -192,6 +208,8 @@ template <> struct FXRep<accum> {
LIBC_INLINE static constexpr Type MAX() { return ACCUM_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0K; }
LIBC_INLINE static constexpr Type EPS() { return ACCUM_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5K; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25K; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_signed_t<StorageType>;
Expand All @@ -210,6 +228,8 @@ template <> struct FXRep<unsigned accum> {
LIBC_INLINE static constexpr Type MAX() { return UACCUM_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0UK; }
LIBC_INLINE static constexpr Type EPS() { return UACCUM_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5UK; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25UK; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_unsigned_t<StorageType>;
Expand All @@ -228,6 +248,8 @@ template <> struct FXRep<long accum> {
LIBC_INLINE static constexpr Type MAX() { return LACCUM_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0LK; }
LIBC_INLINE static constexpr Type EPS() { return LACCUM_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5LK; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25LK; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_signed_t<StorageType>;
Expand All @@ -246,6 +268,8 @@ template <> struct FXRep<unsigned long accum> {
LIBC_INLINE static constexpr Type MAX() { return ULACCUM_MIN; }
LIBC_INLINE static constexpr Type ZERO() { return 0.0ULK; }
LIBC_INLINE static constexpr Type EPS() { return ULACCUM_EPSILON; }
LIBC_INLINE static constexpr Type ONE_HALF() { return 0.5ULK; }
LIBC_INLINE static constexpr Type ONE_FOURTH() { return 0.25ULK; }

using StorageType = typename internal::Storage<TOTAL_LEN>::Type;
using CompType = cpp::make_unsigned_t<StorageType>;
Expand Down
207 changes: 168 additions & 39 deletions libc/src/__support/fixed_point/sqrt.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/limits.h" // CHAR_BIT
#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/attributes.h" // LIBC_INLINE
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
Expand All @@ -28,16 +29,73 @@ template <typename T> struct SqrtConfig;
template <> struct SqrtConfig<unsigned short fract> {
using Type = unsigned short fract;
static constexpr int EXTRA_STEPS = 0;

// Linear approximation for the initial values, with errors bounded by:
// max(1.5 * 2^-11, eps)
// Generated with Sollya:
// > for i from 4 to 15 do {
// P = fpminimax(sqrt(x), 1, [|8, 8|], [i * 2^-4, (i + 1)*2^-4],
// fixed, absolute);
// print("{", coeff(P, 1), "uhr,", coeff(P, 0), "uhr},");
// };
static constexpr Type FIRST_APPROX[12][2] = {
{0x1.e8p-1uhr, 0x1.0cp-2uhr}, {0x1.bap-1uhr, 0x1.28p-2uhr},
{0x1.94p-1uhr, 0x1.44p-2uhr}, {0x1.74p-1uhr, 0x1.6p-2uhr},
{0x1.6p-1uhr, 0x1.74p-2uhr}, {0x1.4ep-1uhr, 0x1.88p-2uhr},
{0x1.3ep-1uhr, 0x1.9cp-2uhr}, {0x1.32p-1uhr, 0x1.acp-2uhr},
{0x1.22p-1uhr, 0x1.c4p-2uhr}, {0x1.18p-1uhr, 0x1.d4p-2uhr},
{0x1.08p-1uhr, 0x1.fp-2uhr}, {0x1.04p-1uhr, 0x1.f8p-2uhr},
};
};

template <> struct SqrtConfig<unsigned fract> {
using Type = unsigned fract;
static constexpr int EXTRA_STEPS = 1;

// Linear approximation for the initial values, with errors bounded by:
// max(1.5 * 2^-11, eps)
// Generated with Sollya:
// > for i from 4 to 15 do {
// P = fpminimax(sqrt(x), 1, [|16, 16|], [i * 2^-4, (i + 1)*2^-4],
// fixed, absolute);
// print("{", coeff(P, 1), "ur,", coeff(P, 0), "ur},");
// };
static constexpr Type FIRST_APPROX[12][2] = {
{0x1.e378p-1ur, 0x1.0ebp-2ur}, {0x1.b512p-1ur, 0x1.2b94p-2ur},
{0x1.91fp-1ur, 0x1.45dcp-2ur}, {0x1.7622p-1ur, 0x1.5e24p-2ur},
{0x1.5f5ap-1ur, 0x1.74e4p-2ur}, {0x1.4c58p-1ur, 0x1.8a4p-2ur},
{0x1.3c1ep-1ur, 0x1.9e84p-2ur}, {0x1.2e0cp-1ur, 0x1.b1d8p-2ur},
{0x1.21aap-1ur, 0x1.c468p-2ur}, {0x1.16bap-1ur, 0x1.d62cp-2ur},
{0x1.0cfp-1ur, 0x1.e74cp-2ur}, {0x1.0418p-1ur, 0x1.f7ep-2ur},
};
};

template <> struct SqrtConfig<unsigned long fract> {
using Type = unsigned long fract;
static constexpr int EXTRA_STEPS = 2;

// Linear approximation for the initial values, with errors bounded by:
// max(1.5 * 2^-11, eps)
// Generated with Sollya:
// > for i from 4 to 15 do {
// P = fpminimax(sqrt(x), 1, [|32, 32|], [i * 2^-4, (i + 1)*2^-4],
// fixed, absolute);
// print("{", coeff(P, 1), "ulr,", coeff(P, 0), "ulr},");
// };
static constexpr Type FIRST_APPROX[12][2] = {
{0x1.e3779b98p-1ulr, 0x1.0eaff788p-2ulr},
{0x1.b5167872p-1ulr, 0x1.2b908ad4p-2ulr},
{0x1.91f195cap-1ulr, 0x1.45da800cp-2ulr},
{0x1.761ebcb4p-1ulr, 0x1.5e27004cp-2ulr},
{0x1.5f619986p-1ulr, 0x1.74db933cp-2ulr},
{0x1.4c583adep-1ulr, 0x1.8a3fbfccp-2ulr},
{0x1.3c1a591cp-1ulr, 0x1.9e88373cp-2ulr},
{0x1.2e08545ap-1ulr, 0x1.b1dd2534p-2ulr},
{0x1.21b05c0ap-1ulr, 0x1.c45e023p-2ulr},
{0x1.16becd02p-1ulr, 0x1.d624031p-2ulr},
{0x1.0cf49fep-1ulr, 0x1.e743b844p-2ulr},
{0x1.04214e9cp-1ulr, 0x1.f7ce2c3cp-2ulr},
};
};

template <>
Expand All @@ -46,46 +104,38 @@ struct SqrtConfig<unsigned short accum> : SqrtConfig<unsigned fract> {};
template <>
struct SqrtConfig<unsigned accum> : SqrtConfig<unsigned long fract> {};

// TODO: unsigned long accum type is 64-bit, and will need 64-bit fract type.
// Probably we will use DyadicFloat<64> for intermediate computations instead.

// Linear approximation for the initial values, with errors bounded by:
// max(1.5 * 2^-11, eps)
// Generated with Sollya:
// > for i from 4 to 15 do {
// P = fpminimax(sqrt(x), 1, [|8, 8|], [i * 2^-4, (i + 1)*2^-4],
// fixed, absolute);
// print("{", coeff(P, 1), "uhr,", coeff(P, 0), "uhr},");
// };
static constexpr unsigned short fract SQRT_FIRST_APPROX[12][2] = {
{0x1.e8p-1uhr, 0x1.0cp-2uhr}, {0x1.bap-1uhr, 0x1.28p-2uhr},
{0x1.94p-1uhr, 0x1.44p-2uhr}, {0x1.74p-1uhr, 0x1.6p-2uhr},
{0x1.6p-1uhr, 0x1.74p-2uhr}, {0x1.4ep-1uhr, 0x1.88p-2uhr},
{0x1.3ep-1uhr, 0x1.9cp-2uhr}, {0x1.32p-1uhr, 0x1.acp-2uhr},
{0x1.22p-1uhr, 0x1.c4p-2uhr}, {0x1.18p-1uhr, 0x1.d4p-2uhr},
{0x1.08p-1uhr, 0x1.fp-2uhr}, {0x1.04p-1uhr, 0x1.f8p-2uhr},
// Integer square root
template <> struct SqrtConfig<unsigned short> {
using OutType = unsigned short accum;
using FracType = unsigned fract;
// For fast-but-less-accurate version
using FastFracType = unsigned short fract;
using HalfType = unsigned char;
};

} // namespace internal
template <> struct SqrtConfig<unsigned int> {
using OutType = unsigned accum;
using FracType = unsigned long fract;
// For fast-but-less-accurate version
using FastFracType = unsigned fract;
using HalfType = unsigned short;
};

template <typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> sqrt(T x) {
using BitType = typename FXRep<T>::StorageType;
BitType x_bit = cpp::bit_cast<BitType>(x);
// TODO: unsigned long accum type is 64-bit, and will need 64-bit fract type.
// Probably we will use DyadicFloat<64> for intermediate computations instead.

if (LIBC_UNLIKELY(x_bit == 0))
return FXRep<T>::ZERO();
} // namespace internal

int leading_zeros = cpp::countl_zero(x_bit);
constexpr int STORAGE_LENGTH = sizeof(BitType) * CHAR_BIT;
constexpr int EXP_ADJUSTMENT = STORAGE_LENGTH - FXRep<T>::FRACTION_LEN - 1;
// x_exp is the real exponent of the leading bit of x.
int x_exp = EXP_ADJUSTMENT - leading_zeros;
int shift = EXP_ADJUSTMENT - 1 - (x_exp & (~1));
// Normalize.
x_bit <<= shift;
using FracType = typename internal::SqrtConfig<T>::Type;
FracType x_frac = cpp::bit_cast<FracType>(x_bit);
// Core computation for sqrt with normalized inputs (0.25 <= x < 1).
template <typename Config>
LIBC_INLINE constexpr typename Config::Type
sqrt_core(typename Config::Type x_frac) {
using FracType = typename Config::Type;
using FXRep = FXRep<FracType>;
using StorageType = typename FXRep::StorageType;
// Exact case:
if (x_frac == FXRep::ONE_FOURTH())
return FXRep::ONE_HALF();

// Use use Newton method to approximate sqrt(a):
// x_{n + 1} = 1/2 (x_n + a / x_n)
Expand All @@ -96,9 +146,10 @@ LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> sqrt(T x) {
// are between 0b0100 and 0b1111. Hence the lookup table only needs 12
// entries, and we can get the index by subtracting the leading 4 bits of
// x_frac by 4 = 0b0100.
int index = (x_bit >> (STORAGE_LENGTH - 4)) - 4;
FracType a = static_cast<FracType>(internal::SQRT_FIRST_APPROX[index][0]);
FracType b = static_cast<FracType>(internal::SQRT_FIRST_APPROX[index][1]);
StorageType x_bit = cpp::bit_cast<StorageType>(x_frac);
int index = (static_cast<int>(x_bit >> (FXRep::TOTAL_LEN - 4))) - 4;
FracType a = Config::FIRST_APPROX[index][0];
FracType b = Config::FIRST_APPROX[index][1];

// Initial approximation step.
// Estimated error bounds: | r - sqrt(x_frac) | < max(1.5 * 2^-11, eps).
Expand All @@ -112,16 +163,94 @@ LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> sqrt(T x) {
// Blanchard, J. D. and Chamberland, M., "Newton's Method Without Division",
// The American Mathematical Monthly (2023).
// https://chamberland.math.grinnell.edu/papers/newton.pdf
for (int i = 0; i < internal::SqrtConfig<T>::EXTRA_STEPS; ++i)
for (int i = 0; i < Config::EXTRA_STEPS; ++i)
r = (r >> 1) + (x_frac >> 1) / r;

return r;
}

template <typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> sqrt(T x) {
using BitType = typename FXRep<T>::StorageType;
BitType x_bit = cpp::bit_cast<BitType>(x);

if (LIBC_UNLIKELY(x_bit == 0))
return FXRep<T>::ZERO();

int leading_zeros = cpp::countl_zero(x_bit);
constexpr int STORAGE_LENGTH = sizeof(BitType) * CHAR_BIT;
constexpr int EXP_ADJUSTMENT = STORAGE_LENGTH - FXRep<T>::FRACTION_LEN - 1;
// x_exp is the real exponent of the leading bit of x.
int x_exp = EXP_ADJUSTMENT - leading_zeros;
int shift = EXP_ADJUSTMENT - 1 - (x_exp & (~1));
// Normalize.
x_bit <<= shift;
using FracType = typename internal::SqrtConfig<T>::Type;
FracType x_frac = cpp::bit_cast<FracType>(x_bit);

// Compute sqrt(x_frac) using Newton-method.
FracType r = sqrt_core<internal::SqrtConfig<T>>(x_frac);

// Re-scaling
r >>= EXP_ADJUSTMENT - (x_exp >> 1);

// Return result.
return cpp::bit_cast<T>(r);
}

// Integer square root - Accurate version:
// Absolute errors < 2^(-fraction length).
template <typename T>
LIBC_INLINE constexpr typename internal::SqrtConfig<T>::OutType isqrt(T x) {
using OutType = typename internal::SqrtConfig<T>::OutType;
using FracType = typename internal::SqrtConfig<T>::FracType;

if (x == 0)
return FXRep<OutType>::ZERO();

// Normalize the leading bits to the first two bits.
// Shift and then Bit cast x to x_frac gives us:
// x = 2^(FRACTION_LEN + 1 - shift) * x_frac;
int leading_zeros = cpp::countl_zero(x);
int shift = ((leading_zeros >> 1) << 1);
x <<= shift;
// Convert to frac type and compute square root.
FracType x_frac = cpp::bit_cast<FracType>(x);
FracType r = sqrt_core<internal::SqrtConfig<FracType>>(x_frac);
// To rescale back to the OutType (Accum)
r >>= (shift >> 1);

return cpp::bit_cast<OutType>(r);
}

// Integer square root - Fast but less accurate version:
// Relative errors < 2^(-fraction length).
template <typename T>
LIBC_INLINE constexpr typename internal::SqrtConfig<T>::OutType
isqrt_fast(T x) {
using OutType = typename internal::SqrtConfig<T>::OutType;
using FracType = typename internal::SqrtConfig<T>::FastFracType;
using StorageType = typename FXRep<FracType>::StorageType;

if (x == 0)
return FXRep<OutType>::ZERO();

// Normalize the leading bits to the first two bits.
// Shift and then Bit cast x to x_frac gives us:
// x = 2^(FRACTION_LEN + 1 - shift) * x_frac;
int leading_zeros = cpp::countl_zero(x);
int shift = (leading_zeros & (~1));
x <<= shift;
// Convert to frac type and compute square root.
FracType x_frac = cpp::bit_cast<FracType>(
static_cast<StorageType>(x >> FXRep<FracType>::FRACTION_LEN));
OutType r =
static_cast<OutType>(sqrt_core<internal::SqrtConfig<FracType>>(x_frac));
// To rescale back to the OutType (Accum)
r <<= (FXRep<OutType>::INTEGRAL_LEN - (shift >> 1));
return cpp::bit_cast<OutType>(r);
}

} // namespace LIBC_NAMESPACE::fixed_point

#endif // LIBC_COMPILER_HAS_FIXED_POINT
Expand Down
116 changes: 63 additions & 53 deletions libc/src/__support/high_precision_decimal.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_HIGH_PRECISION_DECIMAL_H
#define LLVM_LIBC_SRC___SUPPORT_HIGH_PRECISION_DECIMAL_H

#include "src/__support/CPP/limits.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/str_to_integer.h"
#include <stdint.h>
Expand Down Expand Up @@ -115,9 +116,10 @@ class HighPrecisionDecimal {
uint8_t digits[MAX_NUM_DIGITS];

private:
bool should_round_up(int32_t roundToDigit, RoundDirection round) {
if (roundToDigit < 0 ||
static_cast<uint32_t>(roundToDigit) >= this->num_digits) {
LIBC_INLINE bool should_round_up(int32_t round_to_digit,
RoundDirection round) {
if (round_to_digit < 0 ||
static_cast<uint32_t>(round_to_digit) >= this->num_digits) {
return false;
}

Expand All @@ -133,8 +135,8 @@ class HighPrecisionDecimal {
// Else round to nearest.

// If we're right in the middle and there are no extra digits
if (this->digits[roundToDigit] == 5 &&
static_cast<uint32_t>(roundToDigit + 1) == this->num_digits) {
if (this->digits[round_to_digit] == 5 &&
static_cast<uint32_t>(round_to_digit + 1) == this->num_digits) {

// Round up if we've truncated (since that means the result is slightly
// higher than what's represented.)
Expand All @@ -143,22 +145,22 @@ class HighPrecisionDecimal {
}

// If this exactly halfway, round to even.
if (roundToDigit == 0)
if (round_to_digit == 0)
// When the input is ".5".
return false;
return this->digits[roundToDigit - 1] % 2 != 0;
return this->digits[round_to_digit - 1] % 2 != 0;
}
// If there are digits after roundToDigit, they must be non-zero since we
// If there are digits after round_to_digit, they must be non-zero since we
// trim trailing zeroes after all operations that change digits.
return this->digits[roundToDigit] >= 5;
return this->digits[round_to_digit] >= 5;
}

// Takes an amount to left shift and returns the number of new digits needed
// to store the result based on LEFT_SHIFT_DIGIT_TABLE.
uint32_t get_num_new_digits(uint32_t lShiftAmount) {
LIBC_INLINE uint32_t get_num_new_digits(uint32_t lshift_amount) {
const char *power_of_five =
LEFT_SHIFT_DIGIT_TABLE[lShiftAmount].power_of_five;
uint32_t new_digits = LEFT_SHIFT_DIGIT_TABLE[lShiftAmount].new_digits;
LEFT_SHIFT_DIGIT_TABLE[lshift_amount].power_of_five;
uint32_t new_digits = LEFT_SHIFT_DIGIT_TABLE[lshift_amount].new_digits;
uint32_t digit_index = 0;
while (power_of_five[digit_index] != 0) {
if (digit_index >= this->num_digits) {
Expand All @@ -176,7 +178,7 @@ class HighPrecisionDecimal {
}

// Trim all trailing 0s
void trim_trailing_zeroes() {
LIBC_INLINE void trim_trailing_zeroes() {
while (this->num_digits > 0 && this->digits[this->num_digits - 1] == 0) {
--this->num_digits;
}
Expand All @@ -186,19 +188,19 @@ class HighPrecisionDecimal {
}

// Perform a digitwise binary non-rounding right shift on this value by
// shiftAmount. The shiftAmount can't be more than MAX_SHIFT_AMOUNT to prevent
// overflow.
void right_shift(uint32_t shiftAmount) {
// shift_amount. The shift_amount can't be more than MAX_SHIFT_AMOUNT to
// prevent overflow.
LIBC_INLINE void right_shift(uint32_t shift_amount) {
uint32_t read_index = 0;
uint32_t write_index = 0;

uint64_t accumulator = 0;

const uint64_t shift_mask = (uint64_t(1) << shiftAmount) - 1;
const uint64_t shift_mask = (uint64_t(1) << shift_amount) - 1;

// Warm Up phase: we don't have enough digits to start writing, so just
// read them into the accumulator.
while (accumulator >> shiftAmount == 0) {
while (accumulator >> shift_amount == 0) {
uint64_t read_digit = 0;
// If there are still digits to read, read the next one, else the digit is
// assumed to be 0.
Expand All @@ -217,7 +219,7 @@ class HighPrecisionDecimal {
// read. Keep reading until we run out of digits.
while (read_index < this->num_digits) {
uint64_t read_digit = this->digits[read_index];
uint64_t write_digit = accumulator >> shiftAmount;
uint64_t write_digit = accumulator >> shift_amount;
accumulator &= shift_mask;
this->digits[write_index] = static_cast<uint8_t>(write_digit);
accumulator = accumulator * 10 + read_digit;
Expand All @@ -228,7 +230,7 @@ class HighPrecisionDecimal {
// Cool Down phase: All of the readable digits have been read, so just write
// the remainder, while treating any more digits as 0.
while (accumulator > 0) {
uint64_t write_digit = accumulator >> shiftAmount;
uint64_t write_digit = accumulator >> shift_amount;
accumulator &= shift_mask;
if (write_index < MAX_NUM_DIGITS) {
this->digits[write_index] = static_cast<uint8_t>(write_digit);
Expand All @@ -243,10 +245,10 @@ class HighPrecisionDecimal {
}

// Perform a digitwise binary non-rounding left shift on this value by
// shiftAmount. The shiftAmount can't be more than MAX_SHIFT_AMOUNT to prevent
// overflow.
void left_shift(uint32_t shiftAmount) {
uint32_t new_digits = this->get_num_new_digits(shiftAmount);
// shift_amount. The shift_amount can't be more than MAX_SHIFT_AMOUNT to
// prevent overflow.
LIBC_INLINE void left_shift(uint32_t shift_amount) {
uint32_t new_digits = this->get_num_new_digits(shift_amount);

int32_t read_index = this->num_digits - 1;
uint32_t write_index = this->num_digits + new_digits;
Expand All @@ -260,7 +262,7 @@ class HighPrecisionDecimal {
// writing.
while (read_index >= 0) {
accumulator += static_cast<uint64_t>(this->digits[read_index])
<< shiftAmount;
<< shift_amount;
uint64_t next_accumulator = accumulator / 10;
uint64_t write_digit = accumulator - (10 * next_accumulator);
--write_index;
Expand Down Expand Up @@ -296,45 +298,52 @@ class HighPrecisionDecimal {
}

public:
// numString is assumed to be a string of numeric characters. It doesn't
// num_string is assumed to be a string of numeric characters. It doesn't
// handle leading spaces.
HighPrecisionDecimal(const char *__restrict numString) {
LIBC_INLINE
HighPrecisionDecimal(
const char *__restrict num_string,
const size_t num_len = cpp::numeric_limits<size_t>::max()) {
bool saw_dot = false;
size_t num_cur = 0;
// This counts the digits in the number, even if there isn't space to store
// them all.
uint32_t total_digits = 0;
while (isdigit(*numString) || *numString == '.') {
if (*numString == '.') {
while (num_cur < num_len &&
(isdigit(num_string[num_cur]) || num_string[num_cur] == '.')) {
if (num_string[num_cur] == '.') {
if (saw_dot) {
break;
}
this->decimal_point = total_digits;
saw_dot = true;
} else {
if (*numString == '0' && this->num_digits == 0) {
if (num_string[num_cur] == '0' && this->num_digits == 0) {
--this->decimal_point;
++numString;
++num_cur;
continue;
}
++total_digits;
if (this->num_digits < MAX_NUM_DIGITS) {
this->digits[this->num_digits] =
static_cast<uint8_t>(*numString - '0');
static_cast<uint8_t>(num_string[num_cur] - '0');
++this->num_digits;
} else if (*numString != '0') {
} else if (num_string[num_cur] != '0') {
this->truncated = true;
}
}
++numString;
++num_cur;
}

if (!saw_dot)
this->decimal_point = total_digits;

if ((*numString | 32) == 'e') {
++numString;
if (isdigit(*numString) || *numString == '+' || *numString == '-') {
auto result = strtointeger<int32_t>(numString, 10);
if (num_cur < num_len && ((num_string[num_cur] | 32) == 'e')) {
++num_cur;
if (isdigit(num_string[num_cur]) || num_string[num_cur] == '+' ||
num_string[num_cur] == '-') {
auto result =
strtointeger<int32_t>(num_string + num_cur, 10, num_len - num_cur);
if (result.has_error()) {
// TODO: handle error
}
Expand All @@ -358,33 +367,34 @@ class HighPrecisionDecimal {
this->trim_trailing_zeroes();
}

// Binary shift left (shiftAmount > 0) or right (shiftAmount < 0)
void shift(int shiftAmount) {
if (shiftAmount == 0) {
// Binary shift left (shift_amount > 0) or right (shift_amount < 0)
LIBC_INLINE void shift(int shift_amount) {
if (shift_amount == 0) {
return;
}
// Left
else if (shiftAmount > 0) {
while (static_cast<uint32_t>(shiftAmount) > MAX_SHIFT_AMOUNT) {
else if (shift_amount > 0) {
while (static_cast<uint32_t>(shift_amount) > MAX_SHIFT_AMOUNT) {
this->left_shift(MAX_SHIFT_AMOUNT);
shiftAmount -= MAX_SHIFT_AMOUNT;
shift_amount -= MAX_SHIFT_AMOUNT;
}
this->left_shift(shiftAmount);
this->left_shift(shift_amount);
}
// Right
else {
while (static_cast<uint32_t>(shiftAmount) < -MAX_SHIFT_AMOUNT) {
while (static_cast<uint32_t>(shift_amount) < -MAX_SHIFT_AMOUNT) {
this->right_shift(MAX_SHIFT_AMOUNT);
shiftAmount += MAX_SHIFT_AMOUNT;
shift_amount += MAX_SHIFT_AMOUNT;
}
this->right_shift(-shiftAmount);
this->right_shift(-shift_amount);
}
}

// Round the number represented to the closest value of unsigned int type T.
// This is done ignoring overflow.
template <class T>
T round_to_integer_type(RoundDirection round = RoundDirection::Nearest) {
LIBC_INLINE T
round_to_integer_type(RoundDirection round = RoundDirection::Nearest) {
T result = 0;
uint32_t cur_digit = 0;

Expand All @@ -404,10 +414,10 @@ class HighPrecisionDecimal {

// Extra functions for testing.

uint8_t *get_digits() { return this->digits; }
uint32_t get_num_digits() { return this->num_digits; }
int32_t get_decimal_point() { return this->decimal_point; }
void set_truncated(bool trunc) { this->truncated = trunc; }
LIBC_INLINE uint8_t *get_digits() { return this->digits; }
LIBC_INLINE uint32_t get_num_digits() { return this->num_digits; }
LIBC_INLINE int32_t get_decimal_point() { return this->decimal_point; }
LIBC_INLINE void set_truncated(bool trunc) { this->truncated = trunc; }
};

} // namespace internal
Expand Down
40 changes: 25 additions & 15 deletions libc/src/__support/str_to_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,14 +313,15 @@ constexpr int32_t NUM_POWERS_OF_TWO =
// on the Simple Decimal Conversion algorithm by Nigel Tao, described at this
// link: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html
template <class T>
LIBC_INLINE FloatConvertReturn<T>
simple_decimal_conversion(const char *__restrict numStart,
RoundDirection round = RoundDirection::Nearest) {
LIBC_INLINE FloatConvertReturn<T> simple_decimal_conversion(
const char *__restrict numStart,
const size_t num_len = cpp::numeric_limits<size_t>::max(),
RoundDirection round = RoundDirection::Nearest) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;

int32_t exp2 = 0;
HighPrecisionDecimal hpd = HighPrecisionDecimal(numStart);
HighPrecisionDecimal hpd = HighPrecisionDecimal(numStart, num_len);

FloatConvertReturn<T> output;

Expand Down Expand Up @@ -600,13 +601,17 @@ clinger_fast_path(ExpandedFloat<T> init_num,
// non-inf result for this size of float. The value is
// log10(2^(exponent bias)).
// The generic approximation uses the fact that log10(2^x) ~= x/3
template <typename T> constexpr int32_t get_upper_bound() {
template <typename T> LIBC_INLINE constexpr int32_t get_upper_bound() {
return fputil::FPBits<T>::EXP_BIAS / 3;
}

template <> constexpr int32_t get_upper_bound<float>() { return 39; }
template <> LIBC_INLINE constexpr int32_t get_upper_bound<float>() {
return 39;
}

template <> constexpr int32_t get_upper_bound<double>() { return 309; }
template <> LIBC_INLINE constexpr int32_t get_upper_bound<double>() {
return 309;
}

// The lower bound is the largest negative base-10 exponent that could possibly
// give a non-zero result for this size of float. The value is
Expand All @@ -616,18 +621,18 @@ template <> constexpr int32_t get_upper_bound<double>() { return 309; }
// low base 10 exponent with a very high intermediate mantissa can cancel each
// other out, and subnormal numbers allow for the result to be at the very low
// end of the final mantissa.
template <typename T> constexpr int32_t get_lower_bound() {
template <typename T> LIBC_INLINE constexpr int32_t get_lower_bound() {
using FPBits = typename fputil::FPBits<T>;
return -((FPBits::EXP_BIAS +
static_cast<int32_t>(FPBits::FRACTION_LEN + FPBits::STORAGE_LEN)) /
3);
}

template <> constexpr int32_t get_lower_bound<float>() {
template <> LIBC_INLINE constexpr int32_t get_lower_bound<float>() {
return -(39 + 6 + 10);
}

template <> constexpr int32_t get_lower_bound<double>() {
template <> LIBC_INLINE constexpr int32_t get_lower_bound<double>() {
return -(309 + 15 + 20);
}

Expand All @@ -637,9 +642,10 @@ template <> constexpr int32_t get_lower_bound<double>() {
// accuracy. The resulting mantissa and exponent are placed in outputMantissa
// and outputExp2.
template <class T>
LIBC_INLINE FloatConvertReturn<T>
decimal_exp_to_float(ExpandedFloat<T> init_num, const char *__restrict numStart,
bool truncated, RoundDirection round) {
LIBC_INLINE FloatConvertReturn<T> decimal_exp_to_float(
ExpandedFloat<T> init_num, bool truncated, RoundDirection round,
const char *__restrict numStart,
const size_t num_len = cpp::numeric_limits<size_t>::max()) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;

Expand Down Expand Up @@ -701,7 +707,7 @@ decimal_exp_to_float(ExpandedFloat<T> init_num, const char *__restrict numStart,
#endif // LIBC_COPT_STRTOFLOAT_DISABLE_EISEL_LEMIRE

#ifndef LIBC_COPT_STRTOFLOAT_DISABLE_SIMPLE_DECIMAL_CONVERSION
output = simple_decimal_conversion<T>(numStart, round);
output = simple_decimal_conversion<T>(numStart, num_len, round);
#else
#warning "Simple decimal conversion is disabled, result may not be correct."
#endif // LIBC_COPT_STRTOFLOAT_DISABLE_SIMPLE_DECIMAL_CONVERSION
Expand Down Expand Up @@ -894,6 +900,8 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
if (!seen_digit)
return output;

// TODO: When adding max length argument, handle the case of a trailing
// EXPONENT MARKER, see scanf for more details.
if (tolower(src[index]) == EXPONENT_MARKER) {
bool has_sign = false;
if (src[index + 1] == '+' || src[index + 1] == '-') {
Expand Down Expand Up @@ -928,7 +936,7 @@ decimal_string_to_float(const char *__restrict src, const char DECIMAL_POINT,
output.value = {0, 0};
} else {
auto temp =
decimal_exp_to_float<T>({mantissa, exponent}, src, truncated, round);
decimal_exp_to_float<T>({mantissa, exponent}, truncated, round, src);
output.value = temp.num;
output.error = temp.error;
}
Expand Down Expand Up @@ -1071,6 +1079,8 @@ nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {

// Takes a pointer to a string and a pointer to a string pointer. This function
// is used as the backend for all of the string to float functions.
// TODO: Add src_len member to match strtointeger.
// TODO: Next, move from char* and length to string_view
template <class T>
LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
using FPBits = typename fputil::FPBits<T>;
Expand Down
85 changes: 46 additions & 39 deletions libc/src/__support/str_to_integer.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ namespace internal {

// Returns a pointer to the first character in src that is not a whitespace
// character (as determined by isspace())
LIBC_INLINE const char *first_non_whitespace(const char *__restrict src) {
while (internal::isspace(*src)) {
++src;
// TODO: Change from returning a pointer to returning a length.
LIBC_INLINE const char *
first_non_whitespace(const char *__restrict src,
size_t src_len = cpp::numeric_limits<size_t>::max()) {
size_t src_cur = 0;
while (src_cur < src_len && internal::isspace(src[src_cur])) {
++src_cur;
}
return src;
return src + src_cur;
}

LIBC_INLINE int b36_char_to_int(char input) {
Expand All @@ -38,61 +42,64 @@ LIBC_INLINE int b36_char_to_int(char input) {

// checks if the next 3 characters of the string pointer are the start of a
// hexadecimal number. Does not advance the string pointer.
LIBC_INLINE bool is_hex_start(const char *__restrict src) {
LIBC_INLINE bool
is_hex_start(const char *__restrict src,
size_t src_len = cpp::numeric_limits<size_t>::max()) {
if (src_len < 3)
return false;
return *src == '0' && (*(src + 1) | 32) == 'x' && isalnum(*(src + 2)) &&
b36_char_to_int(*(src + 2)) < 16;
}

// Takes the address of the string pointer and parses the base from the start of
// it. This function will advance |src| to the first valid digit in the inferred
// base.
LIBC_INLINE int infer_base(const char *__restrict *__restrict src) {
// it.
LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
// A hexadecimal number is defined as "the prefix 0x or 0X followed by a
// sequence of the decimal digits and the letters a (or A) through f (or F)
// with values 10 through 15 respectively." (C standard 6.4.4.1)
if (is_hex_start(*src)) {
(*src) += 2;
if (is_hex_start(src, src_len))
return 16;
} // An octal number is defined as "the prefix 0 optionally followed by a
// sequence of the digits 0 through 7 only" (C standard 6.4.4.1) and so any
// number that starts with 0, including just 0, is an octal number.
else if (**src == '0') {
// An octal number is defined as "the prefix 0 optionally followed by a
// sequence of the digits 0 through 7 only" (C standard 6.4.4.1) and so any
// number that starts with 0, including just 0, is an octal number.
if (src_len > 0 && src[0] == '0')
return 8;
} // A decimal number is defined as beginning "with a nonzero digit and
// consist[ing] of a sequence of decimal digits." (C standard 6.4.4.1)
else {
return 10;
}
// A decimal number is defined as beginning "with a nonzero digit and
// consist[ing] of a sequence of decimal digits." (C standard 6.4.4.1)
return 10;
}

// Takes a pointer to a string and the base to convert to. This function is used
// as the backend for all of the string to int functions.
template <class T>
LIBC_INLINE StrToNumResult<T> strtointeger(const char *__restrict src,
int base) {
LIBC_INLINE StrToNumResult<T>
strtointeger(const char *__restrict src, int base,
const size_t src_len = cpp::numeric_limits<size_t>::max()) {
// TODO: Rewrite to support numbers longer than long long
unsigned long long result = 0;
bool is_number = false;
const char *original_src = src;
size_t src_cur = 0;
int error_val = 0;

if (base < 0 || base == 1 || base > 36) {
error_val = EINVAL;
return {0, 0, error_val};
}
if (src_len == 0)
return {0, 0, 0};

if (base < 0 || base == 1 || base > 36)
return {0, 0, EINVAL};

src = first_non_whitespace(src);
src_cur = first_non_whitespace(src, src_len) - src;

char result_sign = '+';
if (*src == '+' || *src == '-') {
result_sign = *src;
++src;
if (src[src_cur] == '+' || src[src_cur] == '-') {
result_sign = src[src_cur];
++src_cur;
}

if (base == 0) {
base = infer_base(&src);
} else if (base == 16 && is_hex_start(src)) {
src = src + 2;
}
if (base == 0)
base = infer_base(src + src_cur, src_len - src_cur);

if (base == 16 && is_hex_start(src + src_cur, src_len - src_cur))
src_cur = src_cur + 2;

constexpr bool IS_UNSIGNED = (cpp::numeric_limits<T>::min() == 0);
const bool is_positive = (result_sign == '+');
Expand All @@ -103,13 +110,13 @@ LIBC_INLINE StrToNumResult<T> strtointeger(const char *__restrict src,
unsigned long long const abs_max =
(is_positive ? cpp::numeric_limits<T>::max() : NEGATIVE_MAX);
unsigned long long const abs_max_div_by_base = abs_max / base;
while (isalnum(*src)) {
int cur_digit = b36_char_to_int(*src);
while (src_cur < src_len && isalnum(src[src_cur])) {
int cur_digit = b36_char_to_int(src[src_cur]);
if (cur_digit >= base)
break;

is_number = true;
++src;
++src_cur;

// If the number has already hit the maximum value for the current type then
// the result cannot change, but we still need to advance src to the end of
Expand All @@ -133,7 +140,7 @@ LIBC_INLINE StrToNumResult<T> strtointeger(const char *__restrict src,
}
}

ptrdiff_t str_len = is_number ? (src - original_src) : 0;
ptrdiff_t str_len = is_number ? (src_cur) : 0;

if (error_val == ERANGE) {
if (is_positive || IS_UNSIGNED)
Expand Down
27 changes: 24 additions & 3 deletions libc/src/stdfix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ foreach(suffix IN ITEMS hr r lr hk k lk)
abs${suffix}.cpp
COMPILE_OPTIONS
-O3
-ffixed-point
DEPENDS
libc.src.__support.fixed_point.fx_bits
)
Expand All @@ -26,7 +25,6 @@ foreach(suffix IN ITEMS uhr ur ulr uhk uk)
sqrt${suffix}.cpp
COMPILE_OPTIONS
-O3
-ffixed-point
DEPENDS
libc.src.__support.fixed_point.sqrt
)
Expand All @@ -41,8 +39,31 @@ foreach(suffix IN ITEMS hr r lr hk k lk uhr ur ulr uhk uk ulk)
round${suffix}.cpp
COMPILE_OPTIONS
-O3
-ffixed-point
DEPENDS
libc.src.__support.fixed_point.fx_bits
)
endforeach()

add_entrypoint_object(
uhksqrtus
HDRS
uhksqrtus.h
SRCS
uhksqrtus.cpp
COMPILE_OPTIONS
-O3
DEPENDS
libc.src.__support.fixed_point.sqrt
)

add_entrypoint_object(
uksqrtui
HDRS
uksqrtui.h
SRCS
uksqrtui.cpp
COMPILE_OPTIONS
-O3
DEPENDS
libc.src.__support.fixed_point.sqrt
)
23 changes: 23 additions & 0 deletions libc/src/stdfix/uhksqrtus.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- Implementation of uhksqrtus 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 "uhksqrtus.h"
#include "src/__support/common.h"
#include "src/__support/fixed_point/sqrt.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(unsigned short accum, uhksqrtus, (unsigned short x)) {
#ifdef LIBC_FAST_MATH
return fixed_point::isqrt_fast(x);
#else
return fixed_point::isqrt(x);
#endif
}

} // namespace LIBC_NAMESPACE
20 changes: 20 additions & 0 deletions libc/src/stdfix/uhksqrtus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for uhksqrtus ---------------------*- 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_UHKSQRTUS_H
#define LLVM_LIBC_SRC_STDFIX_UHKSQRTUS_H

#include "include/llvm-libc-macros/stdfix-macros.h"

namespace LIBC_NAMESPACE {

unsigned short accum uhksqrtus(unsigned short x);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDFIX_UHKSQRTUS_H
23 changes: 23 additions & 0 deletions libc/src/stdfix/uksqrtui.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- Implementation of uksqrtui 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 "uksqrtui.h"
#include "src/__support/common.h"
#include "src/__support/fixed_point/sqrt.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(unsigned accum, uksqrtui, (unsigned int x)) {
#ifdef LIBC_FAST_MATH
return fixed_point::isqrt_fast(x);
#else
return fixed_point::isqrt(x);
#endif
}

} // namespace LIBC_NAMESPACE
20 changes: 20 additions & 0 deletions libc/src/stdfix/uksqrtui.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for uksqrtui ----------------------*- 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_UKSQRTUI_H
#define LLVM_LIBC_SRC_STDFIX_UKSQRTUI_H

#include "include/llvm-libc-macros/stdfix-macros.h"

namespace LIBC_NAMESPACE {

unsigned accum uksqrtui(unsigned int x);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDFIX_UKSQRTUI_H
4 changes: 3 additions & 1 deletion libc/test/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ add_subdirectory(__support)
add_subdirectory(ctype)
add_subdirectory(errno)
add_subdirectory(fenv)
add_subdirectory(inttypes)
add_subdirectory(math)
add_subdirectory(search)
add_subdirectory(stdbit)
Expand All @@ -50,6 +49,9 @@ add_subdirectory(stdlib)
add_subdirectory(string)
add_subdirectory(wchar)

# Depends on utilities in stdlib
add_subdirectory(inttypes)

if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(fcntl)
add_subdirectory(sched)
Expand Down
13 changes: 13 additions & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ add_libc_test(
libc.src.errno.errno
)


add_libc_test(
str_to_integer_test
SUITE
libc-support-tests
SRCS
str_to_integer_test.cpp
DEPENDS
libc.src.__support.integer_literals
libc.src.__support.str_to_integer
libc.src.errno.errno
)

add_libc_test(
integer_to_string_test
SUITE
Expand Down
28 changes: 28 additions & 0 deletions libc/test/src/__support/high_precision_decimal_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,31 @@ TEST(LlvmLibcHighPrecisionDecimalTest, BigExpTest) {
// Same, but since the number is negative the net result is -123456788
EXPECT_EQ(big_negative_hpd.get_decimal_point(), -123456789 + 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, NumLenExpTest) {
LIBC_NAMESPACE::internal::HighPrecisionDecimal hpd =
LIBC_NAMESPACE::internal::HighPrecisionDecimal("1e123456789", 5);

// The length of 5 includes things like the "e" so it only gets 3 digits of
// exponent.
EXPECT_EQ(hpd.get_decimal_point(), 123 + 1);

LIBC_NAMESPACE::internal::HighPrecisionDecimal negative_hpd =
LIBC_NAMESPACE::internal::HighPrecisionDecimal("1e-123456789", 5);

// The negative sign also counts as a character.
EXPECT_EQ(negative_hpd.get_decimal_point(), -12 + 1);
}

TEST(LlvmLibcHighPrecisionDecimalTest, NumLenDigitsTest) {
LIBC_NAMESPACE::internal::HighPrecisionDecimal hpd =
LIBC_NAMESPACE::internal::HighPrecisionDecimal("123456789e1", 5);

EXPECT_EQ(hpd.round_to_integer_type<uint64_t>(), uint64_t(12345));

LIBC_NAMESPACE::internal::HighPrecisionDecimal longer_hpd =
LIBC_NAMESPACE::internal::HighPrecisionDecimal("123456789e1", 10);

// With 10 characters it should see the e, but not actually act on it.
EXPECT_EQ(longer_hpd.round_to_integer_type<uint64_t>(), uint64_t(123456789));
}
240 changes: 240 additions & 0 deletions libc/test/src/__support/str_to_integer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
//===-- Unittests for str_to_integer --------------------------------------===//
//
// 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/str_to_integer.h"
#include "src/errno/libc_errno.h"
#include <stddef.h>

#include "test/UnitTest/Test.h"

// This file is for testing the src_len argument and other internal interface
// features. Primary testing is done in stdlib/StrolTest.cpp through the public
// interface.

TEST(LlvmLibcStrToIntegerTest, SimpleLength) {
auto result = LIBC_NAMESPACE::internal::strtointeger<int>("12345", 10, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(5));
ASSERT_EQ(result.value, 12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("12345", 10, 2);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(2));
ASSERT_EQ(result.value, 12);

result = LIBC_NAMESPACE::internal::strtointeger<int>("12345", 10, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, LeadingSpaces) {
auto result =
LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 15);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(10));
ASSERT_EQ(result.value, 12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(10));
ASSERT_EQ(result.value, 12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 7);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
ASSERT_EQ(result.value, 12);

result = LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 5);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, LeadingSign) {
auto result = LIBC_NAMESPACE::internal::strtointeger<int>("+12345", 10, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("-12345", 10, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, -12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("+12345", 10, 6);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("-12345", 10, 6);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, -12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("+12345", 10, 3);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(3));
ASSERT_EQ(result.value, 12);

result = LIBC_NAMESPACE::internal::strtointeger<int>("-12345", 10, 3);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(3));
ASSERT_EQ(result.value, -12);

result = LIBC_NAMESPACE::internal::strtointeger<int>("+12345", 10, 1);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("-12345", 10, 1);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("+12345", 10, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("-12345", 10, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, Base16PrefixAutoSelect) {
auto result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 0, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
ASSERT_EQ(result.value, 0x12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 0, 7);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
ASSERT_EQ(result.value, 0x12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 0, 5);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(5));
ASSERT_EQ(result.value, 0x123);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 0, 2);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 0, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, Base16PrefixManualSelect) {
auto result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 16, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
ASSERT_EQ(result.value, 0x12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 16, 7);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
ASSERT_EQ(result.value, 0x12345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 16, 5);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(5));
ASSERT_EQ(result.value, 0x123);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 16, 2);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("0x12345", 16, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, Base8PrefixAutoSelect) {
auto result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 0, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 012345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 0, 6);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 012345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 0, 4);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(4));
ASSERT_EQ(result.value, 0123);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 0, 1);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 0, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, Base8PrefixManualSelect) {
auto result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 8, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 012345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 8, 6);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 012345);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 8, 4);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(4));
ASSERT_EQ(result.value, 0123);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 8, 1);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
ASSERT_EQ(result.value, 0);

result = LIBC_NAMESPACE::internal::strtointeger<int>("012345", 8, 0);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
ASSERT_EQ(result.value, 0);
}

TEST(LlvmLibcStrToIntegerTest, CombinedTests) {
auto result =
LIBC_NAMESPACE::internal::strtointeger<int>(" -0x123", 0, 10);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(10));
ASSERT_EQ(result.value, -0x123);

result = LIBC_NAMESPACE::internal::strtointeger<int>(" -0x123", 0, 8);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(8));
ASSERT_EQ(result.value, -0x1);

result = LIBC_NAMESPACE::internal::strtointeger<int>(" -0x123", 0, 7);
EXPECT_FALSE(result.has_error());
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
ASSERT_EQ(result.value, 0);
}
41 changes: 38 additions & 3 deletions libc/test/src/stdfix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ foreach(suffix IN ITEMS hr r lr hk k lk)
abs${suffix}_test.cpp
COMPILE_OPTIONS
-O3
-ffixed-point
DEPENDS
libc.src.stdfix.abs${suffix}
libc.src.__support.fixed_point.fx_bits
Expand All @@ -33,7 +32,6 @@ foreach(suffix IN ITEMS uhr ur ulr uhk uk)
sqrt${suffix}_test.cpp
COMPILE_OPTIONS
-O3
-ffixed-point
DEPENDS
libc.src.stdfix.sqrt${suffix}
libc.src.__support.CPP.bit
Expand All @@ -55,9 +53,46 @@ foreach(suffix IN ITEMS hr r lr hk k lk uhr ur ulr uhk uk ulk)
round${suffix}_test.cpp
COMPILE_OPTIONS
-O3
-ffixed-point
DEPENDS
libc.src.stdfix.round${suffix}
libc.src.__support.fixed_point.fx_bits
)
endforeach()

add_libc_test(
uhksqrtus_test
SUITE
libc-stdfix-tests
HDRS
ISqrtTest.h
SRCS
uhksqrtus_test.cpp
COMPILE_OPTIONS
-O3
DEPENDS
libc.src.stdfix.uhksqrtus
libc.src.__support.CPP.bit
libc.src.__support.fixed_point.fx_rep
libc.src.__support.fixed_point.sqrt
libc.src.__support.FPUtil.basic_operations
libc.src.__support.FPUtil.sqrt
)

add_libc_test(
uksqrtui_test
SUITE
libc-stdfix-tests
HDRS
ISqrtTest.h
SRCS
uksqrtui_test.cpp
COMPILE_OPTIONS
-O3
DEPENDS
libc.src.stdfix.uksqrtui
libc.src.__support.CPP.bit
libc.src.__support.fixed_point.fx_rep
libc.src.__support.fixed_point.sqrt
libc.src.__support.FPUtil.basic_operations
libc.src.__support.FPUtil.sqrt
)
63 changes: 63 additions & 0 deletions libc/test/src/stdfix/ISqrtTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//===-- Utility class to test integer sqrt ----------------------*- 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/FPMatcher.h"
#include "test/UnitTest/Test.h"

#include "src/__support/CPP/bit.h"
#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/FPUtil/sqrt.h"
#include "src/__support/fixed_point/fx_rep.h"
#include "src/__support/fixed_point/sqrt.h"

template <typename T> class ISqrtTest : public LIBC_NAMESPACE::testing::Test {

using OutType =
typename LIBC_NAMESPACE::fixed_point::internal::SqrtConfig<T>::OutType;
using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<OutType>;
static constexpr OutType zero = FXRep::ZERO();
static constexpr OutType one = static_cast<OutType>(1);
static constexpr OutType eps = FXRep::EPS();

public:
typedef OutType (*SqrtFunc)(T);

void testSpecialNumbers(SqrtFunc func) {
EXPECT_EQ(zero, func(T(0)));

EXPECT_EQ(one, func(T(1)));
EXPECT_EQ(static_cast<OutType>(2.0), func(T(4)));
EXPECT_EQ(static_cast<OutType>(4.0), func(T(16)));
EXPECT_EQ(static_cast<OutType>(16.0), func(T(256)));

constexpr int COUNT = 255;
constexpr double ERR = 3.0 * static_cast<double>(eps);
double x_d = 0.0;
T x = 0;
for (int i = 0; i < COUNT; ++i) {
x_d += 1.0;
++x;
double y_d = static_cast<double>(func(x));
double result = LIBC_NAMESPACE::fputil::sqrt(x_d);
double errors = LIBC_NAMESPACE::fputil::abs((y_d / result) - 1.0);
if (errors > ERR) {
// Print out the failure input and output.
EXPECT_EQ(x, T(0));
EXPECT_EQ(func(x), zero);
}
ASSERT_TRUE(errors <= ERR);
}
}
};

#define LIST_ISQRT_TESTS(Name, T, func) \
using LlvmLibcISqrt##Name##Test = ISqrtTest<T>; \
TEST_F(LlvmLibcISqrt##Name##Test, SpecialNumbers) { \
testSpecialNumbers(&func); \
} \
static_assert(true, "Require semicolon.")
20 changes: 20 additions & 0 deletions libc/test/src/stdfix/uhksqrtus_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Unittests for uhksqrtus -------------------------------------------===//
//
// 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 "ISqrtTest.h"

#include "src/__support/fixed_point/sqrt.h"
#include "src/stdfix/uhksqrtus.h"

unsigned short accum uhksqrtus_fast(unsigned short x) {
return LIBC_NAMESPACE::fixed_point::isqrt_fast(x);
}

LIST_ISQRT_TESTS(US, unsigned short, LIBC_NAMESPACE::uhksqrtus);

LIST_ISQRT_TESTS(USFast, unsigned short, uhksqrtus_fast);
20 changes: 20 additions & 0 deletions libc/test/src/stdfix/uksqrtui_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Unittests for uksqrtui --------------------------------------------===//
//
// 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 "ISqrtTest.h"

#include "src/__support/fixed_point/sqrt.h"
#include "src/stdfix/uksqrtui.h"

unsigned accum uksqrtui_fast(unsigned int x) {
return LIBC_NAMESPACE::fixed_point::isqrt_fast(x);
}

LIST_ISQRT_TESTS(UI, unsigned int, LIBC_NAMESPACE::uksqrtui);

LIST_ISQRT_TESTS(UIFast, unsigned int, uksqrtui_fast);
114 changes: 114 additions & 0 deletions lldb/include/lldb/API/SBProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,120 @@ class LLDB_API SBProcess {
/// the process isn't loaded from a core file.
lldb::SBFileSpec GetCoreFile();

/// \{
/// \group Mask Address Methods
///
/// \a type
/// All of the methods in this group take \a type argument
/// which is an AddressMaskType enum value.
/// There can be different address masks for code addresses and
/// data addresses, this argument can select which to get/set,
/// or to use when clearing non-addressable bits from an address.
/// This choice of mask can be important for example on AArch32
/// systems. Where instructions where instructions start on even addresses,
/// the 0th bit may be used to indicate that a function is thumb code. On
/// such a target, the eAddressMaskTypeCode may clear the 0th bit from an
/// address to get the actual address Whereas eAddressMaskTypeData would not.
///
/// \a addr_range
/// Many of the methods in this group take an \a addr_range argument
/// which is an AddressMaskRange enum value.
/// Needing to specify the address range is highly unusual, and the
/// default argument can be used in nearly all circumstances.
/// On some architectures (e.g., AArch64), it is possible to have
/// different page table setups for low and high memory, so different
/// numbers of bits relevant to addressing. It is possible to have
/// a program running in one half of memory and accessing the other
/// as heap, so we need to maintain two different sets of address masks
/// to debug this correctly.

/// Get the current address mask that will be applied to addresses
/// before reading from memory.
///
/// \param[in] type
/// See \ref Mask Address Methods description of this argument.
/// eAddressMaskTypeAny is often a suitable value when code and
/// data masks are the same on a given target.
///
/// \param[in] addr_range
/// See \ref Mask Address Methods description of this argument.
/// This will default to eAddressMaskRangeLow which is the
/// only set of masks used normally.
///
/// \return
/// The address mask currently in use. Bits which are not used
/// for addressing will be set to 1 in the mask.
lldb::addr_t GetAddressMask(
lldb::AddressMaskType type,
lldb::AddressMaskRange addr_range = lldb::eAddressMaskRangeLow);

/// Set the current address mask that can be applied to addresses
/// before reading from memory.
///
/// \param[in] type
/// See \ref Mask Address Methods description of this argument.
/// eAddressMaskTypeAll is often a suitable value when the
/// same mask is being set for both code and data.
///
/// \param[in] mask
/// The address mask to set. Bits which are not used for addressing
/// should be set to 1 in the mask.
///
/// \param[in] addr_range
/// See \ref Mask Address Methods description of this argument.
/// This will default to eAddressMaskRangeLow which is the
/// only set of masks used normally.
void SetAddressMask(
lldb::AddressMaskType type, lldb::addr_t mask,
lldb::AddressMaskRange addr_range = lldb::eAddressMaskRangeLow);

/// Set the number of bits used for addressing in this Process.
///
/// On Darwin and similar systems, the addressable bits are expressed
/// as the number of low order bits that are relevant to addressing,
/// instead of a more general address mask.
/// This method calculates the correct mask value for a given number
/// of low order addressable bits.
///
/// \param[in] type
/// See \ref Mask Address Methods description of this argument.
/// eAddressMaskTypeAll is often a suitable value when the
/// same mask is being set for both code and data.
///
/// \param[in] num_bits
/// Number of bits that are used for addressing.
/// For example, a value of 42 indicates that the low 42 bits
/// are relevant for addressing, and that higher-order bits may
/// be used for various metadata like pointer authentication,
/// Type Byte Ignore, etc.
///
/// \param[in] addr_range
/// See \ref Mask Address Methods description of this argument.
/// This will default to eAddressMaskRangeLow which is the
/// only set of masks used normally.
void
SetAddressableBits(AddressMaskType type, uint32_t num_bits,
AddressMaskRange addr_range = lldb::eAddressMaskRangeLow);

/// Clear the non-address bits of an \a addr value and return a
/// virtual address in memory.
///
/// Bits that are not used in addressing may be used for other purposes;
/// pointer authentication, or metadata in the top byte, or the 0th bit
/// of armv7 code addresses to indicate arm/thumb are common examples.
///
/// \param[in] addr
/// The address that should be cleared of non-address bits.
///
/// \param[in] type
/// See \ref Mask Address Methods description of this argument.
/// eAddressMaskTypeAny is the default value, correct when it
/// is unknown if the address is a code or data address.
lldb::addr_t
FixAddress(lldb::addr_t addr,
lldb::AddressMaskType type = lldb::eAddressMaskTypeAny);
/// \}

/// Allocate memory within the process.
///
/// This function will allocate memory in the process's address space.
Expand Down
8 changes: 2 additions & 6 deletions lldb/include/lldb/Target/ABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ class ABI : public PluginInterface {
/// ARM uses bit zero to signify a code address is thumb, so any ARM ABI
/// plug-ins would strip those bits.
/// @{
virtual lldb::addr_t FixCodeAddress(lldb::addr_t pc) { return pc; }
virtual lldb::addr_t FixDataAddress(lldb::addr_t pc) { return pc; }
virtual lldb::addr_t FixCodeAddress(lldb::addr_t pc);
virtual lldb::addr_t FixDataAddress(lldb::addr_t pc);
/// @}

/// Use this method when you do not know, or do not care what kind of address
Expand Down Expand Up @@ -166,10 +166,6 @@ class ABI : public PluginInterface {
lldb::ProcessWP m_process_wp;
std::unique_ptr<llvm::MCRegisterInfo> m_mc_register_info_up;

virtual lldb::addr_t FixCodeAddress(lldb::addr_t pc, lldb::addr_t mask) {
return pc;
}

private:
ABI(const ABI &) = delete;
const ABI &operator=(const ABI &) = delete;
Expand Down
36 changes: 27 additions & 9 deletions lldb/include/lldb/Target/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -1422,9 +1422,23 @@ class Process : public std::enable_shared_from_this<Process>,

virtual void DidExit() {}

/// Get the current address mask in the Process
///
/// This mask can used to set/clear non-address bits in an addr_t.
///
/// \return
/// The current address mask.
/// Bits which are set to 1 are not used for addressing.
/// An address mask of 0 means all bits are used for addressing.
/// An address mask of LLDB_INVALID_ADDRESS_MASK (all 1's) means
/// that no mask has been set.
lldb::addr_t GetCodeAddressMask();
lldb::addr_t GetDataAddressMask();

/// The highmem masks are for targets where we may have different masks
/// for low memory versus high memory addresses, and they will be left
/// as LLDB_INVALID_ADDRESS_MASK normally, meaning the base masks
/// should be applied to all addresses.
lldb::addr_t GetHighmemCodeAddressMask();
lldb::addr_t GetHighmemDataAddressMask();

Expand Down Expand Up @@ -3096,16 +3110,20 @@ void PruneThreadPlans();
// if run while destructing. We use this flag to determine that.
std::atomic<bool> m_destructing;

/// Mask for code an data addresses. The default value (0) means no mask is
/// set. The bits set to 1 indicate bits that are NOT significant for
/// addressing.
/// The highmem versions are for targets where we may have different masks
/// for low memory versus high memory addresses.
/// Mask for code an data addresses.
/// The default value LLDB_INVALID_ADDRESS_MASK means no mask has been set,
/// and addresses values should not be modified.
/// In these masks, the bits are set to 1 indicate bits that are not
/// significant for addressing.
/// The highmem masks are for targets where we may have different masks
/// for low memory versus high memory addresses, and they will be left
/// as LLDB_INVALID_ADDRESS_MASK normally, meaning the base masks
/// should be applied to all addresses.
/// @{
lldb::addr_t m_code_address_mask = 0;
lldb::addr_t m_data_address_mask = 0;
lldb::addr_t m_highmem_code_address_mask = 0;
lldb::addr_t m_highmem_data_address_mask = 0;
lldb::addr_t m_code_address_mask = LLDB_INVALID_ADDRESS_MASK;
lldb::addr_t m_data_address_mask = LLDB_INVALID_ADDRESS_MASK;
lldb::addr_t m_highmem_code_address_mask = LLDB_INVALID_ADDRESS_MASK;
lldb::addr_t m_highmem_data_address_mask = LLDB_INVALID_ADDRESS_MASK;
/// @}

bool m_clear_thread_plans_on_stop;
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/Utility/AddressableBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLDB_UTILITY_ADDRESSABLEBITS_H

#include "lldb/lldb-forward.h"
#include "lldb/lldb-public.h"

namespace lldb_private {

Expand All @@ -33,6 +34,8 @@ class AddressableBits {

void SetHighmemAddressableBits(uint32_t highmem_addressing_bits);

static lldb::addr_t AddressableBitToMask(uint32_t addressable_bits);

void SetProcessMasks(lldb_private::Process &process);

private:
Expand Down
5 changes: 5 additions & 0 deletions lldb/include/lldb/lldb-defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@
#define MAX_PATH 260
#endif

/// Address Mask
/// Bits not used for addressing are set to 1 in the mask;
/// all mask bits set is an invalid value.
#define LLDB_INVALID_ADDRESS_MASK UINT64_MAX

// ignore GCC function attributes
#if defined(_MSC_VER) && !defined(__clang__)
#define __attribute__(X)
Expand Down
16 changes: 16 additions & 0 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,22 @@ enum SymbolDownload {
eSymbolDownloadForeground = 2,
};

/// Used in the SBProcess AddressMask/FixAddress methods.
enum AddressMaskType {
eAddressMaskTypeCode = 0,
eAddressMaskTypeData,
eAddressMaskTypeAny,
eAddressMaskTypeAll = eAddressMaskTypeAny
};

/// Used in the SBProcess AddressMask/FixAddress methods.
enum AddressMaskRange {
eAddressMaskRangeLow = 0,
eAddressMaskRangeHigh,
eAddressMaskRangeAny,
eAddressMaskRangeAll = eAddressMaskRangeAny,
};

} // namespace lldb

#endif // LLDB_LLDB_ENUMERATIONS_H
92 changes: 92 additions & 0 deletions lldb/source/API/SBProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,98 @@ lldb::SBFileSpec SBProcess::GetCoreFile() {
return SBFileSpec(core_file);
}

addr_t SBProcess::GetAddressMask(AddressMaskType type,
AddressMaskRange addr_range) {
LLDB_INSTRUMENT_VA(this, type, addr_range);

if (ProcessSP process_sp = GetSP()) {
switch (type) {
case eAddressMaskTypeCode:
if (addr_range == eAddressMaskRangeHigh)
return process_sp->GetHighmemCodeAddressMask();
else
return process_sp->GetCodeAddressMask();
case eAddressMaskTypeData:
if (addr_range == eAddressMaskRangeHigh)
return process_sp->GetHighmemDataAddressMask();
else
return process_sp->GetDataAddressMask();
case eAddressMaskTypeAny:
if (addr_range == eAddressMaskRangeHigh)
return process_sp->GetHighmemDataAddressMask();
else
return process_sp->GetDataAddressMask();
}
}
return LLDB_INVALID_ADDRESS_MASK;
}

void SBProcess::SetAddressMask(AddressMaskType type, addr_t mask,
AddressMaskRange addr_range) {
LLDB_INSTRUMENT_VA(this, type, mask, addr_range);

if (ProcessSP process_sp = GetSP()) {
switch (type) {
case eAddressMaskTypeCode:
if (addr_range == eAddressMaskRangeAll) {
process_sp->SetCodeAddressMask(mask);
process_sp->SetHighmemCodeAddressMask(mask);
} else if (addr_range == eAddressMaskRangeHigh) {
process_sp->SetHighmemCodeAddressMask(mask);
} else {
process_sp->SetCodeAddressMask(mask);
}
break;
case eAddressMaskTypeData:
if (addr_range == eAddressMaskRangeAll) {
process_sp->SetDataAddressMask(mask);
process_sp->SetHighmemDataAddressMask(mask);
} else if (addr_range == eAddressMaskRangeHigh) {
process_sp->SetHighmemDataAddressMask(mask);
} else {
process_sp->SetDataAddressMask(mask);
}
break;
case eAddressMaskTypeAll:
if (addr_range == eAddressMaskRangeAll) {
process_sp->SetCodeAddressMask(mask);
process_sp->SetDataAddressMask(mask);
process_sp->SetHighmemCodeAddressMask(mask);
process_sp->SetHighmemDataAddressMask(mask);
} else if (addr_range == eAddressMaskRangeHigh) {
process_sp->SetHighmemCodeAddressMask(mask);
process_sp->SetHighmemDataAddressMask(mask);
} else {
process_sp->SetCodeAddressMask(mask);
process_sp->SetDataAddressMask(mask);
}
break;
}
}
}

void SBProcess::SetAddressableBits(AddressMaskType type, uint32_t num_bits,
AddressMaskRange addr_range) {
LLDB_INSTRUMENT_VA(this, type, num_bits, addr_range);

SetAddressMask(type, AddressableBits::AddressableBitToMask(num_bits),
addr_range);
}

addr_t SBProcess::FixAddress(addr_t addr, AddressMaskType type) {
LLDB_INSTRUMENT_VA(this, addr, type);

if (ProcessSP process_sp = GetSP()) {
if (type == eAddressMaskTypeAny)
return process_sp->FixAnyAddress(addr);
else if (type == eAddressMaskTypeData)
return process_sp->FixDataAddress(addr);
else if (type == eAddressMaskTypeCode)
return process_sp->FixCodeAddress(addr);
}
return addr;
}

lldb::addr_t SBProcess::AllocateMemory(size_t size, uint32_t permissions,
lldb::SBError &sb_error) {
LLDB_INSTRUMENT_VA(this, size, permissions, sb_error);
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Breakpoint/BreakpointLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,12 @@ void BreakpointLocation::GetDescription(Stream *s,
s->EOL();
s->Indent("function = ");
s->PutCString(sc.function->GetName().AsCString("<unknown>"));
if (ConstString mangled_name =
sc.function->GetMangled().GetMangledName()) {
s->EOL();
s->Indent("mangled function = ");
s->PutCString(mangled_name.AsCString());
}
}

if (sc.line_entry.line > 0) {
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Commands/CommandObjectProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,7 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
if (m_options.m_verbose) {
addr_t code_mask = process->GetCodeAddressMask();
addr_t data_mask = process->GetDataAddressMask();
if (code_mask != 0) {
if (code_mask != LLDB_INVALID_ADDRESS_MASK) {
int bits = std::bitset<64>(~code_mask).count();
result.AppendMessageWithFormat(
"Addressable code address mask: 0x%" PRIx64 "\n", code_mask);
Expand Down
33 changes: 29 additions & 4 deletions lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//
//===----------------------------------------------------------------------===//

#include "lldb/lldb-types.h"

#include "ABIAArch64.h"
#include "ABIMacOSX_arm64.h"
#include "ABISysV_arm64.h"
Expand All @@ -16,6 +18,8 @@
#include <bitset>
#include <optional>

using namespace lldb;

LLDB_PLUGIN_DEFINE(ABIAArch64)

void ABIAArch64::Initialize() {
Expand All @@ -29,14 +33,35 @@ void ABIAArch64::Terminate() {
}

lldb::addr_t ABIAArch64::FixCodeAddress(lldb::addr_t pc) {
if (lldb::ProcessSP process_sp = GetProcessSP())
return FixAddress(pc, process_sp->GetCodeAddressMask());
if (lldb::ProcessSP process_sp = GetProcessSP()) {
// b55 is the highest bit outside TBI (if it's enabled), use
// it to determine if the high bits are set to 0 or 1.
const addr_t pac_sign_extension = 0x0080000000000000ULL;
addr_t mask = process_sp->GetCodeAddressMask();
// Test if the high memory mask has been overriden separately
if (pc & pac_sign_extension &&
process_sp->GetHighmemCodeAddressMask() != LLDB_INVALID_ADDRESS_MASK)
mask = process_sp->GetHighmemCodeAddressMask();

if (mask != LLDB_INVALID_ADDRESS_MASK)
return FixAddress(pc, mask);
}
return pc;
}

lldb::addr_t ABIAArch64::FixDataAddress(lldb::addr_t pc) {
if (lldb::ProcessSP process_sp = GetProcessSP())
return FixAddress(pc, process_sp->GetDataAddressMask());
if (lldb::ProcessSP process_sp = GetProcessSP()) {
// b55 is the highest bit outside TBI (if it's enabled), use
// it to determine if the high bits are set to 0 or 1.
const addr_t pac_sign_extension = 0x0080000000000000ULL;
addr_t mask = process_sp->GetDataAddressMask();
// Test if the high memory mask has been overriden separately
if (pc & pac_sign_extension &&
process_sp->GetHighmemDataAddressMask() != LLDB_INVALID_ADDRESS_MASK)
mask = process_sp->GetHighmemDataAddressMask();
if (mask != LLDB_INVALID_ADDRESS_MASK)
return FixAddress(pc, mask);
}
return pc;
}

Expand Down
8 changes: 4 additions & 4 deletions lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,11 +814,11 @@ addr_t ABIMacOSX_arm64::FixCodeAddress(addr_t pc) {
mask = process_sp->GetCodeAddressMask();
if (pc & pac_sign_extension) {
addr_t highmem_mask = process_sp->GetHighmemCodeAddressMask();
if (highmem_mask)
if (highmem_mask != LLDB_INVALID_ADDRESS_MASK)
mask = highmem_mask;
}
}
if (mask == 0)
if (mask == LLDB_INVALID_ADDRESS_MASK)
mask = tbi_mask;

return (pc & pac_sign_extension) ? pc | mask : pc & (~mask);
Expand All @@ -833,11 +833,11 @@ addr_t ABIMacOSX_arm64::FixDataAddress(addr_t pc) {
mask = process_sp->GetDataAddressMask();
if (pc & pac_sign_extension) {
addr_t highmem_mask = process_sp->GetHighmemDataAddressMask();
if (highmem_mask)
if (highmem_mask != LLDB_INVALID_ADDRESS_MASK)
mask = highmem_mask;
}
}
if (mask == 0)
if (mask == LLDB_INVALID_ADDRESS_MASK)
mask = tbi_mask;

return (pc & pac_sign_extension) ? pc | mask : pc & (~mask);
Expand Down
40 changes: 30 additions & 10 deletions lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,19 +775,21 @@ ValueObjectSP ABISysV_arm64::GetReturnValueObjectImpl(
}

lldb::addr_t ABISysV_arm64::FixAddress(addr_t pc, addr_t mask) {
if (mask == LLDB_INVALID_ADDRESS_MASK)
return pc;
lldb::addr_t pac_sign_extension = 0x0080000000000000ULL;
return (pc & pac_sign_extension) ? pc | mask : pc & (~mask);
}

// Reads code or data address mask for the current Linux process.
static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp,
llvm::StringRef reg_name) {
// 0 means there isn't a mask or it has not been read yet.
// We do not return the top byte mask unless thread_sp is valid.
// This prevents calls to this function before the thread is setup locking
// in the value to just the top byte mask, in cases where pointer
// authentication might also be active.
uint64_t address_mask = 0;
// LLDB_INVALID_ADDRESS_MASK means there isn't a mask or it has not been read
// yet. We do not return the top byte mask unless thread_sp is valid. This
// prevents calls to this function before the thread is setup locking in the
// value to just the top byte mask, in cases where pointer authentication
// might also be active.
uint64_t address_mask = LLDB_INVALID_ADDRESS_MASK;
lldb::ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
if (thread_sp) {
// Linux configures user-space virtual addresses with top byte ignored.
Expand All @@ -814,23 +816,41 @@ static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp,
lldb::addr_t ABISysV_arm64::FixCodeAddress(lldb::addr_t pc) {
if (lldb::ProcessSP process_sp = GetProcessSP()) {
if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() &&
!process_sp->GetCodeAddressMask())
process_sp->GetCodeAddressMask() == LLDB_INVALID_ADDRESS_MASK)
process_sp->SetCodeAddressMask(
ReadLinuxProcessAddressMask(process_sp, "code_mask"));

return FixAddress(pc, process_sp->GetCodeAddressMask());
// b55 is the highest bit outside TBI (if it's enabled), use
// it to determine if the high bits are set to 0 or 1.
const addr_t pac_sign_extension = 0x0080000000000000ULL;
addr_t mask = process_sp->GetCodeAddressMask();
// Test if the high memory mask has been overriden separately
if (pc & pac_sign_extension &&
process_sp->GetHighmemCodeAddressMask() != LLDB_INVALID_ADDRESS_MASK)
mask = process_sp->GetHighmemCodeAddressMask();

return FixAddress(pc, mask);
}
return pc;
}

lldb::addr_t ABISysV_arm64::FixDataAddress(lldb::addr_t pc) {
if (lldb::ProcessSP process_sp = GetProcessSP()) {
if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() &&
!process_sp->GetDataAddressMask())
process_sp->GetDataAddressMask() == LLDB_INVALID_ADDRESS_MASK)
process_sp->SetDataAddressMask(
ReadLinuxProcessAddressMask(process_sp, "data_mask"));

return FixAddress(pc, process_sp->GetDataAddressMask());
// b55 is the highest bit outside TBI (if it's enabled), use
// it to determine if the high bits are set to 0 or 1.
const addr_t pac_sign_extension = 0x0080000000000000ULL;
addr_t mask = process_sp->GetDataAddressMask();
// Test if the high memory mask has been overriden separately
if (pc & pac_sign_extension &&
process_sp->GetHighmemDataAddressMask() != LLDB_INVALID_ADDRESS_MASK)
mask = process_sp->GetHighmemDataAddressMask();

return FixAddress(pc, mask);
}
return pc;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlanRunToAddress.h"
#include "lldb/Utility/AddressableBits.h"
#include "lldb/Utility/DataBuffer.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/LLDBLog.h"
Expand Down Expand Up @@ -1109,7 +1110,7 @@ void DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() {
// T1Sz is 25, then 64-25 == 39, bits 0..38 are used for
// addressing, bits 39..63 are used for PAC/TBI or whatever.
uint32_t virt_addr_bits = 64 - sym_value;
addr_t mask = ~((1ULL << virt_addr_bits) - 1);
addr_t mask = AddressableBits::AddressableBitToMask(virt_addr_bits);
m_process->SetCodeAddressMask(mask);
m_process->SetDataAddressMask(mask);
} else {
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6620,7 +6620,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
// Bits will be set to indicate which bits are NOT used in
// addressing in this process or 0 for unknown.
uint64_t address_mask = process_sp->GetCodeAddressMask();
if (address_mask != 0) {
if (address_mask != LLDB_INVALID_ADDRESS_MASK) {
// LC_NOTE "addrable bits"
mach_header.ncmds++;
mach_header.sizeofcmds += sizeof(llvm::MachO::note_command);
Expand Down Expand Up @@ -6654,7 +6654,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
std::vector<std::unique_ptr<LCNoteEntry>> lc_notes;

// Add "addrable bits" LC_NOTE when an address mask is available
if (address_mask != 0) {
if (address_mask != LLDB_INVALID_ADDRESS_MASK) {
std::unique_ptr<LCNoteEntry> addrable_bits_lcnote_up(
new LCNoteEntry(addr_byte_size, byte_order));
addrable_bits_lcnote_up->name = "addrable bits";
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ void NativeThreadLinux::SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid) {
m_stop_info.signo = SIGTRAP;
m_stop_info.details.fork.child_pid = child_pid;
m_stop_info.details.fork.child_tid = child_pid;
m_stop_description = std::to_string(child_pid);
m_stop_description += " ";
m_stop_description += std::to_string(child_pid);
}

void NativeThreadLinux::SetStoppedByVForkDone() {
Expand Down
28 changes: 17 additions & 11 deletions lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,9 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,
m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(),
m_max_memory_size(0), m_remote_stub_max_memory_size(0),
m_addr_to_mmap_size(), m_thread_create_bp_sp(),
m_waiting_for_attach(false),
m_command_sp(), m_breakpoint_pc_offset(0),
m_waiting_for_attach(false), m_command_sp(), m_breakpoint_pc_offset(0),
m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false),
m_erased_flash_ranges(), m_vfork_in_progress(false) {
m_erased_flash_ranges(), m_vfork_in_progress_count(0) {
m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
"async thread should exit");
m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
Expand Down Expand Up @@ -5293,8 +5292,10 @@ class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed {
(ProcessGDBRemote *)m_interpreter.GetExecutionContext()
.GetProcessPtr();
if (process) {
StreamSP output_stream_sp(
m_interpreter.GetDebugger().GetAsyncOutputStream());
StreamSP output_stream_sp = result.GetImmediateOutputStream();
if (!output_stream_sp)
output_stream_sp =
StreamSP(m_interpreter.GetDebugger().GetAsyncOutputStream());
result.SetImmediateOutputStream(output_stream_sp);

const uint32_t num_packets =
Expand Down Expand Up @@ -5634,8 +5635,11 @@ void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
Log *log = GetLog(GDBRLog::Process);

assert(!m_vfork_in_progress);
m_vfork_in_progress = true;
LLDB_LOG(
log,
"ProcessGDBRemote::DidFork() called for child_pid: {0}, child_tid {1}",
child_pid, child_tid);
++m_vfork_in_progress_count;

// Disable all software breakpoints for the duration of vfork.
if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
Expand Down Expand Up @@ -5689,8 +5693,8 @@ void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
}

void ProcessGDBRemote::DidVForkDone() {
assert(m_vfork_in_progress);
m_vfork_in_progress = false;
assert(m_vfork_in_progress_count > 0);
--m_vfork_in_progress_count;

// Reenable all software breakpoints that were enabled before vfork.
if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
Expand All @@ -5700,7 +5704,9 @@ void ProcessGDBRemote::DidVForkDone() {
void ProcessGDBRemote::DidExec() {
// If we are following children, vfork is finished by exec (rather than
// vforkdone that is submitted for parent).
if (GetFollowForkMode() == eFollowChild)
m_vfork_in_progress = false;
if (GetFollowForkMode() == eFollowChild) {
if (m_vfork_in_progress_count > 0)
--m_vfork_in_progress_count;
}
Process::DidExec();
}
3 changes: 2 additions & 1 deletion lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ class ProcessGDBRemote : public Process,
using FlashRange = FlashRangeVector::Entry;
FlashRangeVector m_erased_flash_ranges;

bool m_vfork_in_progress;
// Number of vfork() operations being handled.
uint32_t m_vfork_in_progress_count;

// Accessors
bool IsRunning(lldb::StateType state) {
Expand Down
1 change: 0 additions & 1 deletion lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4377,7 +4377,6 @@ const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() {
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
ModuleSpec module_spec;
module_spec.GetFileSpec() = m_objfile_sp->GetFileSpec();
module_spec.GetUUID() = m_objfile_sp->GetUUID();
for (const auto &symfile : symfiles.files()) {
module_spec.GetSymbolFileSpec() =
FileSpec(symfile.GetPath() + ".dwp", symfile.GetPathStyle());
Expand Down
33 changes: 33 additions & 0 deletions lldb/source/Target/ABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,39 @@ ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type,
return return_valobj_sp;
}

addr_t ABI::FixCodeAddress(lldb::addr_t pc) {
ProcessSP process_sp(GetProcessSP());

addr_t mask = process_sp->GetCodeAddressMask();
if (mask == LLDB_INVALID_ADDRESS_MASK)
return pc;

// Assume the high bit is used for addressing, which
// may not be correct on all architectures e.g. AArch64
// where Top Byte Ignore mode is often used to store
// metadata in the top byte, and b55 is the bit used for
// differentiating between low- and high-memory addresses.
// That target's ABIs need to override this method.
bool is_highmem = pc & (1ULL << 63);
return is_highmem ? pc | mask : pc & (~mask);
}

addr_t ABI::FixDataAddress(lldb::addr_t pc) {
ProcessSP process_sp(GetProcessSP());
addr_t mask = process_sp->GetDataAddressMask();
if (mask == LLDB_INVALID_ADDRESS_MASK)
return pc;

// Assume the high bit is used for addressing, which
// may not be correct on all architectures e.g. AArch64
// where Top Byte Ignore mode is often used to store
// metadata in the top byte, and b55 is the bit used for
// differentiating between low- and high-memory addresses.
// That target's ABIs need to override this method.
bool is_highmem = pc & (1ULL << 63);
return is_highmem ? pc | mask : pc & (~mask);
}

ValueObjectSP ABI::GetReturnValueObject(Thread &thread, llvm::Type &ast_type,
bool persistent) const {
ValueObjectSP return_valobj_sp;
Expand Down
14 changes: 8 additions & 6 deletions lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5682,30 +5682,32 @@ void Process::Flush() {

lldb::addr_t Process::GetCodeAddressMask() {
if (uint32_t num_bits_setting = GetVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);
return AddressableBits::AddressableBitToMask(num_bits_setting);

return m_code_address_mask;
}

lldb::addr_t Process::GetDataAddressMask() {
if (uint32_t num_bits_setting = GetVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);
return AddressableBits::AddressableBitToMask(num_bits_setting);

return m_data_address_mask;
}

lldb::addr_t Process::GetHighmemCodeAddressMask() {
if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);
if (m_highmem_code_address_mask)
return AddressableBits::AddressableBitToMask(num_bits_setting);

if (m_highmem_code_address_mask != LLDB_INVALID_ADDRESS_MASK)
return m_highmem_code_address_mask;
return GetCodeAddressMask();
}

lldb::addr_t Process::GetHighmemDataAddressMask() {
if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);
if (m_highmem_data_address_mask)
return AddressableBits::AddressableBitToMask(num_bits_setting);

if (m_highmem_data_address_mask != LLDB_INVALID_ADDRESS_MASK)
return m_highmem_data_address_mask;
return GetDataAddressMask();
}
Expand Down
12 changes: 10 additions & 2 deletions lldb/source/Utility/AddressableBits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,26 @@ void AddressableBits::SetHighmemAddressableBits(
m_high_memory_addr_bits = highmem_addressing_bits;
}

addr_t AddressableBits::AddressableBitToMask(uint32_t addressable_bits) {
assert(addressable_bits <= sizeof(addr_t) * 8);
if (addressable_bits == 64)
return 0; // all bits used for addressing
else
return ~((1ULL << addressable_bits) - 1);
}

void AddressableBits::SetProcessMasks(Process &process) {
if (m_low_memory_addr_bits == 0 && m_high_memory_addr_bits == 0)
return;

if (m_low_memory_addr_bits != 0) {
addr_t low_addr_mask = ~((1ULL << m_low_memory_addr_bits) - 1);
addr_t low_addr_mask = AddressableBitToMask(m_low_memory_addr_bits);
process.SetCodeAddressMask(low_addr_mask);
process.SetDataAddressMask(low_addr_mask);
}

if (m_high_memory_addr_bits != 0) {
addr_t hi_addr_mask = ~((1ULL << m_high_memory_addr_bits) - 1);
addr_t hi_addr_mask = AddressableBitToMask(m_high_memory_addr_bits);
process.SetHighmemCodeAddressMask(hi_addr_mask);
process.SetHighmemDataAddressMask(hi_addr_mask);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,25 @@ def breakpoint_options_language_test(self):
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

# This should create a breakpoint with 1 locations.
lldbutil.run_break_set_by_symbol(
bp_id = lldbutil.run_break_set_by_symbol(
self,
"ns::func",
sym_exact=False,
extra_options="-L c++",
num_expected_locations=1,
)

location = self.target().FindBreakpointByID(bp_id).GetLocationAtIndex(0)
function = location.GetAddress().GetFunction()
self.expect(
"breakpoint list -v",
"Verbose breakpoint list contains mangled names",
substrs=[
"function = ns::func",
f"mangled function = {function.GetMangledName()}",
],
)

# This should create a breakpoint with 0 locations.
lldbutil.run_break_set_by_symbol(
self,
Expand Down
4 changes: 4 additions & 0 deletions lldb/test/API/functionalities/fork/concurrent_vfork/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CXX_SOURCES := main.cpp
ENABLE_THREADS := YES

include Makefile.rules
Loading