[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