[libc-commits] [libc] 861dc75 - [libc] Add x86_64 implementations of double precision cos, sin and tan.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Thu May 13 12:02:09 PDT 2021


Author: Siva Chandra Reddy
Date: 2021-05-13T19:02:00Z
New Revision: 861dc75906829a177dc2e5249747771aa3866a06

URL: https://github.com/llvm/llvm-project/commit/861dc75906829a177dc2e5249747771aa3866a06
DIFF: https://github.com/llvm/llvm-project/commit/861dc75906829a177dc2e5249747771aa3866a06.diff

LOG: [libc] Add x86_64 implementations of double precision cos, sin and tan.

The implementations use the x86_64 FPU instructions. These instructions
are extremely slow compared to a polynomial based software
implementation. Also, their accuracy falls drastically once the input
goes beyond 2PI. To improve both the speed and accuracy, we will be
taking the following approach going forward:
1. As a follow up to this CL, we will implement a range reduction algorithm
which will expand the accuracy to the entire double precision range.
2. After that, we will replace the HW instructions with a polynomial
implementation to improve the run time.

After step 2, the implementations will be accurate, performant and target
architecture independent.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D102384

Added: 
    libc/src/math/cos.h
    libc/src/math/sin.h
    libc/src/math/tan.h
    libc/src/math/x86_64/CMakeLists.txt
    libc/src/math/x86_64/cos.cpp
    libc/src/math/x86_64/sin.cpp
    libc/src/math/x86_64/tan.cpp
    libc/test/src/math/cos_test.cpp
    libc/test/src/math/sin_test.cpp
    libc/test/src/math/tan_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/stdc.td
    libc/src/math/CMakeLists.txt
    libc/test/src/math/CMakeLists.txt
    libc/utils/MPFRWrapper/MPFRUtils.cpp
    libc/utils/MPFRWrapper/MPFRUtils.h

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 764059d692127..ff7a298e7579a 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -65,6 +65,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.ceil
     libc.src.math.ceilf
     libc.src.math.ceill
+    libc.src.math.cos
     libc.src.math.cosf
     libc.src.math.expf
     libc.src.math.exp2f
@@ -136,11 +137,13 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.round
     libc.src.math.roundf
     libc.src.math.roundl
+    libc.src.math.sin
     libc.src.math.sincosf
     libc.src.math.sinf
     libc.src.math.sqrt
     libc.src.math.sqrtf
     libc.src.math.sqrtl
+    libc.src.math.tan
     libc.src.math.trunc
     libc.src.math.truncf
     libc.src.math.truncl

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index b50d2ed44d73b..c2a90450dbd2a 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -388,8 +388,11 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"modff", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatPtr>]>,
           FunctionSpec<"modfl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoublePtr>]>,
 
+          FunctionSpec<"cos", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
           FunctionSpec<"cosf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
+          FunctionSpec<"sin", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
           FunctionSpec<"sinf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
+          FunctionSpec<"tan", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
 
           FunctionSpec<"expf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
           FunctionSpec<"exp2f", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

diff  --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 50fc4a4325e9f..e3bdb30503ca8 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -64,6 +64,7 @@ add_math_entrypoint_object(copysign)
 add_math_entrypoint_object(copysignf)
 add_math_entrypoint_object(copysignl)
 
+add_math_entrypoint_object(cos)
 add_math_entrypoint_object(cosf)
 
 add_math_entrypoint_object(expf)
@@ -155,12 +156,15 @@ add_math_entrypoint_object(roundl)
 
 add_math_entrypoint_object(sincosf)
 
+add_math_entrypoint_object(sin)
 add_math_entrypoint_object(sinf)
 
 add_math_entrypoint_object(sqrt)
 add_math_entrypoint_object(sqrtf)
 add_math_entrypoint_object(sqrtl)
 
+add_math_entrypoint_object(tan)
+
 add_math_entrypoint_object(trunc)
 add_math_entrypoint_object(truncf)
 add_math_entrypoint_object(truncl)

diff  --git a/libc/src/math/cos.h b/libc/src/math/cos.h
new file mode 100644
index 0000000000000..aca1d6d1e2281
--- /dev/null
+++ b/libc/src/math/cos.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for cos ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_COS_H
+#define LLVM_LIBC_SRC_MATH_COS_H
+
+namespace __llvm_libc {
+
+double cos(double x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_COS_H

diff  --git a/libc/src/math/sin.h b/libc/src/math/sin.h
new file mode 100644
index 0000000000000..f3919c46df603
--- /dev/null
+++ b/libc/src/math/sin.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for sin ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_SIN_H
+#define LLVM_LIBC_SRC_MATH_SIN_H
+
+namespace __llvm_libc {
+
+double sin(double x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_SIN_H

diff  --git a/libc/src/math/tan.h b/libc/src/math/tan.h
new file mode 100644
index 0000000000000..05366db52ff87
--- /dev/null
+++ b/libc/src/math/tan.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for tan ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_TAN_H
+#define LLVM_LIBC_SRC_MATH_TAN_H
+
+namespace __llvm_libc {
+
+double tan(double x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_TAN_H

diff  --git a/libc/src/math/x86_64/CMakeLists.txt b/libc/src/math/x86_64/CMakeLists.txt
new file mode 100644
index 0000000000000..cd129e3eefb75
--- /dev/null
+++ b/libc/src/math/x86_64/CMakeLists.txt
@@ -0,0 +1,29 @@
+add_entrypoint_object(
+  cos
+  SRCS
+    cos.cpp
+  HDRS
+    ../cos.h
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  sin
+  SRCS
+    sin.cpp
+  HDRS
+    ../sin.h
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  tan
+  SRCS
+    tan.cpp
+  HDRS
+    ../tan.h
+  COMPILE_OPTIONS
+    -O2
+)

diff  --git a/libc/src/math/x86_64/cos.cpp b/libc/src/math/x86_64/cos.cpp
new file mode 100644
index 0000000000000..9e7e7227c6b1d
--- /dev/null
+++ b/libc/src/math/x86_64/cos.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of the cos function for x86_64 ---------------------===//
+//
+// 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/cos.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(double, cos, (double x)) {
+  double result;
+  __asm__ __volatile__("fcos" : "=t"(result) : "f"(x));
+  return result;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/x86_64/sin.cpp b/libc/src/math/x86_64/sin.cpp
new file mode 100644
index 0000000000000..22774f278956e
--- /dev/null
+++ b/libc/src/math/x86_64/sin.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of the sin function for x86_64 ---------------------===//
+//
+// 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/sin.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(double, sin, (double x)) {
+  double result;
+  __asm__ __volatile__("fsin" : "=t"(result) : "f"(x));
+  return result;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/x86_64/tan.cpp b/libc/src/math/x86_64/tan.cpp
new file mode 100644
index 0000000000000..9146473741f95
--- /dev/null
+++ b/libc/src/math/x86_64/tan.cpp
@@ -0,0 +1,24 @@
+//===-- Implementation of the tan function for x86_64 ---------------------===//
+//
+// 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/tan.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(double, tan, (double x)) {
+  double result;
+  double one;
+  // The fptan instruction pushes the number 1 on to the FP stack after
+  // computing tan. So, we read out the one before popping the actual result.
+  __asm__ __volatile__("fptan" : "=t"(one) : "f"(x));
+  __asm__ __volatile__("fstpl %0" : "=m"(result));
+  return result;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 22ff6d612c9ed..49b529048c31e 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -43,6 +43,18 @@ add_fp_unittest(
     libc.utils.FPUtil.fputil
 )
 
+add_fp_unittest(
+  cos_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    cos_test.cpp
+  DEPENDS
+    libc.src.math.cos
+    libc.utils.FPUtil.fputil
+)
+
 add_fp_unittest(
   sinf_test
   NEED_MPFR
@@ -59,6 +71,18 @@ add_fp_unittest(
     libc.utils.FPUtil.fputil
 )
 
+add_fp_unittest(
+  sin_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    sin_test.cpp
+  DEPENDS
+    libc.src.math.sin
+    libc.utils.FPUtil.fputil
+)
+
 add_fp_unittest(
   sincosf_test
   NEED_MPFR
@@ -1132,6 +1156,18 @@ add_fp_unittest(
     libc.utils.FPUtil.fputil
 )
 
+add_fp_unittest(
+  tan_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    tan_test.cpp
+  DEPENDS
+    libc.src.math.tan
+    libc.utils.FPUtil.fputil
+)
+
 add_subdirectory(generic)
 add_subdirectory(exhaustive)
 add_subdirectory(
diff erential_testing)

diff  --git a/libc/test/src/math/cos_test.cpp b/libc/test/src/math/cos_test.cpp
new file mode 100644
index 0000000000000..36bb905b21d36
--- /dev/null
+++ b/libc/test/src/math/cos_test.cpp
@@ -0,0 +1,32 @@
+//===-- Unittests for cos -------------------------------------------------===//
+//
+// 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/cos.h"
+#include "utils/FPUtil/TestHelpers.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+#include <math.h>
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+DECLARE_SPECIAL_CONSTANTS(double)
+
+TEST(LlvmLibccosTest, Range) {
+  static constexpr double _2pi = 6.283185307179586;
+  constexpr UIntType count = 10000000;
+  constexpr UIntType step = UIntType(-1) / count;
+  for (UIntType i = 0, v = 0; i <= count; ++i, v += step) {
+    double x = double(FPBits(v));
+    // TODO: Expand the range of testing after range reduction is implemented.
+    if (isnan(x) || isinf(x) || x > _2pi || x < -_2pi)
+      continue;
+
+    ASSERT_MPFR_MATCH(mpfr::Operation::Cos, x, __llvm_libc::cos(x), 1.0);
+  }
+}

diff  --git a/libc/test/src/math/sin_test.cpp b/libc/test/src/math/sin_test.cpp
new file mode 100644
index 0000000000000..dd3fcb58c9691
--- /dev/null
+++ b/libc/test/src/math/sin_test.cpp
@@ -0,0 +1,32 @@
+//===-- Unittests for sin -------------------------------------------------===//
+//
+// 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/sin.h"
+#include "utils/FPUtil/TestHelpers.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+#include <math.h>
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+DECLARE_SPECIAL_CONSTANTS(double)
+
+TEST(LlvmLibcSinTest, Range) {
+  static constexpr double _2pi = 6.283185307179586;
+  constexpr UIntType count = 10000000;
+  constexpr UIntType step = UIntType(-1) / count;
+  for (UIntType i = 0, v = 0; i <= count; ++i, v += step) {
+    double x = double(FPBits(v));
+    // TODO: Expand the range of testing after range reduction is implemented.
+    if (isnan(x) || isinf(x) || x > _2pi || x < -_2pi)
+      continue;
+
+    ASSERT_MPFR_MATCH(mpfr::Operation::Sin, x, __llvm_libc::sin(x), 1.0);
+  }
+}

diff  --git a/libc/test/src/math/tan_test.cpp b/libc/test/src/math/tan_test.cpp
new file mode 100644
index 0000000000000..fdd8962be196d
--- /dev/null
+++ b/libc/test/src/math/tan_test.cpp
@@ -0,0 +1,32 @@
+//===-- Unittests for tan -------------------------------------------------===//
+//
+// 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/tan.h"
+#include "utils/FPUtil/TestHelpers.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+#include <math.h>
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+DECLARE_SPECIAL_CONSTANTS(double)
+
+TEST(LlvmLibctanTest, Range) {
+  static constexpr double _2pi = 6.283185307179586;
+  constexpr UIntType count = 10000000;
+  constexpr UIntType step = UIntType(-1) / count;
+  for (UIntType i = 0, v = 0; i <= count; ++i, v += step) {
+    double x = double(FPBits(v));
+    // TODO: Expand the range of testing after range reduction is implemented.
+    if (isnan(x) || isinf(x) || x > _2pi || x < -_2pi)
+      continue;
+
+    ASSERT_MPFR_MATCH(mpfr::Operation::Tan, x, __llvm_libc::tan(x), 1.0);
+  }
+}

diff  --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 7faf75ccde593..7eb2e4f5243f8 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -207,6 +207,12 @@ class MPFRNumber {
     return result;
   }
 
+  MPFRNumber tan() const {
+    MPFRNumber result;
+    mpfr_tan(result.value, value, MPFR_RNDN);
+    return result;
+  }
+
   MPFRNumber trunc() const {
     MPFRNumber result;
     mpfr_trunc(result.value, value);
@@ -311,6 +317,8 @@ unaryOperation(Operation op, InputType input) {
     return mpfrInput.sin();
   case Operation::Sqrt:
     return mpfrInput.sqrt();
+  case Operation::Tan:
+    return mpfrInput.tan();
   case Operation::Trunc:
     return mpfrInput.trunc();
   default:

diff  --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 17f8a09e80baa..604a20d64c99a 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -32,6 +32,7 @@ enum class Operation : int {
   Round,
   Sin,
   Sqrt,
+  Tan,
   Trunc,
   EndUnaryOperationsSingleOutput,
 


        


More information about the libc-commits mailing list