[llvm] [KnownFPClass] Improve known class deductions for exp/exp2/exp10 (PR #217526)

Eric Ross via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 11:55:06 PDT 2026


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

>From aa689c8091932cdea2ac01763077776018ff57d4 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Wed, 19 Aug 2026 22:02:36 -0600
Subject: [PATCH 1/2] [KnownFPClass] Improve known class deductions for
 exp/exp2/exp10

---
 llvm/lib/Support/KnownFPClass.cpp             | 18 ++++--
 .../Transforms/Attributor/nofpclass-exp.ll    | 57 +++++++++++++++++++
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   | 47 +++++++++++++++
 3 files changed, 116 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 1f596edd74100..bb135bdacdf12 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -597,16 +597,22 @@ KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) {
 
   Known.propagateNonNaN(KnownSrc);
 
-  if (KnownSrc.cannotBeOrderedLessThanZero()) {
-    // If the source is positive this cannot underflow.
+  // The following deductions assume that both exp10(-1.0) = +0.1 and
+  // exp10(+1.0) = +10.0 are both finite normal values.
+
+  // Only a negative normal or negative infinity can produce positive zero.
+  // A negative subnormal input is too small to produce positive zero.
+  if (KnownSrc.isKnownNever(fcNegNormal | fcNegInf))
     Known.knownNot(fcPosZero);
 
-    // Cannot introduce denormal values.
+  // Only a negative normal can produce a positive subnormal.
+  // A negative subnormal input is too small to produce a subnormal result.
+  if (KnownSrc.isKnownNever(fcNegNormal))
     Known.knownNot(fcPosSubnormal);
-  }
 
-  // If the source is negative, this cannot overflow to infinity.
-  if (KnownSrc.cannotBeOrderedGreaterThanZero())
+  // Only a positive normal or positive infinity can produce positive infinity.
+  // A positive subnormal input is too small to cause an overflow.
+  if (KnownSrc.isKnownNever(fcPosNormal | fcPosInf))
     Known.knownNot(fcPosInf);
 
   return Known;
diff --git a/llvm/test/Transforms/Attributor/nofpclass-exp.ll b/llvm/test/Transforms/Attributor/nofpclass-exp.ll
index 670e5bce06788..595bf4908b4f0 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-exp.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-exp.ll
@@ -4,6 +4,7 @@
 declare float @llvm.exp.f32(float)
 declare float @llvm.exp2.f32(float)
 declare float @llvm.exp10.f32(float)
+declare float @llvm.sqrt.f32(float)
 
 define float @ret_exp(float %arg0) {
 ; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_exp
@@ -450,5 +451,61 @@ define float @ret_exp_fneg_fabs(float %arg) {
   ret float %call
 }
 
+; A negative subnormal input cannot produce zero or a subnormal result.
+define float @ret_exp_negative_subnormal(float nofpclass(inf zero norm psub) %arg) {
+; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_exp_negative_subnormal
+; CHECK-SAME: (float nofpclass(inf zero psub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub nnorm) float @llvm.exp.f32(float nofpclass(inf zero psub norm) [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.exp.f32(float %arg)
+  ret float %call
+}
+
+; A positive subnormal input cannot produce infinity.
+define float @ret_exp_positive_subnormal(float nofpclass(inf zero norm nsub) %arg) {
+; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_exp_positive_subnormal
+; CHECK-SAME: (float nofpclass(inf zero nsub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub nnorm) float @llvm.exp.f32(float nofpclass(inf zero nsub norm) [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.exp.f32(float %arg)
+  ret float %call
+}
+
+; Exercise exp after 1.0 / sqrt(x). For non-poison inputs, the division is
+; -Inf, +zero, positive normal, or +Inf.
+define float @ret_exp_one_over_sqrt(float %arg) {
+; CHECK-LABEL: define nofpclass(nan ninf nzero sub nnorm) float @ret_exp_one_over_sqrt
+; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nnan float @llvm.sqrt.f32(float [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT:    [[ONE_OVER_SQRT:%.*]] = fdiv nnan float 1.000000e+00, [[SQRT]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.exp.f32(float [[ONE_OVER_SQRT]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %sqrt = call nnan float @llvm.sqrt.f32(float %arg)
+  %one.over.sqrt = fdiv nnan float 1.000000e+00, %sqrt
+  %call = call float @llvm.exp.f32(float %one.over.sqrt)
+  ret float %call
+}
+
+; Exercise exp after -1.0 / sqrt(x). For non-poison inputs, the result is
+; -Inf, negative normal, -zero, or +Inf.
+define float @ret_exp_neg_one_over_sqrt(float %arg) {
+; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float @ret_exp_neg_one_over_sqrt
+; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nnan float @llvm.sqrt.f32(float [[ARG]]) #[[ATTR2]]
+; CHECK-NEXT:    [[ONE_OVER_SQRT:%.*]] = fdiv nnan float 1.000000e+00, [[SQRT]]
+; CHECK-NEXT:    [[NEG_ONE_OVER_SQRT:%.*]] = fneg float [[ONE_OVER_SQRT]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) float @llvm.exp.f32(float [[NEG_ONE_OVER_SQRT]]) #[[ATTR2]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %sqrt = call nnan float @llvm.sqrt.f32(float %arg)
+  %one.over.sqrt = fdiv nnan float 1.000000e+00, %sqrt
+  %neg.one.over.sqrt = fneg float %one.over.sqrt
+  %call = call float @llvm.exp.f32(float %neg.one.over.sqrt)
+  ret float %call
+}
+
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index d1b817934a0fd..e6df41e8af83d 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -924,6 +924,53 @@ TEST_F(AArch64GISelMITest, TestFPClassFLogNegZero) {
   EXPECT_EQ(std::nullopt, Known.getSignBit());
 }
 
+TEST_F(AArch64GISelMITest, TestFPClassFExpNegSubnormal) {
+  StringRef MIRString = R"(
+    %subnormal:_(s32) = G_FCONSTANT float f0x00000001
+    %negative_subnormal:_(s32) = G_FNEG %subnormal
+    %exp:_(s32) = G_FEXP %negative_subnormal
+    %copy:_(s32) = COPY %exp
+)";
+
+  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(fcPosNormal, Known.getKnownFPClasses());
+  EXPECT_EQ(false, Known.getSignBit());
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFExp2Positive) {
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %positive:_(s32) = nnan G_FABS %val
+    %exp2:_(s32) = G_FEXP2 %positive
+    %copy:_(s32) = COPY %exp2
+)";
+
+  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(fcPosNormal | fcPosInf, Known.getKnownFPClasses());
+  EXPECT_EQ(false, Known.getSignBit());
+}
+
 TEST_F(AArch64GISelMITest, TestFPClassCopy) {
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF

>From 04034302e861ee16b03359db7daa6f4e7ff1cf53 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sat, 12 Sep 2026 12:53:16 -0600
Subject: [PATCH 2/2] update KnownFPClass::exp deduction explainations

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

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index bb135bdacdf12..3acd9ccf8ae79 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -597,21 +597,18 @@ KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) {
 
   Known.propagateNonNaN(KnownSrc);
 
-  // The following deductions assume that both exp10(-1.0) = +0.1 and
+  // The following deductions assume that exp10(-1.0) = +0.1 and
   // exp10(+1.0) = +10.0 are both finite normal values.
 
-  // Only a negative normal or negative infinity can produce positive zero.
-  // A negative subnormal input is too small to produce positive zero.
+  // Zero can only be produced if x < -1.0.
   if (KnownSrc.isKnownNever(fcNegNormal | fcNegInf))
     Known.knownNot(fcPosZero);
 
-  // Only a negative normal can produce a positive subnormal.
-  // A negative subnormal input is too small to produce a subnormal result.
+  // Subnormals can only be produced if x < -1.0 and x is finite.
   if (KnownSrc.isKnownNever(fcNegNormal))
     Known.knownNot(fcPosSubnormal);
 
-  // Only a positive normal or positive infinity can produce positive infinity.
-  // A positive subnormal input is too small to cause an overflow.
+  // Infinity can only be produced if x > +1.0.
   if (KnownSrc.isKnownNever(fcPosNormal | fcPosInf))
     Known.knownNot(fcPosInf);
 



More information about the llvm-commits mailing list