Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ConstantFPRange] Implement ConstantFPRange::makeSatisfyingFCmpRegion #110891

Merged
merged 3 commits into from
Oct 8, 2024

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Oct 2, 2024

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.

@llvmbot
Copy link
Collaborator

llvmbot commented Oct 2, 2024

@llvm/pr-subscribers-llvm-ir

Author: Yingwei Zheng (dtcxzyw)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/110891.diff

2 Files Affected:

  • (modified) llvm/lib/IR/ConstantFPRange.cpp (+42-2)
  • (modified) llvm/unittests/IR/ConstantFPRangeTest.cpp (+130)
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..27121a4c017b60 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -470,4 +470,134 @@ 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);
+  }
+}
+
+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

@arsenm arsenm added the floating-point Floating-point math label Oct 2, 2024
@dtcxzyw dtcxzyw merged commit 4647a46 into llvm:main Oct 8, 2024
7 of 9 checks passed
@dtcxzyw dtcxzyw deleted the cfr-satisfying-region branch October 8, 2024 05:41
dtcxzyw added a commit that referenced this pull request Oct 8, 2024
…111490)

Note: The current implementation doesn't return optimal result for `fcmp
one/une x, +/-inf` since we don't handle this case in
#110891. Maybe we can make it
optimal after seeing some real-world cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
floating-point Floating-point math llvm:ir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants