[llvm] [KnownFPClass] Add KnownFPClass deductions for `pow` (PR #215592)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 20:20:00 PDT 2026
https://github.com/ZERICO2005 updated https://github.com/llvm/llvm-project/pull/215592
>From 0d829222dccb733d402d9ec2b605890b0afb8bb3 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 | 37 ++
.../CodeGen/GlobalISel/GISelValueTracking.cpp | 37 ++
llvm/lib/Support/KnownFPClass.cpp | 42 +++
.../Transforms/Attributor/nofpclass-pow.ll | 332 ++++++++++++++++++
.../simplify-demanded-fpclass-pow.ll | 65 ++++
llvm/test/Transforms/InstSimplify/pow.ll | 101 ++++++
llvm/unittests/Analysis/ValueTrackingTest.cpp | 12 +
.../CodeGen/GlobalISel/KnownFPClassTest.cpp | 52 +++
9 files changed, 682 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..8560f66c88690 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5562,6 +5562,43 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
break;
}
+ case Intrinsic::pow: {
+ const bool WantNaN = (InterestedClasses & fcNan) != fcNone;
+ const bool WantNegative = (InterestedClasses & fcNegative) != fcNone;
+ if (!WantNaN && !WantNegative)
+ 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 (WantNegative) {
+ // A negative value is returned when a negative base is raised to an odd
+ // integer power. Only normal values can be odd integers.
+ InterestedLHS |= fcNegative;
+ InterestedRHS |= fcNormal;
+ }
+
+ KnownFPClass KnownLHS;
+ computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedLHS,
+ KnownLHS, Q, Depth + 1);
+
+ // If the LHS is unknown, then querying the RHS is only useful for rare
+ // edge cases.
+ if (KnownLHS.isUnknown())
+ break;
+
+ KnownFPClass KnownRHS;
+ 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..cdae538e29577 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -1630,6 +1630,42 @@ void GISelValueTracking::computeKnownFPClass(Register R,
Known = KnownFPClass::log(KnownSrc, Mode);
break;
}
+ case TargetOpcode::G_FPOW: {
+ const bool WantNaN = (InterestedClasses & fcNan) != fcNone;
+ const bool WantNegative = (InterestedClasses & fcNegative) != fcNone;
+ if (!WantNaN && !WantNegative)
+ 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 (WantNegative) {
+ // A negative value is returned when a negative base is raised to an odd
+ // integer power. Only normal values can be odd integers.
+ InterestedLHS |= fcNegative;
+ InterestedRHS |= fcNormal;
+ }
+
+ KnownFPClass KnownLHS;
+ computeKnownFPClass(MI.getOperand(1).getReg(), DemandedElts, InterestedLHS,
+ KnownLHS, Depth + 1);
+
+ // If the LHS is unknown, then querying the RHS is only useful for rare edge
+ // cases.
+ if (KnownLHS.isUnknown())
+ break;
+
+ KnownFPClass KnownRHS;
+ 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 +2255,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..765c4fceb2fd8
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-pow.ll
@@ -0,0 +1,332 @@
+; 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>)
+
+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]]) #[[ATTR5:[0-9]+]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+; The result is never negative or subnormal if the exponent is never normal.
+; 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_base_any_exponent_no_norm(float %base, float nofpclass(norm) %exp) {
+; CHECK-LABEL: define float @ret_pow_base_any_exponent_no_norm(
+; CHECK-SAME: float [[BASE:%.*]], float nofpclass(norm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[POW:%.*]] = call float @llvm.pow.f32(float [[BASE]], float nofpclass(norm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+; Requesting negative classes also queries whether the exponent is normal,
+; which rules out -0.0 and -Inf here.
+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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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]]) #[[ATTR5]]
+; 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 nofpclass(pnorm) %exp) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_negzero_negative(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_positive(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(nnorm) %exp) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_negzero_positive(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_positive(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(nnorm) %exp) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_neginf_positive(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_negative(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(pnorm) %exp) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_neginf_negative(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_negative_daz(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(pnorm) %exp) denormal_fpenv(preservesign|ieee) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_negzero_negative_daz(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_positive_daz(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(nnorm) %exp) denormal_fpenv(preservesign|ieee) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_negzero_positive_daz(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_positive_daz(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(nnorm) %exp) denormal_fpenv(preservesign|ieee) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_neginf_positive_daz(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_negative_daz(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(pnorm) %exp) denormal_fpenv(preservesign|ieee) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_neginf_negative_daz(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_negative_dapz(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(pnorm) %exp) denormal_fpenv(positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_negzero_negative_dapz(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_positive_dapz(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(nnorm) %exp) denormal_fpenv(positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_negzero_positive_dapz(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_positive_dapz(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(nnorm) %exp) denormal_fpenv(positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_neginf_positive_dapz(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_negative_dapz(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(pnorm) %exp) denormal_fpenv(positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_neginf_negative_dapz(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_negative_dynamic(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(pnorm) %exp) denormal_fpenv(dynamic) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_negzero_negative_dynamic(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_negzero_positive_dynamic(float nofpclass(nan inf pzero sub norm) %base, float nofpclass(nnorm) %exp) denormal_fpenv(dynamic) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_negzero_positive_dynamic(
+; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan inf pzero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_positive_dynamic(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(nnorm) %exp) denormal_fpenv(dynamic) {
+; CHECK-LABEL: define nofpclass(nzero nsub nnorm) float @ret_pow_neginf_positive_dynamic(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(nnorm) [[EXP:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(nzero nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(nnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ ret float %pow
+}
+
+define float @ret_pow_neginf_negative_dynamic(float nofpclass(nan pinf zero sub norm) %base, float nofpclass(pnorm) %exp) denormal_fpenv(dynamic) {
+; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_pow_neginf_negative_dynamic(
+; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[BASE:%.*]], float nofpclass(pnorm) [[EXP:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[POW:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.pow.f32(float nofpclass(nan pinf zero sub norm) [[BASE]], float nofpclass(pnorm) [[EXP]]) #[[ATTR5]]
+; CHECK-NEXT: ret float [[POW]]
+;
+ %pow = call float @llvm.pow.f32(float %base, float %exp)
+ 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]]) #[[ATTR5]]
+; 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..a821fcaf930aa
--- /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) {
+; CHECK-LABEL: define i1 @pow_negzero_negative_is_negzero(
+; CHECK-SAME: float [[X:%.*]]) {
+; 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)
+ ret i1 %class
+}
+
+define i1 @pow_negzero_positive_is_neginf(float %x) {
+; CHECK-LABEL: define i1 @pow_negzero_positive_is_neginf(
+; CHECK-SAME: float [[X:%.*]]) {
+; 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)
+ ret i1 %class
+}
+
+define i1 @pow_neginf_positive_is_negzero(float %x) {
+; CHECK-LABEL: define i1 @pow_neginf_positive_is_negzero(
+; CHECK-SAME: float [[X:%.*]]) {
+; 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)
+ ret i1 %class
+}
+
+define i1 @pow_neginf_negative_is_neginf(float %x) {
+; CHECK-LABEL: define i1 @pow_neginf_negative_is_neginf(
+; CHECK-SAME: float [[X:%.*]]) {
+; 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)
+ 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) {
+; CHECK-LABEL: define i1 @pow_any_nonnormal_may_be_subnormal(
+; CHECK-SAME: float [[X:%.*]], float nofpclass(norm) [[EXP:%.*]]) {
+; 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)
+; 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)
+ 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..5356dd07d42e4 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -1912,6 +1912,18 @@ TEST_F(ComputeKnownFPClassTest, MaximumNumSignBit) {
expectKnownFPClass(fcPositive, false, A7);
}
+TEST_F(ComputeKnownFPClassTest, PowUseRHSToRuleOutNegativeResults) {
+ parseAssembly("declare float @llvm.pow.f32(float, float)\n"
+ "define float @test(float nofpclass(ninf nsub nnorm) %base,\n"
+ " float nofpclass(pnorm) %exp) {\n"
+ " %A = call float @llvm.pow.f32(float %base, float %exp)\n"
+ " ret float %A\n"
+ "}\n");
+
+ KnownFPClass Known = computeKnownFPClass(A, M->getDataLayout(), fcNegative);
+ EXPECT_EQ(~(fcNegNormal | fcNegSubnormal | fcNegZero), 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..dc73ab751da68 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -1001,6 +1001,58 @@ 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, TestFPClassFPowIEvenExp) {
StringRef MIRString = R"(
%ptr:_(p0) = G_IMPLICIT_DEF
More information about the llvm-commits
mailing list