Skip to content

[libc][stdfix] Add exp function for short _Accum and _Accum types. #84391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libc/config/baremetal/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.absr
libc.src.stdfix.abslk
libc.src.stdfix.abslr
libc.src.stdfix.exphk
libc.src.stdfix.expk
libc.src.stdfix.roundhk
libc.src.stdfix.roundhr
libc.src.stdfix.roundk
Expand Down
2 changes: 2 additions & 0 deletions libc/config/baremetal/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.absr
libc.src.stdfix.abslk
libc.src.stdfix.abslr
libc.src.stdfix.exphk
libc.src.stdfix.expk
libc.src.stdfix.roundhk
libc.src.stdfix.roundhr
libc.src.stdfix.roundk
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.absr
libc.src.stdfix.abslk
libc.src.stdfix.abslr
libc.src.stdfix.exphk
libc.src.stdfix.expk
libc.src.stdfix.roundhk
libc.src.stdfix.roundhr
libc.src.stdfix.roundk
Expand Down
2 changes: 1 addition & 1 deletion libc/docs/math/stdfix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ floating point types, but are not part of the ISO/IEC TR 18037:2008 spec.
+===============+================+=============+===============+============+================+=============+================+=============+===============+============+================+=============+
| cos | | | | | | | | | | | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| exp | | | | | | | | | | | | |
| exp | | | | | | | | |check| | | |check| | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| log | | | | | | | | | | | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
Expand Down
3 changes: 3 additions & 0 deletions libc/spec/llvm_libc_stdfix_ext.td
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ def LLVMLibcStdfixExt : StandardSpec<"llvm_libc_stdfix_ext"> {
[], // types
[], // enums
[ // functions
GuardedFunctionSpec<"exphk", RetValSpec<ShortAccumType>, [ArgSpec<ShortAccumType>], "LIBC_COMPILER_HAS_FIXED_POINT">,
GuardedFunctionSpec<"expk", RetValSpec<AccumType>, [ArgSpec<AccumType>], "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">,
Expand Down
24 changes: 12 additions & 12 deletions libc/src/__support/fixed_point/fx_rep.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ template <> struct FXRep<short fract> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return SFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return SFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return SFRACT_MAX; }
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; }
Expand All @@ -65,7 +65,7 @@ template <> struct FXRep<unsigned short fract> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return USFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return USFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return USFRACT_MAX; }
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; }
Expand All @@ -85,7 +85,7 @@ template <> struct FXRep<fract> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return FRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return FRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return FRACT_MAX; }
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; }
Expand All @@ -105,7 +105,7 @@ template <> struct FXRep<unsigned fract> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return UFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return UFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return UFRACT_MAX; }
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; }
Expand All @@ -125,7 +125,7 @@ template <> struct FXRep<long fract> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return LFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return LFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return LFRACT_MAX; }
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; }
Expand All @@ -145,7 +145,7 @@ template <> struct FXRep<unsigned long fract> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return ULFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return ULFRACT_MIN; }
LIBC_INLINE static constexpr Type MAX() { return ULFRACT_MAX; }
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; }
Expand All @@ -165,7 +165,7 @@ template <> struct FXRep<short accum> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return SACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return SACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return SACCUM_MAX; }
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; }
Expand All @@ -185,7 +185,7 @@ template <> struct FXRep<unsigned short accum> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return USACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return USACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return USACCUM_MAX; }
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; }
Expand All @@ -205,7 +205,7 @@ template <> struct FXRep<accum> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return ACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return ACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return ACCUM_MAX; }
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; }
Expand All @@ -225,7 +225,7 @@ template <> struct FXRep<unsigned accum> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return UACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return UACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return UACCUM_MAX; }
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; }
Expand All @@ -245,7 +245,7 @@ template <> struct FXRep<long accum> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return LACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return LACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return LACCUM_MAX; }
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; }
Expand All @@ -265,7 +265,7 @@ template <> struct FXRep<unsigned long accum> {
SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN;

LIBC_INLINE static constexpr Type MIN() { return ULACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return ULACCUM_MIN; }
LIBC_INLINE static constexpr Type MAX() { return ULACCUM_MAX; }
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; }
Expand Down
26 changes: 26 additions & 0 deletions libc/src/stdfix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,29 @@ add_entrypoint_object(
DEPENDS
libc.src.__support.fixed_point.sqrt
)

add_entrypoint_object(
exphk
HDRS
exphk.h
SRCS
exphk.cpp
COMPILE_OPTIONS
-O3
DEPENDS
libc.src.__support.fixed_point.fx_rep
libc.src.__support.CPP.bit
)

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

namespace LIBC_NAMESPACE {

namespace {

// Look up tables for exp(hi) and exp(mid).
// Generated with Sollya:
// > for i from 0 to 89 do {
// hi = floor(i/8) - 5;
// m = i/8 - floor(i/8) - 0.5;
// e_hi = nearestint(exp(hi) * 2^7) * 2^-7;
// e_mid = nearestint(exp(m) * 2^7) * 2^-7;
// print(hi, e_hi, m, e_mid);
// };
// Notice that when i = 88 and 89, e_hi will overflow short accum range.
static constexpr short accum EXP_HI[12] = {
0x1.0p-7hk, 0x1.0p-6hk, 0x1.8p-5hk, 0x1.1p-3hk, 0x1.78p-2hk, 0x1.0p0hk,
0x1.5cp1hk, 0x1.d9p2hk, 0x1.416p4hk, 0x1.b4dp5hk, 0x1.28d4p7hk, SACCUM_MAX,
};

static constexpr short accum EXP_MID[8] = {
0x1.38p-1hk, 0x1.6p-1hk, 0x1.9p-1hk, 0x1.c4p-1hk,
0x1.0p0hk, 0x1.22p0hk, 0x1.48p0hk, 0x1.74p0hk,
};

} // anonymous namespace

LLVM_LIBC_FUNCTION(short accum, exphk, (short accum x)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in future it would probably be better to have a shared backing function for exp_fixed but given there are only two functions for the moment this seems fine.

using FXRep = fixed_point::FXRep<short accum>;
using StorageType = typename FXRep::StorageType;
// Output overflow
if (LIBC_UNLIKELY(x >= 0x1.64p2hk))
return FXRep::MAX();
// Lower bound where exp(x) -> 0:
// floor(log(2^-8) * 2^7) * 2^-7
if (LIBC_UNLIKELY(x <= -0x1.63p2hk))
return FXRep::ZERO();

// Current range of x:
// -0x1.628p2 <= x <= 0x1.638p2
// Range reduction:
// x = hi + mid + lo,
// where:
// hi is an integer
// mid * 2^3 is an integer
// |lo| <= 2^-4.
// Then exp(x) = exp(hi + mid + lo) = exp(hi) * exp(mid) * exp(lo)
// ~ exp(hi) * exp(mid) * (1 + lo)
// with relative errors < |lo|^2 <= 2^-8.
// exp(hi) and exp(mid) are extracted from small lookup tables.

// Round-to-nearest 1/8, tie-to-(+Int):
constexpr short accum ONE_SIXTEENTH = 0x1.0p-4hk;
// x_rounded = floor(x + 1/16).
short accum x_rounded = ((x + ONE_SIXTEENTH) >> (FXRep::FRACTION_LEN - 3))
<< (FXRep::FRACTION_LEN - 3);
short accum lo = x - x_rounded;

// Range of x_rounded:
// x_rounded >= floor((-0x1.628p2 + 0x1.0p-4) * 2^3) * 2^-3
// = -0x1.6p2 = -5.5
// To get the indices, we shift the values so that it start with 0.
// Range of indices: 0 <= indices <= 89
StorageType indices = cpp::bit_cast<StorageType>((x_rounded + 0x1.6p2hk) >>
(FXRep::FRACTION_LEN - 3));
// So we have the following relation:
// indices = (hi + mid + 44/8) * 8
// That implies:
// hi + mid = indices/8 - 5.5
// So for lookup tables, we can use the upper 4 bits to get:
// exp( floor(indices / 8) - 5 )
// and lower 3 bits for:
// exp( (indices - floor(indices)) - 0.5 )
short accum exp_hi = EXP_HI[indices >> 3];
short accum exp_mid = EXP_MID[indices & 0x7];
// exp(x) ~ exp(hi) * exp(mid) * (1 + lo);
return (exp_hi * (exp_mid * (0x1.0p0hk + lo)));
}

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

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

namespace LIBC_NAMESPACE {

short accum exphk(short accum x);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDFIX_EXPHK_H
Loading