[libc] Add x86_64 implementations of double precision cos, sin and tan.
authorSiva Chandra Reddy <sivachandra@google.com>
Wed, 12 May 2021 23:05:13 +0000 (23:05 +0000)
committerSiva Chandra Reddy <sivachandra@google.com>
Thu, 13 May 2021 19:02:00 +0000 (19:02 +0000)
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

16 files changed:
libc/config/linux/x86_64/entrypoints.txt
libc/spec/stdc.td
libc/src/math/CMakeLists.txt
libc/src/math/cos.h [new file with mode: 0644]
libc/src/math/sin.h [new file with mode: 0644]
libc/src/math/tan.h [new file with mode: 0644]
libc/src/math/x86_64/CMakeLists.txt [new file with mode: 0644]
libc/src/math/x86_64/cos.cpp [new file with mode: 0644]
libc/src/math/x86_64/sin.cpp [new file with mode: 0644]
libc/src/math/x86_64/tan.cpp [new file with mode: 0644]
libc/test/src/math/CMakeLists.txt
libc/test/src/math/cos_test.cpp [new file with mode: 0644]
libc/test/src/math/sin_test.cpp [new file with mode: 0644]
libc/test/src/math/tan_test.cpp [new file with mode: 0644]
libc/utils/MPFRWrapper/MPFRUtils.cpp
libc/utils/MPFRWrapper/MPFRUtils.h

index 764059d..ff7a298 100644 (file)
@@ -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
index b50d2ed..c2a9045 100644 (file)
@@ -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>]>,
index 50fc4a4..e3bdb30 100644 (file)
@@ -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 (file)
index 0000000..aca1d6d
--- /dev/null
@@ -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 (file)
index 0000000..f3919c4
--- /dev/null
@@ -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 (file)
index 0000000..05366db
--- /dev/null
@@ -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 (file)
index 0000000..cd129e3
--- /dev/null
@@ -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 (file)
index 0000000..9e7e722
--- /dev/null
@@ -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 (file)
index 0000000..22774f2
--- /dev/null
@@ -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 (file)
index 0000000..9146473
--- /dev/null
@@ -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
index 22ff6d6..49b5290 100644 (file)
@@ -44,6 +44,18 @@ add_fp_unittest(
 )
 
 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
   SUITE
@@ -60,6 +72,18 @@ add_fp_unittest(
 )
 
 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
   SUITE
@@ -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(differential_testing)
diff --git a/libc/test/src/math/cos_test.cpp b/libc/test/src/math/cos_test.cpp
new file mode 100644 (file)
index 0000000..36bb905
--- /dev/null
@@ -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 (file)
index 0000000..dd3fcb5
--- /dev/null
@@ -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 (file)
index 0000000..fdd8962
--- /dev/null
@@ -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);
+  }
+}
index 7faf75c..7eb2e4f 100644 (file)
@@ -207,6 +207,12 @@ public:
     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:
index 17f8a09..604a20d 100644 (file)
@@ -32,6 +32,7 @@ enum class Operation : int {
   Round,
   Sin,
   Sqrt,
+  Tan,
   Trunc,
   EndUnaryOperationsSingleOutput,