// This will lead to a single equality in 'set'.
explicit IntegerValueSet(const AffineValueMap &avm);
- /// Returns true if this integer set is empty.
+ /// Returns true if this integer set is determined to be empty. Emptiness is
+ /// checked by by eliminating identifiers successively (through either
+ /// Gaussian or Fourier-Motzkin) while using the GCD test and a trivial
+ /// constraint check. Returns 'true' if the constaint system is found to be
+ /// empty; false otherwise.
bool isEmpty() const;
bool getNumDims() const { return set.getNumDims(); }
// returns true, no integer solution to the equality constraints can exist.
bool isEmptyByGCDTest() const;
- /// Tightens inequalities given that we are dealing with integer spaces. This
- /// is similar 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.,
- /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a
- /// fast method (linear in the number of coefficients).
- void GCDTightenInequalities();
-
- // Eliminates a single identifier at 'position' from equality and inequality
- // constraints. Returns 'true' if the identifier was eliminated.
- // Returns 'false' otherwise.
- bool gaussianEliminateId(unsigned position);
-
- // Eliminates identifiers from equality and inequality constraints
- // in column range [posStart, posLimit).
- // Returns the number of variables eliminated.
- unsigned gaussianEliminateIds(unsigned posStart, unsigned posLimit);
-
// Clones this object.
std::unique_ptr<FlatAffineConstraints> clone() const;
/// if the composition fails (when vMap is a semi-affine map).
bool composeMap(AffineValueMap *vMap, unsigned pos = 0);
- /// Eliminates identifier at the specified position using Fourier-Motzkin
- /// variable elimination. If the result of the elimination is integer exact,
- /// *isResultIntegerExact is set to true. If 'darkShadow' is set to true, a
- /// potential under approximation (subset) of the rational shadow / exact
- /// integer shadow is computed.
- // See implementation comments for more details.
- void FourierMotzkinEliminate(unsigned pos, bool darkShadow = false,
- bool *isResultIntegerExact = nullptr);
-
/// Projects out (aka eliminates) 'num' identifiers starting at position
/// 'pos'. The resulting constraint system is the shadow along the dimensions
/// that still exist. This method may not always be integer exact.
// TODO(bondhugula): deal with integer exactness when necessary - can return a
// value to mark exactness for example.
void projectOut(unsigned pos, unsigned num);
+ inline void projectOut(unsigned pos) { return projectOut(pos, 1); }
/// Projects out the identifier that is associate with MLValue *.
void projectOut(MLValue *id);
SmallVectorImpl<AffineMap> *ubs,
MLIRContext *context);
- /// Returns true if the set is hyper-rectangular on the specified contiguous
- /// set of identifiers.
+ /// Returns true if the set can be trivially detected as being
+ /// hyper-rectangular on the specified contiguous set of identifiers.
bool isHyperRectangular(unsigned pos, unsigned num) const;
// More expensive ones.
/// 'false'otherwise.
bool hasInvalidConstraint() const;
+ // Eliminates a single identifier at 'position' from equality and inequality
+ // constraints. Returns 'true' if the identifier was eliminated, and false
+ // otherwise.
+ inline bool gaussianEliminateId(unsigned position) {
+ return gaussianEliminateIds(position, position + 1) == 1;
+ }
+
+ // Eliminates identifiers from equality and inequality constraints
+ // in column range [posStart, posLimit).
+ // Returns the number of variables eliminated.
+ unsigned gaussianEliminateIds(unsigned posStart, unsigned posLimit);
+
+ /// Eliminates identifier at the specified position using Fourier-Motzkin
+ /// variable elimination, but uses Gaussian elimination if there is an
+ /// equality involving that identifier. If the result of the elimination is
+ /// integer exact, *isResultIntegerExact is set to true. If 'darkShadow' is
+ /// set to true, a potential under approximation (subset) of the rational
+ /// shadow / exact integer shadow is computed.
+ // See implementation comments for more details.
+ void FourierMotzkinEliminate(unsigned pos, bool darkShadow = false,
+ bool *isResultIntegerExact = nullptr);
+
+ /// Tightens inequalities given that we are dealing with integer spaces. This
+ /// is similar 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.,
+ /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a
+ /// fast method (linear in the number of coefficients).
+ void GCDTightenInequalities();
+
+ /// Normalized each constraints by the GCD of its coefficients.
+ void normalizeConstraintsByGCD();
+
/// Removes identifiers in column range [idStart, idLimit), and copies any
/// remaining valid data into place, updates member variables, and resizes
/// arrays as needed.
// Normalizes the coefficient values across all columns in 'rowIDx' by their
// GCD in equality or inequality contraints as specified by 'isEq'.
+template <bool isEq>
static void normalizeConstraintByGCD(FlatAffineConstraints *constraints,
- unsigned rowIdx, bool isEq) {
+ unsigned rowIdx) {
auto at = [&](unsigned colIdx) -> int64_t {
return isEq ? constraints->atEq(rowIdx, colIdx)
: constraints->atIneq(rowIdx, colIdx);
}
}
+void FlatAffineConstraints::normalizeConstraintsByGCD() {
+ for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) {
+ normalizeConstraintByGCD</*isEq=*/true>(this, i);
+ }
+ for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) {
+ normalizeConstraintByGCD</*isEq=*/false>(this, i);
+ }
+}
+
bool FlatAffineConstraints::hasConsistentState() const {
if (inequalities.size() != getNumInequalities() * numReservedCols)
return false;
/// Checks all rows of equality/inequality constraints for trivial
/// contradictions (for example: 1 == 0, 0 >= 1), which may have surfaced
/// after elimination. Returns 'true' if an invalid constraint is found;
-/// 'false'otherwise.
+/// 'false' otherwise.
bool FlatAffineConstraints::hasInvalidConstraint() const {
assert(hasConsistentState());
auto check = [&](bool isEq) -> bool {
// No resize necessary. numReservedCols remains the same.
}
-// Performs variable elimination on all identifiers, runs the GCD test on
-// all equality constraint rows, and checks the constraint validity.
-// Returns 'true' if the GCD test fails on any row, or if any invalid
-// constraint is detected. Returns 'false' otherwise.
+// Checks for emptiness of the set by eliminating identifiers successively and
+// using the GCD test (on all equality constraints) and checking for trivially
+// invalid constraints. Returns 'true' if the constaint system is found to be
+// empty; false otherwise.
bool FlatAffineConstraints::isEmpty() const {
- if (isEmptyByGCDTest())
+ if (isEmptyByGCDTest() || hasInvalidConstraint())
return true;
+
auto tmpCst = clone();
- if (tmpCst->gaussianEliminateIds(0, numIds) < numIds) {
- for (unsigned i = 0, e = tmpCst->getNumIds(); i < e; i++)
+ for (unsigned i = 0, e = tmpCst->getNumIds(); i < e; i++) {
+ // We check emptiness through trivial checks after eliminating each ID to
+ // detect emptiness early. Since the checks isEmptyByGCDTest() and
+ // hasInvalidConstraint() are linear time and single sweep on the constraint
+ // buffer, this appears reasonable - but can optimize in the future.
+ if (tmpCst->gaussianEliminateId(0)) {
+ if (tmpCst->hasInvalidConstraint() || tmpCst->isEmptyByGCDTest())
+ return true;
+ } else {
tmpCst->FourierMotzkinEliminate(0);
+ // If the variable couldn't be eliminated by Gaussian, FM wouldn't have
+ // modified the equalities in any way. So no need to again run GCD test.
+ // Check for trivial invalid constraints.
+ if (tmpCst->hasInvalidConstraint())
+ return true;
+ }
}
- if (tmpCst->hasInvalidConstraint())
- return true;
return false;
}
}
/// Tightens inequalities given that we are dealing with integer spaces. This is
-/// similar to the GCD test but applied to inequalities. The constant term can
+/// 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.,
/// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a
/// fast method - linear in the number of coefficients.
}
}
-// Eliminates a single identifier at 'position' from equality and inequality
-// constraints. Returns 'true' if the identifier was eliminated.
-// Returns 'false' otherwise.
-bool FlatAffineConstraints::gaussianEliminateId(unsigned position) {
- return gaussianEliminateIds(position, position + 1) == 1;
-}
-
// Eliminates all identifer variables in column range [posStart, posLimit).
// Returns the number of variables eliminated.
unsigned FlatAffineConstraints::gaussianEliminateIds(unsigned posStart,
// No pivot row in equalities with non-zero at 'pivotCol'.
if (!findConstraintWithNonZeroAt(*this, pivotCol, /*isEq=*/false,
pivotRow)) {
- // If inequalities are also non-zero in 'pivotCol' it can be eliminated.
+ // If inequalities are also non-zero in 'pivotCol', it can be
+ // eliminated.
continue;
}
break;
for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) {
eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart,
/*isEq=*/true);
- normalizeConstraintByGCD(this, i, /*isEq=*/true);
+ normalizeConstraintByGCD</*isEq=*/true>(this, i);
}
// Eliminate identifier at 'pivotCol' from each inequality row.
for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) {
eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart,
/*isEq=*/false);
- normalizeConstraintByGCD(this, i, /*isEq=*/false);
+ normalizeConstraintByGCD</*isEq=*/false>(this, i);
}
removeEquality(pivotRow);
}
return false;
(*lbs)[i] = AffineMap::getConstantMap(lb.getValue(), context);
(*ubs)[i] = AffineMap::getConstantMap(ub.getValue(), context);
- projectOut(i, 1);
+ projectOut(i);
}
return true;
}
void FlatAffineConstraints::dump() const { print(llvm::errs()); }
void FlatAffineConstraints::removeDuplicates() {
- // TODO: remove redundant constraints.
+ // TODO(mlir-team): remove redundant constraints.
}
void FlatAffineConstraints::clearAndCopyFrom(
assert(pos < getNumIds() && "invalid position");
assert(hasConsistentState());
- // A fast linear time tightening.
- GCDTightenInequalities();
-
// Check if this identifier can be eliminated through a substitution.
for (unsigned r = 0, e = getNumEqualities(); r < e; r++) {
if (atEq(r, pos) != 0) {
}
}
+ // A fast linear time tightening.
+ GCDTightenInequalities();
+
// Check if the identifier appears at all in any of the inequalities.
unsigned r, e;
for (r = 0, e = getNumInequalities(); r < e; r++) {
}
void FlatAffineConstraints::projectOut(unsigned pos, unsigned num) {
- // 'pos' can be at most getNumCols() - 2.
if (num == 0)
return;
+
+ // 'pos' can be at most getNumCols() - 2.
assert(pos <= getNumCols() - 2 && "invalid position");
assert(pos + num < getNumCols() && "invalid range");
- for (unsigned i = 0; i < num; i++) {
+
+ for (unsigned i = 0; i < num; i++)
FourierMotzkinEliminate(pos);
- }
+
+ // Fast/trivial simplifications.
+ normalizeConstraintsByGCD();
+ GCDTightenInequalities();
}
void FlatAffineConstraints::projectOut(MLValue *id) {
regionCst->getNumSymbolIds(),
regionCst->getNumLocalIds());
- // Tighten the set.
- regionCst->GCDTightenInequalities();
-
// Set all identifiers appearing after the first 'rank' identifiers as
// symbolic identifiers - so that the ones correspoding to the memref
// dimensions are the dimensional identifiers for the memref region.
return new SimplifyAffineStructures();
}
+/// Performs basic integer set simplifications. Checks if it's empty, and
+/// replaces it with the canonical empty set if it is.
static IntegerSet simplifyIntegerSet(IntegerSet set) {
FlatAffineConstraints fac(set);
if (fac.isEmpty())
// Set for test case: test_gaussian_elimination_non_empty_set4
#set4 = (d0, d1)[s0, s1] : (d0 * 7 + d1 * 5 + s0 * 11 + s1 == 0,
- d0 * 5 - d1 * 11 + s0 * 7 + s1 == 0,
- d0 * 11 + d1 * 7 - s0 * 5 + s1 == 0,
- d0 * 7 + d1 * 5 + s0 * 11 + s1 == 0)
+ d0 * 5 - d1 * 11 + s0 * 7 + s1 == 0,
+ d0 * 11 + d1 * 7 - s0 * 5 + s1 == 0,
+ d0 * 7 + d1 * 5 + s0 * 11 + s1 == 0)
// Add invalid constraints to previous non-empty set to make it empty.
// Set for test case: test_gaussian_elimination_empty_set5
#set5 = (d0, d1)[s0, s1] : (d0 * 7 + d1 * 5 + s0 * 11 + s1 == 0,
d0 * 5 - d1 * 11 + s0 * 7 + s1 == 0,
- d0 * 11 + d1 * 7 - s0 * 5 + s1 == 0,
- d0 * 7 + d1 * 5 + s0 * 11 + s1 == 0,
- d0 - 1 == 0, d0 + 2 == 0)
+ d0 * 11 + d1 * 7 - s0 * 5 + s1 == 0,
+ d0 * 7 + d1 * 5 + s0 * 11 + s1 == 0,
+ d0 - 1 == 0, d0 + 2 == 0)
mlfunc @test() {
for %n0 = 0 to 127 {
"foo"() : () -> ()
}
// Same as above but with a combination of multiple identifiers. 4*d0 +
- // 8*d1 here is a multiple of 4, and so can't lie between 9 and 11.
+ // 8*d1 here is a multiple of 4, and so can't lie between 9 and 11. GCD
+ // tightening will tighten constraints to 4*d0 + 8*d1 >= 12 and 4*d0 +
+ // 8*d1 <= 8; hence infeasible.
// CHECK: if [[SET_EMPTY_2D]](%i2, %i3)
if (d0, d1) : (4*d0 + 8*d1 - 9 >= 0, -4*d0 - 8*d1 + 11 >= 0)(%k, %l) {
"foo"() : () -> ()
}
+ // Same as above but with equalities added into the mix.
// CHECK: if [[SET_EMPTY_3D]](%i2, %i2, %i3)
if (d0, d1, d2) : (d0 - 4*d2 == 0, d0 + 8*d1 - 9 >= 0, -d0 - 8*d1 + 11 >= 0)(%k, %k, %l) {
"foo"() : () -> ()