[llvm] 65898e5 - [ConstantRange] Handle `Intrinsic::ctlz`

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 17 00:57:43 PST 2023


Author: Antonio Frighetto
Date: 2023-02-17T09:57:35+01:00
New Revision: 65898e526060ae0f02de556c63dd3ce599d08c52

URL: https://github.com/llvm/llvm-project/commit/65898e526060ae0f02de556c63dd3ce599d08c52
DIFF: https://github.com/llvm/llvm-project/commit/65898e526060ae0f02de556c63dd3ce599d08c52.diff

LOG: [ConstantRange] Handle `Intrinsic::ctlz`

Introduce ConstantRange support for ctlz intrinsic, including
exhaustive testing. Among other things, LVI may now be able to
propagate information about cltz constant ranges lattice values.

Differential Revision: https://reviews.llvm.org/D142234

Added: 
    

Modified: 
    llvm/include/llvm/IR/ConstantRange.h
    llvm/lib/IR/ConstantRange.cpp
    llvm/test/Transforms/CorrelatedValuePropagation/range.ll
    llvm/unittests/IR/ConstantRangeTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h
index 0b9ac18d0a928..ca36732e4e2e8 100644
--- a/llvm/include/llvm/IR/ConstantRange.h
+++ b/llvm/include/llvm/IR/ConstantRange.h
@@ -526,6 +526,10 @@ class [[nodiscard]] ConstantRange {
   /// \p IntMinIsPoison is false.
   ConstantRange abs(bool IntMinIsPoison = false) const;
 
+  /// Calculate ctlz range. If \p ZeroIsPoison is set, the range is computed
+  /// ignoring a possible zero value contained in the input range.
+  ConstantRange ctlz(bool ZeroIsPoison = false) const;
+
   /// Represents whether an operation on the given constant range is known to
   /// always or never overflow.
   enum class OverflowResult {

diff  --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index 0dbccaa1a66aa..ab94053df45a1 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -945,6 +945,7 @@ bool ConstantRange::isIntrinsicSupported(Intrinsic::ID IntrinsicID) {
   case Intrinsic::smin:
   case Intrinsic::smax:
   case Intrinsic::abs:
+  case Intrinsic::ctlz:
     return true;
   default:
     return false;
@@ -976,6 +977,12 @@ ConstantRange ConstantRange::intrinsic(Intrinsic::ID IntrinsicID,
     assert(IntMinIsPoison->getBitWidth() == 1 && "Must be boolean");
     return Ops[0].abs(IntMinIsPoison->getBoolValue());
   }
+  case Intrinsic::ctlz: {
+    const APInt *ZeroIsPoison = Ops[1].getSingleElement();
+    assert(ZeroIsPoison && "Must be known (immarg)");
+    assert(ZeroIsPoison->getBitWidth() == 1 && "Must be boolean");
+    return Ops[0].ctlz(ZeroIsPoison->getBoolValue());
+  }
   default:
     assert(!isIntrinsicSupported(IntrinsicID) && "Shouldn't be supported");
     llvm_unreachable("Unsupported intrinsic");
@@ -1667,6 +1674,45 @@ ConstantRange ConstantRange::abs(bool IntMinIsPoison) const {
                                     APIntOps::umax(-SMin, SMax) + 1);
 }
 
+ConstantRange ConstantRange::ctlz(bool ZeroIsPoison) const {
+  if (isEmptySet())
+    return getEmpty();
+
+  APInt Zero = APInt::getZero(getBitWidth());
+  if (ZeroIsPoison && contains(Zero)) {
+    // ZeroIsPoison is set, and zero is contained. We discern three cases, in
+    // which a zero can appear:
+    // 1) Lower is zero, handling cases of kind [0, 1), [0, 2), etc.
+    // 2) Upper is zero, wrapped set, handling cases of kind [3, 0], etc.
+    // 3) Zero contained in a wrapped set, e.g., [3, 2), [3, 1), etc.
+
+    if (getLower().isZero()) {
+      if ((getUpper() - 1).isZero()) {
+        // We have in input interval of kind [0, 1). In this case we cannot
+        // really help but return empty-set.
+        return getEmpty();
+      }
+
+      // Compute the resulting range by excluding zero from Lower.
+      return ConstantRange(
+          APInt(getBitWidth(), (getUpper() - 1).countLeadingZeros()),
+          APInt(getBitWidth(), (getLower() + 1).countLeadingZeros() + 1));
+    } else if ((getUpper() - 1).isZero()) {
+      // Compute the resulting range by excluding zero from Upper.
+      return ConstantRange(
+          Zero, APInt(getBitWidth(), getLower().countLeadingZeros() + 1));
+    } else {
+      return ConstantRange(Zero, APInt(getBitWidth(), getBitWidth()));
+    }
+  }
+
+  // Zero is either safe or not in the range. The output range is composed by
+  // the result of countLeadingZero of the two extremes.
+  return getNonEmpty(
+      APInt(getBitWidth(), getUnsignedMax().countLeadingZeros()),
+      APInt(getBitWidth(), getUnsignedMin().countLeadingZeros() + 1));
+}
+
 ConstantRange::OverflowResult ConstantRange::unsignedAddMayOverflow(
     const ConstantRange &Other) const {
   if (isEmptySet() || Other.isEmptySet())

diff  --git a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
index 4a364458ed1a2..7e89f864c8110 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
@@ -964,12 +964,10 @@ define i1 @ctlz_fold(i16 %x) {
 ; CHECK-NEXT:    br i1 [[CMP]], label [[IF:%.*]], label [[ELSE:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    [[CTLZ:%.*]] = call i16 @llvm.ctlz.i16(i16 [[X]], i1 false)
-; CHECK-NEXT:    [[RES:%.*]] = icmp uge i16 [[CTLZ]], 8
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ; CHECK:       else:
 ; CHECK-NEXT:    [[CTLZ2:%.*]] = call i16 @llvm.ctlz.i16(i16 [[X]], i1 false)
-; CHECK-NEXT:    [[RES2:%.*]] = icmp ult i16 [[CTLZ2]], 8
-; CHECK-NEXT:    ret i1 [[RES2]]
+; CHECK-NEXT:    ret i1 true
 ;
   %cmp = icmp ult i16 %x, 256
   br i1 %cmp, label %if, label %else

diff  --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index b54746b005367..5d1931ebf6025 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -2397,6 +2397,21 @@ TEST_F(ConstantRangeTest, Abs) {
       });
 }
 
+TEST_F(ConstantRangeTest, Ctlz) {
+  TestUnaryOpExhaustive([](const ConstantRange &CR) { return CR.ctlz(); },
+                        [](const APInt &N) {
+                          return APInt(N.getBitWidth(), N.countLeadingZeros());
+                        });
+
+  TestUnaryOpExhaustive(
+      [](const ConstantRange &CR) { return CR.ctlz(/*ZeroIsPoison=*/true); },
+      [](const APInt &N) -> std::optional<APInt> {
+        if (N.isZero())
+          return std::nullopt;
+        return APInt(N.getBitWidth(), N.countLeadingZeros());
+      });
+}
+
 TEST_F(ConstantRangeTest, castOps) {
   ConstantRange A(APInt(16, 66), APInt(16, 128));
   ConstantRange FpToI8 = A.castOp(Instruction::FPToSI, 8);


        


More information about the llvm-commits mailing list