Skip to content

[libc] Add sinpif16 function #110994

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 21 commits into from
Oct 15, 2024
Merged

[libc] Add sinpif16 function #110994

merged 21 commits into from
Oct 15, 2024

Conversation

wldfngrs
Copy link
Contributor

@wldfngrs wldfngrs commented Oct 3, 2024

Half-precision floating point (16-bit) implementation of the trigonometric function Sin for inputs scaled by pi

@llvmbot llvmbot added the libc label Oct 3, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 3, 2024

@llvm/pr-subscribers-libc

Author: wldfngrs (wldfngrs)

Changes

Half-precision floating point (16-bit) implementation of the trigonometric function Sin for inputs scaled by pi


Full diff: https://github.com/llvm/llvm-project/pull/110994.diff

12 Files Affected:

  • (modified) libc/config/linux/aarch64/entrypoints.txt (+1)
  • (modified) libc/config/linux/arm/entrypoints.txt (+1)
  • (modified) libc/config/linux/riscv/entrypoints.txt (+1)
  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/config/windows/entrypoints.txt (+2-1)
  • (modified) libc/newhdrgen/yaml/math.yaml (+6)
  • (modified) libc/src/math/CMakeLists.txt (+1)
  • (modified) libc/src/math/generic/CMakeLists.txt (+16)
  • (added) libc/src/math/generic/sinpif16.cpp (+173)
  • (added) libc/src/math/sinpif16.h (+22)
  • (modified) libc/test/src/math/smoke/CMakeLists.txt (+13)
  • (added) libc/test/src/math/smoke/sinpif16_test.cpp (+44)
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 283c7bd77b4e8e..85ae7be80b4616 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -570,6 +570,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sinf
     libc.src.math.sinhf
     libc.src.math.sinpif
+    libc.src.math.sinpif16
     libc.src.math.sqrt
     libc.src.math.sqrtf
     libc.src.math.sqrtl
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 1be9a872dd2f7f..dbd21f6b5d4388 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -390,6 +390,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sincosf
     libc.src.math.sinf
     libc.src.math.sinhf
+    libc.src.math.sinpif16
     libc.src.math.sqrt
     libc.src.math.sqrtf
     libc.src.math.sqrtl
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 8312b2c453f231..8b71f44cf93549 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -573,6 +573,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sinf
     libc.src.math.sinhf
     libc.src.math.sinpif
+    libc.src.math.sinpif16
     libc.src.math.sqrt
     libc.src.math.sqrtf
     libc.src.math.sqrtl
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 806ccbcdf820a0..b7d0406fe6f007 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -573,6 +573,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sinf
     libc.src.math.sinhf
     libc.src.math.sinpif
+    libc.src.math.sinpif16
     libc.src.math.sqrt
     libc.src.math.sqrtf
     libc.src.math.sqrtl
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index 8f0b50bcc83ea2..5b0d84669e9244 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -275,7 +275,8 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.sincosf
     libc.src.math.sincosf
     libc.src.math.sinf
-    libc.src.math.sinhf
+    libc.src.math.sinhfi
+    libc.src.math.sinpif16
     libc.src.math.sqrt
     libc.src.math.sqrtf
     libc.src.math.sqrtl
diff --git a/libc/newhdrgen/yaml/math.yaml b/libc/newhdrgen/yaml/math.yaml
index 04b6a073deace0..fb79f15a314604 100644
--- a/libc/newhdrgen/yaml/math.yaml
+++ b/libc/newhdrgen/yaml/math.yaml
@@ -2290,6 +2290,12 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: sinpif16
+    standards:
+      - stdc
+    return_type: _Float16
+    arguments:
+      - type: _Float16
   - name: sqrt
     standards:
       - stdc
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 97e2d0d64a147b..ad3f916130e86d 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -474,6 +474,7 @@ add_math_entrypoint_object(sincosf)
 add_math_entrypoint_object(sin)
 add_math_entrypoint_object(sinf)
 add_math_entrypoint_object(sinpif)
+add_math_entrypoint_object(sinpif16)
 
 add_math_entrypoint_object(sinh)
 add_math_entrypoint_object(sinhf)
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 93e632c38af238..a87f041b558440 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -528,6 +528,22 @@ add_entrypoint_object(
     -O3
 )
 
+add_entrypoint_object(
+  sinpif16
+  SRCS
+    sinpif16.cpp
+  HDRS
+    ../sinpif16.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.common
+    libc.src.__support.macros.config
+  COMPILE_OPTIONS
+    -O3
+)
+
 add_entrypoint_object(
   tan
   SRCS
diff --git a/libc/src/math/generic/sinpif16.cpp b/libc/src/math/generic/sinpif16.cpp
new file mode 100644
index 00000000000000..b878e09d542007
--- /dev/null
+++ b/libc/src/math/generic/sinpif16.cpp
@@ -0,0 +1,173 @@
+//===-- Half-precision sinpif function ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "src/math/sinpif16.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+// TODO: Should probably create a new file; sincospif16_utils.h
+// To store the following helper functions and constants.
+// I'd defer to @lntue for suggestions regarding that
+
+// HELPER_START
+namespace LIBC_NAMESPACE_DECL {
+
+constexpr float PI_OVER_32 = 0x1.921fb6p-4f;
+
+// In Sollya generate 10 coeffecients for a degree-9 chebyshev polynomial
+// approximating the sine function in [-pi / 32, pi / 32] with the following
+// commands:
+// > prec=24;
+// > TL = chebyshevform(sin(x), 9, [-pi / 32, pi / 32]);
+// > TL[0];
+const float SIN_COEFF[10] = {
+  0x1.d333p-26, 0x1.000048p0, -0x1.a5d2p-14, -0x1.628588p-3, 0x1.c1eep-5,
+  0x1.4455p1, -0x1.317a8p3, -0x1.6bb9p8, 0x1.00ef8p9, 0x1.0edcp14
+};
+// In Sollya generate 10 coefficients for a degree-9 chebyshev polynomial
+// approximating the sine function in [-pi/32, pi/32] with the following
+// commands:
+// > prec = 24;
+// > TL = chebyshevform(cos(x), 9, [-pi / 32, pi / 32]);
+// > TL[0];
+const float COS_COEFF[10] = {
+  0x1.000006p0, 0x1.e1eap-15, -0x1.0071p-1, -0x1.3b56p-4, 0x1.f3dfp-2,
+  0x1.ccbap4, -0x1.3034p6, -0x1.f817p11, 0x1.fc59p11, 0x1.7079p17
+};
+// Lookup table for sin(k * pi / 32) with k = 0, ..., 63.
+// Table is generated with Sollya as follows:
+// > display = hexadecimmal;
+// > prec = 24;
+// > for k from 0 to 63 do {sin(k * pi/32);};
+
+const float SIN_K_PI_OVER_32[64] = {
+  0, 0x1.917a6cp-4,
+  0x1.8f8b84p-3, 0x1.294062p-2,
+  0x1.87de2ap-2, 0x1.e2b5d4p-2,
+  0x1.1c73b4p-1, 0x1.44cf32p-1,
+  0x1.6a09e6p-1, 0x1.8bc806p-1,
+  0x1.a9b662p-1, 0x1.c38b3p-1,
+  0x1.d906bcp-1, 0x1.e9f416p-1,
+  0x1.f6297cp-1, 0x1.fd88dap-1,
+  0x1p0, 0x1.fd88dap-1,
+  0x1.f6297cp-1, 0x1.e9f416p-1,
+  0x1.d906bcp-1, 0x1.c38b3p-1,
+  0x1.a9b662p-1, 0x1.8bc806p-1,
+  0x1.6a09e6p-1, 0x1.44cf32p-1,
+  0x1.1c73b4p-1, 0x1.e2b5d4p-2,
+  0x1.87de2ap-2, 0x1.294062p-2,
+  0x1.8f8b84p-3, 0x1.917a6cp-4,
+  0, -0x1.917a6cp-4,
+  -0x1.8f8b84p-3, -0x1.294062p-2,
+  -0x1.87de2ap-2, -0x1.e2b5d4p-2,
+  -0x1.1c73b4p-1, -0x1.44cf32p-1,
+  -0x1.6a09e6p-1, -0x1.8bc806p-1,
+  -0x1.a9b662p-1, -0x1.c38b3p-1,
+  -0x1.d906bcp-1, -0x1.e9f416p-1,
+  -0x1.f6297ep-1, -0x1.fd88dap-1,
+  -0x1p0, -0x1.fd88dap-1,
+  -0x1.f6297cp-1, -0x1.e9f416p-1,
+  -0x1.d906bcp-1, -0x1.c38b3p-1,
+  -0x1.a9b662p-1, -0x1.8bc806p-1,
+  -0x1.6a09e6p-1, -0x1.44cf32p-1,
+  -0x1.1c73b4p-1, -0x1.e2b5d4p-2,
+  -0x1.87de2ap-2, -0x1.294062p-2,
+  -0x1.8f8b84p-3, -0x1.917a6cp-4
+};
+
+int32_t range_reduction(float x, float &y) {
+  float kf = fputil::nearest_integer(x * 32);
+  y = fputil::multiply_add<float>(x, 32.0, -kf);
+
+  return static_cast<int32_t>(kf);
+}
+// HELPER_END
+
+LLVM_LIBC_FUNCTION(float16, sinpif16, (float16 x)) {
+  using FPBits = typename fputil::FPBits<float16>;
+  FPBits xbits(x);
+
+  uint16_t x_u = xbits.uintval();
+  uint16_t x_abs = x_u & 0x7fff;
+
+  // Range reduction:
+  // For |x| > 1/32, we perform range reduction as follows:
+  // Find k and y such that:
+  //   x = (k + y) * 1/32
+  //   k is an integer
+  //   |y| < 0.5
+  //
+  // This is done by performing:
+  //   k = round(x * 32)
+  //   y = x * 32 - k
+  //
+  // Once k and y are computed, we then deduce the answer by the sine of sum
+  // formula:
+  //   sin(x * pi) = sin((k + y) * pi/32)
+  //           = sin(k * pi/32) * cos(y * pi/32) + sin (y * pi/32) * cos (k *
+  //           pi/32)
+  // The values of sin(k * pi/32) and cos (k * pi/32) for k = 0...63 are
+  // precomputed and stored using a vector of 64 single precision floats. sin(y
+  // * pi/32) and cos(y * pi/32) are computed using degree-9 chebyshev
+  // polynomials generated by Sollya.
+
+  if (LIBC_UNLIKELY(x_abs == 0U)) {
+    // For signed zeros
+    return x;
+  }
+
+  // Numbers greater or equal to 2^10 are integers or NaN
+  if (LIBC_UNLIKELY(x_abs >= 0x6400)) {
+    // Check for NaN or infinity values
+    if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
+      // If value is equal to infinity
+      if (x_abs == 0x7c00) {
+        fputil::set_errno_if_required(EDOM);
+	fputil::raise_except_if_required(FE_INVALID);
+      }
+      
+      // If value is NaN
+      return x + FPBits::quiet_nan().get_val();
+    }
+    return FPBits::zero(xbits.sign()).get_val();
+  }
+
+  float f32 = static_cast<float>(x);
+  float y;
+  int32_t k = range_reduction(f32, y);
+
+  float sin_k = SIN_K_PI_OVER_32[k & 63];
+  float cos_k = SIN_K_PI_OVER_32[(k + 16) & 63];
+
+  float cos_y, sin_y;
+  if (y == 0) {
+    cos_y = 1;
+    sin_y = 0;
+  } else {
+    cos_y = fputil::polyeval(y * PI_OVER_32,
+	COS_COEFF[0], COS_COEFF[1],
+	COS_COEFF[2], COS_COEFF[3],
+	COS_COEFF[4], COS_COEFF[5],
+	COS_COEFF[6], COS_COEFF[7],
+	COS_COEFF[8], COS_COEFF[9]);
+    sin_y = fputil::polyeval(y * PI_OVER_32,
+        SIN_COEFF[0], SIN_COEFF[1],
+	SIN_COEFF[2], SIN_COEFF[3],
+	SIN_COEFF[4], SIN_COEFF[5],
+	SIN_COEFF[6], SIN_COEFF[7],
+	SIN_COEFF[8], SIN_COEFF[9]);
+  }
+
+  return static_cast<float16>(fputil::multiply_add(
+      sin_k, cos_y, fputil::multiply_add(sin_y, cos_k, 0.0f)));
+}
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/sinpif16.h b/libc/src/math/sinpif16.h
new file mode 100644
index 00000000000000..f7c653383ba5ef
--- /dev/null
+++ b/libc/src/math/sinpif16.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for sinpif16 ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache Licese v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_SINPIF16_H
+#define LLVM_LIBC_SRC_MATH_SINPIF16_H
+
+#include "include/llvm-libc-macros/float16-macros.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 sinpif16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_SINPIF16_H
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index a70b65f23f4b71..a72ad128e21d1d 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -51,6 +51,19 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  sinpif16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    sinpif16_test.cpp
+  DEPENDS
+    libc.src.math.sinpif16
+    libc.src.errno.errno
+    libc.src.__support.CPP.array
+    libc.src.__support.FPUtil.fp_bits  
+)
+
 add_fp_unittest(
   sincosf_test
   SUITE
diff --git a/libc/test/src/math/smoke/sinpif16_test.cpp b/libc/test/src/math/smoke/sinpif16_test.cpp
new file mode 100644
index 00000000000000..bf55a4854bfb3a
--- /dev/null
+++ b/libc/test/src/math/smoke/sinpif16_test.cpp
@@ -0,0 +1,44 @@
+//===-- Unittests for sinpif16 --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//
+// ===----------------------------------------------------------------------==//
+
+#include "src/errno/libc_errno.h"
+#include "src/math/sinpif16.h"
+#include "test/UnitTest/FPMatcher.h"
+
+#include <stdint.h>
+
+using LlvmLibcSinpif16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcSinpif16Test, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif16(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::sinpif16(0.0f));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(-0.0f, LIBC_NAMESPACE::sinpif16(-0.0f));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif16(inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif16(neg_inf));
+  EXPECT_MATH_ERRNO(EDOM);
+}
+
+TEST_F(LlvmLibcSinpif16Test, Integers) {
+  EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif16(-0x420));
+  EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif16(-0x1p+10));
+  EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif16(-0x1.4p+14));
+  EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif16(0x420));
+  EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif16(0x1.cp+15));
+  EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif16(0x1.cp+7));
+}

@wldfngrs
Copy link
Contributor Author

wldfngrs commented Oct 3, 2024

@lntue please review!

Copy link

github-actions bot commented Oct 3, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@nickdesaulniers nickdesaulniers requested a review from lntue October 3, 2024 15:07
Copy link
Member

@nickdesaulniers nickdesaulniers left a comment

Choose a reason for hiding this comment

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

Thanks for the patch! I'll defer to @lntue here.

Comment on lines 17 to 19
// TODO: Should probably create a new file; sincospif16_utils.h
// To store the following helper functions and constants.
// I'd defer to @lntue for suggestions regarding that
Copy link
Member

Choose a reason for hiding this comment

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

Either way, let's ensure we don't commit this comment when merging.

@wldfngrs wldfngrs requested a review from overmighty October 7, 2024 16:11
@wldfngrs wldfngrs requested a review from overmighty October 14, 2024 19:33
Copy link
Member

@overmighty overmighty left a comment

Choose a reason for hiding this comment

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

Should be good after switching to fputil::cast to fix rounding with compiler-rt.

Copy link
Member

@overmighty overmighty left a comment

Choose a reason for hiding this comment

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

LGTM, just make sure to sort dependencies with sort.

@lntue lntue merged commit ddc3f2d into llvm:main Oct 15, 2024
8 checks passed
DanielCChen pushed a commit to DanielCChen/llvm-project that referenced this pull request Oct 16, 2024
Half-precision floating point (16-bit) implementation of the
trigonometric function Sin for inputs scaled by pi
@wldfngrs wldfngrs deleted the add_sinpif16_function branch October 18, 2024 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants