[llvm] [ValueTracking][KnownFPClass] cover known no-infinity cases for powi (PR #191736)

Max Graey via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 13 03:01:00 PDT 2026


https://github.com/MaxGraey updated https://github.com/llvm/llvm-project/pull/191736

>From ae2adeff45221ad79e4cd01c432e5d3d968850ef Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 13 Apr 2026 00:51:22 +0300
Subject: [PATCH 1/4] init

---
 llvm/lib/Support/KnownFPClass.cpp             | 28 +++++++++-
 llvm/unittests/Analysis/ValueTrackingTest.cpp | 55 +++++++++++++++++++
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 96a1b26daf04d..8bc3741149f24 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -862,7 +862,6 @@ KnownFPClass KnownFPClass::ldexp(const KnownFPClass &KnownSrc,
   return Known;
 }
 
-// TODO: Detect no-infinity cases
 KnownFPClass KnownFPClass::powi(const KnownFPClass &KnownSrc,
                                 const KnownBits &ExponentKnownBits) {
   KnownFPClass Known;
@@ -880,6 +879,33 @@ KnownFPClass KnownFPClass::powi(const KnownFPClass &KnownSrc,
     return Known;
   }
 
+  // powi(x, exp) --> inf
+  // when:
+  //   * powi(inf, exp), exp > 0
+  //   * powi(+/-0, exp), exp < 0
+  //   * powi(finite, exp) --> overflow, |exp| > 1 or powi(subnormal, -1)
+  {
+    APInt MinExp = ExponentKnownBits.getSignedMinValue();
+    APInt MaxExp = ExponentKnownBits.getSignedMaxValue();
+
+    // powi(inf, exp), exp > 0
+    bool MayInfSrc =
+        !KnownSrc.isKnownNever(fcInf) && MaxExp.isStrictlyPositive();
+
+    // powi(+/-0, exp), exp < 0
+    bool MayDivByZero = !KnownSrc.isKnownNever(fcZero) && MinExp.isNegative();
+
+    // powi(finite, exp) --> overflow, |exp| > 1 or powi(subnormal, -1)
+    bool MayFinite = !KnownSrc.isKnownNever(fcNormal | fcSubnormal);
+    bool MayAbsExpGT1 = MinExp.slt(-1) || MaxExp.sgt(1);
+    bool MayBeNegOne = ExponentKnownBits.Zero.isZero();
+    bool MaySubnormInv = !KnownSrc.isKnownNever(fcSubnormal) && MayBeNegOne;
+    bool MayOverflow = (MayFinite && MayAbsExpGT1) || MaySubnormInv;
+
+    if (!MayInfSrc && !MayDivByZero && !MayOverflow)
+      Known.knownNot(fcInf);
+  }
+
   if (ExponentKnownBits.isEven()) {
     Known.knownNot(fcNegative);
     return Known;
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index de481e39307cb..632279b8e6b8f 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -1913,6 +1913,61 @@ TEST_F(ComputeKnownFPClassTest, MaximumNumSignBit) {
   expectKnownFPClass(fcPositive, false, A7);
 }
 
+TEST_F(ComputeKnownFPClassTest, PowiInfBasic) {
+  parseAssembly(
+      "declare float @llvm.powi.f32.i32(float, i32)\n"
+      "define float @test(i1 %cond, i32 %x, float %unknown,\n"
+      "                   float nofpclass(inf) %finite,\n"
+      "                   float nofpclass(zero sub inf) %normal,\n"
+      "                   float nofpclass(zero inf) %finite.may.sub,\n"
+      "                   float nofpclass(inf norm sub) %zero.or.nan) {\n"
+      "  %small = zext i1 %cond to i32\n"
+      "  %nonneg = and i32 %x, 2147483647\n"
+      "  %A = call float @llvm.powi.f32.i32(float %finite, i32 1)\n"
+      "  %A2 = call float @llvm.powi.f32.i32(float %finite, i32 2)\n"
+      "  %A3 = call float @llvm.powi.f32.i32(float %normal, i32 -1)\n"
+      "  %A4 = call float @llvm.powi.f32.i32(float %finite, i32 %small)\n"
+      "  %A5 = call float @llvm.powi.f32.i32(float %unknown, i32 %small)\n"
+      "  %A6 = call float @llvm.powi.f32.i32(float %finite.may.sub, i32 -1)\n"
+      "  %A7 = call float @llvm.powi.f32.i32(float %zero.or.nan, i32 %nonneg)\n"
+      "  ret float %A\n"
+      "}\n");
+  expectKnownFPClass(~fcInf, std::nullopt, A);
+  expectKnownFPClass(fcPositive | fcNan, std::nullopt, A2);
+  expectKnownFPClass(~fcInf, std::nullopt, A3);
+  expectKnownFPClass(~fcInf, std::nullopt, A4);
+  expectKnownFPClass(fcAllFlags, std::nullopt, A5);
+  expectKnownFPClass(fcAllFlags, std::nullopt, A6);
+  expectKnownFPClass(~fcInf, std::nullopt, A7);
+}
+
+TEST_F(ComputeKnownFPClassTest, PowiInfSpecial) {
+  parseAssembly(
+      "declare float @llvm.powi.f32.i32(float, i32)\n"
+      "define float @test(i32 %x, float %unknown,\n"
+      "                   float nofpclass(inf) %finite,\n"
+      "                   float nofpclass(inf norm sub) %zero.or.nan,\n"
+      "                   float nofpclass(zero norm sub) %inf.or.nan) {\n"
+      "  %neg = or i32 %x, -2147483648\n"
+      "  %nonneg = and i32 %x, 2147483647\n"
+      "  %A = call float @llvm.powi.f32.i32(float %unknown, i32 0)\n"
+      "  %A2 = call float @llvm.powi.f32.i32(float %finite, i32 -2)\n"
+      "  %A3 = call float @llvm.powi.f32.i32(float %zero.or.nan, i32 -1)\n"
+      "  %A4 = call float @llvm.powi.f32.i32(float %inf.or.nan, i32 -1)\n"
+      "  %A5 = call float @llvm.powi.f32.i32(float %inf.or.nan, i32 %neg)\n"
+      "  %A6 = call float @llvm.powi.f32.i32(float %finite, i32 %nonneg)\n"
+      "  %A7 = call float @llvm.powi.f32.i32(float %zero.or.nan, i32 %neg)\n"
+      "  ret float %A\n"
+      "}\n");
+  expectKnownFPClass(fcPosNormal | fcNan, std::nullopt, A);
+  expectKnownFPClass(fcAllFlags, std::nullopt, A2);
+  expectKnownFPClass(fcAllFlags, std::nullopt, A3);
+  expectKnownFPClass(~fcInf, std::nullopt, A4);
+  expectKnownFPClass(~fcInf, std::nullopt, A5);
+  expectKnownFPClass(fcAllFlags, std::nullopt, A6);
+  expectKnownFPClass(fcAllFlags, std::nullopt, A7);
+}
+
 TEST_F(ComputeKnownFPClassTest, Phi) {
   parseAssembly(
       "define float @test(i1 %cond, float nofpclass(nan inf) %arg0, float nofpclass(nan) %arg1) {\n"

>From 862ab9f6e91a7039e314a8c9228c8c7dce5e328f Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 13 Apr 2026 01:04:05 +0300
Subject: [PATCH 2/4] refactor & add TODO

---
 llvm/lib/Support/KnownFPClass.cpp | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 8bc3741149f24..6a33569ef9717 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -883,7 +883,11 @@ KnownFPClass KnownFPClass::powi(const KnownFPClass &KnownSrc,
   // when:
   //   * powi(inf, exp), exp > 0
   //   * powi(+/-0, exp), exp < 0
-  //   * powi(finite, exp) --> overflow, |exp| > 1 or powi(subnormal, -1)
+  //   * powi(finite, exp), |exp| > 1
+  //   * powi(subnormal, -1)
+  // TODO:
+  //   This simple all or nothing approach. We can do better
+  //   and cover sign/parity and exp > 1 vs exp < -1 separately.
   {
     APInt MinExp = ExponentKnownBits.getSignedMinValue();
     APInt MaxExp = ExponentKnownBits.getSignedMaxValue();
@@ -895,14 +899,16 @@ KnownFPClass KnownFPClass::powi(const KnownFPClass &KnownSrc,
     // powi(+/-0, exp), exp < 0
     bool MayDivByZero = !KnownSrc.isKnownNever(fcZero) && MinExp.isNegative();
 
-    // powi(finite, exp) --> overflow, |exp| > 1 or powi(subnormal, -1)
+    // powi(finite, exp), |exp| > 1
     bool MayFinite = !KnownSrc.isKnownNever(fcNormal | fcSubnormal);
     bool MayAbsExpGT1 = MinExp.slt(-1) || MaxExp.sgt(1);
+    bool MayFiniteOverflow = MayFinite && MayAbsExpGT1;
+
+    // powi(subnormal, -1)
     bool MayBeNegOne = ExponentKnownBits.Zero.isZero();
     bool MaySubnormInv = !KnownSrc.isKnownNever(fcSubnormal) && MayBeNegOne;
-    bool MayOverflow = (MayFinite && MayAbsExpGT1) || MaySubnormInv;
 
-    if (!MayInfSrc && !MayDivByZero && !MayOverflow)
+    if (!MayInfSrc && !MayDivByZero && !MayFiniteOverflow && !MaySubnormInv)
       Known.knownNot(fcInf);
   }
 

>From ec8784e49b8827e12805ded6297bbb9f5165566d Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 13 Apr 2026 01:41:45 +0300
Subject: [PATCH 3/4] fix expected in one test

---
 llvm/unittests/Analysis/ValueTrackingTest.cpp | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 632279b8e6b8f..1546d8e6fafd7 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -1939,6 +1939,19 @@ TEST_F(ComputeKnownFPClassTest, PowiInfBasic) {
   expectKnownFPClass(fcAllFlags, std::nullopt, A5);
   expectKnownFPClass(fcAllFlags, std::nullopt, A6);
   expectKnownFPClass(~fcInf, std::nullopt, A7);
+
+  // auto ExpectKnownNeverInf = [&](Instruction *I, bool Expected) {
+  //   KnownFPClass Known = computeKnownFPClass(I, M->getDataLayout(), fcInf);
+  //   EXPECT_EQ(Expected, Known.isKnownNeverInfinity());
+  // };
+
+  // ExpectKnownNeverInf(A, true);
+  // ExpectKnownNeverInf(A2, false);
+  // ExpectKnownNeverInf(A3, true);
+  // ExpectKnownNeverInf(A4, true);
+  // ExpectKnownNeverInf(A5, false);
+  // ExpectKnownNeverInf(A6, false);
+  // ExpectKnownNeverInf(A7, true);
 }
 
 TEST_F(ComputeKnownFPClassTest, PowiInfSpecial) {
@@ -1960,7 +1973,7 @@ TEST_F(ComputeKnownFPClassTest, PowiInfSpecial) {
       "  ret float %A\n"
       "}\n");
   expectKnownFPClass(fcPosNormal | fcNan, std::nullopt, A);
-  expectKnownFPClass(fcAllFlags, std::nullopt, A2);
+  expectKnownFPClass(fcPositive | fcNan, std::nullopt, A2);
   expectKnownFPClass(fcAllFlags, std::nullopt, A3);
   expectKnownFPClass(~fcInf, std::nullopt, A4);
   expectKnownFPClass(~fcInf, std::nullopt, A5);

>From 599d5da7b2dea22a525559148a0be93e2535bf81 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 13 Apr 2026 13:00:45 +0300
Subject: [PATCH 4/4] prepare InterestedClasses in value tracking

---
 llvm/lib/Analysis/ValueTracking.cpp           | 17 +++++++++---
 llvm/unittests/Analysis/ValueTrackingTest.cpp | 26 +++++++++----------
 2 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5b52efb9e1dc6..54e04eccc6613 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5459,7 +5459,7 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       break;
     }
     case Intrinsic::powi: {
-      if ((InterestedClasses & fcNegative) == fcNone)
+      if ((InterestedClasses & (fcNan | fcInf | fcNegative)) == fcNone)
         break;
 
       const Value *Exp = II->getArgOperand(1);
@@ -5469,11 +5469,20 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       computeKnownBits(Exp, isa<VectorType>(ExpTy) ? DemandedElts : APInt(1, 1),
                        ExponentKnownBits, Q, Depth + 1);
 
+      FPClassTest InterestedSrcs = fcNone;
+      if (InterestedClasses & fcNan)
+        InterestedSrcs |= fcNan;
+      if (!ExponentKnownBits.isZero()) {
+        if (InterestedClasses & fcInf)
+          InterestedSrcs |= fcFinite | fcInf;
+        if ((InterestedClasses & fcNegative) && !ExponentKnownBits.isEven())
+          InterestedSrcs |= fcNegative;
+      }
+
       KnownFPClass KnownSrc;
-      if (ExponentKnownBits.isZero() || !ExponentKnownBits.isEven()) {
-        computeKnownFPClass(II->getArgOperand(0), DemandedElts, fcNegative,
+      if (InterestedSrcs != fcNone)
+        computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedSrcs,
                             KnownSrc, Q, Depth + 1);
-      }
 
       Known = KnownFPClass::powi(KnownSrc, ExponentKnownBits);
       break;
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 1546d8e6fafd7..4034e2982df7f 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -1913,7 +1913,7 @@ TEST_F(ComputeKnownFPClassTest, MaximumNumSignBit) {
   expectKnownFPClass(fcPositive, false, A7);
 }
 
-TEST_F(ComputeKnownFPClassTest, PowiInfBasic) {
+TEST_F(ComputeKnownFPClassTest, PowiInfFirst) {
   parseAssembly(
       "declare float @llvm.powi.f32.i32(float, i32)\n"
       "define float @test(i1 %cond, i32 %x, float %unknown,\n"
@@ -1940,21 +1940,21 @@ TEST_F(ComputeKnownFPClassTest, PowiInfBasic) {
   expectKnownFPClass(fcAllFlags, std::nullopt, A6);
   expectKnownFPClass(~fcInf, std::nullopt, A7);
 
-  // auto ExpectKnownNeverInf = [&](Instruction *I, bool Expected) {
-  //   KnownFPClass Known = computeKnownFPClass(I, M->getDataLayout(), fcInf);
-  //   EXPECT_EQ(Expected, Known.isKnownNeverInfinity());
-  // };
+  auto ExpectKnownNeverInf = [&](Instruction *I, bool Expected) {
+    KnownFPClass Known = computeKnownFPClass(I, M->getDataLayout(), fcInf);
+    EXPECT_EQ(Expected, Known.isKnownNeverInfinity());
+  };
 
-  // ExpectKnownNeverInf(A, true);
-  // ExpectKnownNeverInf(A2, false);
-  // ExpectKnownNeverInf(A3, true);
-  // ExpectKnownNeverInf(A4, true);
-  // ExpectKnownNeverInf(A5, false);
-  // ExpectKnownNeverInf(A6, false);
-  // ExpectKnownNeverInf(A7, true);
+  ExpectKnownNeverInf(A,  true);
+  ExpectKnownNeverInf(A2, false);
+  ExpectKnownNeverInf(A3, true);
+  ExpectKnownNeverInf(A4, true);
+  ExpectKnownNeverInf(A5, false);
+  ExpectKnownNeverInf(A6, false);
+  ExpectKnownNeverInf(A7, true);
 }
 
-TEST_F(ComputeKnownFPClassTest, PowiInfSpecial) {
+TEST_F(ComputeKnownFPClassTest, PowiInfSecond) {
   parseAssembly(
       "declare float @llvm.powi.f32.i32(float, i32)\n"
       "define float @test(i32 %x, float %unknown,\n"



More information about the llvm-commits mailing list