[libc] [llvm] [LLVM] Simplify cabs libcall if real or imaginary part of input is zero (PR #97976)
Hendrik Hübner via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 7 16:54:36 PDT 2024
https://github.com/HendrikHuebner created https://github.com/llvm/llvm-project/pull/97976
cabs(a + i0) -> absf(a)
cabs(0 +ib) -> absf(b)
Issue: #97336
cc: @dtcxzyw
>From d1ab5682d74355335bb52a900b264c8469a04dc8 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Mon, 1 Jul 2024 23:16:30 +0200
Subject: [PATCH 1/4] [libc][math][C23] Correct sinpif range reduction comment
---
libc/src/math/generic/sinpif.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 662263c9fc43e..05bdad3ab4d0e 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -26,13 +26,13 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
double xd = static_cast<double>(x);
// Range reduction:
- // For |x| > pi/32, we perform range reduction as follows:
+ // 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
- // For small range (|x| < 2^45 when FMA instructions are available, 2^22
- // otherwise), this is done by performing:
+ //
+ // This is done by performing:
// k = round(x * 32)
// y = x * 32 - k
//
>From a6c8dbd56385df6e43c0811e3ac792bf03d06473 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Tue, 2 Jul 2024 21:18:03 +0200
Subject: [PATCH 2/4] [libc][C23][math] Implement cospif function correctly
rounded for all rounding modes
---
libc/config/darwin/arm/entrypoints.txt | 1 +
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/docs/math/index.rst | 2 +-
libc/src/math/CMakeLists.txt | 1 +
libc/src/math/cospif.h | 18 +++
libc/src/math/generic/CMakeLists.txt | 17 +++
libc/src/math/generic/cospif.cpp | 96 ++++++++++++++
libc/test/src/math/CMakeLists.txt | 16 +++
libc/test/src/math/cospif_test.cpp | 120 ++++++++++++++++++
libc/test/src/math/exhaustive/CMakeLists.txt | 16 +++
libc/test/src/math/exhaustive/cospif_test.cpp | 33 +++++
libc/test/src/math/smoke/CMakeLists.txt | 11 ++
libc/test/src/math/smoke/cospif_test.cpp | 34 +++++
libc/utils/MPFRWrapper/MPFRUtils.cpp | 35 +++++
libc/utils/MPFRWrapper/MPFRUtils.h | 1 +
17 files changed, 403 insertions(+), 1 deletion(-)
create mode 100644 libc/src/math/cospif.h
create mode 100644 libc/src/math/generic/cospif.cpp
create mode 100644 libc/test/src/math/cospif_test.cpp
create mode 100644 libc/test/src/math/exhaustive/cospif_test.cpp
create mode 100644 libc/test/src/math/smoke/cospif_test.cpp
diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index cb4603c79c79c..d32745c9c9467 100644
--- a/libc/config/darwin/arm/entrypoints.txt
+++ b/libc/config/darwin/arm/entrypoints.txt
@@ -132,6 +132,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.coshf
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cospif
libc.src.math.erff
libc.src.math.exp
libc.src.math.expf
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index ff35e8fffec19..79e880b1bec8e 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -346,6 +346,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.cos
libc.src.math.cosf
libc.src.math.coshf
+ libc.src.math.cospif
libc.src.math.erff
libc.src.math.exp
libc.src.math.exp10
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 51d85eed9ff16..fb9e4e3763844 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -354,6 +354,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.cos
libc.src.math.cosf
libc.src.math.coshf
+ libc.src.math.cospif
libc.src.math.erff
libc.src.math.exp
libc.src.math.exp10
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3eefa129c9758..6a61b51d60242 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -371,6 +371,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.cos
libc.src.math.cosf
libc.src.math.coshf
+ libc.src.math.cospif
libc.src.math.erff
libc.src.math.exp
libc.src.math.exp10
diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index e4da3d42baf7a..6c84b10122677 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -274,7 +274,7 @@ Higher Math Functions
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| cosh | |check| | | | | | 7.12.5.4 | F.10.2.4 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| cospi | | | | | | 7.12.4.12 | F.10.1.12 |
+| cospi | |check| | | | | | 7.12.4.12 | F.10.1.12 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| dsqrt | N/A | N/A | | N/A | | 7.12.14.6 | F.10.11 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 5b20913134fdf..ce4f8f993526d 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -81,6 +81,7 @@ add_math_entrypoint_object(cos)
add_math_entrypoint_object(cosf)
add_math_entrypoint_object(cosh)
add_math_entrypoint_object(coshf)
+add_math_entrypoint_object(cospif)
add_math_entrypoint_object(erf)
add_math_entrypoint_object(erff)
diff --git a/libc/src/math/cospif.h b/libc/src/math/cospif.h
new file mode 100644
index 0000000000000..50935bc33e59d
--- /dev/null
+++ b/libc/src/math/cospif.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for cospif ------------------------*- 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_COSPIF_H
+#define LLVM_LIBC_SRC_MATH_COSPIF_H
+
+namespace LIBC_NAMESPACE {
+
+float cospif(float x);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_COSPIF_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index d6ea8c54174b6..6ace14063bcb1 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -217,6 +217,23 @@ add_entrypoint_object(
-O3
)
+add_entrypoint_object(
+ cospif
+ SRCS
+ cospif.cpp
+ HDRS
+ ../cospif.h
+ DEPENDS
+ .sincosf_utils
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.fma
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.macros.optimization
+ COMPILE_OPTIONS
+ -O3
+)
+
add_entrypoint_object(
sin
SRCS
diff --git a/libc/src/math/generic/cospif.cpp b/libc/src/math/generic/cospif.cpp
new file mode 100644
index 0000000000000..713619430fe4b
--- /dev/null
+++ b/libc/src/math/generic/cospif.cpp
@@ -0,0 +1,96 @@
+//===-- Single-precision cospi 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/cospif.h"
+#include "sincosf_utils.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float, cospif, (float x)) {
+ using FPBits = typename fputil::FPBits<float>;
+
+ FPBits xbits(x);
+ Sign xsign = xbits.sign();
+ xbits.set_sign(Sign::POS);
+
+ uint32_t x_abs = xbits.uintval();
+ double xd = static_cast<double>(xbits.get_val());
+
+ // 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 cosine of sum
+ // formula:
+ // cospi(x) = cos((k + y)*pi/32)
+ // = cos(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(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 32 doubles. Sin(y*pi/32) and cos(y*pi/32) are
+ // computed using degree-7 and degree-6 minimax polynomials generated by
+ // Sollya respectively.
+
+ // The exhautive test passes for smaller values
+ if (LIBC_UNLIKELY(x_abs < 0x38A2'F984U)) {
+
+#if defined(LIBC_TARGET_CPU_HAS_FMA)
+ return fputil::multiply_add(xbits.get_val(), -0x1.0p-25f, 1.0f);
+#else
+ return static_cast<float>(fputil::multiply_add(xd, -0x1.0p-25, 1.0));
+#endif // LIBC_TARGET_CPU_HAS_FMA
+ }
+
+ // Numbers greater or equal to 2^23 are always integers or NaN
+ if (LIBC_UNLIKELY(x_abs >= 0x4B00'0000)) {
+
+ if (LIBC_UNLIKELY(x_abs < 0x4B80'0000)) {
+ return (x_abs & 0x1) ? -1.0f : 1.0f;
+ }
+
+ // x is inf or nan.
+ if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
+ if (x_abs == 0x7f80'0000U) {
+ fputil::set_errno_if_required(EDOM);
+ fputil::raise_except_if_required(FE_INVALID);
+ }
+ return x + FPBits::quiet_nan().get_val();
+ }
+
+ return 1.0f;
+ }
+
+ // Combine the results with the sine of sum formula:
+ // cos(pi * x) = cos((k + y)*pi/32)
+ // = cos(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(k*pi/32)
+ // = (cosm1_y + 1) * cos_k - sin_y * sin_k
+ // = (cosm1_y * cos_k + cos_k) - sin_y * sin_k
+ double sin_k, cos_k, sin_y, cosm1_y;
+
+ sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
+
+ if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0)) {
+ return FPBits::zero(xsign).get_val();
+ }
+
+ return static_cast<float>(fputil::multiply_add(
+ sin_y, -sin_k, fputil::multiply_add(cosm1_y, cos_k, cos_k)));
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 637e6720400ff..0ae7464e8aa40 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -28,6 +28,22 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
+add_fp_unittest(
+ cospif_test
+ NEED_MPFR
+ SUITE
+ libc-math-unittests
+ SRCS
+ cospif_test.cpp
+ HDRS
+ sdcomp26094.h
+ DEPENDS
+ libc.src.errno.errno
+ libc.src.math.cospif
+ libc.src.__support.CPP.array
+ libc.src.__support.FPUtil.fp_bits
+)
+
add_fp_unittest(
sinf_test
NEED_MPFR
diff --git a/libc/test/src/math/cospif_test.cpp b/libc/test/src/math/cospif_test.cpp
new file mode 100644
index 0000000000000..8a39957d1a274
--- /dev/null
+++ b/libc/test/src/math/cospif_test.cpp
@@ -0,0 +1,120 @@
+//===-- Unittests for cospif ----------------------------------------------===//
+//
+// 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/cospif.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/src/math/sdcomp26094.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcCospifTest = LIBC_NAMESPACE::testing::FPTest<float>;
+
+using LIBC_NAMESPACE::testing::SDCOMP26094_VALUES;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
+ LIBC_NAMESPACE::libc_errno = 0;
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(-0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(inf));
+ EXPECT_MATH_ERRNO(EDOM);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(neg_inf));
+ EXPECT_MATH_ERRNO(EDOM);
+}
+
+TEST_F(LlvmLibcCospifTest, SpecificBitPatterns) {
+ constexpr int N = 36;
+ constexpr uint32_t INPUTS[N] = {
+ 0x3f06'0a92U, // x = pi/6
+ 0x3f3a'dc51U, // x = 0x1.75b8a2p-1f
+ 0x3f49'0fdbU, // x = pi/4
+ 0x3f86'0a92U, // x = pi/3
+ 0x3fa7'832aU, // x = 0x1.4f0654p+0f
+ 0x3fc9'0fdbU, // x = pi/2
+ 0x4017'1973U, // x = 0x1.2e32e6p+1f
+ 0x4049'0fdbU, // x = pi
+ 0x4096'cbe4U, // x = 0x1.2d97c8p+2f
+ 0x40c9'0fdbU, // x = 2*pi
+ 0x433b'7490U, // x = 0x1.76e92p+7f
+ 0x437c'e5f1U, // x = 0x1.f9cbe2p+7f
+ 0x4619'9998U, // x = 0x1.33333p+13f
+ 0x474d'246fU, // x = 0x1.9a48dep+15f
+ 0x4afd'ece4U, // x = 0x1.fbd9c8p+22f
+ 0x4c23'32e9U, // x = 0x1.4665d2p+25f
+ 0x50a3'e87fU, // x = 0x1.47d0fep+34f
+ 0x5239'47f6U, // x = 0x1.728fecp+37f
+ 0x53b1'46a6U, // x = 0x1.628d4cp+40f
+ 0x55ca'fb2aU, // x = 0x1.95f654p+44f
+ 0x588e'f060U, // x = 0x1.1de0cp+50f
+ 0x5c07'bcd0U, // x = 0x1.0f79ap+57f
+ 0x5ebc'fddeU, // x = 0x1.79fbbcp+62f
+ 0x5fa6'eba7U, // x = 0x1.4dd74ep+64f
+ 0x61a4'0b40U, // x = 0x1.48168p+68f
+ 0x6386'134eU, // x = 0x1.0c269cp+72f
+ 0x6589'8498U, // x = 0x1.13093p+76f
+ 0x6600'0001U, // x = 0x1.000002p+77f
+ 0x664e'46e4U, // x = 0x1.9c8dc8p+77f
+ 0x66b0'14aaU, // x = 0x1.602954p+78f
+ 0x67a9'242bU, // x = 0x1.524856p+80f
+ 0x6a19'76f1U, // x = 0x1.32ede2p+85f
+ 0x6c55'da58U, // x = 0x1.abb4bp+89f
+ 0x6f79'be45U, // x = 0x1.f37c8ap+95f
+ 0x7276'69d4U, // x = 0x1.ecd3a8p+101f
+ 0x7758'4625U, // x = 0x1.b08c4ap+111f
+ };
+
+ for (int i = 0; i < N; ++i) {
+ float x = FPBits(INPUTS[i]).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+ LIBC_NAMESPACE::cospif(x), 0.5);
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, -x,
+ LIBC_NAMESPACE::cospif(-x), 0.5);
+ }
+}
+
+// For small values, sinpi(x) is pi * x.
+TEST_F(LlvmLibcCospifTest, SmallValues) {
+ float x = FPBits(0x1780'0000U).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+ LIBC_NAMESPACE::cospif(x), 0.5);
+
+ x = FPBits(0x0040'0000U).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+ LIBC_NAMESPACE::cospif(x), 0.5);
+}
+
+// SDCOMP-26094: check sinfpi in the cases for which the range reducer
+// returns values furthest beyond its nominal upper bound of pi/4.
+TEST_F(LlvmLibcCospifTest, SDCOMP_26094) {
+ for (uint32_t v : SDCOMP26094_VALUES) {
+ float x = FPBits((v)).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+ LIBC_NAMESPACE::cospif(x), 0.5);
+ }
+}
+
+// sinpi(-n) = -0.0
+// sinpi(+n) = +0.0
+TEST_F(LlvmLibcCospifTest, SignedZeros) {
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(100.5f));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-100.5f));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(45678.5f));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-45678.5f));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(8000000.5f));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-8000000.5f));
+}
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index 412ca031d0e99..c5f75b51cbd9f 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -74,6 +74,22 @@ add_fp_unittest(
-lpthread
)
+add_fp_unittest(
+ cospif_test
+ NO_RUN_POSTBUILD
+ NEED_MPFR
+ SUITE
+ libc_math_exhaustive_tests
+ SRCS
+ cospif_test.cpp
+ DEPENDS
+ .exhaustive_test
+ libc.src.math.cospif
+ libc.src.__support.FPUtil.fp_bits
+ LINK_LIBRARIES
+ -lpthread
+)
+
add_fp_unittest(
sincosf_test
NO_RUN_POSTBUILD
diff --git a/libc/test/src/math/exhaustive/cospif_test.cpp b/libc/test/src/math/exhaustive/cospif_test.cpp
new file mode 100644
index 0000000000000..59077d5909937
--- /dev/null
+++ b/libc/test/src/math/exhaustive/cospif_test.cpp
@@ -0,0 +1,33 @@
+//===-- Exhaustive test for cospif ----------------------------------------===//
+//
+// 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 "exhaustive_test.h"
+#include "src/math/cospif.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+using LlvmLibcCospifExhaustiveTest =
+ LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Cospi,
+ LIBC_NAMESPACE::cospif>;
+
+static constexpr uint32_t POS_START = 0x0000'0000U;
+static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+
+// Range: [0, Inf]
+TEST_F(LlvmLibcCospifExhaustiveTest, PostiveRange) {
+ test_full_range_all_roundings(POS_START, POS_STOP);
+}
+
+// Range: [-Inf, 0]
+static constexpr uint32_t NEG_START = 0xb000'0000U;
+static constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+TEST_F(LlvmLibcCospifExhaustiveTest, NegativeRange) {
+ test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 1b269edaa2477..72d04439e2d93 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -10,6 +10,17 @@ add_fp_unittest(
DEPENDS
libc.src.errno.errno
libc.src.math.cosf
+)
+
+add_fp_unittest(
+ cospif_test
+ SUITE
+ libc-math-smoke-tests
+ SRCS
+ cospif_test.cpp
+ DEPENDS
+ libc.src.errno.errno
+ libc.src.math.cospif
libc.src.__support.CPP.array
libc.src.__support.FPUtil.fp_bits
)
diff --git a/libc/test/src/math/smoke/cospif_test.cpp b/libc/test/src/math/smoke/cospif_test.cpp
new file mode 100644
index 0000000000000..007c4c45e3b15
--- /dev/null
+++ b/libc/test/src/math/smoke/cospif_test.cpp
@@ -0,0 +1,34 @@
+//===-- Unittests for cospif ----------------------------------------------===//
+//
+// 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/cospif.h"
+#include "test/UnitTest/FPMatcher.h"
+
+#include <stdint.h>
+
+using LlvmLibcCospifTest = LIBC_NAMESPACE::testing::FPTest<float>;
+
+TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
+ LIBC_NAMESPACE::libc_errno = 0;
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(-0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(inf));
+ EXPECT_MATH_ERRNO(EDOM);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(neg_inf));
+ EXPECT_MATH_ERRNO(EDOM);
+}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index f0a653824bea2..6548fc36cb6b4 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -239,6 +239,39 @@ class MPFRNumber {
return result;
}
+ MPFRNumber cospi() const {
+ MPFRNumber result(*this);
+
+#if MPFR_VERSION_MAJOR > 4 || \
+ (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
+ mpfr_cospi(result.value, value, mpfr_rounding);
+ return result;
+#else
+ MPFRNumber value_frac(*this);
+ mpfr_frac(value_frac.value, value, MPFR_RNDN);
+
+ if (mpfr_cmp_si(value_frac.value, 0.0) == 0) {
+ mpz_t integer_part;
+ mpz_init(integer_part);
+ mpfr_get_z(integer_part, value, MPFR_RNDN);
+
+ if (mpz_tstbit(integer_part, 0)) {
+ mpfr_set_si(result.value, -1.0, MPFR_RNDN); // odd
+ } else {
+ mpfr_set_si(result.value, 1.0, MPFR_RNDN); // even
+ }
+ return result;
+ }
+
+ MPFRNumber value_pi(0.0, 1280);
+ mpfr_const_pi(value_pi.value, MPFR_RNDN);
+ mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
+ mpfr_cos(result.value, value_pi.value, mpfr_rounding);
+
+ return result;
+#endif
+ }
+
MPFRNumber erf() const {
MPFRNumber result(*this);
mpfr_erf(result.value, value, mpfr_rounding);
@@ -675,6 +708,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.cos();
case Operation::Cosh:
return mpfrInput.cosh();
+ case Operation::Cospi:
+ return mpfrInput.cospi();
case Operation::Erf:
return mpfrInput.erf();
case Operation::Exp:
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 213dc7a65c3bc..002dc919396e7 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -34,6 +34,7 @@ enum class Operation : int {
Ceil,
Cos,
Cosh,
+ Cospi,
Erf,
Exp,
Exp2,
>From 78b3574dc83fb48b57e02449c76e51ca13fb821d Mon Sep 17 00:00:00 2001
From: hhuebner <ge84pip at mytum.de>
Date: Mon, 8 Jul 2024 01:33:49 +0200
Subject: [PATCH 3/4] Simplify cabs libcall if real or imaginary part is zero
---
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 36 ++++++++++---
.../Transforms/InstCombine/cabs-discrete.ll | 54 +++++++++++++++++++
2 files changed, 83 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index e61440e6e2daf..a2188422cd6f6 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -27,6 +27,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Support/MathExtras.h"
@@ -1958,25 +1959,46 @@ static Value *optimizeBinaryDoubleFP(CallInst *CI, IRBuilderBase &B,
// cabs(z) -> sqrt((creal(z)*creal(z)) + (cimag(z)*cimag(z)))
Value *LibCallSimplifier::optimizeCAbs(CallInst *CI, IRBuilderBase &B) {
- if (!CI->isFast())
- return nullptr;
-
- // Propagate fast-math flags from the existing call to new instructions.
- IRBuilderBase::FastMathFlagGuard Guard(B);
- B.setFastMathFlags(CI->getFastMathFlags());
-
Value *Real, *Imag;
+
if (CI->arg_size() == 1) {
Value *Op = CI->getArgOperand(0);
assert(Op->getType()->isArrayTy() && "Unexpected signature for cabs!");
Real = B.CreateExtractValue(Op, 0, "real");
Imag = B.CreateExtractValue(Op, 1, "imag");
+
} else {
assert(CI->arg_size() == 2 && "Unexpected signature for cabs!");
Real = CI->getArgOperand(0);
Imag = CI->getArgOperand(1);
}
+ // if real or imaginary part is zero, simplify to abs(cimag(z))
+ // or abs(creal(z))
+ if (ConstantFP *ConstReal = dyn_cast<ConstantFP>(Real)) {
+ if (ConstReal->isZeroValue()) {
+ IRBuilderBase::FastMathFlagGuard Guard(B);
+ B.setFastMathFlags(CI->getFastMathFlags());
+
+ return copyFlags(
+ *CI, B.CreateUnaryIntrinsic(Intrinsic::fabs, Imag, nullptr, "cabs"));
+ }
+ } else if (ConstantFP *ConstReal = dyn_cast<ConstantFP>(Imag)) {
+ if (ConstReal->isZeroValue()) {
+ IRBuilderBase::FastMathFlagGuard Guard(B);
+ B.setFastMathFlags(CI->getFastMathFlags());
+ return copyFlags(
+ *CI, B.CreateUnaryIntrinsic(Intrinsic::fabs, Real, nullptr, "cabs"));
+ }
+ }
+
+ if (!CI->isFast())
+ return nullptr;
+
+ // Propagate fast-math flags from the existing call to new instructions.
+ IRBuilderBase::FastMathFlagGuard Guard(B);
+ B.setFastMathFlags(CI->getFastMathFlags());
+
Value *RealReal = B.CreateFMul(Real, Real);
Value *ImagImag = B.CreateFMul(Imag, Imag);
diff --git a/llvm/test/Transforms/InstCombine/cabs-discrete.ll b/llvm/test/Transforms/InstCombine/cabs-discrete.ll
index 38aaf45e65fa4..7acd5edf61c40 100644
--- a/llvm/test/Transforms/InstCombine/cabs-discrete.ll
+++ b/llvm/test/Transforms/InstCombine/cabs-discrete.ll
@@ -40,6 +40,24 @@ define double @fast_cabs(double %real, double %imag) {
ret double %call
}
+define double @fast_cabs_zero_real(double %imag) {
+; CHECK-LABEL: @fast_cabs_zero_real(
+; CHECK-NEXT: [[CABS:%.*]] = tail call double @llvm.fabs.f64(double [[IMAG:%.*]])
+; CHECK-NEXT: ret double [[CABS]]
+;
+ %call = tail call double @cabs(double 0.0, double %imag)
+ ret double %call
+}
+
+define double @fast_cabs_zero_real(double %real) {
+; CHECK-LABEL: @fast_cabs_zero_real(
+; CHECK-NEXT: [[CABS:%.*]] = tail call double @llvm.fabs.f64(double [[REAL:%.*]])
+; CHECK-NEXT: ret double [[CABS]]
+;
+ %call = tail call double @cabs(double %real, 0.0)
+ ret double %call
+}
+
define float @fast_cabsf(float %real, float %imag) {
; CHECK-LABEL: @fast_cabsf(
; CHECK-NEXT: [[TMP1:%.*]] = fmul fast float [[REAL:%.*]], [[REAL]]
@@ -52,6 +70,24 @@ define float @fast_cabsf(float %real, float %imag) {
ret float %call
}
+define float @fast_cabsf_zero_real(float %imag) {
+; CHECK-LABEL: @fast_cabsf_zero_real(
+; CHECK-NEXT: [[CABS:%.*]] = tail call float @llvm.fabs.f32(float [[IMAG:%.*]])
+; CHECK-NEXT: ret float [[CABS]]
+;
+ %call = tail call float @cabsf(float 0.0, float %imag)
+ ret float %call
+}
+
+define float @fast_cabsf_zero_real(float %real) {
+; CHECK-LABEL: @fast_cabsf_zero_real(
+; CHECK-NEXT: [[CABS:%.*]] = tail call float @llvm.fabs.f64(float [[REAL:%.*]])
+; CHECK-NEXT: ret float [[CABS]]
+;
+ %call = tail call float @cabsf(float %real, 0.0)
+ ret float %call
+}
+
define fp128 @fast_cabsl(fp128 %real, fp128 %imag) {
; CHECK-LABEL: @fast_cabsl(
; CHECK-NEXT: [[TMP1:%.*]] = fmul fast fp128 [[REAL:%.*]], [[REAL]]
@@ -64,6 +100,24 @@ define fp128 @fast_cabsl(fp128 %real, fp128 %imag) {
ret fp128 %call
}
+define fp128 @fast_cabsl_zero_real(fp128 %imag) {
+; CHECK-LABEL: @fast_cabsl_zero_real(
+; CHECK-NEXT: [[CABS:%.*]] = tail call fp128 @llvm.fabsl.f128(fp128 [[IMAG:%.*]])
+; CHECK-NEXT: ret fp128 [[CABS]]
+;
+ %call = tail call fp128 @cabsl(double 0.0, fp128 %imag)
+ ret fp128 %call
+}
+
+define fp128 @fast_cabsl_zero_real(fp128 %real) {
+; CHECK-LABEL: @fast_cabsl_zero_real(
+; CHECK-NEXT: [[CABS:%.*]] = tail call fp128 @llvm.fabs.f128(fp128 [[REAL:%.*]])
+; CHECK-NEXT: ret fp128 [[CABS]]
+;
+ %call = tail call fp128 @cabsl(fp128 %real, 0.0)
+ ret fp128 %call
+}
+
declare double @cabs(double %real, double %imag)
declare float @cabsf(float %real, float %imag)
declare fp128 @cabsl(fp128 %real, fp128 %imag)
>From 389cf0cec0a229ba25c55b26718f846f85b0e3a1 Mon Sep 17 00:00:00 2001
From: hhuebner <ge84pip at mytum.de>
Date: Mon, 8 Jul 2024 01:51:21 +0200
Subject: [PATCH 4/4] Add more tests
---
llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp | 1 -
.../Transforms/InstCombine/cabs-discrete.ll | 18 +++++++++---------
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index a2188422cd6f6..12eb8eed50426 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -1979,7 +1979,6 @@ Value *LibCallSimplifier::optimizeCAbs(CallInst *CI, IRBuilderBase &B) {
if (ConstReal->isZeroValue()) {
IRBuilderBase::FastMathFlagGuard Guard(B);
B.setFastMathFlags(CI->getFastMathFlags());
-
return copyFlags(
*CI, B.CreateUnaryIntrinsic(Intrinsic::fabs, Imag, nullptr, "cabs"));
}
diff --git a/llvm/test/Transforms/InstCombine/cabs-discrete.ll b/llvm/test/Transforms/InstCombine/cabs-discrete.ll
index 7acd5edf61c40..68b962d90e2bd 100644
--- a/llvm/test/Transforms/InstCombine/cabs-discrete.ll
+++ b/llvm/test/Transforms/InstCombine/cabs-discrete.ll
@@ -49,12 +49,12 @@ define double @fast_cabs_zero_real(double %imag) {
ret double %call
}
-define double @fast_cabs_zero_real(double %real) {
-; CHECK-LABEL: @fast_cabs_zero_real(
+define double @fast_cabs_zero_imag(double %real) {
+; CHECK-LABEL: @fast_cabs_zero_imag(
; CHECK-NEXT: [[CABS:%.*]] = tail call double @llvm.fabs.f64(double [[REAL:%.*]])
; CHECK-NEXT: ret double [[CABS]]
;
- %call = tail call double @cabs(double %real, 0.0)
+ %call = tail call double @cabs(double %real, double 0.0)
ret double %call
}
@@ -79,12 +79,12 @@ define float @fast_cabsf_zero_real(float %imag) {
ret float %call
}
-define float @fast_cabsf_zero_real(float %real) {
-; CHECK-LABEL: @fast_cabsf_zero_real(
+define float @fast_cabsf_zero_imag(float %real) {
+; CHECK-LABEL: @fast_cabsf_zero_imag(
; CHECK-NEXT: [[CABS:%.*]] = tail call float @llvm.fabs.f64(float [[REAL:%.*]])
; CHECK-NEXT: ret float [[CABS]]
;
- %call = tail call float @cabsf(float %real, 0.0)
+ %call = tail call float @cabsf(float %real, float 0.0)
ret float %call
}
@@ -109,12 +109,12 @@ define fp128 @fast_cabsl_zero_real(fp128 %imag) {
ret fp128 %call
}
-define fp128 @fast_cabsl_zero_real(fp128 %real) {
-; CHECK-LABEL: @fast_cabsl_zero_real(
+define fp128 @fast_cabsl_zero_imag(fp128 %real) {
+; CHECK-LABEL: @fast_cabsl_zero_imag(
; CHECK-NEXT: [[CABS:%.*]] = tail call fp128 @llvm.fabs.f128(fp128 [[REAL:%.*]])
; CHECK-NEXT: ret fp128 [[CABS]]
;
- %call = tail call fp128 @cabsl(fp128 %real, 0.0)
+ %call = tail call fp128 @cabsl(fp128 %real, fp128 0.0)
ret fp128 %call
}
More information about the llvm-commits
mailing list