1 //===- IntegerRelation.h - MLIR IntegerRelation Class ---------*- C++ -*---===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // A class to represent a relation over integer tuples. A relation is 10 // represented as a constraint system over a space of tuples of integer valued 11 // variables supporting symbolic variables and existential quantification. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef MLIR_ANALYSIS_PRESBURGER_INTEGERRELATION_H 16 #define MLIR_ANALYSIS_PRESBURGER_INTEGERRELATION_H 17 18 #include "mlir/Analysis/Presburger/Fraction.h" 19 #include "mlir/Analysis/Presburger/Matrix.h" 20 #include "mlir/Analysis/Presburger/PresburgerSpace.h" 21 #include "mlir/Analysis/Presburger/Utils.h" 22 #include "llvm/ADT/DynamicAPInt.h" 23 #include "llvm/ADT/SmallVector.h" 24 #include "llvm/Support/LogicalResult.h" 25 #include <optional> 26 27 namespace mlir { 28 namespace presburger { 29 using llvm::DynamicAPInt; 30 using llvm::failure; 31 using llvm::int64fromDynamicAPInt; 32 using llvm::LogicalResult; 33 using llvm::SmallVectorImpl; 34 using llvm::success; 35 36 class IntegerRelation; 37 class IntegerPolyhedron; 38 class PresburgerSet; 39 class PresburgerRelation; 40 struct SymbolicLexOpt; 41 42 /// The type of bound: equal, lower bound or upper bound. 43 enum class BoundType { EQ, LB, UB }; 44 45 /// An IntegerRelation represents the set of points from a PresburgerSpace that 46 /// satisfy a list of affine constraints. Affine constraints can be inequalities 47 /// or equalities in the form: 48 /// 49 /// Inequality: c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} + c_n >= 0 50 /// Equality : c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} + c_n == 0 51 /// 52 /// where c_0, c_1, ..., c_n are integers and n is the total number of 53 /// variables in the space. 54 /// 55 /// Such a relation corresponds to the set of integer points lying in a convex 56 /// polyhedron. For example, consider the relation: 57 /// (x) -> (y) : (1 <= x <= 7, x = 2y) 58 /// These can be thought of as points in the polyhedron: 59 /// (x, y) : (1 <= x <= 7, x = 2y) 60 /// This relation contains the pairs (2, 1), (4, 2), and (6, 3). 61 /// 62 /// Since IntegerRelation makes a distinction between dimensions, VarKind::Range 63 /// and VarKind::Domain should be used to refer to dimension variables. 64 class IntegerRelation { 65 public: 66 /// All derived classes of IntegerRelation. 67 enum class Kind { 68 IntegerRelation, 69 IntegerPolyhedron, 70 FlatLinearConstraints, 71 FlatLinearValueConstraints, 72 FlatAffineValueConstraints, 73 FlatAffineRelation 74 }; 75 76 /// Constructs a relation reserving memory for the specified number 77 /// of constraints and variables. IntegerRelation(unsigned numReservedInequalities,unsigned numReservedEqualities,unsigned numReservedCols,const PresburgerSpace & space)78 IntegerRelation(unsigned numReservedInequalities, 79 unsigned numReservedEqualities, unsigned numReservedCols, 80 const PresburgerSpace &space) 81 : space(space), equalities(0, space.getNumVars() + 1, 82 numReservedEqualities, numReservedCols), 83 inequalities(0, space.getNumVars() + 1, numReservedInequalities, 84 numReservedCols) { 85 assert(numReservedCols >= space.getNumVars() + 1); 86 } 87 88 /// Constructs a relation with the specified number of dimensions and symbols. IntegerRelation(const PresburgerSpace & space)89 explicit IntegerRelation(const PresburgerSpace &space) 90 : IntegerRelation(/*numReservedInequalities=*/0, 91 /*numReservedEqualities=*/0, 92 /*numReservedCols=*/space.getNumVars() + 1, space) {} 93 94 virtual ~IntegerRelation() = default; 95 96 /// Return a system with no constraints, i.e., one which is satisfied by all 97 /// points. getUniverse(const PresburgerSpace & space)98 static IntegerRelation getUniverse(const PresburgerSpace &space) { 99 return IntegerRelation(space); 100 } 101 102 /// Return an empty system containing an invalid equation 0 = 1. getEmpty(const PresburgerSpace & space)103 static IntegerRelation getEmpty(const PresburgerSpace &space) { 104 IntegerRelation result(0, 1, space.getNumVars() + 1, space); 105 SmallVector<int64_t> invalidEq(space.getNumVars() + 1, 0); 106 invalidEq.back() = 1; 107 result.addEquality(invalidEq); 108 return result; 109 } 110 111 /// Return the kind of this IntegerRelation. getKind()112 virtual Kind getKind() const { return Kind::IntegerRelation; } 113 classof(const IntegerRelation * cst)114 static bool classof(const IntegerRelation *cst) { return true; } 115 116 // Clones this object. 117 std::unique_ptr<IntegerRelation> clone() const; 118 119 /// Returns a reference to the underlying space. getSpace()120 const PresburgerSpace &getSpace() const { return space; } 121 122 /// Set the space to `oSpace`, which should have the same number of ids as 123 /// the current space. 124 void setSpace(const PresburgerSpace &oSpace); 125 126 /// Set the space to `oSpace`, which should not have any local ids. 127 /// `oSpace` can have fewer ids than the current space; in that case, the 128 /// the extra ids in `this` that are not accounted for by `oSpace` will be 129 /// considered as local ids. `oSpace` should not have more ids than the 130 /// current space; this will result in an assert failure. 131 void setSpaceExceptLocals(const PresburgerSpace &oSpace); 132 133 /// Set the identifier for the ith variable of the specified kind of the 134 /// IntegerRelation's PresburgerSpace. The index is relative to the kind of 135 /// the variable. 136 void setId(VarKind kind, unsigned i, Identifier id); 137 resetIds()138 void resetIds() { space.resetIds(); } 139 140 /// Get the identifiers for the variables of specified varKind. Calls resetIds 141 /// on the relations space if identifiers are not enabled. 142 ArrayRef<Identifier> getIds(VarKind kind); 143 144 /// Returns a copy of the space without locals. getSpaceWithoutLocals()145 PresburgerSpace getSpaceWithoutLocals() const { 146 return PresburgerSpace::getRelationSpace(space.getNumDomainVars(), 147 space.getNumRangeVars(), 148 space.getNumSymbolVars()); 149 } 150 151 /// Appends constraints from `other` into `this`. This is equivalent to an 152 /// intersection with no simplification of any sort attempted. 153 void append(const IntegerRelation &other); 154 155 /// Return the intersection of the two relations. 156 /// If there are locals, they will be merged. 157 IntegerRelation intersect(IntegerRelation other) const; 158 159 /// Return whether `this` and `other` are equal. This is integer-exact 160 /// and somewhat expensive, since it uses the integer emptiness check 161 /// (see IntegerRelation::findIntegerSample()). 162 bool isEqual(const IntegerRelation &other) const; 163 164 /// Perform a quick equality check on `this` and `other`. The relations are 165 /// equal if the check return true, but may or may not be equal if the check 166 /// returns false. The equality check is performed in a plain manner, by 167 /// comparing if all the equalities and inequalities in `this` and `other` 168 /// are the same. 169 bool isObviouslyEqual(const IntegerRelation &other) const; 170 171 /// Return whether this is a subset of the given IntegerRelation. This is 172 /// integer-exact and somewhat expensive, since it uses the integer emptiness 173 /// check (see IntegerRelation::findIntegerSample()). 174 bool isSubsetOf(const IntegerRelation &other) const; 175 176 /// Returns the value at the specified equality row and column. atEq(unsigned i,unsigned j)177 inline DynamicAPInt atEq(unsigned i, unsigned j) const { 178 return equalities(i, j); 179 } 180 /// The same, but casts to int64_t. This is unsafe and will assert-fail if the 181 /// value does not fit in an int64_t. atEq64(unsigned i,unsigned j)182 inline int64_t atEq64(unsigned i, unsigned j) const { 183 return int64_t(equalities(i, j)); 184 } atEq(unsigned i,unsigned j)185 inline DynamicAPInt &atEq(unsigned i, unsigned j) { return equalities(i, j); } 186 187 /// Returns the value at the specified inequality row and column. atIneq(unsigned i,unsigned j)188 inline DynamicAPInt atIneq(unsigned i, unsigned j) const { 189 return inequalities(i, j); 190 } 191 /// The same, but casts to int64_t. This is unsafe and will assert-fail if the 192 /// value does not fit in an int64_t. atIneq64(unsigned i,unsigned j)193 inline int64_t atIneq64(unsigned i, unsigned j) const { 194 return int64_t(inequalities(i, j)); 195 } atIneq(unsigned i,unsigned j)196 inline DynamicAPInt &atIneq(unsigned i, unsigned j) { 197 return inequalities(i, j); 198 } 199 getNumConstraints()200 unsigned getNumConstraints() const { 201 return getNumInequalities() + getNumEqualities(); 202 } 203 getNumDomainVars()204 unsigned getNumDomainVars() const { return space.getNumDomainVars(); } getNumRangeVars()205 unsigned getNumRangeVars() const { return space.getNumRangeVars(); } getNumSymbolVars()206 unsigned getNumSymbolVars() const { return space.getNumSymbolVars(); } getNumLocalVars()207 unsigned getNumLocalVars() const { return space.getNumLocalVars(); } 208 getNumDimVars()209 unsigned getNumDimVars() const { return space.getNumDimVars(); } getNumDimAndSymbolVars()210 unsigned getNumDimAndSymbolVars() const { 211 return space.getNumDimAndSymbolVars(); 212 } getNumVars()213 unsigned getNumVars() const { return space.getNumVars(); } 214 215 /// Returns the number of columns in the constraint system. getNumCols()216 inline unsigned getNumCols() const { return space.getNumVars() + 1; } 217 getNumEqualities()218 inline unsigned getNumEqualities() const { return equalities.getNumRows(); } 219 getNumInequalities()220 inline unsigned getNumInequalities() const { 221 return inequalities.getNumRows(); 222 } 223 getNumReservedEqualities()224 inline unsigned getNumReservedEqualities() const { 225 return equalities.getNumReservedRows(); 226 } 227 getNumReservedInequalities()228 inline unsigned getNumReservedInequalities() const { 229 return inequalities.getNumReservedRows(); 230 } 231 getEquality(unsigned idx)232 inline ArrayRef<DynamicAPInt> getEquality(unsigned idx) const { 233 return equalities.getRow(idx); 234 } getInequality(unsigned idx)235 inline ArrayRef<DynamicAPInt> getInequality(unsigned idx) const { 236 return inequalities.getRow(idx); 237 } 238 /// The same, but casts to int64_t. This is unsafe and will assert-fail if the 239 /// value does not fit in an int64_t. getEquality64(unsigned idx)240 inline SmallVector<int64_t, 8> getEquality64(unsigned idx) const { 241 return getInt64Vec(equalities.getRow(idx)); 242 } getInequality64(unsigned idx)243 inline SmallVector<int64_t, 8> getInequality64(unsigned idx) const { 244 return getInt64Vec(inequalities.getRow(idx)); 245 } 246 getInequalities()247 inline IntMatrix getInequalities() const { return inequalities; } 248 249 /// Get the number of vars of the specified kind. getNumVarKind(VarKind kind)250 unsigned getNumVarKind(VarKind kind) const { 251 return space.getNumVarKind(kind); 252 } 253 254 /// Return the index at which the specified kind of vars starts. getVarKindOffset(VarKind kind)255 unsigned getVarKindOffset(VarKind kind) const { 256 return space.getVarKindOffset(kind); 257 } 258 259 /// Return the index at Which the specified kind of vars ends. getVarKindEnd(VarKind kind)260 unsigned getVarKindEnd(VarKind kind) const { 261 return space.getVarKindEnd(kind); 262 } 263 264 /// Get the number of elements of the specified kind in the range 265 /// [varStart, varLimit). getVarKindOverlap(VarKind kind,unsigned varStart,unsigned varLimit)266 unsigned getVarKindOverlap(VarKind kind, unsigned varStart, 267 unsigned varLimit) const { 268 return space.getVarKindOverlap(kind, varStart, varLimit); 269 } 270 271 /// Return the VarKind of the var at the specified position. getVarKindAt(unsigned pos)272 VarKind getVarKindAt(unsigned pos) const { return space.getVarKindAt(pos); } 273 274 /// The struct CountsSnapshot stores the count of each VarKind, and also of 275 /// each constraint type. getCounts() returns a CountsSnapshot object 276 /// describing the current state of the IntegerRelation. truncate() truncates 277 /// all vars of each VarKind and all constraints of both kinds beyond the 278 /// counts in the specified CountsSnapshot object. This can be used to achieve 279 /// rudimentary rollback support. As long as none of the existing constraints 280 /// or vars are disturbed, and only additional vars or constraints are added, 281 /// this addition can be rolled back using truncate. 282 struct CountsSnapshot { 283 public: CountsSnapshotCountsSnapshot284 CountsSnapshot(const PresburgerSpace &space, unsigned numIneqs, 285 unsigned numEqs) 286 : space(space), numIneqs(numIneqs), numEqs(numEqs) {} getSpaceCountsSnapshot287 const PresburgerSpace &getSpace() const { return space; }; getNumIneqsCountsSnapshot288 unsigned getNumIneqs() const { return numIneqs; } getNumEqsCountsSnapshot289 unsigned getNumEqs() const { return numEqs; } 290 291 private: 292 PresburgerSpace space; 293 unsigned numIneqs, numEqs; 294 }; 295 CountsSnapshot getCounts() const; 296 void truncate(const CountsSnapshot &counts); 297 298 /// Insert `num` variables of the specified kind at position `pos`. 299 /// Positions are relative to the kind of variable. The coefficient columns 300 /// corresponding to the added variables are initialized to zero. Return the 301 /// absolute column position (i.e., not relative to the kind of variable) 302 /// of the first added variable. 303 virtual unsigned insertVar(VarKind kind, unsigned pos, unsigned num = 1); 304 305 /// Append `num` variables of the specified kind after the last variable 306 /// of that kind. The coefficient columns corresponding to the added variables 307 /// are initialized to zero. Return the absolute column position (i.e., not 308 /// relative to the kind of variable) of the first appended variable. 309 unsigned appendVar(VarKind kind, unsigned num = 1); 310 311 /// Adds an inequality (>= 0) from the coefficients specified in `inEq`. 312 void addInequality(ArrayRef<DynamicAPInt> inEq); addInequality(ArrayRef<int64_t> inEq)313 void addInequality(ArrayRef<int64_t> inEq) { 314 addInequality(getDynamicAPIntVec(inEq)); 315 } 316 /// Adds an equality from the coefficients specified in `eq`. 317 void addEquality(ArrayRef<DynamicAPInt> eq); addEquality(ArrayRef<int64_t> eq)318 void addEquality(ArrayRef<int64_t> eq) { 319 addEquality(getDynamicAPIntVec(eq)); 320 } 321 322 /// Eliminate the `posB^th` local variable, replacing every instance of it 323 /// with the `posA^th` local variable. This should be used when the two 324 /// local variables are known to always take the same values. 325 virtual void eliminateRedundantLocalVar(unsigned posA, unsigned posB); 326 327 /// Removes variables of the specified kind with the specified pos (or 328 /// within the specified range) from the system. The specified location is 329 /// relative to the first variable of the specified kind. 330 void removeVar(VarKind kind, unsigned pos); 331 virtual void removeVarRange(VarKind kind, unsigned varStart, 332 unsigned varLimit); 333 334 /// Removes the specified variable from the system. 335 void removeVar(unsigned pos); 336 337 void removeEquality(unsigned pos); 338 void removeInequality(unsigned pos); 339 340 /// Remove the (in)equalities at positions [start, end). 341 void removeEqualityRange(unsigned start, unsigned end); 342 void removeInequalityRange(unsigned start, unsigned end); 343 344 /// Get the lexicographically minimum rational point satisfying the 345 /// constraints. Returns an empty optional if the relation is empty or if 346 /// the lexmin is unbounded. Symbols are not supported and will result in 347 /// assert-failure. Note that Domain is minimized first, then range. 348 MaybeOptimum<SmallVector<Fraction, 8>> findRationalLexMin() const; 349 350 /// Same as above, but returns lexicographically minimal integer point. 351 /// Note: this should be used only when the lexmin is really required. 352 /// For a generic integer sampling operation, findIntegerSample is more 353 /// robust and should be preferred. Note that Domain is minimized first, then 354 /// range. 355 MaybeOptimum<SmallVector<DynamicAPInt, 8>> findIntegerLexMin() const; 356 357 /// Swap the posA^th variable with the posB^th variable. 358 virtual void swapVar(unsigned posA, unsigned posB); 359 360 /// Removes all equalities and inequalities. 361 void clearConstraints(); 362 363 /// Sets the `values.size()` variables starting at `po`s to the specified 364 /// values and removes them. 365 void setAndEliminate(unsigned pos, ArrayRef<DynamicAPInt> values); setAndEliminate(unsigned pos,ArrayRef<int64_t> values)366 void setAndEliminate(unsigned pos, ArrayRef<int64_t> values) { 367 setAndEliminate(pos, getDynamicAPIntVec(values)); 368 } 369 370 /// Replaces the contents of this IntegerRelation with `other`. 371 virtual void clearAndCopyFrom(const IntegerRelation &other); 372 373 /// Gather positions of all lower and upper bounds of the variable at `pos`, 374 /// and optionally any equalities on it. In addition, the bounds are to be 375 /// independent of variables in position range [`offset`, `offset` + `num`). 376 void 377 getLowerAndUpperBoundIndices(unsigned pos, 378 SmallVectorImpl<unsigned> *lbIndices, 379 SmallVectorImpl<unsigned> *ubIndices, 380 SmallVectorImpl<unsigned> *eqIndices = nullptr, 381 unsigned offset = 0, unsigned num = 0) const; 382 383 /// Checks for emptiness by performing variable elimination on all 384 /// variables, running the GCD test on each equality constraint, and 385 /// checking for invalid constraints. Returns true if the GCD test fails for 386 /// any equality, or if any invalid constraints are discovered on any row. 387 /// Returns false otherwise. 388 bool isEmpty() const; 389 390 /// Performs GCD checks and invalid constraint checks. 391 bool isObviouslyEmpty() const; 392 393 /// Runs the GCD test on all equality constraints. Returns true if this test 394 /// fails on any equality. Returns false otherwise. 395 /// This test can be used to disprove the existence of a solution. If it 396 /// returns true, no integer solution to the equality constraints can exist. 397 bool isEmptyByGCDTest() const; 398 399 /// Returns true if the set of constraints is found to have no solution, 400 /// false if a solution exists. Uses the same algorithm as 401 /// `findIntegerSample`. 402 bool isIntegerEmpty() const; 403 404 /// Returns a matrix where each row is a vector along which the polytope is 405 /// bounded. The span of the returned vectors is guaranteed to contain all 406 /// such vectors. The returned vectors are NOT guaranteed to be linearly 407 /// independent. This function should not be called on empty sets. 408 IntMatrix getBoundedDirections() const; 409 410 /// Find an integer sample point satisfying the constraints using a 411 /// branch and bound algorithm with generalized basis reduction, with some 412 /// additional processing using Simplex for unbounded sets. 413 /// 414 /// Returns an integer sample point if one exists, or an empty Optional 415 /// otherwise. The returned value also includes values of local ids. 416 std::optional<SmallVector<DynamicAPInt, 8>> findIntegerSample() const; 417 418 /// Compute an overapproximation of the number of integer points in the 419 /// relation. Symbol vars currently not supported. If the computed 420 /// overapproximation is infinite, an empty optional is returned. 421 std::optional<DynamicAPInt> computeVolume() const; 422 423 /// Returns true if the given point satisfies the constraints, or false 424 /// otherwise. Takes the values of all vars including locals. 425 bool containsPoint(ArrayRef<DynamicAPInt> point) const; containsPoint(ArrayRef<int64_t> point)426 bool containsPoint(ArrayRef<int64_t> point) const { 427 return containsPoint(getDynamicAPIntVec(point)); 428 } 429 /// Given the values of non-local vars, return a satisfying assignment to the 430 /// local if one exists, or an empty optional otherwise. 431 std::optional<SmallVector<DynamicAPInt, 8>> 432 containsPointNoLocal(ArrayRef<DynamicAPInt> point) const; 433 std::optional<SmallVector<DynamicAPInt, 8>> containsPointNoLocal(ArrayRef<int64_t> point)434 containsPointNoLocal(ArrayRef<int64_t> point) const { 435 return containsPointNoLocal(getDynamicAPIntVec(point)); 436 } 437 438 /// Returns a `DivisonRepr` representing the division representation of local 439 /// variables in the constraint system. 440 /// 441 /// If `repr` is not `nullptr`, the equality and pairs of inequality 442 /// constraints identified by their position indices using which an explicit 443 /// representation for each local variable can be computed are set in `repr` 444 /// in the form of a `MaybeLocalRepr` struct. If no such inequality 445 /// pair/equality can be found, the kind attribute in `MaybeLocalRepr` is set 446 /// to None. 447 DivisionRepr getLocalReprs(std::vector<MaybeLocalRepr> *repr = nullptr) const; 448 449 /// Adds a constant bound for the specified variable. 450 void addBound(BoundType type, unsigned pos, const DynamicAPInt &value); addBound(BoundType type,unsigned pos,int64_t value)451 void addBound(BoundType type, unsigned pos, int64_t value) { 452 addBound(type, pos, DynamicAPInt(value)); 453 } 454 455 /// Adds a constant bound for the specified expression. 456 void addBound(BoundType type, ArrayRef<DynamicAPInt> expr, 457 const DynamicAPInt &value); addBound(BoundType type,ArrayRef<int64_t> expr,int64_t value)458 void addBound(BoundType type, ArrayRef<int64_t> expr, int64_t value) { 459 addBound(type, getDynamicAPIntVec(expr), DynamicAPInt(value)); 460 } 461 462 /// Adds a new local variable as the floordiv of an affine function of other 463 /// variables, the coefficients of which are provided in `dividend` and with 464 /// respect to a positive constant `divisor`. Two constraints are added to the 465 /// system to capture equivalence with the floordiv: 466 /// q = dividend floordiv c <=> c*q <= dividend <= c*q + c - 1. 467 void addLocalFloorDiv(ArrayRef<DynamicAPInt> dividend, 468 const DynamicAPInt &divisor); addLocalFloorDiv(ArrayRef<int64_t> dividend,int64_t divisor)469 void addLocalFloorDiv(ArrayRef<int64_t> dividend, int64_t divisor) { 470 addLocalFloorDiv(getDynamicAPIntVec(dividend), DynamicAPInt(divisor)); 471 } 472 473 /// Projects out (aka eliminates) `num` variables starting at position 474 /// `pos`. The resulting constraint system is the shadow along the dimensions 475 /// that still exist. This method may not always be integer exact. 476 // TODO: deal with integer exactness when necessary - can return a value to 477 // mark exactness for example. 478 void projectOut(unsigned pos, unsigned num); projectOut(unsigned pos)479 inline void projectOut(unsigned pos) { return projectOut(pos, 1); } 480 481 /// Tries to fold the specified variable to a constant using a trivial 482 /// equality detection; if successful, the constant is substituted for the 483 /// variable everywhere in the constraint system and then removed from the 484 /// system. 485 LogicalResult constantFoldVar(unsigned pos); 486 487 /// This method calls `constantFoldVar` for the specified range of variables, 488 /// `num` variables starting at position `pos`. 489 void constantFoldVarRange(unsigned pos, unsigned num); 490 491 /// Updates the constraints to be the smallest bounding (enclosing) box that 492 /// contains the points of `this` set and that of `other`, with the symbols 493 /// being treated specially. For each of the dimensions, the min of the lower 494 /// bounds (symbolic) and the max of the upper bounds (symbolic) is computed 495 /// to determine such a bounding box. `other` is expected to have the same 496 /// dimensional variables as this constraint system (in the same order). 497 /// 498 /// E.g.: 499 /// 1) this = {0 <= d0 <= 127}, 500 /// other = {16 <= d0 <= 192}, 501 /// output = {0 <= d0 <= 192} 502 /// 2) this = {s0 + 5 <= d0 <= s0 + 20}, 503 /// other = {s0 + 1 <= d0 <= s0 + 9}, 504 /// output = {s0 + 1 <= d0 <= s0 + 20} 505 /// 3) this = {0 <= d0 <= 5, 1 <= d1 <= 9} 506 /// other = {2 <= d0 <= 6, 5 <= d1 <= 15}, 507 /// output = {0 <= d0 <= 6, 1 <= d1 <= 15} 508 LogicalResult unionBoundingBox(const IntegerRelation &other); 509 510 /// Returns the smallest known constant bound for the extent of the specified 511 /// variable (pos^th), i.e., the smallest known constant that is greater 512 /// than or equal to 'exclusive upper bound' - 'lower bound' of the 513 /// variable. This constant bound is guaranteed to be non-negative. Returns 514 /// std::nullopt if it's not a constant. This method employs trivial (low 515 /// complexity / cost) checks and detection. Symbolic variables are treated 516 /// specially, i.e., it looks for constant differences between affine 517 /// expressions involving only the symbolic variables. `lb` and `ub` (along 518 /// with the `boundFloorDivisor`) are set to represent the lower and upper 519 /// bound associated with the constant difference: `lb`, `ub` have the 520 /// coefficients, and `boundFloorDivisor`, their divisor. `minLbPos` and 521 /// `minUbPos` if non-null are set to the position of the constant lower bound 522 /// and upper bound respectively (to the same if they are from an 523 /// equality). Ex: if the lower bound is [(s0 + s2 - 1) floordiv 32] for a 524 /// system with three symbolic variables, *lb = [1, 0, 1], lbDivisor = 32. See 525 /// comments at function definition for examples. 526 std::optional<DynamicAPInt> getConstantBoundOnDimSize( 527 unsigned pos, SmallVectorImpl<DynamicAPInt> *lb = nullptr, 528 DynamicAPInt *boundFloorDivisor = nullptr, 529 SmallVectorImpl<DynamicAPInt> *ub = nullptr, unsigned *minLbPos = nullptr, 530 unsigned *minUbPos = nullptr) const; 531 /// The same, but casts to int64_t. This is unsafe and will assert-fail if the 532 /// value does not fit in an int64_t. 533 std::optional<int64_t> getConstantBoundOnDimSize64( 534 unsigned pos, SmallVectorImpl<int64_t> *lb = nullptr, 535 int64_t *boundFloorDivisor = nullptr, 536 SmallVectorImpl<int64_t> *ub = nullptr, unsigned *minLbPos = nullptr, 537 unsigned *minUbPos = nullptr) const { 538 SmallVector<DynamicAPInt, 8> ubDynamicAPInt, lbDynamicAPInt; 539 DynamicAPInt boundFloorDivisorDynamicAPInt; 540 std::optional<DynamicAPInt> result = getConstantBoundOnDimSize( 541 pos, &lbDynamicAPInt, &boundFloorDivisorDynamicAPInt, &ubDynamicAPInt, 542 minLbPos, minUbPos); 543 if (lb) 544 *lb = getInt64Vec(lbDynamicAPInt); 545 if (ub) 546 *ub = getInt64Vec(ubDynamicAPInt); 547 if (boundFloorDivisor) 548 *boundFloorDivisor = static_cast<int64_t>(boundFloorDivisorDynamicAPInt); 549 return llvm::transformOptional(result, int64fromDynamicAPInt); 550 } 551 552 /// Returns the constant bound for the pos^th variable if there is one; 553 /// std::nullopt otherwise. 554 std::optional<DynamicAPInt> getConstantBound(BoundType type, 555 unsigned pos) const; 556 /// The same, but casts to int64_t. This is unsafe and will assert-fail if the 557 /// value does not fit in an int64_t. getConstantBound64(BoundType type,unsigned pos)558 std::optional<int64_t> getConstantBound64(BoundType type, 559 unsigned pos) const { 560 return llvm::transformOptional(getConstantBound(type, pos), 561 int64fromDynamicAPInt); 562 } 563 564 /// Removes constraints that are independent of (i.e., do not have a 565 /// coefficient) variables in the range [pos, pos + num). 566 void removeIndependentConstraints(unsigned pos, unsigned num); 567 568 /// Returns true if the set can be trivially detected as being 569 /// hyper-rectangular on the specified contiguous set of variables. 570 bool isHyperRectangular(unsigned pos, unsigned num) const; 571 572 /// Removes duplicate constraints, trivially true constraints, and constraints 573 /// that can be detected as redundant as a result of differing only in their 574 /// constant term part. A constraint of the form <non-negative constant> >= 0 575 /// is considered trivially true. This method is a linear time method on the 576 /// constraints, does a single scan, and updates in place. It also normalizes 577 /// constraints by their GCD and performs GCD tightening on inequalities. 578 void removeTrivialRedundancy(); 579 580 /// A more expensive check than `removeTrivialRedundancy` to detect redundant 581 /// inequalities. 582 void removeRedundantInequalities(); 583 584 /// Removes redundant constraints using Simplex. Although the algorithm can 585 /// theoretically take exponential time in the worst case (rare), it is known 586 /// to perform much better in the average case. If V is the number of vertices 587 /// in the polytope and C is the number of constraints, the algorithm takes 588 /// O(VC) time. 589 void removeRedundantConstraints(); 590 591 void removeDuplicateDivs(); 592 593 /// Simplify the constraint system by removing canonicalizing constraints and 594 /// removing redundant constraints. 595 void simplify(); 596 597 /// Converts variables of kind srcKind in the range [varStart, varLimit) to 598 /// variables of kind dstKind. If `pos` is given, the variables are placed at 599 /// position `pos` of dstKind, otherwise they are placed after all the other 600 /// variables of kind dstKind. The internal ordering among the moved variables 601 /// is preserved. 602 void convertVarKind(VarKind srcKind, unsigned varStart, unsigned varLimit, 603 VarKind dstKind, unsigned pos); convertVarKind(VarKind srcKind,unsigned varStart,unsigned varLimit,VarKind dstKind)604 void convertVarKind(VarKind srcKind, unsigned varStart, unsigned varLimit, 605 VarKind dstKind) { 606 convertVarKind(srcKind, varStart, varLimit, dstKind, 607 getNumVarKind(dstKind)); 608 } convertToLocal(VarKind kind,unsigned varStart,unsigned varLimit)609 void convertToLocal(VarKind kind, unsigned varStart, unsigned varLimit) { 610 convertVarKind(kind, varStart, varLimit, VarKind::Local); 611 } 612 613 /// Merge and align symbol variables of `this` and `other` with respect to 614 /// identifiers. After this operation the symbol variables of both relations 615 /// have the same identifiers in the same order. 616 void mergeAndAlignSymbols(IntegerRelation &other); 617 618 /// Adds additional local vars to the sets such that they both have the union 619 /// of the local vars in each set, without changing the set of points that 620 /// lie in `this` and `other`. 621 /// 622 /// While taking union, if a local var in `other` has a division 623 /// representation which is a duplicate of division representation, of another 624 /// local var, it is not added to the final union of local vars and is instead 625 /// merged. The new ordering of local vars is: 626 /// 627 /// [Local vars of `this`] [Non-merged local vars of `other`] 628 /// 629 /// The relative ordering of local vars is same as before. 630 /// 631 /// After merging, if the `i^th` local variable in one set has a known 632 /// division representation, then the `i^th` local variable in the other set 633 /// either has the same division representation or no known division 634 /// representation. 635 /// 636 /// The spaces of both relations should be compatible. 637 /// 638 /// Returns the number of non-merged local vars of `other`, i.e. the number of 639 /// locals that have been added to `this`. 640 unsigned mergeLocalVars(IntegerRelation &other); 641 642 /// Check whether all local ids have a division representation. 643 bool hasOnlyDivLocals() const; 644 645 /// Changes the partition between dimensions and symbols. Depending on the new 646 /// symbol count, either a chunk of dimensional variables immediately before 647 /// the split become symbols, or some of the symbols immediately after the 648 /// split become dimensions. setDimSymbolSeparation(unsigned newSymbolCount)649 void setDimSymbolSeparation(unsigned newSymbolCount) { 650 space.setVarSymbolSeparation(newSymbolCount); 651 } 652 653 /// Return a set corresponding to all points in the domain of the relation. 654 IntegerPolyhedron getDomainSet() const; 655 656 /// Return a set corresponding to all points in the range of the relation. 657 IntegerPolyhedron getRangeSet() const; 658 659 /// Intersect the given `poly` with the domain in-place. 660 /// 661 /// Formally, let the relation `this` be R: A -> B and poly is C, then this 662 /// operation modifies R to be (A intersection C) -> B. 663 void intersectDomain(const IntegerPolyhedron &poly); 664 665 /// Intersect the given `poly` with the range in-place. 666 /// 667 /// Formally, let the relation `this` be R: A -> B and poly is C, then this 668 /// operation modifies R to be A -> (B intersection C). 669 void intersectRange(const IntegerPolyhedron &poly); 670 671 /// Invert the relation i.e., swap its domain and range. 672 /// 673 /// Formally, let the relation `this` be R: A -> B, then this operation 674 /// modifies R to be B -> A. 675 void inverse(); 676 677 /// Let the relation `this` be R1, and the relation `rel` be R2. Modifies R1 678 /// to be the composition of R1 and R2: R1;R2. 679 /// 680 /// Formally, if R1: A -> B, and R2: B -> C, then this function returns a 681 /// relation R3: A -> C such that a point (a, c) belongs to R3 iff there 682 /// exists b such that (a, b) is in R1 and, (b, c) is in R2. 683 void compose(const IntegerRelation &rel); 684 685 /// Given a relation `rel`, apply the relation to the domain of this relation. 686 /// 687 /// R1: i -> j : (0 <= i < 2, j = i) 688 /// R2: i -> k : (k = i floordiv 2) 689 /// R3: k -> j : (0 <= k < 1, 2k <= j <= 2k + 1) 690 /// 691 /// R1 = {(0, 0), (1, 1)}. R2 maps both 0 and 1 to 0. 692 /// So R3 = {(0, 0), (0, 1)}. 693 /// 694 /// Formally, R1.applyDomain(R2) = R2.inverse().compose(R1). 695 void applyDomain(const IntegerRelation &rel); 696 697 /// Given a relation `rel`, apply the relation to the range of this relation. 698 /// 699 /// Formally, R1.applyRange(R2) is the same as R1.compose(R2) but we provide 700 /// this for uniformity with `applyDomain`. 701 void applyRange(const IntegerRelation &rel); 702 703 /// Given a relation `other: (A -> B)`, this operation merges the symbol and 704 /// local variables and then takes the composition of `other` on `this: (B -> 705 /// C)`. The resulting relation represents tuples of the form: `A -> C`. 706 void mergeAndCompose(const IntegerRelation &other); 707 708 /// Compute an equivalent representation of the same set, such that all local 709 /// vars in all disjuncts have division representations. This representation 710 /// may involve local vars that correspond to divisions, and may also be a 711 /// union of convex disjuncts. 712 PresburgerRelation computeReprWithOnlyDivLocals() const; 713 714 /// Compute the symbolic integer lexmin of the relation. 715 /// 716 /// This finds, for every assignment to the symbols and domain, 717 /// the lexicographically minimum value attained by the range. 718 /// 719 /// For example, the symbolic lexmin of the set 720 /// 721 /// (x, y)[a, b, c] : (a <= x, b <= x, x <= c) 722 /// 723 /// can be written as 724 /// 725 /// x = a if b <= a, a <= c 726 /// x = b if a < b, b <= c 727 /// 728 /// This function is stored in the `lexopt` function in the result. 729 /// Some assignments to the symbols might make the set empty. 730 /// Such points are not part of the function's domain. 731 /// In the above example, this happens when max(a, b) > c. 732 /// 733 /// For some values of the symbols, the lexmin may be unbounded. 734 /// `SymbolicLexOpt` stores these parts of the symbolic domain in a separate 735 /// `PresburgerSet`, `unboundedDomain`. 736 SymbolicLexOpt findSymbolicIntegerLexMin() const; 737 738 /// Same as findSymbolicIntegerLexMin but produces lexmax instead of lexmin 739 SymbolicLexOpt findSymbolicIntegerLexMax() const; 740 741 /// Return the set difference of this set and the given set, i.e., 742 /// return `this \ set`. 743 PresburgerRelation subtract(const PresburgerRelation &set) const; 744 745 // Remove equalities which have only zero coefficients. 746 void removeTrivialEqualities(); 747 748 // Verify whether the relation is full-dimensional, i.e., 749 // no equality holds for the relation. 750 // 751 // If there are no variables, it always returns true. 752 // If there is at least one variable and the relation is empty, it returns 753 // false. 754 bool isFullDim(); 755 756 void print(raw_ostream &os) const; 757 void dump() const; 758 759 protected: 760 /// Checks all rows of equality/inequality constraints for trivial 761 /// contradictions (for example: 1 == 0, 0 >= 1), which may have surfaced 762 /// after elimination. Returns true if an invalid constraint is found; 763 /// false otherwise. 764 bool hasInvalidConstraint() const; 765 766 /// Returns the constant lower bound if isLower is true, and the upper 767 /// bound if isLower is false. 768 template <bool isLower> 769 std::optional<DynamicAPInt> computeConstantLowerOrUpperBound(unsigned pos); 770 /// The same, but casts to int64_t. This is unsafe and will assert-fail if the 771 /// value does not fit in an int64_t. 772 template <bool isLower> computeConstantLowerOrUpperBound64(unsigned pos)773 std::optional<int64_t> computeConstantLowerOrUpperBound64(unsigned pos) { 774 return computeConstantLowerOrUpperBound<isLower>(pos).map( 775 int64fromDynamicAPInt); 776 } 777 778 /// Eliminates a single variable at `position` from equality and inequality 779 /// constraints. Returns `success` if the variable was eliminated, and 780 /// `failure` otherwise. gaussianEliminateVar(unsigned position)781 inline LogicalResult gaussianEliminateVar(unsigned position) { 782 return success(gaussianEliminateVars(position, position + 1) == 1); 783 } 784 785 /// Removes local variables using equalities. Each equality is checked if it 786 /// can be reduced to the form: `e = affine-expr`, where `e` is a local 787 /// variable and `affine-expr` is an affine expression not containing `e`. 788 /// If an equality satisfies this form, the local variable is replaced in 789 /// each constraint and then removed. The equality used to replace this local 790 /// variable is also removed. 791 void removeRedundantLocalVars(); 792 793 /// Eliminates variables from equality and inequality constraints 794 /// in column range [posStart, posLimit). 795 /// Returns the number of variables eliminated. 796 unsigned gaussianEliminateVars(unsigned posStart, unsigned posLimit); 797 798 /// Perform a Gaussian elimination operation to reduce all equations to 799 /// standard form. Returns whether the constraint system was modified. 800 bool gaussianEliminate(); 801 802 /// Eliminates the variable at the specified position using Fourier-Motzkin 803 /// variable elimination, but uses Gaussian elimination if there is an 804 /// equality involving that variable. If the result of the elimination is 805 /// integer exact, `*isResultIntegerExact` is set to true. If `darkShadow` is 806 /// set to true, a potential under approximation (subset) of the rational 807 /// shadow / exact integer shadow is computed. 808 // See implementation comments for more details. 809 virtual void fourierMotzkinEliminate(unsigned pos, bool darkShadow = false, 810 bool *isResultIntegerExact = nullptr); 811 812 /// Tightens inequalities given that we are dealing with integer spaces. This 813 /// is similar to the GCD test but applied to inequalities. The constant term 814 /// can be reduced to the preceding multiple of the GCD of the coefficients, 815 /// i.e., 816 /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a 817 /// fast method (linear in the number of coefficients). 818 void gcdTightenInequalities(); 819 820 /// Normalized each constraints by the GCD of its coefficients. 821 void normalizeConstraintsByGCD(); 822 823 /// Searches for a constraint with a non-zero coefficient at `colIdx` in 824 /// equality (isEq=true) or inequality (isEq=false) constraints. 825 /// Returns true and sets row found in search in `rowIdx`, false otherwise. 826 bool findConstraintWithNonZeroAt(unsigned colIdx, bool isEq, 827 unsigned *rowIdx) const; 828 829 /// Returns true if the pos^th column is all zero for both inequalities and 830 /// equalities. 831 bool isColZero(unsigned pos) const; 832 833 /// Checks for identical inequalities and eliminates redundant inequalities. 834 /// Returns whether the constraint system was modified. 835 bool removeDuplicateConstraints(); 836 837 /// Returns false if the fields corresponding to various variable counts, or 838 /// equality/inequality buffer sizes aren't consistent; true otherwise. This 839 /// is meant to be used within an assert internally. 840 virtual bool hasConsistentState() const; 841 842 /// Prints the number of constraints, dimensions, symbols and locals in the 843 /// IntegerRelation. 844 virtual void printSpace(raw_ostream &os) const; 845 846 /// Removes variables in the column range [varStart, varLimit), and copies any 847 /// remaining valid data into place, updates member variables, and resizes 848 /// arrays as needed. 849 void removeVarRange(unsigned varStart, unsigned varLimit); 850 851 /// Truncate the vars of the specified kind to the specified number by 852 /// dropping some vars at the end. `num` must be less than the current number. 853 void truncateVarKind(VarKind kind, unsigned num); 854 855 /// Truncate the vars to the number in the space of the specified 856 /// CountsSnapshot. 857 void truncateVarKind(VarKind kind, const CountsSnapshot &counts); 858 859 /// A parameter that controls detection of an unrealistic number of 860 /// constraints. If the number of constraints is this many times the number of 861 /// variables, we consider such a system out of line with the intended use 862 /// case of IntegerRelation. 863 // The rationale for 32 is that in the typical simplest of cases, an 864 // variable is expected to have one lower bound and one upper bound 865 // constraint. With a level of tiling or a connection to another variable 866 // through a div or mod, an extra pair of bounds gets added. As a limit, we 867 // don't expect a variable to have more than 32 lower/upper/equality 868 // constraints. This is conservatively set low and can be raised if needed. 869 constexpr static unsigned kExplosionFactor = 32; 870 871 PresburgerSpace space; 872 873 /// Coefficients of affine equalities (in == 0 form). 874 IntMatrix equalities; 875 876 /// Coefficients of affine inequalities (in >= 0 form). 877 IntMatrix inequalities; 878 }; 879 880 /// An IntegerPolyhedron represents the set of points from a PresburgerSpace 881 /// that satisfy a list of affine constraints. Affine constraints can be 882 /// inequalities or equalities in the form: 883 /// 884 /// Inequality: c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} + c_n >= 0 885 /// Equality : c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} + c_n == 0 886 /// 887 /// where c_0, c_1, ..., c_n are integers and n is the total number of 888 /// variables in the space. 889 /// 890 /// An IntegerPolyhedron is similar to an IntegerRelation but it does not make a 891 /// distinction between Domain and Range variables. Internally, 892 /// IntegerPolyhedron is implemented as a IntegerRelation with zero domain vars. 893 /// 894 /// Since IntegerPolyhedron does not make a distinction between kinds of 895 /// dimensions, VarKind::SetDim should be used to refer to dimension 896 /// variables. 897 class IntegerPolyhedron : public IntegerRelation { 898 public: 899 /// Constructs a set reserving memory for the specified number 900 /// of constraints and variables. IntegerPolyhedron(unsigned numReservedInequalities,unsigned numReservedEqualities,unsigned numReservedCols,const PresburgerSpace & space)901 IntegerPolyhedron(unsigned numReservedInequalities, 902 unsigned numReservedEqualities, unsigned numReservedCols, 903 const PresburgerSpace &space) 904 : IntegerRelation(numReservedInequalities, numReservedEqualities, 905 numReservedCols, space) { 906 assert(space.getNumDomainVars() == 0 && 907 "Number of domain vars should be zero in Set kind space."); 908 } 909 910 /// Constructs a relation with the specified number of dimensions and 911 /// symbols. IntegerPolyhedron(const PresburgerSpace & space)912 explicit IntegerPolyhedron(const PresburgerSpace &space) 913 : IntegerPolyhedron(/*numReservedInequalities=*/0, 914 /*numReservedEqualities=*/0, 915 /*numReservedCols=*/space.getNumVars() + 1, space) {} 916 917 /// Constructs a relation with the specified number of dimensions and symbols 918 /// and adds the given inequalities. IntegerPolyhedron(const PresburgerSpace & space,IntMatrix inequalities)919 explicit IntegerPolyhedron(const PresburgerSpace &space, 920 IntMatrix inequalities) 921 : IntegerPolyhedron(space) { 922 for (unsigned i = 0, e = inequalities.getNumRows(); i < e; i++) 923 addInequality(inequalities.getRow(i)); 924 } 925 926 /// Constructs a relation with the specified number of dimensions and symbols 927 /// and adds the given inequalities, after normalizing row-wise to integer 928 /// values. IntegerPolyhedron(const PresburgerSpace & space,FracMatrix inequalities)929 explicit IntegerPolyhedron(const PresburgerSpace &space, 930 FracMatrix inequalities) 931 : IntegerPolyhedron(space) { 932 IntMatrix ineqsNormalized = inequalities.normalizeRows(); 933 for (unsigned i = 0, e = inequalities.getNumRows(); i < e; i++) 934 addInequality(ineqsNormalized.getRow(i)); 935 } 936 937 /// Construct a set from an IntegerRelation. The relation should have 938 /// no domain vars. IntegerPolyhedron(const IntegerRelation & rel)939 explicit IntegerPolyhedron(const IntegerRelation &rel) 940 : IntegerRelation(rel) { 941 assert(space.getNumDomainVars() == 0 && 942 "Number of domain vars should be zero in Set kind space."); 943 } 944 945 /// Construct a set from an IntegerRelation, but instead of creating a copy, 946 /// use move constructor. The relation should have no domain vars. IntegerPolyhedron(IntegerRelation && rel)947 explicit IntegerPolyhedron(IntegerRelation &&rel) : IntegerRelation(rel) { 948 assert(space.getNumDomainVars() == 0 && 949 "Number of domain vars should be zero in Set kind space."); 950 } 951 952 /// Return a system with no constraints, i.e., one which is satisfied by all 953 /// points. getUniverse(const PresburgerSpace & space)954 static IntegerPolyhedron getUniverse(const PresburgerSpace &space) { 955 return IntegerPolyhedron(space); 956 } 957 958 /// Return the kind of this IntegerRelation. getKind()959 Kind getKind() const override { return Kind::IntegerPolyhedron; } 960 classof(const IntegerRelation * cst)961 static bool classof(const IntegerRelation *cst) { 962 return cst->getKind() >= Kind::IntegerPolyhedron && 963 cst->getKind() <= Kind::FlatAffineRelation; 964 } 965 966 // Clones this object. 967 std::unique_ptr<IntegerPolyhedron> clone() const; 968 969 /// Insert `num` variables of the specified kind at position `pos`. 970 /// Positions are relative to the kind of variable. Return the absolute 971 /// column position (i.e., not relative to the kind of variable) of the 972 /// first added variable. 973 unsigned insertVar(VarKind kind, unsigned pos, unsigned num = 1) override; 974 975 /// Return the intersection of the two relations. 976 /// If there are locals, they will be merged. 977 IntegerPolyhedron intersect(const IntegerPolyhedron &other) const; 978 979 /// Return the set difference of this set and the given set, i.e., 980 /// return `this \ set`. 981 PresburgerSet subtract(const PresburgerSet &other) const; 982 }; 983 984 } // namespace presburger 985 } // namespace mlir 986 987 #endif // MLIR_ANALYSIS_PRESBURGER_INTEGERRELATION_H 988