forked from OSchip/llvm-project
FlatAffineConstraints API cleanup; add normalizeConstraintsByGCD().
- add method normalizeConstraintsByGCD - call normalizeConstraintsByGCD() and GCDTightenInequalities() at the end of projectOut. - remove call to GCDTightenInequalities() from getMemRefRegion - change isEmpty() to check isEmptyByGCDTest() / hasInvalidConstraint() each time an identifier is eliminated (to detect emptiness early). - make FourierMotzkinEliminate, gaussianEliminateId(s), GCDTightenInequalities() private - improve / update stale comments PiperOrigin-RevId: 224866741
This commit is contained in:
parent
2ef57806ba
commit
6757fb151d
|
@ -202,7 +202,11 @@ class IntegerValueSet {
|
|||
// 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(); }
|
||||
|
@ -319,24 +323,6 @@ public:
|
|||
// 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;
|
||||
|
||||
|
@ -445,21 +431,13 @@ public:
|
|||
/// 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);
|
||||
|
@ -538,8 +516,8 @@ public:
|
|||
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.
|
||||
|
@ -560,6 +538,39 @@ private:
|
|||
/// '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.
|
||||
|
|
|
@ -772,8 +772,9 @@ findConstraintWithNonZeroAt(const FlatAffineConstraints &constraints,
|
|||
|
||||
// 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);
|
||||
|
@ -791,6 +792,15 @@ static void normalizeConstraintByGCD(FlatAffineConstraints *constraints,
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -809,7 +819,7 @@ bool FlatAffineConstraints::hasConsistentState() const {
|
|||
/// 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 {
|
||||
|
@ -925,20 +935,32 @@ void FlatAffineConstraints::removeIdRange(unsigned idStart, unsigned idLimit) {
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
@ -974,7 +996,7 @@ bool FlatAffineConstraints::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
|
||||
/// 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.
|
||||
|
@ -996,13 +1018,6 @@ void FlatAffineConstraints::GCDTightenInequalities() {
|
|||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
@ -1025,7 +1040,8 @@ 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;
|
||||
|
@ -1035,14 +1051,14 @@ unsigned FlatAffineConstraints::gaussianEliminateIds(unsigned posStart,
|
|||
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);
|
||||
}
|
||||
|
@ -1289,7 +1305,7 @@ bool FlatAffineConstraints::getDimensionBounds(unsigned pos, unsigned num,
|
|||
return false;
|
||||
(*lbs)[i] = AffineMap::getConstantMap(lb.getValue(), context);
|
||||
(*ubs)[i] = AffineMap::getConstantMap(ub.getValue(), context);
|
||||
projectOut(i, 1);
|
||||
projectOut(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1564,7 +1580,7 @@ void FlatAffineConstraints::print(raw_ostream &os) const {
|
|||
void FlatAffineConstraints::dump() const { print(llvm::errs()); }
|
||||
|
||||
void FlatAffineConstraints::removeDuplicates() {
|
||||
// TODO: remove redundant constraints.
|
||||
// TODO(mlir-team): remove redundant constraints.
|
||||
}
|
||||
|
||||
void FlatAffineConstraints::clearAndCopyFrom(
|
||||
|
@ -1647,9 +1663,6 @@ void FlatAffineConstraints::FourierMotzkinEliminate(
|
|||
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) {
|
||||
|
@ -1663,6 +1676,9 @@ void FlatAffineConstraints::FourierMotzkinEliminate(
|
|||
}
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
|
@ -1800,14 +1816,19 @@ void FlatAffineConstraints::FourierMotzkinEliminate(
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -225,9 +225,6 @@ bool mlir::getMemRefRegion(OperationStmt *opStmt, unsigned loopDepth,
|
|||
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.
|
||||
|
|
|
@ -60,6 +60,8 @@ FunctionPass *mlir::createSimplifyAffineStructuresPass() {
|
|||
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())
|
||||
|
|
|
@ -44,17 +44,17 @@
|
|||
|
||||
// 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 {
|
||||
|
@ -200,11 +200,14 @@ mlfunc @test_empty_set(%N : index) {
|
|||
"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"() : () -> ()
|
||||
|
|
Loading…
Reference in New Issue