[llvm] [KnownFPClass] Add KnownFPClass deductions for `pow` (PR #215592)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 22:20:20 PDT 2026


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

>From 3104be008bdb532e4838e8843c5873cc15168858 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Mon, 10 Aug 2026 23:30:26 -0600
Subject: [PATCH] [KnownFPClass] Add KnownFPClass deductions for pow

---
 llvm/include/llvm/Support/KnownFPClass.h      |   4 +
 llvm/lib/Analysis/ValueTracking.cpp           |  56 +++++
 .../CodeGen/GlobalISel/GISelValueTracking.cpp |  56 +++++
 llvm/lib/Support/KnownFPClass.cpp             |  42 ++++
 .../Transforms/Attributor/nofpclass-pow.ll    | 226 ++++++++++++++++++
 .../simplify-demanded-fpclass-pow.ll          |  65 +++++
 llvm/test/Transforms/InstSimplify/pow.ll      | 101 ++++++++
 llvm/unittests/Analysis/ValueTrackingTest.cpp |  14 ++
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   | 106 ++++++++
 9 files changed, 670 insertions(+)
 create mode 100644 llvm/test/Transforms/Attributor/nofpclass-pow.ll
 create mode 100644 llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-pow.ll
 create mode 100644 llvm/test/Transforms/InstSimplify/pow.ll

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index dadf4133d826b..389128969ac4c 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -479,6 +479,10 @@ struct KnownFPClass {
       const KnownFPClass &Src, const KnownBits &ExpBits,
       const fltSemantics &Flt, DenormalMode Mode = DenormalMode::getDynamic());
 
+  /// Propagate known class for pow
+  LLVM_ABI static KnownFPClass pow(const KnownFPClass &LHS,
+                                   const KnownFPClass &RHS);
+
   /// Propagate known class for powi
   static LLVM_ABI KnownFPClass powi(const KnownFPClass &Src,
                                     const KnownBits &N);
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 55f6696fe4914..d297fe8a8895c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5562,6 +5562,62 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
 
       break;
     }
+    case Intrinsic::pow: {
+      const bool WantNaN = (InterestedClasses & fcNan) != fcNone;
+      const bool WantNegNonZeroFinite =
+          (InterestedClasses & (fcNegNormal | fcNegSubnormal)) != fcNone;
+      const bool WantNegZero = (InterestedClasses & fcNegZero) != fcNone;
+      const bool WantNegInf = (InterestedClasses & fcNegInf) != fcNone;
+      if (!WantNaN && !WantNegNonZeroFinite && !WantNegZero && !WantNegInf)
+        break;
+
+      FPClassTest InterestedLHS = fcNone;
+      FPClassTest InterestedRHS = fcNone;
+      if (WantNaN) {
+        // pow may return NaN if one of the arguments is NaN. NaN may also be
+        // produced from a negative, non-zero finite base and a non-integer
+        // exponent.
+        InterestedLHS |= fcNan | fcNegNormal | fcNegSubnormal;
+        InterestedRHS |= fcNan;
+      }
+      if (WantNegNonZeroFinite)
+        InterestedLHS |= fcNegNormal | fcNegSubnormal;
+      if (WantNegZero || WantNegInf)
+        InterestedLHS |= fcNegative;
+
+      KnownFPClass KnownLHS;
+      computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedLHS,
+                          KnownLHS, Q, Depth + 1);
+
+      // If the LHS is unknown, querying the RHS is only useful for rare edge
+      // cases.
+      if (KnownLHS.isUnknown())
+        break;
+
+      // If only one negative special value remains possible for the LHS,
+      // request only the sign of normal RHS needed for each interested result.
+      if (!KnownLHS.isKnownNever(fcNegative) &&
+          KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal)) {
+        if (KnownLHS.isKnownNever(fcNegInf)) {
+          if (WantNegZero)
+            InterestedRHS |= fcPosNormal;
+          if (WantNegInf)
+            InterestedRHS |= fcNegNormal;
+        } else if (KnownLHS.isKnownNever(fcNegZero)) {
+          if (WantNegZero)
+            InterestedRHS |= fcNegNormal;
+          if (WantNegInf)
+            InterestedRHS |= fcPosNormal;
+        }
+      }
+
+      KnownFPClass KnownRHS;
+      if (InterestedRHS != fcNone)
+        computeKnownFPClass(II->getArgOperand(1), DemandedElts, InterestedRHS,
+                            KnownRHS, Q, Depth + 1);
+      Known = KnownFPClass::pow(KnownLHS, KnownRHS);
+      break;
+    }
     case Intrinsic::powi: {
       if ((InterestedClasses & (fcNan | fcInf | fcNegative)) == fcNone)
         break;
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index f5b8cb3a0fbd7..2f5188d479007 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -1630,6 +1630,61 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     Known = KnownFPClass::log(KnownSrc, Mode);
     break;
   }
+  case TargetOpcode::G_FPOW: {
+    const bool WantNaN = (InterestedClasses & fcNan) != fcNone;
+    const bool WantNegNonZeroFinite =
+        (InterestedClasses & (fcNegNormal | fcNegSubnormal)) != fcNone;
+    const bool WantNegZero = (InterestedClasses & fcNegZero) != fcNone;
+    const bool WantNegInf = (InterestedClasses & fcNegInf) != fcNone;
+    if (!WantNaN && !WantNegNonZeroFinite && !WantNegZero && !WantNegInf)
+      break;
+
+    FPClassTest InterestedLHS = fcNone;
+    FPClassTest InterestedRHS = fcNone;
+    if (WantNaN) {
+      // pow may return NaN if one of the arguments is NaN. NaN may be produced
+      // from a non-zero-finite-negative base and a non-integer exponent.
+      InterestedLHS |= fcNan | fcNegNormal | fcNegSubnormal;
+      InterestedRHS |= fcNan;
+    }
+    if (WantNegNonZeroFinite)
+      InterestedLHS |= fcNegNormal | fcNegSubnormal;
+    if (WantNegZero || WantNegInf)
+      InterestedLHS |= fcNegative;
+
+    KnownFPClass KnownLHS;
+    computeKnownFPClass(MI.getOperand(1).getReg(), DemandedElts, InterestedLHS,
+                        KnownLHS, Depth + 1);
+
+    // If the LHS is unknown, querying the RHS is only useful for rare edge
+    // cases.
+    if (KnownLHS.isUnknown())
+      break;
+
+    // If only one negative special value remains possible for the LHS,
+    // request only the sign of normal RHS needed for each interested result.
+    if (!KnownLHS.isKnownNever(fcNegative) &&
+        KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal)) {
+      if (KnownLHS.isKnownNever(fcNegInf)) {
+        if (WantNegZero)
+          InterestedRHS |= fcPosNormal;
+        if (WantNegInf)
+          InterestedRHS |= fcNegNormal;
+      } else if (KnownLHS.isKnownNever(fcNegZero)) {
+        if (WantNegZero)
+          InterestedRHS |= fcNegNormal;
+        if (WantNegInf)
+          InterestedRHS |= fcPosNormal;
+      }
+    }
+
+    KnownFPClass KnownRHS;
+    if (InterestedRHS != fcNone)
+      computeKnownFPClass(MI.getOperand(2).getReg(), DemandedElts,
+                          InterestedRHS, KnownRHS, Depth + 1);
+    Known = KnownFPClass::pow(KnownLHS, KnownRHS);
+    break;
+  }
   case TargetOpcode::G_FPOWI: {
     if ((InterestedClasses & (fcNan | fcInf | fcNegative)) == fcNone)
       break;
@@ -2219,6 +2274,7 @@ bool GISelValueTracking::isKnownNeverNaN(Register Val, bool SNaN) {
     case TargetOpcode::G_FLOG:
     case TargetOpcode::G_FLOG2:
     case TargetOpcode::G_FLOG10:
+    case TargetOpcode::G_FPOW:
     case TargetOpcode::G_FPOWI:
     case TargetOpcode::G_FLDEXP:
     case TargetOpcode::G_STRICT_FLDEXP:
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index ac9406ecd0280..b581cea83a322 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -853,6 +853,48 @@ KnownFPClass KnownFPClass::ldexp(const KnownFPClass &KnownSrc,
                ExpBits.getSignedMaxValue(), Flt, Mode);
 }
 
+KnownFPClass KnownFPClass::pow(const KnownFPClass &KnownLHS,
+                               const KnownFPClass &KnownRHS) {
+  KnownFPClass Known;
+
+  Known.propagateNonSNaN(KnownLHS, KnownRHS);
+
+  // pow may return NaN if one of the arguments is NaN. NaN may be produced from
+  // a non-zero-finite-negative base and a non-integer exponent.
+  if (KnownLHS.isKnownNever(fcNan | fcNegNormal | fcNegSubnormal) &&
+      KnownRHS.isKnownNeverNaN())
+    Known.knownNot(fcNan);
+
+  // We could rule out negative and subnormal results when exponent is known to
+  // never be a normal value, but having either argument being known to never be
+  // normal is unlikely and not worth considering.
+
+  // Only a negative base raised to an odd power returns a negative value.
+  if (KnownLHS.isKnownNever(fcNegative)) {
+    Known.knownNot(fcNegative);
+  } else if (KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal)) {
+    Known.knownNot(fcNegNormal | fcNegSubnormal);
+    // See if we can also rule out -0.0 or -inf.
+    // Here at least one of -0.0 or -inf is a possible base.
+
+    // pow(-0.0, odd-positive) = -0.0
+    // pow(-inf, odd-negative) = -0.0
+    if ((KnownLHS.isKnownNever(fcNegZero) ||
+         KnownRHS.isKnownNever(fcPosNormal)) &&
+        (KnownLHS.isKnownNever(fcNegInf) || KnownRHS.isKnownNever(fcNegNormal)))
+      Known.knownNot(fcNegZero);
+
+    // pow(-0.0, odd-negative) = -inf
+    // pow(-inf, odd-positive) = -inf
+    if ((KnownLHS.isKnownNever(fcNegZero) ||
+         KnownRHS.isKnownNever(fcNegNormal)) &&
+        (KnownLHS.isKnownNever(fcNegInf) || KnownRHS.isKnownNever(fcPosNormal)))
+      Known.knownNot(fcNegInf);
+  }
+
+  return Known;
+}
+
 KnownFPClass KnownFPClass::powi(const KnownFPClass &KnownSrc,
                                 const KnownBits &ExponentKnownBits) {
   KnownFPClass Known;
diff --git a/llvm/test/Transforms/Attributor/nofpclass-pow.ll b/llvm/test/Transforms/Attributor/nofpclass-pow.ll
new file mode 100644
index 0000000000000..395e0cd5131e9
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-pow.ll
@@ -0,0 +1,226 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal < %s | FileCheck %s
+
+declare float @llvm.pow.f32(float, float)
+declare <2 x float> @llvm.pow.v2f32(<2 x float>, <2 x float>)
+declare float @llvm.fabs.f32(float)
+
+define float @ret_pow_nonneg_base(float nofpclass(ninf nzero nsub nnorm) %base, float %exp) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_pow_nonneg_base(
+; CHECK-SAME: float nofpclass(ninf nzero nsub nnorm) [[BASE:%.*]], float [[EXP:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(ninf nzero nsub nnorm) [[BASE]], float [[EXP]]) #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+; It is rare that the exponent is known never normal, so we will not go out of
+; our way to perform this deduction.
+define float @ret_pow_nonnormal_exponent(float %base, float nofpclass(norm) %exp) {
+; CHECK-LABEL: define float @ret_pow_nonnormal_exponent(
+; CHECK-SAME: float [[BASE:%.*]], float nofpclass(norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call float @llvm.pow.f32(float [[BASE]], float nofpclass(norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_nonneg_nonan(float nofpclass(nan ninf nzero nsub nnorm) %base, float nofpclass(nan) %exp) {
+; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float @ret_pow_nonneg_nonan(
+; CHECK-SAME: float nofpclass(nan ninf nzero nsub nnorm) [[BASE:%.*]], float nofpclass(nan) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan ninf nzero nsub nnorm) [[BASE]], float nofpclass(nan) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_base_no_nsub_nnorm_exponent_any(float nofpclass(nsub nnorm) %base, float %exp) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_pow_base_no_nsub_nnorm_exponent_any(
+; CHECK-SAME: float nofpclass(nsub nnorm) [[BASE:%.*]], float [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nsub nnorm) float @llvm.pow.f32(float nofpclass(nsub nnorm) [[BASE]], float [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_base_no_nan_nsub_nnorm_exponent_no_nan(float nofpclass(nan nsub nnorm) %base, float nofpclass(nan) %exp) {
+; CHECK-LABEL: define nofpclass(nan nsub nnorm) float @ret_pow_base_no_nan_nsub_nnorm_exponent_no_nan(
+; CHECK-SAME: float nofpclass(nan nsub nnorm) [[BASE:%.*]], float nofpclass(nan) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nan nsub nnorm) float @llvm.pow.f32(float nofpclass(nan nsub nnorm) [[BASE]], float nofpclass(nan) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+; Attributor also requests NaN information, so ValueTracking queries the RHS
+; after obtaining useful LHS information. The RHS's nofpclass(norm) attribute
+; then rules out -0.0 and -Inf as well. A negative-only query would not inspect
+; the RHS.
+define float @ret_pow_base_no_nsub_nnorm_exponent_no_norm(float nofpclass(nsub nnorm) %base, float nofpclass(norm) %exp) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_pow_base_no_nsub_nnorm_exponent_no_norm(
+; CHECK-SAME: float nofpclass(nsub nnorm) [[BASE:%.*]], float nofpclass(norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(ninf nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nsub nnorm) [[BASE]], float nofpclass(norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_base_no_nan_nsub_nnorm_exponent_no_nan_norm(float nofpclass(nan nsub nnorm) %base, float nofpclass(nan norm) %exp) {
+; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float @ret_pow_base_no_nan_nsub_nnorm_exponent_no_nan_norm(
+; CHECK-SAME: float nofpclass(nan nsub nnorm) [[BASE:%.*]], float nofpclass(nan norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan nsub nnorm) [[BASE]], float nofpclass(nan norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+; qNaN may be produced when a negative base is raised to a non-integer exponent.
+define float @ret_pow_neg_base_nonan(float nofpclass(nan inf zero sub pnorm) %base, float nofpclass(nan) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_neg_base_nonan(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[BASE:%.*]], float nofpclass(nan) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan inf zero sub pnorm) [[BASE]], float nofpclass(nan) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_non_snan_inputs(float nofpclass(snan) %base, float nofpclass(snan) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_non_snan_inputs(
+; CHECK-SAME: float nofpclass(snan) [[BASE:%.*]], float nofpclass(snan) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(snan) [[BASE]], float nofpclass(snan) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_subnormal_posnormal(float nofpclass(nan inf norm zero) %base, float nofpclass(nan inf sub nnorm zero) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_subnormal_posnormal(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[BASE:%.*]], float nofpclass(nan inf zero sub nnorm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan inf zero norm) [[BASE]], float nofpclass(nan inf zero sub nnorm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_subnormal_possubnormal(float nofpclass(nan inf norm zero) %base, float nofpclass(nan inf nsub norm zero) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_subnormal_possubnormal(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[BASE:%.*]], float nofpclass(nan inf zero nsub norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan inf zero norm) [[BASE]], float nofpclass(nan inf zero nsub norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_subnormal_negsubnormal(float nofpclass(nan inf norm zero) %base, float nofpclass(nan inf psub norm zero) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_subnormal_negsubnormal(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[BASE:%.*]], float nofpclass(nan inf zero psub norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan inf zero norm) [[BASE]], float nofpclass(nan inf zero psub norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_subnormal_negnormal(float nofpclass(nan inf norm zero) %base, float nofpclass(nan inf sub pnorm zero) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_subnormal_negnormal(
+; CHECK-SAME: float nofpclass(nan inf zero norm) [[BASE:%.*]], float nofpclass(nan inf zero sub pnorm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan inf zero norm) [[BASE]], float nofpclass(nan inf zero sub pnorm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+; Negative subnormal exponents cannot be odd integers.
+define float @ret_pow_neg_negsubnormal(float nofpclass(nan pinf psub pnorm pzero) %base, float nofpclass(nan inf psub norm zero) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_neg_negsubnormal(
+; CHECK-SAME: float nofpclass(nan pinf pzero psub pnorm) [[BASE:%.*]], float nofpclass(nan inf zero psub norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan pinf pzero psub pnorm) [[BASE]], float nofpclass(nan inf zero psub norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_negnormalsubnormal_negsubnormal(float nofpclass(nan inf psub pnorm zero) %base, float nofpclass(nan inf psub norm zero) %exp) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_pow_negnormalsubnormal_negsubnormal(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[BASE:%.*]], float nofpclass(nan inf zero psub norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(snan) float @llvm.pow.f32(float nofpclass(nan inf zero psub pnorm) [[BASE]], float nofpclass(nan inf zero psub norm) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  ret float %pow
+}
+
+define float @ret_pow_negzero_negative(float nofpclass(nan inf pzero sub norm) %base, float %exp) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_negzero_negative(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POS:%.*]] = call float @llvm.fabs.f32(float [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[POS]]
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float [[NEG]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pos = call float @llvm.fabs.f32(float %exp)
+  %neg = fneg float %pos
+  %pow = call float @llvm.pow.f32(float %base, float %neg)
+  ret float %pow
+}
+
+define float @ret_pow_negzero_positive(float nofpclass(nan inf pzero sub norm) %base, float %exp) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_negzero_positive(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POS:%.*]] = call float @llvm.fabs.f32(float [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float [[POS]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pos = call float @llvm.fabs.f32(float %exp)
+  %pow = call float @llvm.pow.f32(float %base, float %pos)
+  ret float %pow
+}
+
+define float @ret_pow_neginf_positive(float nofpclass(nan pinf zero sub norm) %base, float %exp) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_neginf_positive(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POS:%.*]] = call float @llvm.fabs.f32(float [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float [[POS]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pos = call float @llvm.fabs.f32(float %exp)
+  %pow = call float @llvm.pow.f32(float %base, float %pos)
+  ret float %pow
+}
+
+define float @ret_pow_neginf_negative(float nofpclass(nan pinf zero sub norm) %base, float %exp) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_neginf_negative(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POS:%.*]] = call float @llvm.fabs.f32(float [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[POS]]
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float [[NEG]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %pos = call float @llvm.fabs.f32(float %exp)
+  %neg = fneg float %pos
+  %pow = call float @llvm.pow.f32(float %base, float %neg)
+  ret float %pow
+}
+
+define <2 x float> @ret_pow_nonneg_nonan_v2f32(<2 x float> nofpclass(nan ninf nzero nsub nnorm) %base, <2 x float> nofpclass(nan) %exp) {
+; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) <2 x float> @ret_pow_nonneg_nonan_v2f32(
+; CHECK-SAME: <2 x float> nofpclass(nan ninf nzero nsub nnorm) [[BASE:%.*]], <2 x float> nofpclass(nan) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) <2 x float> @llvm.pow.v2f32(<2 x float> nofpclass(nan ninf nzero nsub nnorm) [[BASE]], <2 x float> nofpclass(nan) [[EXP]]) #[[ATTR2]]
+; CHECK-NEXT:    ret <2 x float> [[POW]]
+;
+  %pow = call <2 x float> @llvm.pow.v2f32(<2 x float> %base, <2 x float> %exp)
+  ret <2 x float> %pow
+}
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-pow.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-pow.ll
new file mode 100644
index 0000000000000..4d34a2d99bd2c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-pow.ll
@@ -0,0 +1,65 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+declare float @llvm.fabs.f32(float)
+declare float @llvm.pow.f32(float, float)
+declare i1 @llvm.is.fpclass.f32(float, i32 immarg)
+
+define i1 @pow_negzero_negative_is_negzero(float %x) strictfp {
+; CHECK-LABEL: define i1 @pow_negzero_negative_is_negzero(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    ret i1 false
+;
+  %pos = call float @llvm.fabs.f32(float %x)
+  %neg = fneg float %pos
+  %pow = call float @llvm.pow.f32(float f0x80000000, float %neg)
+  %class = call i1 @llvm.is.fpclass.f32(float %pow, i32 32) strictfp
+  ret i1 %class
+}
+
+define i1 @pow_negzero_positive_is_neginf(float %x) strictfp {
+; CHECK-LABEL: define i1 @pow_negzero_positive_is_neginf(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    ret i1 false
+;
+  %pos = call float @llvm.fabs.f32(float %x)
+  %pow = call float @llvm.pow.f32(float f0x80000000, float %pos)
+  %class = call i1 @llvm.is.fpclass.f32(float %pow, i32 4) strictfp
+  ret i1 %class
+}
+
+define i1 @pow_neginf_positive_is_negzero(float %x) strictfp {
+; CHECK-LABEL: define i1 @pow_neginf_positive_is_negzero(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    ret i1 false
+;
+  %pos = call float @llvm.fabs.f32(float %x)
+  %pow = call float @llvm.pow.f32(float f0xFF800000, float %pos)
+  %class = call i1 @llvm.is.fpclass.f32(float %pow, i32 32) strictfp
+  ret i1 %class
+}
+
+define i1 @pow_neginf_negative_is_neginf(float %x) strictfp {
+; CHECK-LABEL: define i1 @pow_neginf_negative_is_neginf(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    ret i1 false
+;
+  %pos = call float @llvm.fabs.f32(float %x)
+  %neg = fneg float %pos
+  %pow = call float @llvm.pow.f32(float f0xFF800000, float %neg)
+  %class = call i1 @llvm.is.fpclass.f32(float %pow, i32 4) strictfp
+  ret i1 %class
+}
+
+; This fold is only possible in special cases, and not worth deducing.
+define i1 @pow_any_nonnormal_may_be_subnormal(float %x, float nofpclass(norm) %exp) strictfp {
+; CHECK-LABEL: define i1 @pow_any_nonnormal_may_be_subnormal(
+; CHECK-SAME: float [[X:%.*]], float nofpclass(norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[POW:%.*]] = call float @llvm.pow.f32(float [[X]], float [[EXP]])
+; CHECK-NEXT:    [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[POW]], /* (sub) */ i32 144) #[[ATTR1]]
+; CHECK-NEXT:    ret i1 [[CLASS]]
+;
+  %pow = call float @llvm.pow.f32(float %x, float %exp)
+  %class = call i1 @llvm.is.fpclass.f32(float %pow, i32 144) strictfp
+  ret i1 %class
+}
diff --git a/llvm/test/Transforms/InstSimplify/pow.ll b/llvm/test/Transforms/InstSimplify/pow.ll
new file mode 100644
index 0000000000000..dd000d35b3631
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/pow.ll
@@ -0,0 +1,101 @@
+; 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 @llvm.fabs.f32(float)
+declare float @llvm.pow.f32(float, float)
+declare <2 x float> @llvm.fabs.v2f32(<2 x float>)
+declare <2 x float> @llvm.pow.v2f32(<2 x float>, <2 x float>)
+
+define i1 @pow_fabs_olt_zero(float %base, float %exp) {
+; CHECK-LABEL: define i1 @pow_fabs_olt_zero(
+; CHECK-SAME: float [[BASE:%.*]], float [[EXP:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %abs = call float @llvm.fabs.f32(float %base)
+  %pow = call float @llvm.pow.f32(float %abs, float %exp)
+  %cmp = fcmp olt float %pow, 0.0
+  ret i1 %cmp
+}
+
+define float @fabs_pow_nonneg_base(float nofpclass(nan) %base, float nofpclass(nan) %exp) {
+; CHECK-LABEL: define float @fabs_pow_nonneg_base(
+; CHECK-SAME: float nofpclass(nan) [[BASE:%.*]], float nofpclass(nan) [[EXP:%.*]]) {
+; CHECK-NEXT:    [[ABS_BASE:%.*]] = call float @llvm.fabs.f32(float [[BASE]])
+; CHECK-NEXT:    [[POW:%.*]] = call float @llvm.pow.f32(float [[ABS_BASE]], float [[EXP]])
+; CHECK-NEXT:    ret float [[POW]]
+;
+  %abs.base = call float @llvm.fabs.f32(float %base)
+  %pow = call float @llvm.pow.f32(float %abs.base, float %exp)
+  %abs.pow = call float @llvm.fabs.f32(float %pow)
+  ret float %abs.pow
+}
+
+define i1 @pow_nonnormal_exponent_olt_zero(float %base, float nofpclass(norm) %exp) {
+; CHECK-LABEL: define i1 @pow_nonnormal_exponent_olt_zero(
+; CHECK-SAME: float [[BASE:%.*]], float nofpclass(norm) [[EXP:%.*]]) {
+; CHECK-NEXT:    [[POW:%.*]] = call float @llvm.pow.f32(float [[BASE]], float [[EXP]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[POW]], 0.000000e+00
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  %cmp = fcmp olt float %pow, 0.0
+  ret i1 %cmp
+}
+
+define i1 @isnan_pow_nonneg_or_negzero_base(float nofpclass(nan ninf nsub nnorm) %base, float nofpclass(nan) %exp) {
+; CHECK-LABEL: define i1 @isnan_pow_nonneg_or_negzero_base(
+; CHECK-SAME: float nofpclass(nan ninf nsub nnorm) [[BASE:%.*]], float nofpclass(nan) [[EXP:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  %isnan = fcmp uno float %pow, %pow
+  ret i1 %isnan
+}
+
+; qNaN may be produced when a negative base is raised to a non-integer exponent.
+define i1 @isnan_pow_negative_base(float nofpclass(nan inf zero psub pnorm) %base, float nofpclass(nan) %exp) {
+; CHECK-LABEL: define i1 @isnan_pow_negative_base(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[BASE:%.*]], float nofpclass(nan) [[EXP:%.*]]) {
+; CHECK-NEXT:    [[POW:%.*]] = call float @llvm.pow.f32(float [[BASE]], float [[EXP]])
+; CHECK-NEXT:    [[ISNAN:%.*]] = fcmp uno float [[POW]], [[POW]]
+; CHECK-NEXT:    ret i1 [[ISNAN]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  %isnan = fcmp uno float %pow, %pow
+  ret i1 %isnan
+}
+
+; A negative result is produced when a negative base is raised to an odd integer
+; exponent.
+define float @fabs_pow_negative_base_normal_exponent(float nofpclass(nan inf zero psub pnorm) %base, float nofpclass(nan inf zero sub) %exp) {
+; CHECK-LABEL: define float @fabs_pow_negative_base_normal_exponent(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[BASE:%.*]], float nofpclass(nan inf zero sub) [[EXP:%.*]]) {
+; CHECK-NEXT:    [[POW:%.*]] = call float @llvm.pow.f32(float [[BASE]], float [[EXP]])
+; CHECK-NEXT:    [[ABS_POW:%.*]] = call float @llvm.fabs.f32(float [[POW]])
+; CHECK-NEXT:    ret float [[ABS_POW]]
+;
+  %pow = call float @llvm.pow.f32(float %base, float %exp)
+  %abs.pow = call float @llvm.fabs.f32(float %pow)
+  ret float %abs.pow
+}
+
+define <2 x i1> @pow_fabs_olt_zero_v2f32(<2 x float> %base, <2 x float> %exp) {
+; CHECK-LABEL: define <2 x i1> @pow_fabs_olt_zero_v2f32(
+; CHECK-SAME: <2 x float> [[BASE:%.*]], <2 x float> [[EXP:%.*]]) {
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
+;
+  %abs = call <2 x float> @llvm.fabs.v2f32(<2 x float> %base)
+  %pow = call <2 x float> @llvm.pow.v2f32(<2 x float> %abs, <2 x float> %exp)
+  %cmp = fcmp olt <2 x float> %pow, zeroinitializer
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @isnan_pow_nonneg_or_negzero_base_v2f32(<2 x float> nofpclass(nan ninf nsub nnorm) %base, <2 x float> nofpclass(nan) %exp) {
+; CHECK-LABEL: define <2 x i1> @isnan_pow_nonneg_or_negzero_base_v2f32(
+; CHECK-SAME: <2 x float> nofpclass(nan ninf nsub nnorm) [[BASE:%.*]], <2 x float> nofpclass(nan) [[EXP:%.*]]) {
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
+;
+  %pow = call <2 x float> @llvm.pow.v2f32(<2 x float> %base, <2 x float> %exp)
+  %isnan = fcmp uno <2 x float> %pow, %pow
+  ret <2 x i1> %isnan
+}
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 382cf0f2b57ed..bbbf1cc1b9933 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -1912,6 +1912,20 @@ TEST_F(ComputeKnownFPClassTest, MaximumNumSignBit) {
   expectKnownFPClass(fcPositive, false, A7);
 }
 
+TEST_F(ComputeKnownFPClassTest, PowIgnoreRHSForNegativeSpecialResults) {
+  parseAssembly(
+      "declare float @llvm.pow.f32(float, float)\n"
+      "define float @test(float nofpclass(nsub nnorm) %base,\n"
+      "                   float nofpclass(norm) %exp) {\n"
+      "  %A = call float @llvm.pow.f32(float %base, float %exp)\n"
+      "  ret float %A\n"
+      "}\n");
+
+  KnownFPClass Known = computeKnownFPClass(
+      A, M->getDataLayout(), fcNegInf | fcNegZero);
+  EXPECT_EQ(~(fcNegNormal | fcNegSubnormal), Known.KnownFPClasses);
+}
+
 TEST_F(ComputeKnownFPClassTest, PowiInfFirst) {
   parseAssembly(
       "declare float @llvm.powi.f32.i32(float, i32)\n"
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index d995c1e15f9e5..b74d83bf407c0 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -1001,6 +1001,112 @@ TEST_F(AArch64GISelMITest, TestFPClassFLDExp) {
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
+TEST_F(AArch64GISelMITest, TestFPClassFPowPos) {
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %exp:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = G_FABS %val
+    %fpow:_(s32) = G_FPOW %fabs, %exp
+    %copy_fpow:_(s32) = COPY %fpow
+)";
+
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+
+  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
+  EXPECT_TRUE(Info.isKnownNeverNaN(SrcReg, true));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFPowPosNNaN) {
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %exp:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan G_FABS %val
+    %fabs_exp:_(s32) = nnan G_FABS %exp
+    %fpow:_(s32) = G_FPOW %fabs, %fabs_exp
+    %copy_fpow:_(s32) = COPY %fpow
+)";
+
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+
+  EXPECT_EQ(fcPositive, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFPowNegativeSpecialBases) {
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %cond:_(s1) = G_LOAD %ptr(p0) :: (load (s1))
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %pos:_(s32) = G_FABS %val
+    %neg:_(s32) = G_FNEG %pos
+    %negzero:_(s32) = G_FCONSTANT float -0.0
+    %neginf:_(s32) = G_FCONSTANT float f0xFF800000
+    %zero:_(s32) = G_FCONSTANT float 0.0
+    %pow_negzero_neg:_(s32) = G_FPOW %negzero, %neg
+    %copy_pow_negzero_neg:_(s32) = COPY %pow_negzero_neg
+    %pow_negzero_pos:_(s32) = G_FPOW %negzero, %pos
+    %copy_pow_negzero_pos:_(s32) = COPY %pow_negzero_pos
+    %pow_neginf_pos:_(s32) = G_FPOW %neginf, %pos
+    %copy_pow_neginf_pos:_(s32) = COPY %pow_neginf_pos
+    %pow_neginf_neg:_(s32) = G_FPOW %neginf, %neg
+    %copy_pow_neginf_neg:_(s32) = COPY %pow_neginf_neg
+    %negspecial:_(s32) = G_SELECT %cond, %negzero, %neginf
+    %pow_negspecial_zero:_(s32) = G_FPOW %negspecial, %zero
+    %copy_pow_negspecial_zero:_(s32) = COPY %pow_negspecial_zero
+)";
+
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+
+  GISelValueTracking Info(*MF);
+  auto ExpectKnownNever = [&](unsigned CopyIndex, FPClassTest Class) {
+    Register CopyReg = Copies[Copies.size() - 5 + CopyIndex];
+    MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+    Register SrcReg = FinalCopy->getOperand(1).getReg();
+    KnownFPClass Known = Info.computeKnownFPClass(SrcReg, Class);
+    EXPECT_TRUE(Known.isKnownNever(Class));
+  };
+
+  // pow(-0.0, RHS) cannot return -0.0 if RHS cannot be positive normal.
+  ExpectKnownNever(0, fcNegZero);
+  // pow(-0.0, RHS) cannot return -Inf if RHS cannot be negative normal.
+  ExpectKnownNever(1, fcNegInf);
+  // pow(-Inf, RHS) cannot return -0.0 if RHS cannot be negative normal.
+  ExpectKnownNever(2, fcNegZero);
+  // pow(-Inf, RHS) cannot return -Inf if RHS cannot be positive normal.
+  ExpectKnownNever(3, fcNegInf);
+
+  // Do not query the RHS when both negative special bases remain possible.
+  Register CopyReg = Copies.back();
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  KnownFPClass Known =
+      Info.computeKnownFPClass(SrcReg, fcNegInf | fcNegZero);
+  EXPECT_EQ(~(fcNegNormal | fcNegSubnormal), Known.KnownFPClasses);
+}
+
 TEST_F(AArch64GISelMITest, TestFPClassFPowIEvenExp) {
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF



More information about the llvm-commits mailing list