[llvm] [Support] KnownFPClass: add static helpers for more trig intrinsics (PR #190609)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 6 09:47:59 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-support
Author: Tim Gymnich (tgymnich)
<details>
<summary>Changes</summary>
Add KnownFPClass::tan, sinh, cosh, tanh, asin, acos, atan, and atan2 static helper methods, following the same pattern as the existing sin/cos helpers.
---
Full diff: https://github.com/llvm/llvm-project/pull/190609.diff
5 Files Affected:
- (modified) llvm/include/llvm/Support/KnownFPClass.h (+25)
- (modified) llvm/lib/Analysis/ValueTracking.cpp (+48-4)
- (modified) llvm/lib/Support/KnownFPClass.cpp (+111)
- (modified) llvm/test/Transforms/InstSimplify/known-never-infinity.ll (+118)
- (modified) llvm/test/Transforms/InstSimplify/known-never-nan.ll (+20)
``````````diff
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index c48e0f8b7b65b..b18bfb55eacd1 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -299,6 +299,31 @@ struct KnownFPClass {
/// Report known values for cos
LLVM_ABI static KnownFPClass cos(const KnownFPClass &Src);
+ /// Report known values for tan
+ LLVM_ABI static KnownFPClass tan(const KnownFPClass &Src);
+
+ /// Report known values for sinh
+ LLVM_ABI static KnownFPClass sinh(const KnownFPClass &Src);
+
+ /// Report known values for cosh
+ LLVM_ABI static KnownFPClass cosh(const KnownFPClass &Src);
+
+ /// Report known values for tanh
+ LLVM_ABI static KnownFPClass tanh(const KnownFPClass &Src);
+
+ /// Report known values for asin
+ LLVM_ABI static KnownFPClass asin(const KnownFPClass &Src);
+
+ /// Report known values for acos
+ LLVM_ABI static KnownFPClass acos(const KnownFPClass &Src);
+
+ /// Report known values for atan
+ LLVM_ABI static KnownFPClass atan(const KnownFPClass &Src);
+
+ /// Report known values for atan2
+ LLVM_ABI static KnownFPClass atan2(const KnownFPClass &LHS,
+ const KnownFPClass &RHS);
+
/// Return true if the sign bit must be 0, ignoring the sign of nans.
bool signBitIsZeroOrNaN() const { return isKnownNever(fcNegative); }
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index a0fb28612c534..7f20f8f43f110 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5258,13 +5258,57 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
break;
}
case Intrinsic::sin:
- case Intrinsic::cos: {
- // Return NaN on infinite inputs.
+ case Intrinsic::cos:
+ case Intrinsic::tan:
+ case Intrinsic::sinh:
+ case Intrinsic::cosh:
+ case Intrinsic::tanh:
+ case Intrinsic::asin:
+ case Intrinsic::acos:
+ case Intrinsic::atan: {
KnownFPClass KnownSrc;
computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses,
KnownSrc, Q, Depth + 1);
- Known = IID == Intrinsic::sin ? KnownFPClass::sin(KnownSrc)
- : KnownFPClass::cos(KnownSrc);
+ switch (IID) {
+ case Intrinsic::sin:
+ Known = KnownFPClass::sin(KnownSrc);
+ break;
+ case Intrinsic::cos:
+ Known = KnownFPClass::cos(KnownSrc);
+ break;
+ case Intrinsic::tan:
+ Known = KnownFPClass::tan(KnownSrc);
+ break;
+ case Intrinsic::sinh:
+ Known = KnownFPClass::sinh(KnownSrc);
+ break;
+ case Intrinsic::cosh:
+ Known = KnownFPClass::cosh(KnownSrc);
+ break;
+ case Intrinsic::tanh:
+ Known = KnownFPClass::tanh(KnownSrc);
+ break;
+ case Intrinsic::asin:
+ Known = KnownFPClass::asin(KnownSrc);
+ break;
+ case Intrinsic::acos:
+ Known = KnownFPClass::acos(KnownSrc);
+ break;
+ case Intrinsic::atan:
+ Known = KnownFPClass::atan(KnownSrc);
+ break;
+ default:
+ llvm_unreachable("unhandled trig intrinsic");
+ }
+ break;
+ }
+ case Intrinsic::atan2: {
+ KnownFPClass KnownLHS, KnownRHS;
+ computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses,
+ KnownLHS, Q, Depth + 1);
+ computeKnownFPClass(II->getArgOperand(1), DemandedElts, InterestedClasses,
+ KnownRHS, Q, Depth + 1);
+ Known = KnownFPClass::atan2(KnownLHS, KnownRHS);
break;
}
case Intrinsic::maxnum:
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 4f704eabfc6ec..3d361c80ff154 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -612,6 +612,117 @@ KnownFPClass KnownFPClass::cos(const KnownFPClass &KnownSrc) {
return sin(KnownSrc);
}
+KnownFPClass KnownFPClass::tan(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // tan never returns Inf (tan(±Inf) = NaN; tan(finite) = finite).
+ Known.knownNot(fcInf);
+
+ // NaN propagates. tan(±Inf) is NaN.
+ if (KnownSrc.isKnownNeverNaN() && KnownSrc.isKnownNeverInfinity())
+ Known.knownNot(fcNan);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::sinh(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // sinh is sign-preserving: sinh(x) < 0 iff x < 0.
+ if (KnownSrc.isKnownNever(fcNegative))
+ Known.knownNot(fcNegative);
+
+ Known.propagateNaN(KnownSrc);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::cosh(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // cosh(x) >= 1 for all real x; cosh(±Inf) = +Inf. Never negative.
+ Known.knownNot(fcNegative);
+
+ Known.propagateNaN(KnownSrc);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::tanh(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // tanh is bounded to (-1, 1), never Inf.
+ Known.knownNot(fcInf);
+
+ // tanh is sign-preserving: tanh(x) < 0 iff x < 0.
+ if (KnownSrc.isKnownNever(fcNegative))
+ Known.knownNot(fcNegative);
+
+ Known.propagateNaN(KnownSrc);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::asin(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // asin is bounded to [-π/2, π/2], never Inf.
+ Known.knownNot(fcInf);
+
+ // asin is sign-preserving.
+ if (KnownSrc.isKnownNever(fcNegative))
+ Known.knownNot(fcNegative);
+
+ // NaN propagates. asin(x) is also NaN for |x| > 1, so we cannot rule
+ // out NaN without knowing the source is in [-1, 1].
+ Known.propagateNaN(KnownSrc);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::acos(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // acos is bounded to [0, π], never Inf or negative.
+ Known.knownNot(fcInf);
+ Known.knownNot(fcNegative);
+
+ // NaN propagates. acos(x) is also NaN for |x| > 1, so we cannot rule
+ // out NaN without knowing the source is in [-1, 1].
+ Known.propagateNaN(KnownSrc);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::atan(const KnownFPClass &KnownSrc) {
+ KnownFPClass Known;
+
+ // atan is bounded to (-π/2, π/2), never Inf. atan(±Inf) = ±π/2 (finite).
+ Known.knownNot(fcInf);
+
+ // atan is sign-preserving: atan(x) < 0 iff x < 0.
+ if (KnownSrc.isKnownNever(fcNegative))
+ Known.knownNot(fcNegative);
+
+ Known.propagateNaN(KnownSrc);
+
+ return Known;
+}
+
+KnownFPClass KnownFPClass::atan2(const KnownFPClass &KnownLHS,
+ const KnownFPClass &KnownRHS) {
+ KnownFPClass Known;
+
+ // atan2 result is in (-π, π], never Inf.
+ Known.knownNot(fcInf);
+
+ // NaN if either operand is NaN.
+ if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN())
+ Known.knownNot(fcNan);
+
+ return Known;
+}
+
KnownFPClass KnownFPClass::fpext(const KnownFPClass &KnownSrc,
const fltSemantics &DstTy,
const fltSemantics &SrcTy) {
diff --git a/llvm/test/Transforms/InstSimplify/known-never-infinity.ll b/llvm/test/Transforms/InstSimplify/known-never-infinity.ll
index af83f00368597..11051b32ac2a1 100644
--- a/llvm/test/Transforms/InstSimplify/known-never-infinity.ll
+++ b/llvm/test/Transforms/InstSimplify/known-never-infinity.ll
@@ -750,6 +750,116 @@ define i1 @isKnownNeverInfinity_cos(double %x) {
ret i1 %r
}
+; No source check required
+define i1 @isKnownNeverInfinity_tan(double %x) {
+; CHECK-LABEL: define i1 @isKnownNeverInfinity_tan
+; CHECK-SAME: (double [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %e = call double @llvm.tan.f64(double %x)
+ %r = fcmp une double %e, 0x7ff0000000000000
+ ret i1 %r
+}
+
+; No source check required
+define i1 @isKnownNeverInfinity_tanh(double %x) {
+; CHECK-LABEL: define i1 @isKnownNeverInfinity_tanh
+; CHECK-SAME: (double [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %e = call double @llvm.tanh.f64(double %x)
+ %r = fcmp une double %e, 0x7ff0000000000000
+ ret i1 %r
+}
+
+; No source check required
+define i1 @isKnownNeverInfinity_asin(double %x) {
+; CHECK-LABEL: define i1 @isKnownNeverInfinity_asin
+; CHECK-SAME: (double [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %e = call double @llvm.asin.f64(double %x)
+ %r = fcmp une double %e, 0x7ff0000000000000
+ ret i1 %r
+}
+
+; No source check required
+define i1 @isKnownNeverInfinity_acos(double %x) {
+; CHECK-LABEL: define i1 @isKnownNeverInfinity_acos
+; CHECK-SAME: (double [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %e = call double @llvm.acos.f64(double %x)
+ %r = fcmp une double %e, 0x7ff0000000000000
+ ret i1 %r
+}
+
+; No source check required
+define i1 @isKnownNeverInfinity_atan(double %x) {
+; CHECK-LABEL: define i1 @isKnownNeverInfinity_atan
+; CHECK-SAME: (double [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %e = call double @llvm.atan.f64(double %x)
+ %r = fcmp une double %e, 0x7ff0000000000000
+ ret i1 %r
+}
+
+; No source check required
+define i1 @isKnownNeverInfinity_atan2(double %x, double %y) {
+; CHECK-LABEL: define i1 @isKnownNeverInfinity_atan2
+; CHECK-SAME: (double [[X:%.*]], double [[Y:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %e = call double @llvm.atan2.f64(double %x, double %y)
+ %r = fcmp une double %e, 0x7ff0000000000000
+ ret i1 %r
+}
+
+; sinh is sign-preserving: a non-negative input produces a non-negative result.
+define i1 @isKnownNeverNegative_sinh_nonneg(double nofpclass(nan ninf nzero nsub nnorm) %x) {
+; CHECK-LABEL: define i1 @isKnownNeverNegative_sinh_nonneg
+; CHECK-SAME: (double nofpclass(nan ninf nzero nsub nnorm) [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %e = call double @llvm.sinh.f64(double %x)
+ %r = fcmp olt double %e, 0.0
+ ret i1 %r
+}
+
+; tanh is sign-preserving: a non-negative input produces a non-negative result.
+define i1 @isKnownNeverNegative_tanh_nonneg(double nofpclass(nan ninf nzero nsub nnorm) %x) {
+; CHECK-LABEL: define i1 @isKnownNeverNegative_tanh_nonneg
+; CHECK-SAME: (double nofpclass(nan ninf nzero nsub nnorm) [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %e = call double @llvm.tanh.f64(double %x)
+ %r = fcmp olt double %e, 0.0
+ ret i1 %r
+}
+
+; asin is sign-preserving: a non-negative input produces a non-negative result.
+define i1 @isKnownNeverNegative_asin_nonneg(double nofpclass(nan ninf nzero nsub nnorm) %x) {
+; CHECK-LABEL: define i1 @isKnownNeverNegative_asin_nonneg
+; CHECK-SAME: (double nofpclass(nan ninf nzero nsub nnorm) [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %e = call double @llvm.asin.f64(double %x)
+ %r = fcmp olt double %e, 0.0
+ ret i1 %r
+}
+
+; atan is sign-preserving: a non-negative input produces a non-negative result.
+define i1 @isKnownNeverNegative_atan_nonneg(double nofpclass(nan ninf nzero nsub nnorm) %x) {
+; CHECK-LABEL: define i1 @isKnownNeverNegative_atan_nonneg
+; CHECK-SAME: (double nofpclass(nan ninf nzero nsub nnorm) [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %e = call double @llvm.atan.f64(double %x)
+ %r = fcmp olt double %e, 0.0
+ ret i1 %r
+}
+
define i1 @isKnownNeverInfinity_log(double %x) {
; CHECK-LABEL: define i1 @isKnownNeverInfinity_log
; CHECK-SAME: (double [[X:%.*]]) {
@@ -1209,11 +1319,16 @@ define i1 @isKnownNeverInfinity_vector_reduce_fmin_fail(<4 x double> %x) {
ret i1 %cmp
}
+declare double @llvm.acos.f64(double)
+declare double @llvm.asin.f64(double)
+declare double @llvm.atan.f64(double)
+declare double @llvm.atan2.f64(double, double)
declare double @llvm.arithmetic.fence.f64(double)
declare double @llvm.canonicalize.f64(double)
declare double @llvm.ceil.f64(double)
declare double @llvm.copysign.f64(double, double)
declare double @llvm.cos.f64(double)
+declare double @llvm.cosh.f64(double)
declare double @llvm.exp2.f64(double)
declare double @llvm.exp.f64(double)
declare double @llvm.fabs.f64(double)
@@ -1235,7 +1350,10 @@ declare double @llvm.rint.f64(double)
declare double @llvm.roundeven.f64(double)
declare double @llvm.round.f64(double)
declare double @llvm.sin.f64(double)
+declare double @llvm.sinh.f64(double)
declare double @llvm.sqrt.f64(double)
+declare double @llvm.tan.f64(double)
+declare double @llvm.tanh.f64(double)
declare double @llvm.trunc.f64(double)
declare float @llvm.fptrunc.round.f32.f64(double, metadata)
declare ppc_fp128 @llvm.ceil.ppcf128(ppc_fp128)
diff --git a/llvm/test/Transforms/InstSimplify/known-never-nan.ll b/llvm/test/Transforms/InstSimplify/known-never-nan.ll
index 907eca0a856a8..a98d98259df2d 100644
--- a/llvm/test/Transforms/InstSimplify/known-never-nan.ll
+++ b/llvm/test/Transforms/InstSimplify/known-never-nan.ll
@@ -554,6 +554,26 @@ unwind:
resume ptr null
}
+; tan(x) is never NaN when x is finite and not NaN (tan(±Inf) = NaN).
+define i1 @isKnownNeverNaN_tan_nonan_noinf(double nofpclass(nan inf) %x) {
+; CHECK-LABEL: @isKnownNeverNaN_tan_nonan_noinf(
+; CHECK-NEXT: ret i1 false
+;
+ %e = call double @llvm.tan.f64(double %x)
+ %r = fcmp uno double %e, 0.0
+ ret i1 %r
+}
+
+; atan2 is never NaN when neither operand is NaN.
+define i1 @isKnownNeverNaN_atan2_nonan(double nofpclass(nan) %x, double nofpclass(nan) %y) {
+; CHECK-LABEL: @isKnownNeverNaN_atan2_nonan(
+; CHECK-NEXT: ret i1 false
+;
+ %e = call double @llvm.atan2.f64(double %x, double %y)
+ %r = fcmp uno double %e, 0.0
+ ret i1 %r
+}
+
; This should not fold to false because fmul 0 * inf = nan
define i1 @issue63316(i64 %arg) {
; CHECK-LABEL: @issue63316(
``````````
</details>
https://github.com/llvm/llvm-project/pull/190609
More information about the llvm-commits
mailing list