APFloat: Add isSmallestNormalized predicate function
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Thu, 1 Dec 2022 14:12:20 +0000 (09:12 -0500)
committerMatt Arsenault <arsenm2@gmail.com>
Thu, 15 Dec 2022 19:04:26 +0000 (14:04 -0500)
It was annoying to write the check for this in the one case I added,
and I'm planning on adding another, so add a convenient PatternMatch
like for other special case values.

I have no idea what is going on in the DoubleAPFloat case, I reversed
this from the makeSmallestNormalized test. Also could implement this
as *this == getSmallestNormalized() for less code, but this avoids the
construction of a temporary APFloat copy and follows the style of the
other functions.

llvm/include/llvm/ADT/APFloat.h
llvm/lib/Support/APFloat.cpp
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
llvm/unittests/ADT/APFloatTest.cpp

index d86f72b..a396144 100644 (file)
@@ -393,6 +393,10 @@ public:
   /// magnitude in the current semantics.
   bool isSmallest() const;
 
+  /// Returns true if this is the smallest (by magnitude) normalized finite
+  /// number in the given semantics.
+  bool isSmallestNormalized() const;
+
   /// Returns true if and only if the number has the largest possible finite
   /// magnitude in the current semantics.
   bool isLargest() const;
@@ -517,6 +521,7 @@ private:
   bool isSignificandAllOnesExceptLSB() const;
   /// Return true if the significand excluding the integral bit is all zeros.
   bool isSignificandAllZeros() const;
+  bool isSignificandAllZerosExceptMSB() const;
 
   /// @}
 
@@ -694,6 +699,7 @@ public:
 
   bool isDenormal() const;
   bool isSmallest() const;
+  bool isSmallestNormalized() const;
   bool isLargest() const;
   bool isInteger() const;
 
@@ -1247,6 +1253,10 @@ public:
   bool isInteger() const { APFLOAT_DISPATCH_ON_SEMANTICS(isInteger()); }
   bool isIEEE() const { return usesLayout<IEEEFloat>(getSemantics()); }
 
+  bool isSmallestNormalized() const {
+    APFLOAT_DISPATCH_ON_SEMANTICS(isSmallestNormalized());
+  }
+
   APFloat &operator=(const APFloat &RHS) = default;
   APFloat &operator=(APFloat &&RHS) = default;
 
index 587521d..bed4a3b 100644 (file)
@@ -883,6 +883,11 @@ bool IEEEFloat::isSmallest() const {
     significandMSB() == 0;
 }
 
+bool IEEEFloat::isSmallestNormalized() const {
+  return getCategory() == fcNormal && exponent == semantics->minExponent &&
+         isSignificandAllZerosExceptMSB();
+}
+
 bool IEEEFloat::isSignificandAllOnes() const {
   // Test if the significand excluding the integral bit is all ones. This allows
   // us to test for binade boundaries.
@@ -955,6 +960,21 @@ bool IEEEFloat::isSignificandAllZeros() const {
   return true;
 }
 
+bool IEEEFloat::isSignificandAllZerosExceptMSB() const {
+  const integerPart *Parts = significandParts();
+  const unsigned PartCount = partCountForBits(semantics->precision);
+
+  for (unsigned i = 0; i < PartCount - 1; i++) {
+    if (Parts[i])
+      return false;
+  }
+
+  const unsigned NumHighBits =
+      PartCount * integerPartWidth - semantics->precision + 1;
+  return Parts[PartCount - 1] == integerPart(1)
+                                     << (integerPartWidth - NumHighBits);
+}
+
 bool IEEEFloat::isLargest() const {
   if (semantics->nonFiniteBehavior == fltNonfiniteBehavior::NanOnly) {
     // The largest number by magnitude in our format will be the floating point
@@ -4998,6 +5018,15 @@ bool DoubleAPFloat::isSmallest() const {
   return Tmp.compare(*this) == cmpEqual;
 }
 
+bool DoubleAPFloat::isSmallestNormalized() const {
+  if (getCategory() != fcNormal)
+    return false;
+
+  DoubleAPFloat Tmp(*this);
+  Tmp.makeSmallestNormalized(this->isNegative());
+  return Tmp.compare(*this) == cmpEqual;
+}
+
 bool DoubleAPFloat::isLargest() const {
   if (getCategory() != fcNormal)
     return false;
index c3d281e..4877a5e 100644 (file)
@@ -6781,7 +6781,7 @@ static Instruction *foldFabsWithFcmpZero(FCmpInst &I, InstCombinerImpl &IC) {
     return nullptr;
 
   if (!C->isPosZero()) {
-    if (*C != APFloat::getSmallestNormalized(C->getSemantics()))
+    if (!C->isSmallestNormalized())
       return nullptr;
 
     const Function *F = I.getFunction();
index c46508a..ff295f7 100644 (file)
@@ -664,6 +664,57 @@ TEST(APFloatTest, Denormal) {
   }
 }
 
+TEST(APFloatTest, IsSmallestNormalized) {
+  for (unsigned I = 0; I != APFloat::S_MaxSemantics + 1; ++I) {
+    const fltSemantics &Semantics =
+        APFloat::EnumToSemantics(static_cast<APFloat::Semantics>(I));
+
+    EXPECT_FALSE(APFloat::getZero(Semantics, false).isSmallestNormalized());
+    EXPECT_FALSE(APFloat::getZero(Semantics, true).isSmallestNormalized());
+
+    EXPECT_FALSE(APFloat::getInf(Semantics, false).isSmallestNormalized());
+    EXPECT_FALSE(APFloat::getInf(Semantics, true).isSmallestNormalized());
+
+    EXPECT_FALSE(APFloat::getQNaN(Semantics).isSmallestNormalized());
+    EXPECT_FALSE(APFloat::getSNaN(Semantics).isSmallestNormalized());
+
+    EXPECT_FALSE(APFloat::getLargest(Semantics).isSmallestNormalized());
+    EXPECT_FALSE(APFloat::getLargest(Semantics, true).isSmallestNormalized());
+
+    EXPECT_FALSE(APFloat::getSmallest(Semantics).isSmallestNormalized());
+    EXPECT_FALSE(APFloat::getSmallest(Semantics, true).isSmallestNormalized());
+
+    EXPECT_FALSE(APFloat::getAllOnesValue(Semantics).isSmallestNormalized());
+
+    APFloat PosSmallestNormalized =
+        APFloat::getSmallestNormalized(Semantics, false);
+    APFloat NegSmallestNormalized =
+        APFloat::getSmallestNormalized(Semantics, true);
+    EXPECT_TRUE(PosSmallestNormalized.isSmallestNormalized());
+    EXPECT_TRUE(NegSmallestNormalized.isSmallestNormalized());
+
+    for (APFloat *Val : {&PosSmallestNormalized, &NegSmallestNormalized}) {
+      bool OldSign = Val->isNegative();
+
+      // Step down, make sure it's still not smallest normalized.
+      EXPECT_EQ(APFloat::opOK, Val->next(false));
+      EXPECT_EQ(OldSign, Val->isNegative());
+      EXPECT_FALSE(Val->isSmallestNormalized());
+      EXPECT_EQ(OldSign, Val->isNegative());
+
+      // Step back up should restore it to being smallest normalized.
+      EXPECT_EQ(APFloat::opOK, Val->next(true));
+      EXPECT_TRUE(Val->isSmallestNormalized());
+      EXPECT_EQ(OldSign, Val->isNegative());
+
+      // Step beyond should no longer smallest normalized.
+      EXPECT_EQ(APFloat::opOK, Val->next(true));
+      EXPECT_FALSE(Val->isSmallestNormalized());
+      EXPECT_EQ(OldSign, Val->isNegative());
+    }
+  }
+}
+
 TEST(APFloatTest, Zero) {
   EXPECT_EQ(0.0f,  APFloat(0.0f).convertToFloat());
   EXPECT_EQ(-0.0f, APFloat(-0.0f).convertToFloat());
@@ -1723,6 +1774,7 @@ TEST(APFloatTest, getSmallestNormalized) {
   EXPECT_TRUE(test.isFiniteNonZero());
   EXPECT_FALSE(test.isDenormal());
   EXPECT_TRUE(test.bitwiseIsEqual(expected));
+  EXPECT_TRUE(test.isSmallestNormalized());
 
   test = APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);
   expected = APFloat(APFloat::IEEEsingle(), "-0x1p-126");
@@ -1730,6 +1782,23 @@ TEST(APFloatTest, getSmallestNormalized) {
   EXPECT_TRUE(test.isFiniteNonZero());
   EXPECT_FALSE(test.isDenormal());
   EXPECT_TRUE(test.bitwiseIsEqual(expected));
+  EXPECT_TRUE(test.isSmallestNormalized());
+
+  test = APFloat::getSmallestNormalized(APFloat::IEEEdouble(), false);
+  expected = APFloat(APFloat::IEEEdouble(), "0x1p-1022");
+  EXPECT_FALSE(test.isNegative());
+  EXPECT_TRUE(test.isFiniteNonZero());
+  EXPECT_FALSE(test.isDenormal());
+  EXPECT_TRUE(test.bitwiseIsEqual(expected));
+  EXPECT_TRUE(test.isSmallestNormalized());
+
+  test = APFloat::getSmallestNormalized(APFloat::IEEEdouble(), true);
+  expected = APFloat(APFloat::IEEEdouble(), "-0x1p-1022");
+  EXPECT_TRUE(test.isNegative());
+  EXPECT_TRUE(test.isFiniteNonZero());
+  EXPECT_FALSE(test.isDenormal());
+  EXPECT_TRUE(test.bitwiseIsEqual(expected));
+  EXPECT_TRUE(test.isSmallestNormalized());
 
   test = APFloat::getSmallestNormalized(APFloat::IEEEquad(), false);
   expected = APFloat(APFloat::IEEEquad(), "0x1p-16382");
@@ -1737,6 +1806,7 @@ TEST(APFloatTest, getSmallestNormalized) {
   EXPECT_TRUE(test.isFiniteNonZero());
   EXPECT_FALSE(test.isDenormal());
   EXPECT_TRUE(test.bitwiseIsEqual(expected));
+  EXPECT_TRUE(test.isSmallestNormalized());
 
   test = APFloat::getSmallestNormalized(APFloat::IEEEquad(), true);
   expected = APFloat(APFloat::IEEEquad(), "-0x1p-16382");
@@ -1744,6 +1814,7 @@ TEST(APFloatTest, getSmallestNormalized) {
   EXPECT_TRUE(test.isFiniteNonZero());
   EXPECT_FALSE(test.isDenormal());
   EXPECT_TRUE(test.bitwiseIsEqual(expected));
+  EXPECT_TRUE(test.isSmallestNormalized());
 }
 
 TEST(APFloatTest, getZero) {