[llvm] [KnownFPClass] Add support for non-intrinsic math libcalls (PR #216234)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 13 19:59:46 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: ZERICO2005

<details>
<summary>Changes</summary>

This PR adds support for these non-intrinsic libcall functions, such as `acosh` and `cbrt` (since `G_FACOSH` and `G_FCBRT` do not exist).

I have added two functions just so we can see if this PR is going in the right direction with its implementation:
- `acosh` to represent functions where a non-`NaN` input may return `NaN` (Such as `acosh(0.0)`).
- `cbrt` to represent functions that use the `DenormalMode` to make deductions.

Currently, I do not pass the `DenormalMode` into `cbrt`.

***

Future work:
Implement the following C99 `<math.h>` functions for `KnownFPClass`: `expm1`, `log1p`/`logp1`, `cbrt`, `hypot`, `asinh`, `acosh`, atanh`, `erf`, `erfc`, `tgamma`, `lgamma`.

---

Patch is 21.24 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/216234.diff


7 Files Affected:

- (modified) llvm/include/llvm/Support/KnownFPClass.h (+7) 
- (modified) llvm/lib/Analysis/ValueTracking.cpp (+38) 
- (modified) llvm/lib/Support/KnownFPClass.cpp (+31) 
- (added) llvm/test/Analysis/ValueTracking/known-fpclass-nonintrinsic-libcall.ll (+27) 
- (added) llvm/test/Transforms/Attributor/nofpclass-acosh.ll (+36) 
- (added) llvm/test/Transforms/Attributor/nofpclass-cbrt.ll (+262) 
- (modified) llvm/unittests/Analysis/ValueTrackingTest.cpp (+77) 


``````````diff
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 956141b9449a2..2187c562f8117 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -323,6 +323,9 @@ struct KnownFPClass {
   /// Report known values for tanh
   LLVM_ABI static KnownFPClass tanh(const KnownFPClass &Src);
 
+  /// Report known values for acosh
+  LLVM_ABI static KnownFPClass acosh(const KnownFPClass &Src);
+
   /// Report known values for asin
   LLVM_ABI static KnownFPClass asin(const KnownFPClass &Src);
 
@@ -430,6 +433,10 @@ struct KnownFPClass {
   static LLVM_ABI KnownFPClass
   sqrt(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
 
+  /// Propagate known class for cbrt
+  static LLVM_ABI KnownFPClass
+  cbrt(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
+
   /// Propagate known class for fpext.
   static LLVM_ABI KnownFPClass fpext(const KnownFPClass &KnownSrc,
                                      const fltSemantics &DstTy,
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 28d47d8f7df27..18ac7d9e5d649 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5766,6 +5766,44 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
         Known.knownNot(fcSubnormal);
       break;
     }
+    case Intrinsic::not_intrinsic: {
+      // Recognize the libcall directly for functions with no intrinsic
+      // equivalent. TLI validates the declaration's prototype and rejects calls
+      // marked 'nobuiltin'. Do not recognize locally-defined functions.
+      if (!Q.TLI)
+        break;
+      const Function *F = II->getCalledFunction();
+      if (!F || F->hasLocalLinkage())
+        break;
+      LibFunc Func;
+      if (!Q.TLI->getLibFunc(*II, Func))
+        break;
+
+      switch (Func) {
+      case LibFunc_acosh:
+      case LibFunc_acoshf:
+      case LibFunc_acoshl: {
+        KnownFPClass KnownSrc;
+        computeKnownFPClass(II->getArgOperand(0), DemandedElts,
+                            InterestedClasses, KnownSrc, Q, Depth + 1);
+        Known = KnownFPClass::acosh(KnownSrc);
+        break;
+      }
+      case LibFunc_cbrt:
+      case LibFunc_cbrtf:
+      case LibFunc_cbrtl: {
+        KnownFPClass KnownSrc;
+        computeKnownFPClass(II->getArgOperand(0), DemandedElts,
+                            InterestedClasses, KnownSrc, Q, Depth + 1);
+        Known = KnownFPClass::cbrt(KnownSrc);
+        break;
+      }
+      default:
+        break;
+      }
+
+      break;
+    }
     default:
       break;
     }
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 0d72e2317ef6f..f18cbeca2df8e 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -571,6 +571,26 @@ KnownFPClass KnownFPClass::sqrt(const KnownFPClass &KnownSrc,
   return Known;
 }
 
+KnownFPClass KnownFPClass::cbrt(const KnownFPClass &KnownSrc,
+                                DenormalMode Mode) {
+  KnownFPClass Known;
+
+  Known.propagateNaN(KnownSrc);
+
+  // cbrt is never subnormal for the same reason that sqrt is never subnormal.
+  Known.knownNot(fcSubnormal);
+
+  // cbrt(x) == +0.0 iff x == +0.0
+  if (KnownSrc.isKnownNeverLogicalPosZero(Mode))
+    Known.knownNot(fcPosZero);
+
+  // cbrt(x) == -0.0 iff x == -0.0
+  if (KnownSrc.isKnownNeverLogicalNegZero(Mode))
+    Known.knownNot(fcNegZero);
+
+  return Known;
+}
+
 KnownFPClass KnownFPClass::sin(const KnownFPClass &KnownSrc) {
   KnownFPClass Known;
 
@@ -638,6 +658,17 @@ KnownFPClass KnownFPClass::tanh(const KnownFPClass &KnownSrc) {
   return Known;
 }
 
+KnownFPClass KnownFPClass::acosh(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  Known.knownNot(fcNegative);
+
+  if (KnownSrc.isKnownNever(fcSNan))
+    Known.knownNot(fcSNan);
+
+  return Known;
+}
+
 KnownFPClass KnownFPClass::asin(const KnownFPClass &KnownSrc) {
   KnownFPClass Known;
 
diff --git a/llvm/test/Analysis/ValueTracking/known-fpclass-nonintrinsic-libcall.ll b/llvm/test/Analysis/ValueTracking/known-fpclass-nonintrinsic-libcall.ll
new file mode 100644
index 0000000000000..d22ffdbe29cc5
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/known-fpclass-nonintrinsic-libcall.ll
@@ -0,0 +1,27 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=instsimplify < %s -S | FileCheck %s
+
+declare float @acoshf(float)
+declare float @cbrtf(float)
+
+define i1 @acosh_nonnegative(float %x) {
+; CHECK-LABEL: define i1 @acosh_nonnegative(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[ACOSH:%.*]] = call float @acoshf(float [[X]])
+; CHECK-NEXT:    ret i1 true
+;
+  %acosh = call float @acoshf(float %x)
+  %cmp = fcmp uge float %acosh, 0.0
+  ret i1 %cmp
+}
+
+define i1 @cbrt_nonnan(float nofpclass(nan) %x) {
+; CHECK-LABEL: define i1 @cbrt_nonnan(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]]) {
+; CHECK-NEXT:    [[CBRT:%.*]] = call float @cbrtf(float [[X]])
+; CHECK-NEXT:    ret i1 false
+;
+  %cbrt = call float @cbrtf(float %x)
+  %cmp = fcmp uno float %cbrt, 0.0
+  ret i1 %cmp
+}
diff --git a/llvm/test/Transforms/Attributor/nofpclass-acosh.ll b/llvm/test/Transforms/Attributor/nofpclass-acosh.ll
new file mode 100644
index 0000000000000..b000283b4e2d3
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-acosh.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s
+
+declare float @acoshf(float)
+
+; acosh is never negative.
+define float @ret_acosh(float %arg) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_acosh(
+; CHECK-SAME: float [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @acoshf(float [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @acoshf(float %arg)
+  ret float %call
+}
+
+; A non-NaN input below 1 can still produce a quiet NaN.
+define float @ret_acosh_nonnan(float nofpclass(nan) %arg) {
+; CHECK-LABEL: define nofpclass(snan ninf nzero nsub nnorm) float @ret_acosh_nonnan(
+; CHECK-SAME: float nofpclass(nan) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan ninf nzero nsub nnorm) float @acoshf(float nofpclass(nan) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @acoshf(float %arg)
+  ret float %call
+}
+
+define float @ret_acosh_nonsnan(float nofpclass(snan) %arg) {
+; CHECK-LABEL: define nofpclass(snan ninf nzero nsub nnorm) float @ret_acosh_nonsnan(
+; CHECK-SAME: float nofpclass(snan) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan ninf nzero nsub nnorm) float @acoshf(float nofpclass(snan) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @acoshf(float %arg)
+  ret float %call
+}
diff --git a/llvm/test/Transforms/Attributor/nofpclass-cbrt.ll b/llvm/test/Transforms/Attributor/nofpclass-cbrt.ll
new file mode 100644
index 0000000000000..49a09944e5fbf
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-cbrt.ll
@@ -0,0 +1,262 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s
+
+declare float @cbrtf(float)
+
+define float @ret_cbrt(float %arg) {
+; CHECK-LABEL: define nofpclass(sub) float @ret_cbrt(
+; CHECK-SAME: float [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @cbrtf(float [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_nonnan(float nofpclass(nan) %arg) {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_nonnan(
+; CHECK-SAME: float nofpclass(nan) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_nonqnan(float nofpclass(qnan) %arg) {
+; CHECK-LABEL: define nofpclass(sub) float @ret_cbrt_nonqnan(
+; CHECK-SAME: float nofpclass(qnan) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @cbrtf(float nofpclass(qnan) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_nonsnan(float nofpclass(snan) %arg) {
+; CHECK-LABEL: define nofpclass(snan sub) float @ret_cbrt_nonsnan(
+; CHECK-SAME: float nofpclass(snan) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @cbrtf(float nofpclass(snan) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_positive(float nofpclass(nan ninf nzero nsub nnorm) %arg) {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_positive(
+; CHECK-SAME: float nofpclass(nan ninf nzero nsub nnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan ninf nzero nsub nnorm) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_negative(float nofpclass(nan pinf pzero psub pnorm) %arg) {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_negative(
+; CHECK-SAME: float nofpclass(nan pinf pzero psub pnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan pinf pzero psub pnorm) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_normal(float nofpclass(nan inf sub zero) %arg) {
+; CHECK-LABEL: define nofpclass(nan zero sub) float @ret_cbrt_normal(
+; CHECK-SAME: float nofpclass(nan inf zero sub) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan zero sub) float @cbrtf(float nofpclass(nan inf zero sub) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_posnormal(float nofpclass(nan ninf nzero sub nnorm) %arg) {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_posnormal(
+; CHECK-SAME: float nofpclass(nan ninf nzero sub nnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan ninf nzero sub nnorm) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_negnormal(float nofpclass(nan pinf pzero sub pnorm) %arg) {
+; CHECK-LABEL: define nofpclass(nan pzero sub) float @ret_cbrt_negnormal(
+; CHECK-SAME: float nofpclass(nan pinf pzero sub pnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan pzero sub) float @cbrtf(float nofpclass(nan pinf pzero sub pnorm) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_finite(float nofpclass(nan inf) %arg) {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_finite(
+; CHECK-SAME: float nofpclass(nan inf) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_posfinite(float nofpclass(nan inf nzero nsub nnorm) %arg) {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_posfinite(
+; CHECK-SAME: float nofpclass(nan inf nzero nsub nnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan inf nzero nsub nnorm) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+define float @ret_cbrt_negfinite(float nofpclass(nan inf pzero psub pnorm) %arg) {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_negfinite(
+; CHECK-SAME: float nofpclass(nan inf pzero psub pnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf pzero psub pnorm) [[ARG]])
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @cbrtf(float %arg)
+  ret float %call
+}
+
+; cbrt cannot produce a subnormal result, so only the input denormal mode
+; affects the following tests.
+
+define float @ret_cbrt_ieee_psub(float nofpclass(nan inf zero nsub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_ieee_psub(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan inf zero nsub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_ieee_nsub(float nofpclass(nan inf zero psub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_ieee_nsub(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero psub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_ieee_sub(float nofpclass(nan inf zero norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_ieee_sub(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_ieee_pzero(float nofpclass(nan inf nzero sub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_ieee_pzero(
+; CHECK-SAME: float nofpclass(nan inf nzero sub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan inf nzero sub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_ieee_nzero(float nofpclass(nan inf pzero sub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan pzero sub) float @ret_cbrt_ieee_nzero(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan pzero sub) float @cbrtf(float nofpclass(nan inf pzero sub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_ieee_zero(float nofpclass(nan inf sub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_ieee_zero(
+; CHECK-SAME: float nofpclass(nan inf sub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf sub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_daz_psub(float nofpclass(nan inf zero nsub norm) %arg) #1 {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_daz_psub(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[ARG:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan inf zero nsub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_daz_nsub(float nofpclass(nan inf zero psub norm) %arg) #1 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_daz_nsub(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero psub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_daz_sub(float nofpclass(nan inf zero norm) %arg) #1 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_daz_sub(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_dapz_sub(float nofpclass(nan inf zero norm) %arg) #2 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_dapz_sub(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_dynamic_psub(float nofpclass(nan inf zero nsub norm) %arg) #3 {
+; CHECK-LABEL: define nofpclass(nan nzero sub) float @ret_cbrt_dynamic_psub(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[ARG:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan nzero sub) float @cbrtf(float nofpclass(nan inf zero nsub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_dynamic_nsub(float nofpclass(nan inf zero psub norm) %arg) #3 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_dynamic_nsub(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero psub norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+define float @ret_cbrt_dynamic_sub(float nofpclass(nan inf zero norm) %arg) #3 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_cbrt_dynamic_sub(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan sub) float @cbrtf(float nofpclass(nan inf zero norm) [[ARG]])
+; CHECK-NEXT:    ret float [[CBRT]]
+;
+  %cbrt = call float @cbrtf(float %arg)
+  ret float %cbrt
+}
+
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 382cf0f2b57ed..267a7d3a7b5b9 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -10,6 +10,7 @@
 #include "llvm/ADT/FloatingPointMode.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/FloatingPointPredicateUtils.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Dominators.h"
@@ -2380,6 +2381,82 @@ TEST_F(ComputeKnownFPClassTest, SqrtNszSignBit) {
   }
 }
 
+TEST_F(ComputeKnownFPClassTest, CbrtLibCall) {
+  parseAssembly(R"(
+    declare float @cbrtf(float)
+
+    define float @test(float nofpclass(nan) %arg) {
+      %A = call float @cbrtf(float %arg)
+      ret float %A
+    })");
+
+  TargetLibraryInfoImpl TLII(M->getTargetTriple());
+  TargetLibraryInfo TLI(TLII);
+
+  EXPECT_EQ(fcInf | fcNormal | fcZero,
+            computeKnownFPClass(A, M->getDataLayout(), fcAllFlags, &TLI)
+                .KnownFPClasses);
+}
+
+TEST_F(ComputeKnownFPClassTest, CbrtLibCallRequiresTLIAndBuiltin) {
+  parseAssembly(R"(
+    declare float @cbrtf(float)
+
+    define float @test(float %arg) {
+      %A = call float @cbrtf(float %arg)
+      %A2 = call float @cbrtf(float %arg) #0
+      ret float %A2
+    }
+
+    attributes #0 = { nobuiltin }
+    )");
+
+  TargetLibraryInfoImpl TLII(M->getTargetTriple());
+  TargetLibraryInfo TLI(TLII);
+
+  EXPECT_EQ(fcAllFlags,
+            computeKnownFPClass(A, M->getDat...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/216234


More information about the llvm-commits mailing list