[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