[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