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

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 21 16:55:26 PDT 2026


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

>From 0d9884777065b8c8127ed4cbae38934cc9a4b6ed Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Thu, 13 Aug 2026 20:42:00 -0600
Subject: [PATCH] [KnownFPClass] Add support for non-intrinsic math libcalls

---
 llvm/include/llvm/Support/KnownFPClass.h      |   7 +
 llvm/lib/Analysis/ValueTracking.cpp           |  44 +++
 llvm/lib/Support/KnownFPClass.cpp             |  30 ++
 .../known-fpclass-nonintrinsic-libcall.ll     |  27 ++
 .../Transforms/Attributor/nofpclass-acosh.ll  |  36 +++
 .../Transforms/Attributor/nofpclass-cbrt.ll   | 262 ++++++++++++++++++
 llvm/unittests/Analysis/ValueTrackingTest.cpp |  77 +++++
 7 files changed, 483 insertions(+)
 create mode 100644 llvm/test/Analysis/ValueTracking/known-fpclass-nonintrinsic-libcall.ll
 create mode 100644 llvm/test/Transforms/Attributor/nofpclass-acosh.ll
 create mode 100644 llvm/test/Transforms/Attributor/nofpclass-cbrt.ll

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index dadf4133d826b..3af0d2f1fb53f 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);
 
@@ -448,6 +451,10 @@ struct KnownFPClass {
   static LLVM_ABI KnownFPClass
   sqrt(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
 
+  /// Propagate known class for cbrt
+  LLVM_ABI static 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 55f6696fe4914..280314f017df0 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5765,6 +5765,50 @@ 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);
+        const Function *Caller = II->getFunction();
+        const fltSemantics &FltSem =
+            Op->getType()->getScalarType()->getFltSemantics();
+        DenormalMode Mode =
+            Caller ? Caller->getDenormalMode(FltSem)
+                   : DenormalMode::getDynamic();
+        Known = KnownFPClass::cbrt(KnownSrc, Mode);
+        break;
+      }
+      default:
+        break;
+      }
+
+      break;
+    }
     default:
       break;
     }
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index ac9406ecd0280..397c8f0977ced 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -583,6 +583,26 @@ KnownFPClass KnownFPClass::sqrt(const KnownFPClass &KnownSrc,
   return Known;
 }
 
+KnownFPClass KnownFPClass::cbrt(const KnownFPClass &KnownSrc,
+                                DenormalMode Mode) {
+  KnownFPClass Known;
+
+  Known.propagateNonNaN(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;
 
@@ -650,6 +670,16 @@ KnownFPClass KnownFPClass::tanh(const KnownFPClass &KnownSrc) {
   return Known;
 }
 
+KnownFPClass KnownFPClass::acosh(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  Known.knownNot(fcNegative);
+
+  Known.propagateNonSNaN(KnownSrc);
+
+  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..f8145adfc363f
--- /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 pzero sub) float @ret_cbrt_negative(
+; CHECK-SAME: float nofpclass(nan pinf pzero psub pnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan pzero 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 pzero sub) float @ret_cbrt_negfinite(
+; CHECK-SAME: float nofpclass(nan inf pzero psub pnorm) [[ARG:%.*]]) {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan pzero 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 zero 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 zero 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 zero sub) float @ret_cbrt_ieee_nsub(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan zero 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 zero sub) float @ret_cbrt_ieee_sub(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan zero 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 pzero sub) float @ret_cbrt_daz_nsub(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan pzero 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 nzero sub) float @ret_cbrt_dapz_sub(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[CBRT:%.*]] = call nofpclass(nan nzero 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->getDataLayout()).KnownFPClasses);
+  EXPECT_EQ(fcAllFlags,
+            computeKnownFPClass(A2, M->getDataLayout(), fcAllFlags, &TLI)
+                .KnownFPClasses);
+}
+
+TEST_F(ComputeKnownFPClassTest, CbrtLibCallLocalDefinition) {
+  parseAssembly(R"(
+    define internal float @cbrtf(float %arg) {
+      ret float %arg
+    }
+
+    define float @test(float %arg) {
+      %A = call float @cbrtf(float %arg)
+      ret float %A
+    })");
+
+  TargetLibraryInfoImpl TLII(M->getTargetTriple());
+  TargetLibraryInfo TLI(TLII);
+
+  EXPECT_EQ(fcAllFlags,
+            computeKnownFPClass(A, M->getDataLayout(), fcAllFlags, &TLI)
+                .KnownFPClasses);
+}
+
+TEST_F(ComputeKnownFPClassTest, CbrtLibCallInvalidPrototype) {
+  parseAssembly(R"(
+    declare double @cbrtf(double)
+
+    define double @test(double %arg) {
+      %A = call double @cbrtf(double %arg)
+      ret double %A
+    })");
+
+  TargetLibraryInfoImpl TLII(M->getTargetTriple());
+  TargetLibraryInfo TLI(TLII);
+
+  EXPECT_EQ(fcAllFlags,
+            computeKnownFPClass(A, M->getDataLayout(), fcAllFlags, &TLI)
+                .KnownFPClasses);
+}
+
 TEST_F(ComputeKnownFPClassTest, Constants) {
   parseAssembly("declare float @func()\n"
                 "define float @test() {\n"



More information about the llvm-commits mailing list