[llvm] [KnownFPClass] Improve known classes for `acos` (PR #214603)
Tim Gymnich via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 01:37:37 PDT 2026
https://github.com/tgymnich updated https://github.com/llvm/llvm-project/pull/214603
>From ca1db861042a184464616352fd514ea53952b219 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Thu, 6 Aug 2026 18:48:08 -0600
Subject: [PATCH] [KnownFPClass] Improve known classes for acos
acos can never return a subnormal value. Additionally, acos returns +0.0
only for an input of +1.0, so it cannot return zero when the input is
known not to be a positive normal value.
---
llvm/lib/Support/KnownFPClass.cpp | 13 ++++++--
.../Transforms/Attributor/nofpclass-trig.ll | 31 ++++++++++++++++---
.../CodeGen/GlobalISel/KnownFPClassTest.cpp | 6 ++--
3 files changed, 39 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 37a6160b316ff..cb6cad2373fb1 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -703,9 +703,16 @@ KnownFPClass KnownFPClass::asin(const KnownFPClass &KnownSrc) {
KnownFPClass KnownFPClass::acos(const KnownFPClass &KnownSrc) {
KnownFPClass Known;
- // acos is bounded to [0, pi], never Inf or negative.
- Known.knownNot(fcInf);
- Known.knownNot(fcNegative);
+ // acos(x) is bounded to [0, pi] for -1 <= x <= 1, and is never negative,
+ // infinite, or subnormal. The smallest non-zero value occurs when x is
+ // close to 1.0, where acos(x) can be approximated by sqrt(2 * (1 - x)).
+ // Since sqrt cannot produce a subnormal result, we can conclude that
+ // acos(x) will also never produce a subnormal result.
+ Known.knownNot(fcNegative | fcInf | fcSubnormal);
+
+ // acos(x) == +0.0 iff x == +1.0
+ if (KnownSrc.isKnownNever(fcPosNormal))
+ Known.knownNot(fcZero);
Known.propagateNonSNaN(KnownSrc);
diff --git a/llvm/test/Transforms/Attributor/nofpclass-trig.ll b/llvm/test/Transforms/Attributor/nofpclass-trig.ll
index 7a6c583653650..1a1ade239ce59 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-trig.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-trig.ll
@@ -156,11 +156,11 @@ define float @ret_asin_nonan(float nofpclass(nan) %arg) {
ret float %call
}
-; acos is bounded to [0, pi], never Inf or negative.
+; acos is bounded to [0, pi], never infinite, negative, or subnormal.
define float @ret_acos(float %arg) {
-; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float @ret_acos
+; CHECK-LABEL: define nofpclass(inf nzero sub nnorm) float @ret_acos
; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float @llvm.acos.f32(float [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(inf nzero sub nnorm) float @llvm.acos.f32(float [[ARG]]) #[[ATTR2]]
; CHECK-NEXT: ret float [[CALL]]
;
%call = call float @llvm.acos.f32(float %arg)
@@ -168,9 +168,30 @@ define float @ret_acos(float %arg) {
}
define float @ret_acos_nonan(float nofpclass(nan) %arg) {
-; CHECK-LABEL: define nofpclass(snan inf nzero nsub nnorm) float @ret_acos_nonan
+; CHECK-LABEL: define nofpclass(snan inf nzero sub nnorm) float @ret_acos_nonan
; CHECK-SAME: (float nofpclass(nan) [[ARG:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(snan inf nzero nsub nnorm) float @llvm.acos.f32(float nofpclass(nan) [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(snan inf nzero sub nnorm) float @llvm.acos.f32(float nofpclass(nan) [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT: ret float [[CALL]]
+;
+ %call = call float @llvm.acos.f32(float %arg)
+ ret float %call
+}
+
+; acos(x) == +0.0 iff x == +1.0
+define float @ret_acos_no_pos_normal(float nofpclass(pnorm) %arg) {
+; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_acos_no_pos_normal
+; CHECK-SAME: (float nofpclass(pnorm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(inf zero sub nnorm) float @llvm.acos.f32(float nofpclass(pnorm) [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT: ret float [[CALL]]
+;
+ %call = call float @llvm.acos.f32(float %arg)
+ ret float %call
+}
+
+define float @ret_acos_no_neg_normal(float nofpclass(nnorm) %arg) {
+; CHECK-LABEL: define nofpclass(inf nzero sub nnorm) float @ret_acos_no_neg_normal
+; CHECK-SAME: (float nofpclass(nnorm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(inf nzero sub nnorm) float @llvm.acos.f32(float nofpclass(nnorm) [[ARG]]) #[[ATTR2]]
; CHECK-NEXT: ret float [[CALL]]
;
%call = call float @llvm.acos.f32(float %arg)
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index d995c1e15f9e5..91b7e53a49e58 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -1604,7 +1604,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAsinPos) {
}
TEST_F(AArch64GISelMITest, TestFPClassFAcos) {
- // acos is bounded to [0, π]: never Inf, never negative.
+ // acos is bounded to [0, π]: never infinite, negative, or subnormal.
StringRef MIRString = R"(
%ptr:_(p0) = G_IMPLICIT_DEF
%val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1619,7 +1619,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAcos) {
Register SrcReg = FinalCopy->getOperand(1).getReg();
GISelValueTracking Info(*MF);
KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
- EXPECT_EQ(fcPosFinite | fcNan, Known.KnownFPClasses);
+ EXPECT_EQ(fcPosZero | fcPosNormal | fcNan, Known.KnownFPClasses);
EXPECT_EQ(std::nullopt, Known.SignBit);
}
@@ -1641,7 +1641,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAcosPos) {
Register SrcReg = FinalCopy->getOperand(1).getReg();
GISelValueTracking Info(*MF);
KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
- EXPECT_EQ(fcPosFinite | fcQNan, Known.KnownFPClasses);
+ EXPECT_EQ(fcPosZero | fcPosNormal | fcQNan, Known.KnownFPClasses);
EXPECT_EQ(std::nullopt, Known.SignBit);
}
More information about the llvm-commits
mailing list