[Mlir-commits] [mlir] dea76cc - [MLIR] FlatAffineConstraints: Refactored computation of explicit representation for identifiers

Arjun P llvmlistbot at llvm.org
Wed Sep 8 07:55:34 PDT 2021


Author: Kunwar Shaanjeet Singh Grover
Date: 2021-09-08T20:24:46+05:30
New Revision: dea76ccaf40f1762915213d0ea91d0c707705683

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

LOG: [MLIR] FlatAffineConstraints: Refactored computation of explicit representation for identifiers

This patch refactors the existing implementation of computing an explicit
representation of an identifier as a floordiv in terms of other identifiers and
exposes this computation as a public function.

The computation of this representation is required to support local identifiers
in PresburgerSet subtract, complement and isEqual.

Reviewed By: bondhugula, arjunp

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

Added: 
    

Modified: 
    mlir/include/mlir/Analysis/AffineStructures.h
    mlir/lib/Analysis/AffineStructures.cpp
    mlir/unittests/Analysis/AffineStructuresTest.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Analysis/AffineStructures.h b/mlir/include/mlir/Analysis/AffineStructures.h
index 7cb53a271abe2..c2dbc5c89da73 100644
--- a/mlir/include/mlir/Analysis/AffineStructures.h
+++ b/mlir/include/mlir/Analysis/AffineStructures.h
@@ -156,7 +156,14 @@ class FlatAffineConstraints {
   /// otherwise.
   bool containsPoint(ArrayRef<int64_t> point) const;
 
-  /// Clones this object.
+  /// Find pairs of inequalities identified by their position indices, using
+  /// which an explicit representation for each local variable can be computed
+  /// The pairs are stored as indices of upperbound, lowerbound
+  /// inequalities. If no such pair can be found, it is stored as llvm::None.
+  void getLocalReprLbUbPairs(
+      std::vector<llvm::Optional<std::pair<unsigned, unsigned>>> &repr) const;
+
+  // Clones this object.
   std::unique_ptr<FlatAffineConstraints> clone() const;
 
   /// Returns the value at the specified equality row and column.

diff  --git a/mlir/lib/Analysis/AffineStructures.cpp b/mlir/lib/Analysis/AffineStructures.cpp
index be1c8f39cd004..43de94407db2b 100644
--- a/mlir/lib/Analysis/AffineStructures.cpp
+++ b/mlir/lib/Analysis/AffineStructures.cpp
@@ -1237,6 +1237,117 @@ bool FlatAffineConstraints::containsPoint(ArrayRef<int64_t> point) const {
   return true;
 }
 
+/// Check if the pos^th identifier can be expressed as a floordiv of an affine
+/// function of other identifiers (where the divisor is a positive constant),
+/// `foundRepr` contains a boolean for each identifier indicating if the
+/// explicit representation for that identifier has already been computed.
+static Optional<std::pair<unsigned, unsigned>>
+computeSingleVarRepr(const FlatAffineConstraints &cst,
+                     const SmallVector<bool, 8> &foundRepr, unsigned pos) {
+  assert(pos < cst.getNumIds() && "invalid position");
+  assert(foundRepr.size() == cst.getNumIds() &&
+         "Size of foundRepr does not match total number of variables");
+
+  SmallVector<unsigned, 4> lbIndices, ubIndices;
+  cst.getLowerAndUpperBoundIndices(pos, &lbIndices, &ubIndices);
+
+  // `id` is equivalent to `expr floordiv divisor` if there
+  // are constraints of the form:
+  //      0 <= expr - divisor * id <= divisor - 1
+  // Rearranging, we have:
+  //       divisor * id - expr + (divisor - 1) >= 0  <-- Lower bound for 'id'
+  //      -divisor * id + expr                 >= 0  <-- Upper bound for 'id'
+  //
+  // For example:
+  //       32*k >= 16*i + j - 31                 <-- Lower bound for 'k'
+  //       32*k  <= 16*i + j                     <-- Upper bound for 'k'
+  //       expr = 16*i + j, divisor = 32
+  //       k = ( 16*i + j ) floordiv 32
+  //
+  //       4q >= i + j - 2                       <-- Lower bound for 'q'
+  //       4q <= i + j + 1                       <-- Upper bound for 'q'
+  //       expr = i + j + 1, divisor = 4
+  //       q = (i + j + 1) floordiv 4
+  for (unsigned ubPos : ubIndices) {
+    for (unsigned lbPos : lbIndices) {
+      // Due to the form of the inequalities, sum of constants of the
+      // inequalities is (divisor - 1).
+      int64_t divisor = cst.atIneq(lbPos, cst.getNumCols() - 1) +
+                        cst.atIneq(ubPos, cst.getNumCols() - 1) + 1;
+
+      // Divisor should be positive.
+      if (divisor <= 0)
+        continue;
+
+      // Check if coeff of variable is equal to divisor.
+      if (divisor != cst.atIneq(lbPos, pos))
+        continue;
+
+      // Check if constraints are opposite of each other. Constant term
+      // is not required to be opposite and is not checked.
+      unsigned c = 0, f = 0;
+      for (c = 0, f = cst.getNumIds(); c < f; ++c)
+        if (cst.atIneq(ubPos, c) != -cst.atIneq(lbPos, c))
+          break;
+
+      if (c < f)
+        continue;
+
+      // Check if the inequalities depend on a variable for which
+      // an explicit representation has not been found yet.
+      // Exit to avoid circular dependencies between divisions.
+      for (c = 0, f = cst.getNumIds(); c < f; ++c) {
+        if (c == pos)
+          continue;
+        if (!foundRepr[c] && cst.atIneq(lbPos, c) != 0)
+          break;
+      }
+
+      // Expression can't be constructed as it depends on a yet unknown
+      // identifier.
+      // TODO: Visit/compute the identifiers in an order so that this doesn't
+      // happen. More complex but much more efficient.
+      if (c < f)
+        continue;
+
+      return std::make_pair(ubPos, lbPos);
+    }
+  }
+
+  return llvm::None;
+}
+
+/// Find pairs of inequalities identified by their position indices, using
+/// which an explicit representation for each local variable can be computed
+/// The pairs are stored as indices of upperbound, lowerbound
+/// inequalities. If no such pair can be found, it is stored as llvm::None.
+void FlatAffineConstraints::getLocalReprLbUbPairs(
+    std::vector<llvm::Optional<std::pair<unsigned, unsigned>>> &repr) const {
+  assert(repr.size() == getNumLocalIds() &&
+         "Size of repr does not match number of local variables");
+
+  SmallVector<bool, 8> foundRepr(getNumIds(), false);
+  for (unsigned i = 0, e = getNumDimAndSymbolIds(); i < e; ++i)
+    foundRepr[i] = true;
+
+  unsigned divOffset = getNumDimAndSymbolIds();
+  bool changed;
+  do {
+    // Each time changed is true, at end of this iteration, one or more local
+    // vars have been detected as floor divs.
+    changed = false;
+    for (unsigned i = 0, e = getNumLocalIds(); i < e; ++i) {
+      if (!foundRepr[i + divOffset]) {
+        if (auto res = computeSingleVarRepr(*this, foundRepr, divOffset + i)) {
+          foundRepr[i + divOffset] = true;
+          repr[i] = res;
+          changed = true;
+        }
+      }
+    }
+  } while (changed);
+}
+
 /// Tightens inequalities given that we are dealing with integer spaces. This is
 /// analogous to the GCD test but applied to inequalities. The constant term can
 /// be reduced to the preceding multiple of the GCD of the coefficients, i.e.,
@@ -1504,73 +1615,46 @@ static bool detectAsFloorDiv(const FlatAffineConstraints &cst, unsigned pos,
                              SmallVectorImpl<AffineExpr> &exprs) {
   assert(pos < cst.getNumIds() && "invalid position");
 
-  SmallVector<unsigned, 4> lbIndices, ubIndices;
-  cst.getLowerAndUpperBoundIndices(pos, &lbIndices, &ubIndices);
+  // Get upper-lower bound pair for this variable.
+  SmallVector<bool, 8> foundRepr(cst.getNumIds(), false);
+  for (unsigned i = 0, e = cst.getNumIds(); i < e; ++i)
+    if (exprs[i])
+      foundRepr[i] = true;
 
-  // Check if any lower bound, upper bound pair is of the form:
-  // divisor * id >=  expr - (divisor - 1)    <-- Lower bound for 'id'
-  // divisor * id <=  expr                    <-- Upper bound for 'id'
-  // Then, 'id' is equivalent to 'expr floordiv divisor'.  (where divisor > 1).
-  //
-  // For example:
-  //    32*k >= 16*i + j - 31                 <-- Lower bound for 'k'
-  //    32*k  <= 16*i + j                     <-- Upper bound for 'k'
-  //    expr = 16*i + j, divisor = 32
-  //    k = ( 16*i + j ) floordiv 32
+  auto ulPair = computeSingleVarRepr(cst, foundRepr, pos);
+
+  // No upper-lower bound pair found for this var.
+  if (!ulPair)
+    return false;
+
+  unsigned ubPos = ulPair->first;
+
+  // Upper bound is of the form:
+  //      -divisor * id + expr >= 0
+  // where `id` is equivalent to `expr floordiv divisor`.
   //
-  //    4q >= i + j - 2                       <-- Lower bound for 'q'
-  //    4q <= i + j + 1                       <-- Upper bound for 'q'
-  //    expr = i + j + 1, divisor = 4
-  //    q = (i + j + 1) floordiv 4
-  for (auto ubPos : ubIndices) {
-    for (auto lbPos : lbIndices) {
-      // Due to the form of the inequalities, the sum of constants of upper
-      // bound and lower bound is divisor - 1. The 'divisor' here is
-      // cst.atIneq(lbPos, pos) and we already know that it's positive (since
-      // cst.Ineq(lbPos, ...) is a lower bound expr for 'pos'.
-      // Check if this sum of constants is divisor - 1.
-      int64_t divisor = cst.atIneq(lbPos, pos);
-      int64_t constantSum = cst.atIneq(lbPos, cst.getNumCols() - 1) +
-                            cst.atIneq(ubPos, cst.getNumCols() - 1);
-      if (constantSum != divisor - 1)
-        continue;
-      // For the remaining part, check if the lower bound expr's coeff's are
-      // negations of corresponding upper bound ones'.
-      unsigned c, f;
-      for (c = 0, f = cst.getNumCols() - 1; c < f; ++c)
-        if (cst.atIneq(lbPos, c) != -cst.atIneq(ubPos, c))
-          break;
-      // Lb coeff's aren't negative of ub coeff's (for the non constant term
-      // part).
-      if (c < f)
-        continue;
-      // Due to the form of the upper bound inequality, the constant term of
-      // `expr` is the constant term of upper bound inequality.
-      int64_t divConstantTerm = cst.atIneq(ubPos, cst.getNumCols() - 1);
-      // Construct the dividend expression.
-      auto dividendExpr = getAffineConstantExpr(divConstantTerm, context);
-      for (c = 0, f = cst.getNumCols() - 1; c < f; ++c) {
-        if (c == pos)
-          continue;
-        int64_t ubVal = cst.atIneq(ubPos, c);
-        if (ubVal == 0)
-          continue;
-        if (!exprs[c])
-          break;
-        dividendExpr = dividendExpr + ubVal * exprs[c];
-      }
-      // Expression can't be constructed as it depends on a yet unknown
-      // identifier.
-      // TODO: Visit/compute the identifiers in an order so that this doesn't
-      // happen. More complex but much more efficient.
-      if (c < f)
-        continue;
-      // Successfully detected the floordiv.
-      exprs[pos] = dividendExpr.floorDiv(divisor);
-      return true;
-    }
+  // Since the division cannot be dependent on itself, the coefficient of
+  // of `id` in `expr` is zero. The coefficient of `id` in the upperbound
+  // is -divisor.
+  int64_t divisor = -cst.atIneq(ubPos, pos);
+  int64_t constantTerm = cst.atIneq(ubPos, cst.getNumCols() - 1);
+
+  // Construct the dividend expression.
+  auto dividendExpr = getAffineConstantExpr(constantTerm, context);
+  unsigned c, f;
+  for (c = 0, f = cst.getNumCols() - 1; c < f; c++) {
+    if (c == pos)
+      continue;
+    int64_t ubVal = cst.atIneq(ubPos, c);
+    if (ubVal == 0)
+      continue;
+    // computeSingleVarRepr guarantees that expr is known here.
+    dividendExpr = dividendExpr + ubVal * exprs[c];
   }
-  return false;
+
+  // Successfully detected the floordiv.
+  exprs[pos] = dividendExpr.floorDiv(divisor);
+  return true;
 }
 
 // Fills an inequality row with the value 'val'.

diff  --git a/mlir/unittests/Analysis/AffineStructuresTest.cpp b/mlir/unittests/Analysis/AffineStructuresTest.cpp
index 971ca2b2ce303..56f0dbdc950c4 100644
--- a/mlir/unittests/Analysis/AffineStructuresTest.cpp
+++ b/mlir/unittests/Analysis/AffineStructuresTest.cpp
@@ -589,25 +589,126 @@ TEST(FlatAffineConstraintsTest, clearConstraints) {
   EXPECT_EQ(fac.atIneq(0, 1), 0);
 }
 
-TEST(FlatAffineConstraintsTest, constantDivs) {
-  // This test checks if floordivs with numerator containing non zero constant
-  // term can be computed from a FlatAffineConstraints instance.
+/// Check if the expected division representation of local variables matches the
+/// computed representation. The expected division representation is given as
+/// a vector of expressions set in `divisions` and the corressponding
+/// denominator in `denoms`. If expected denominator for a variable is
+/// non-positive, the local variable is expected to not have a computed
+/// representation.
+static void checkDivisionRepresentation(
+    FlatAffineConstraints &fac,
+    const std::vector<SmallVector<int64_t, 8>> &divisions,
+    const SmallVector<int64_t, 8> &denoms) {
+
+  assert(divisions.size() == fac.getNumLocalIds() &&
+         "Size of expected divisions does not match number of local variables");
+  assert(
+      denoms.size() == fac.getNumLocalIds() &&
+      "Size of expected denominators does not match number of local variables");
+
+  std::vector<llvm::Optional<std::pair<unsigned, unsigned>>> res(
+      fac.getNumLocalIds(), llvm::None);
+  fac.getLocalReprLbUbPairs(res);
+
+  // Check if all expected divisions are computed.
+  for (unsigned i = 0, e = fac.getNumLocalIds(); i < e; ++i)
+    if (denoms[i] > 0)
+      EXPECT_TRUE(res[i].hasValue());
+    else
+      EXPECT_FALSE(res[i].hasValue());
+
+  unsigned divOffset = fac.getNumDimAndSymbolIds();
+  for (unsigned i = 0, e = fac.getNumLocalIds(); i < e; ++i) {
+    if (!res[i])
+      continue;
+
+    // Check if the bounds are of the form:
+    //      0 <= expr - divisor * id <= divisor - 1
+    // Rearranging, we have:
+    //       divisor * id - expr + (divisor - 1) >= 0  <-- Lower bound for 'id'
+    //      -divisor * id + expr                 >= 0  <-- Upper bound for 'id'
+    // where `id = expr floordiv divisor`.
+    unsigned ubPos = res[i]->first, lbPos = res[i]->second;
+    const SmallVector<int64_t, 8> &expr = divisions[i];
+
+    // Check if lower bound is of the correct form.
+    int64_t computedDivisorLb = fac.atIneq(lbPos, i + divOffset);
+    EXPECT_EQ(computedDivisorLb, denoms[i]);
+    for (unsigned c = 0, f = fac.getNumLocalIds(); c < f; ++c) {
+      if (c == i + divOffset)
+        continue;
+      EXPECT_EQ(fac.atIneq(lbPos, c), -expr[c]);
+    }
+    // Check if constant term of lower bound matches expected constant term.
+    EXPECT_EQ(fac.atIneq(lbPos, fac.getNumCols() - 1),
+              -expr.back() + (denoms[i] - 1));
+
+    // Check if upper bound is of the correct form.
+    int64_t computedDivisorUb = fac.atIneq(ubPos, i + divOffset);
+    EXPECT_EQ(computedDivisorUb, -denoms[i]);
+    for (unsigned c = 0, f = fac.getNumLocalIds(); c < f; ++c) {
+      if (c == i + divOffset)
+        continue;
+      EXPECT_EQ(fac.atIneq(ubPos, c), expr[c]);
+    }
+    // Check if constant term of upper bound matches expected constant term.
+    EXPECT_EQ(fac.atIneq(ubPos, fac.getNumCols() - 1), expr.back());
+  }
+}
+
+TEST(FlatAffineConstraintsTest, computeLocalReprSimple) {
+  FlatAffineConstraints fac = makeFACFromConstraints(1, {}, {});
+
+  fac.addLocalFloorDiv({1, 4}, 10);
+  fac.addLocalFloorDiv({1, 0, 100}, 10);
+
+  std::vector<SmallVector<int64_t, 8>> divisions = {{1, 0, 0, 4},
+                                                    {1, 0, 0, 100}};
+  SmallVector<int64_t, 8> denoms = {10, 10};
+
+  // Check if floordivs can be computed when no other inequalities exist
+  // and floor divs do not depend on each other.
+  checkDivisionRepresentation(fac, divisions, denoms);
+}
+
+TEST(FlatAffineConstraintsTest, computeLocalReprConstantFloorDiv) {
   FlatAffineConstraints fac = makeFACFromConstraints(4, {}, {});
 
-  // Build a FlatAffineConstraints instance with floordivs containing numerator
-  // with non zero constant term.
-  fac.addLocalFloorDiv({0, 1, 0, 0, 10}, 30);
-  fac.addLocalFloorDiv({1, 0, 0, 0, 0, 99}, 101);
+  fac.addInequality({1, 0, 3, 1, 2});
+  fac.addInequality({1, 2, -8, 1, 10});
+  fac.addEquality({1, 2, -4, 1, 10});
+
+  fac.addLocalFloorDiv({0, 0, 0, 0, 10}, 30);
+  fac.addLocalFloorDiv({0, 0, 0, 0, 0, 99}, 101);
+
+  std::vector<SmallVector<int64_t, 8>> divisions = {{0, 0, 0, 0, 0, 0, 10},
+                                                    {0, 0, 0, 0, 0, 0, 99}};
+  SmallVector<int64_t, 8> denoms = {30, 101};
+
+  // Check if floordivs with constant numerator can be computed.
+  checkDivisionRepresentation(fac, divisions, denoms);
+}
+
+TEST(FlatAffineConstraintsTest, computeLocalReprRecursive) {
+  FlatAffineConstraints fac = makeFACFromConstraints(4, {}, {});
+  fac.addInequality({1, 0, 3, 1, 2});
+  fac.addInequality({1, 2, -8, 1, 10});
+  fac.addEquality({1, 2, -4, 1, 10});
+
+  fac.addLocalFloorDiv({0, -2, 7, 2, 10}, 3);
+  fac.addLocalFloorDiv({3, 0, 9, 2, 2, 10}, 5);
+  fac.addLocalFloorDiv({0, 1, -123, 2, 0, -4, 10}, 3);
+
+  fac.addInequality({1, 2, -2, 1, -5, 0, 6, 100});
+  fac.addInequality({1, 2, -8, 1, 3, 7, 0, -9});
 
-  // Add inequalities using the local variables created above.
-  fac.addInequality({1, 0, 0, 0, 1, 0, 2});
-  fac.addInequality({1, 0, 0, 0, 0, 1, 5});
+  std::vector<SmallVector<int64_t, 8>> divisions = {{0, -2, 7, 2, 0, 0, 0, 10},
+                                                    {3, 0, 9, 2, 2, 0, 0, 10},
+                                                    {0, 1, -123, 2, 0, -4, 10}};
+  SmallVector<int64_t, 8> denoms = {3, 5, 3};
 
-  // FlatAffineConstraints::getAsIntegerSet returns a null integer set if an
-  // explicit representation for each local variable could not be found.
-  MLIRContext ctx;
-  IntegerSet iSet = fac.getAsIntegerSet(&ctx);
-  EXPECT_TRUE((bool)iSet);
+  // Check if floordivs which may depend on other floordivs can be computed.
+  checkDivisionRepresentation(fac, divisions, denoms);
 }
 
 } // namespace mlir


        


More information about the Mlir-commits mailing list