diff --git a/mlir/include/mlir/Analysis/Presburger/IntegerRelation.h b/mlir/include/mlir/Analysis/Presburger/IntegerRelation.h --- a/mlir/include/mlir/Analysis/Presburger/IntegerRelation.h +++ b/mlir/include/mlir/Analysis/Presburger/IntegerRelation.h @@ -324,6 +324,10 @@ /// Removes all equalities and inequalities. void clearConstraints(); + /// Clear all constraints and add an invalid equation indicating that the set + /// is empty. + void markSetEmpty(); + /// Sets the `values.size()` variables starting at `po`s to the specified /// values and removes them. void setAndEliminate(unsigned pos, ArrayRef values); @@ -351,6 +355,9 @@ /// Returns false otherwise. bool isEmpty() const; + /// Performs GCD checks and invalid constraint checks. + bool isPlainEmpty() const; + /// Runs the GCD test on all equality constraints. Returns true if this test /// fails on any equality. Returns false otherwise. /// This test can be used to disprove the existence of a solution. If it @@ -545,6 +552,11 @@ void removeDuplicateDivs(); + /// Simplify iteratively attempt to remove redundant equations by Gaussian + /// elimination and attempt to remove duplicate inequalities until a fixed + /// point is reached. + void simplify(); + /// Converts variables of kind srcKind in the range [varStart, varLimit) to /// variables of kind dstKind. If `pos` is given, the variables are placed at /// position `pos` of dstKind, otherwise they are placed after all the other @@ -724,6 +736,10 @@ /// Returns the number of variables eliminated. unsigned gaussianEliminateVars(unsigned posStart, unsigned posLimit); + /// Perform a Gaussian elimination operation to reduce all equations to + /// standard form. The return value indicates if anything was modified. + bool gaussianEliminate(); + /// Eliminates the variable at the specified position using Fourier-Motzkin /// variable elimination, but uses Gaussian elimination if there is an /// equality involving that variable. If the result of the elimination is @@ -755,6 +771,10 @@ /// equalities. bool isColZero(unsigned pos) const; + /// Checks for identical inequalities and eliminates redundant inequalities. + /// The return value indicates if anything has changed. + bool removeDuplicateConstraints(); + /// Returns false if the fields corresponding to various variable counts, or /// equality/inequality buffer sizes aren't consistent; true otherwise. This /// is meant to be used within an assert internally. diff --git a/mlir/include/mlir/Analysis/Presburger/PresburgerRelation.h b/mlir/include/mlir/Analysis/Presburger/PresburgerRelation.h --- a/mlir/include/mlir/Analysis/Presburger/PresburgerRelation.h +++ b/mlir/include/mlir/Analysis/Presburger/PresburgerRelation.h @@ -190,6 +190,10 @@ /// also be a union of convex disjuncts. PresburgerRelation computeReprWithOnlyDivLocals() const; + /// Simplify each disjunct, see IntegerRelation::simplify for details. If + /// disjunct is empty it will not be merged into the new set. + PresburgerRelation simplify() const; + /// Print the set's internal state. void print(raw_ostream &os) const; void dump() const; diff --git a/mlir/lib/Analysis/Presburger/IntegerRelation.cpp b/mlir/lib/Analysis/Presburger/IntegerRelation.cpp --- a/mlir/lib/Analysis/Presburger/IntegerRelation.cpp +++ b/mlir/lib/Analysis/Presburger/IntegerRelation.cpp @@ -445,6 +445,14 @@ inequalities.resizeVertically(0); } +void IntegerRelation::markSetEmpty() { + clearConstraints(); + unsigned col = getNumCols(); + SmallVector eqeff(col, 0); + eqeff.back() = 1; + addEquality(eqeff); +} + /// Gather all lower and upper bounds of the variable at `pos`, and /// optionally any equalities on it. In addition, the bounds are to be /// independent of variables in position range [`offset`, `offset` + `num`). @@ -701,6 +709,12 @@ return false; } +bool IntegerRelation::isPlainEmpty() const { + if (isEmptyByGCDTest() || hasInvalidConstraint()) + return true; + return false; +} + // Runs the GCD test on all equality constraints. Returns 'true' if this test // fails on any equality. Returns 'false' otherwise. // This test can be used to disprove the existence of a solution. If it returns @@ -1079,6 +1093,57 @@ return posLimit - posStart; } +bool IntegerRelation::gaussianEliminate() { + gcdTightenInequalities(); + unsigned firstVar = 0, vars = getNumVars(); + unsigned nowDone, eqs, pivotRow; + for (nowDone = 0, eqs = getNumEqualities(); nowDone < eqs; ++nowDone) { + // Finds the first non-empty column. + for (; firstVar < vars; ++firstVar) { + if (!findConstraintWithNonZeroAt(firstVar, true, &pivotRow)) + continue; + break; + } + // All columns have been normalized. + if (firstVar >= vars) + break; + + // The first pivot row found is below where it should currently be placed. + if (pivotRow > nowDone) { + equalities.swapRows(pivotRow, nowDone); + pivotRow = nowDone; + } + + // Normalize all lower equations and all inequalities. + for (unsigned i = nowDone + 1; i < eqs; ++i) { + eliminateFromConstraint(this, i, pivotRow, firstVar, 0, true); + equalities.normalizeRow(i); + } + for (unsigned i = 0, ineqs = getNumInequalities(); i < ineqs; ++i) { + eliminateFromConstraint(this, i, pivotRow, firstVar, 0, false); + inequalities.normalizeRow(i); + } + gcdTightenInequalities(); + } + + // No redundant rows. + if (nowDone == eqs) + return false; + + // Check to see if the redundant rows constant is zero, a non-zero value means + // the set is empty. + for (unsigned i = nowDone; i < eqs; ++i) { + if (atEq(i, vars) == 0) + continue; + + markSetEmpty(); + return true; + } + // Rows that are confirmed to be all zeros can be eliminated. + removeEqualityRange(nowDone, eqs); + return true; +} + // A more complex check to eliminate redundant inequalities. Uses FourierMotzkin // to check if a constraint is redundant. void IntegerRelation::removeRedundantInequalities() { @@ -1269,6 +1334,21 @@ divs.removeDuplicateDivs(merge); } +void IntegerRelation::simplify() { + bool changed = true; + // Repeat until we reach a fixed point. + while (changed) { + changed = false; + normalizeConstraintsByGCD(); + changed |= gaussianEliminate(); + if (isPlainEmpty()) + return; + changed |= removeDuplicateConstraints(); + } + // Current set is not empty. + return; +} + /// Removes local variables using equalities. Each equality is checked if it /// can be reduced to the form: `e = affine-expr`, where `e` is a local /// variable and `affine-expr` is an affine expression not containing `e`. @@ -2216,6 +2296,74 @@ return IntegerPolyhedron(std::move(copyRel)); } +bool IntegerRelation::removeDuplicateConstraints() { + bool changed = false; + SmallDenseMap, unsigned> hashTable; + unsigned ineqs = getNumInequalities(), cols = getNumCols(); + + if (ineqs <= 1) + return changed; + + // Check only the non-constant part of the constraint is the same. + auto row = getInequality(0).drop_back(); + hashTable.insert({row, 0}); + for (unsigned k = 1; k < ineqs; ++k) { + auto nRow = getInequality(k).drop_back(); + if (!hashTable.contains(nRow)) { + hashTable.insert({nRow, k}); + continue; + } + + // For identical cases, keep only the smaller part of the constant term. + unsigned l = hashTable[nRow]; + changed = true; + if (atIneq(k, cols - 1) <= atIneq(l, cols - 1)) + inequalities.swapRows(k, l); + removeInequality(k); + --k; + --ineqs; + } + + // Check the neg form of each inequality, need an extra space to store it. + inequalities.appendExtraRow(); + bool negChanged = false; + for (unsigned k = 0; k < ineqs; ++k) { + inequalities.copyRow(k, ineqs); + inequalities.negateRow(ineqs); + auto nRow = getInequality(ineqs).drop_back(); + if (!hashTable.contains(nRow)) + continue; + + // For cases where the neg is the same as other inequalities, check that the + // sum of their constant terms is positive. + unsigned l = hashTable[nRow]; + auto sum = atIneq(l, cols - 1) + atIneq(k, cols - 1); + if (sum > 0 || l == k) + continue; + + // A sum of constant terms equal to zero combines two inequalities into one + // equation, less than zero means the set is empty. + negChanged = true; + changed = true; + if (k < l) + std::swap(l, k); + if (sum == 0) { + addEquality(getInequality(k)); + removeInequality(ineqs); + removeInequality(k); + removeInequality(l); + } else + markSetEmpty(); + break; + } + + // Need to remove the extra space requested. + if (!negChanged) + removeInequality(ineqs); + + return changed; +} + IntegerPolyhedron IntegerRelation::getRangeSet() const { IntegerRelation copyRel = *this; diff --git a/mlir/lib/Analysis/Presburger/PresburgerRelation.cpp b/mlir/lib/Analysis/Presburger/PresburgerRelation.cpp --- a/mlir/lib/Analysis/Presburger/PresburgerRelation.cpp +++ b/mlir/lib/Analysis/Presburger/PresburgerRelation.cpp @@ -515,6 +515,9 @@ } } + // Try to simplify the results. + result = result.simplify(); + return result; } @@ -958,6 +961,17 @@ }); } +PresburgerRelation PresburgerRelation::simplify() const { + PresburgerRelation origin = *this; + PresburgerRelation result = PresburgerRelation(getSpace()); + for (IntegerRelation &disjunct : origin.disjuncts) { + disjunct.simplify(); + if (!disjunct.isPlainEmpty()) + result.unionInPlace(disjunct); + } + return result; +} + void PresburgerRelation::print(raw_ostream &os) const { os << "Number of Disjuncts: " << getNumDisjuncts() << "\n"; for (const IntegerRelation &disjunct : disjuncts) {