[llvm] [ConstantFPRange] Implement `ConstantFPRange::makeSatisfyingFCmpRegion` (PR #110891)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 2 10:09:31 PDT 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/110891

This patch adds support for `ConstantFPRange::makeSatisfyingFCmpRegion`. We only check the optimality for cases where the result can be represented by a ConstantFPRange.

This patch also adds some tests for `ConstantFPRange::fcmp` because it depends on `makeSatisfyingFCmpRegion`. Unfortunately we cannot exhaustively test this function due to time limit. I just pick some interesting ranges instead.


>From 2633b93eabc15bf208a1d11f5f74335030da691e Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 3 Oct 2024 00:28:14 +0800
Subject: [PATCH 1/2] [ConstantFPRange] Implement
 `ConstantFPRange::makeSatisfyingFCmpRegion`

---
 llvm/lib/IR/ConstantFPRange.cpp           | 44 ++++++++++++++-
 llvm/unittests/IR/ConstantFPRangeTest.cpp | 69 +++++++++++++++++++++++
 2 files changed, 111 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 74c9797d969f9d..d3c89daa9ce148 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -221,8 +221,48 @@ ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
 ConstantFPRange
 ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
                                           const ConstantFPRange &Other) {
-  // TODO
-  return getEmpty(Other.getSemantics());
+  if (Other.isEmptySet())
+    return getFull(Other.getSemantics());
+  if (Other.containsNaN() && FCmpInst::isOrdered(Pred))
+    return getEmpty(Other.getSemantics());
+  if (Other.isNaNOnly() && FCmpInst::isUnordered(Pred))
+    return getFull(Other.getSemantics());
+
+  switch (Pred) {
+  case FCmpInst::FCMP_TRUE:
+    return getFull(Other.getSemantics());
+  case FCmpInst::FCMP_FALSE:
+    return getEmpty(Other.getSemantics());
+  case FCmpInst::FCMP_ORD:
+    return getNonNaN(Other.getSemantics());
+  case FCmpInst::FCMP_UNO:
+    return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true,
+                      /*MayBeSNaN=*/true);
+  case FCmpInst::FCMP_OEQ:
+  case FCmpInst::FCMP_UEQ:
+    return setNaNField(Other.isSingleElement(/*ExcludesNaN=*/true) ||
+                               ((Other.classify() & ~fcNan) == fcZero)
+                           ? extendZeroIfEqual(Other, Pred)
+                           : getEmpty(Other.getSemantics()),
+                       Pred);
+  case FCmpInst::FCMP_ONE:
+  case FCmpInst::FCMP_UNE:
+    return getEmpty(Other.getSemantics());
+  case FCmpInst::FCMP_OLT:
+  case FCmpInst::FCMP_OLE:
+  case FCmpInst::FCMP_ULT:
+  case FCmpInst::FCMP_ULE:
+    return setNaNField(
+        extendZeroIfEqual(makeLessThan(Other.getLower(), Pred), Pred), Pred);
+  case FCmpInst::FCMP_OGT:
+  case FCmpInst::FCMP_OGE:
+  case FCmpInst::FCMP_UGT:
+  case FCmpInst::FCMP_UGE:
+    return setNaNField(
+        extendZeroIfEqual(makeGreaterThan(Other.getUpper(), Pred), Pred), Pred);
+  default:
+    llvm_unreachable("Unexpected predicate");
+  }
 }
 
 std::optional<ConstantFPRange>
diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 17a08207fe1ba0..9a83de5e126e5d 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -470,4 +470,73 @@ TEST_F(ConstantFPRangeTest, makeAllowedFCmpRegion) {
   }
 }
 
+TEST_F(ConstantFPRangeTest, makeSatisfyingFCmpRegion) {
+  for (auto Pred : FCmpInst::predicates()) {
+    EnumerateConstantFPRanges(
+        [Pred](const ConstantFPRange &CR) {
+          ConstantFPRange Res =
+              ConstantFPRange::makeSatisfyingFCmpRegion(Pred, CR);
+          // Super set of the optimal set excluding NaNs
+          ConstantFPRange SuperSet(CR.getSemantics());
+          bool ContainsSNaN = false;
+          bool ContainsQNaN = false;
+          unsigned NonNaNValsInOptimalSet = 0;
+          EnumerateValuesInConstantFPRange(
+              ConstantFPRange::getFull(CR.getSemantics()),
+              [&](const APFloat &V) {
+                if (AnyOfValueInConstantFPRange(CR, [&](const APFloat &U) {
+                      return !FCmpInst::compare(V, U, Pred);
+                    })) {
+                  EXPECT_FALSE(Res.contains(V))
+                      << "Wrong result for makeSatisfyingFCmpRegion(" << Pred
+                      << ", " << CR << "). The result " << Res
+                      << " should not contain " << V;
+                } else {
+                  if (V.isNaN()) {
+                    if (V.isSignaling())
+                      ContainsSNaN = true;
+                    else
+                      ContainsQNaN = true;
+                  } else {
+                    SuperSet = SuperSet.unionWith(ConstantFPRange(V));
+                    ++NonNaNValsInOptimalSet;
+                  }
+                }
+              });
+
+          // Check optimality
+
+          // The usefullness of making the result optimal for one/une is
+          // questionable.
+          if (Pred == FCmpInst::FCMP_ONE || Pred == FCmpInst::FCMP_UNE)
+            return;
+
+          EXPECT_FALSE(ContainsSNaN && !Res.containsSNaN())
+              << "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
+              << ", " << CR << "), should contain SNaN, but got " << Res;
+          EXPECT_FALSE(ContainsQNaN && !Res.containsQNaN())
+              << "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
+              << ", " << CR << "), should contain QNaN, but got " << Res;
+
+          // We only care about the cases where the result is representable by
+          // ConstantFPRange.
+          unsigned NonNaNValsInSuperSet = 0;
+          EnumerateValuesInConstantFPRange(SuperSet, [&](const APFloat &V) {
+            if (!V.isNaN())
+              ++NonNaNValsInSuperSet;
+          });
+
+          if (NonNaNValsInSuperSet == NonNaNValsInOptimalSet) {
+            ConstantFPRange Optimal =
+                ConstantFPRange(SuperSet.getLower(), SuperSet.getUpper(),
+                                ContainsQNaN, ContainsSNaN);
+            EXPECT_EQ(Res, Optimal)
+                << "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
+                << ", " << CR << ")";
+          }
+        },
+        /*Exhaustive=*/false);
+  }
+}
+
 } // anonymous namespace

>From 3a98c87f68b10fc45fe31f04e03da051f94dd3a9 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 3 Oct 2024 01:01:51 +0800
Subject: [PATCH 2/2] [ConstantFPRange] Add unittests for fcmp. NFC.

---
 llvm/unittests/IR/ConstantFPRangeTest.cpp | 61 +++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 9a83de5e126e5d..27121a4c017b60 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -539,4 +539,65 @@ TEST_F(ConstantFPRangeTest, makeSatisfyingFCmpRegion) {
   }
 }
 
+TEST_F(ConstantFPRangeTest, fcmp) {
+  std::vector<ConstantFPRange> InterestingRanges;
+  const fltSemantics &Sem = APFloat::Float8E4M3();
+  auto FpImm = [&](double V) {
+    bool ignored;
+    APFloat APF(V);
+    APF.convert(Sem, APFloat::rmNearestTiesToEven, &ignored);
+    return APF;
+  };
+
+  InterestingRanges.push_back(ConstantFPRange::getEmpty(Sem));
+  InterestingRanges.push_back(ConstantFPRange::getFull(Sem));
+  InterestingRanges.push_back(ConstantFPRange::getFinite(Sem));
+  InterestingRanges.push_back(ConstantFPRange(FpImm(1.0)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/false)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/true)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/false)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/true)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/false)));
+  InterestingRanges.push_back(
+      ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true)));
+  InterestingRanges.push_back(
+      ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true));
+  InterestingRanges.push_back(
+      ConstantFPRange::getNonNaN(FpImm(0.0), FpImm(1.0)));
+  InterestingRanges.push_back(
+      ConstantFPRange::getNonNaN(FpImm(2.0), FpImm(3.0)));
+  InterestingRanges.push_back(
+      ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(1.0)));
+  InterestingRanges.push_back(
+      ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(-0.0)));
+  InterestingRanges.push_back(ConstantFPRange::getNonNaN(
+      APFloat::getInf(Sem, /*Negative=*/true), FpImm(-1.0)));
+  InterestingRanges.push_back(ConstantFPRange::getNonNaN(
+      FpImm(1.0), APFloat::getInf(Sem, /*Negative=*/false)));
+
+  for (auto &LHS : InterestingRanges) {
+    for (auto &RHS : InterestingRanges) {
+      for (auto Pred : FCmpInst::predicates()) {
+        if (LHS.fcmp(Pred, RHS)) {
+          EnumerateValuesInConstantFPRange(LHS, [&](const APFloat &LHSC) {
+            EnumerateValuesInConstantFPRange(RHS, [&](const APFloat &RHSC) {
+              EXPECT_TRUE(FCmpInst::compare(LHSC, RHSC, Pred))
+                  << LHS << " " << Pred << " " << RHS << " doesn't hold";
+            });
+          });
+        }
+      }
+    }
+  }
+}
+
 } // anonymous namespace



More information about the llvm-commits mailing list