1 //===- AffineStructures.h - MLIR Affine Structures 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 // Structures for affine/polyhedral analysis of ML functions. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_ANALYSIS_AFFINE_STRUCTURES_H 14 #define MLIR_ANALYSIS_AFFINE_STRUCTURES_H 15 16 #include "mlir/IR/AffineExpr.h" 17 #include "mlir/IR/OpDefinition.h" 18 #include "mlir/Support/LogicalResult.h" 19 20 namespace mlir { 21 22 class AffineCondition; 23 class AffineForOp; 24 class AffineIfOp; 25 class AffineMap; 26 class AffineValueMap; 27 class IntegerSet; 28 class MLIRContext; 29 class Value; 30 class MemRefType; 31 struct MutableAffineMap; 32 33 /// A flat list of affine equalities and inequalities in the form. 34 /// Inequality: c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} >= 0 35 /// Equality: c_0*x_0 + c_1*x_1 + .... + c_{n-1}*x_{n-1} == 0 36 /// 37 /// FlatAffineConstraints stores coefficients in a contiguous buffer (one buffer 38 /// for equalities and one for inequalities). The size of each buffer is 39 /// numReservedCols * number of inequalities (or equalities). The reserved size 40 /// is numReservedCols * numReservedInequalities (or numReservedEqualities). A 41 /// coefficient (r, c) lives at the location numReservedCols * r + c in the 42 /// buffer. The extra space between getNumCols() and numReservedCols exists to 43 /// prevent frequent movement of data when adding columns, especially at the 44 /// end. 45 /// 46 /// The identifiers x_0, x_1, ... appear in the order: dimensional identifiers, 47 /// symbolic identifiers, and local identifiers. The local identifiers 48 /// correspond to local/internal variables created when converting from 49 /// AffineExpr's containing mod's and div's; they are thus needed to increase 50 /// representational power. Each local identifier is always (by construction) a 51 /// floordiv of a pure add/mul affine function of dimensional, symbolic, and 52 /// other local identifiers, in a non-mutually recursive way. Hence, every local 53 /// identifier can ultimately always be recovered as an affine function of 54 /// dimensional and symbolic identifiers (involving floordiv's); note however 55 /// that some floordiv combinations are converted to mod's by AffineExpr 56 /// construction. 57 /// 58 class FlatAffineConstraints { 59 public: 60 enum IdKind { Dimension, Symbol, Local }; 61 62 /// Constructs a constraint system reserving memory for the specified number 63 /// of constraints and identifiers.. 64 FlatAffineConstraints(unsigned numReservedInequalities, 65 unsigned numReservedEqualities, 66 unsigned numReservedCols, unsigned numDims = 0, 67 unsigned numSymbols = 0, unsigned numLocals = 0, 68 ArrayRef<Optional<Value>> idArgs = {}) numReservedCols(numReservedCols)69 : numReservedCols(numReservedCols), numDims(numDims), 70 numSymbols(numSymbols) { 71 assert(numReservedCols >= numDims + numSymbols + 1); 72 assert(idArgs.empty() || idArgs.size() == numDims + numSymbols + numLocals); 73 equalities.reserve(numReservedCols * numReservedEqualities); 74 inequalities.reserve(numReservedCols * numReservedInequalities); 75 numIds = numDims + numSymbols + numLocals; 76 ids.reserve(numReservedCols); 77 if (idArgs.empty()) 78 ids.resize(numIds, None); 79 else 80 ids.append(idArgs.begin(), idArgs.end()); 81 } 82 83 /// Constructs a constraint system with the specified number of 84 /// dimensions and symbols. 85 FlatAffineConstraints(unsigned numDims = 0, unsigned numSymbols = 0, 86 unsigned numLocals = 0, 87 ArrayRef<Optional<Value>> idArgs = {}) 88 : numReservedCols(numDims + numSymbols + numLocals + 1), numDims(numDims), 89 numSymbols(numSymbols) { 90 assert(numReservedCols >= numDims + numSymbols + 1); 91 assert(idArgs.empty() || idArgs.size() == numDims + numSymbols + numLocals); 92 numIds = numDims + numSymbols + numLocals; 93 ids.reserve(numIds); 94 if (idArgs.empty()) 95 ids.resize(numIds, None); 96 else 97 ids.append(idArgs.begin(), idArgs.end()); 98 } 99 100 /// Return a system with no constraints, i.e., one which is satisfied by all 101 /// points. 102 static FlatAffineConstraints getUniverse(unsigned numDims = 0, 103 unsigned numSymbols = 0) { 104 return FlatAffineConstraints(numDims, numSymbols); 105 } 106 107 /// Create a flat affine constraint system from an AffineValueMap or a list of 108 /// these. The constructed system will only include equalities. 109 explicit FlatAffineConstraints(const AffineValueMap &avm); 110 explicit FlatAffineConstraints(ArrayRef<const AffineValueMap *> avmRef); 111 112 /// Creates an affine constraint system from an IntegerSet. 113 explicit FlatAffineConstraints(IntegerSet set); 114 115 FlatAffineConstraints(const FlatAffineConstraints &other); 116 117 FlatAffineConstraints(ArrayRef<const AffineValueMap *> avmRef, 118 IntegerSet set); 119 120 FlatAffineConstraints(const MutableAffineMap &map); 121 ~FlatAffineConstraints()122 ~FlatAffineConstraints() {} 123 124 // Clears any existing data and reserves memory for the specified constraints. 125 void reset(unsigned numReservedInequalities, unsigned numReservedEqualities, 126 unsigned numReservedCols, unsigned numDims, unsigned numSymbols, 127 unsigned numLocals = 0, ArrayRef<Value> idArgs = {}); 128 129 void reset(unsigned numDims = 0, unsigned numSymbols = 0, 130 unsigned numLocals = 0, ArrayRef<Value> idArgs = {}); 131 132 /// Appends constraints from 'other' into this. This is equivalent to an 133 /// intersection with no simplification of any sort attempted. 134 void append(const FlatAffineConstraints &other); 135 136 /// Checks for emptiness by performing variable elimination on all 137 /// identifiers, running the GCD test on each equality constraint, and 138 /// checking for invalid constraints. Returns true if the GCD test fails for 139 /// any equality, or if any invalid constraints are discovered on any row. 140 /// Returns false otherwise. 141 bool isEmpty() const; 142 143 /// Runs the GCD test on all equality constraints. Returns 'true' if this test 144 /// fails on any equality. Returns 'false' otherwise. 145 /// This test can be used to disprove the existence of a solution. If it 146 /// returns true, no integer solution to the equality constraints can exist. 147 bool isEmptyByGCDTest() const; 148 149 /// Runs the GCD test heuristic. If it proves inconclusive, falls back to 150 /// generalized basis reduction if the set is bounded. 151 /// 152 /// Returns true if the set of constraints is found to have no solution, 153 /// false if a solution exists or all tests were inconclusive. 154 bool isIntegerEmpty() const; 155 156 /// Find a sample point satisfying the constraints. This uses a branch and 157 /// bound algorithm with generalized basis reduction, which always works if 158 /// the set is bounded. This should not be called for unbounded sets. 159 /// 160 /// Returns such a point if one exists, or an empty Optional otherwise. 161 Optional<SmallVector<int64_t, 8>> findIntegerSample() const; 162 163 /// Returns true if the given point satisfies the constraints, or false 164 /// otherwise. 165 bool containsPoint(ArrayRef<int64_t> point) const; 166 167 // Clones this object. 168 std::unique_ptr<FlatAffineConstraints> clone() const; 169 170 /// Returns the value at the specified equality row and column. atEq(unsigned i,unsigned j)171 inline int64_t atEq(unsigned i, unsigned j) const { 172 return equalities[i * numReservedCols + j]; 173 } atEq(unsigned i,unsigned j)174 inline int64_t &atEq(unsigned i, unsigned j) { 175 return equalities[i * numReservedCols + j]; 176 } 177 atIneq(unsigned i,unsigned j)178 inline int64_t atIneq(unsigned i, unsigned j) const { 179 return inequalities[i * numReservedCols + j]; 180 } 181 atIneq(unsigned i,unsigned j)182 inline int64_t &atIneq(unsigned i, unsigned j) { 183 return inequalities[i * numReservedCols + j]; 184 } 185 186 /// Returns the number of columns in the constraint system. getNumCols()187 inline unsigned getNumCols() const { return numIds + 1; } 188 getNumEqualities()189 inline unsigned getNumEqualities() const { 190 assert(equalities.size() % numReservedCols == 0 && 191 "inconsistent equality buffer size"); 192 return equalities.size() / numReservedCols; 193 } 194 getNumInequalities()195 inline unsigned getNumInequalities() const { 196 assert(inequalities.size() % numReservedCols == 0 && 197 "inconsistent inequality buffer size"); 198 return inequalities.size() / numReservedCols; 199 } 200 getNumReservedEqualities()201 inline unsigned getNumReservedEqualities() const { 202 return equalities.capacity() / numReservedCols; 203 } 204 getNumReservedInequalities()205 inline unsigned getNumReservedInequalities() const { 206 return inequalities.capacity() / numReservedCols; 207 } 208 getEquality(unsigned idx)209 inline ArrayRef<int64_t> getEquality(unsigned idx) const { 210 return ArrayRef<int64_t>(&equalities[idx * numReservedCols], getNumCols()); 211 } 212 getInequality(unsigned idx)213 inline ArrayRef<int64_t> getInequality(unsigned idx) const { 214 return ArrayRef<int64_t>(&inequalities[idx * numReservedCols], 215 getNumCols()); 216 } 217 218 /// Adds constraints (lower and upper bounds) for the specified 'affine.for' 219 /// operation's Value using IR information stored in its bound maps. The 220 /// right identifier is first looked up using forOp's Value. Asserts if the 221 /// Value corresponding to the 'affine.for' operation isn't found in the 222 /// constraint system. Returns failure for the yet unimplemented/unsupported 223 /// cases. Any new identifiers that are found in the bound operands of the 224 /// 'affine.for' operation are added as trailing identifiers (either 225 /// dimensional or symbolic depending on whether the operand is a valid 226 /// symbol). 227 // TODO: add support for non-unit strides. 228 LogicalResult addAffineForOpDomain(AffineForOp forOp); 229 230 /// Adds constraints imposed by the `affine.if` operation. These constraints 231 /// are collected from the IntegerSet attached to the given `affine.if` 232 /// instance argument (`ifOp`). It is asserted that: 233 /// 1) The IntegerSet of the given `affine.if` instance should not contain 234 /// semi-affine expressions, 235 /// 2) The columns of the constraint system created from `ifOp` should match 236 /// the columns in the current one regarding numbers and values. 237 void addAffineIfOpDomain(AffineIfOp ifOp); 238 239 /// Adds a lower or an upper bound for the identifier at the specified 240 /// position with constraints being drawn from the specified bound map and 241 /// operands. If `eq` is true, add a single equality equal to the bound map's 242 /// first result expr. 243 LogicalResult addLowerOrUpperBound(unsigned pos, AffineMap boundMap, 244 ValueRange operands, bool eq, 245 bool lower = true); 246 247 /// Returns the bound for the identifier at `pos` from the inequality at 248 /// `ineqPos` as a 1-d affine value map (affine map + operands). The returned 249 /// affine value map can either be a lower bound or an upper bound depending 250 /// on the sign of atIneq(ineqPos, pos). Asserts if the row at `ineqPos` does 251 /// not involve the `pos`th identifier. 252 void getIneqAsAffineValueMap(unsigned pos, unsigned ineqPos, 253 AffineValueMap &vmap, 254 MLIRContext *context) const; 255 256 /// Returns the constraint system as an integer set. Returns a null integer 257 /// set if the system has no constraints, or if an integer set couldn't be 258 /// constructed as a result of a local variable's explicit representation not 259 /// being known and such a local variable appearing in any of the constraints. 260 IntegerSet getAsIntegerSet(MLIRContext *context) const; 261 262 /// Computes the lower and upper bounds of the first 'num' dimensional 263 /// identifiers (starting at 'offset') as an affine map of the remaining 264 /// identifiers (dimensional and symbolic). This method is able to detect 265 /// identifiers as floordiv's and mod's of affine expressions of other 266 /// identifiers with respect to (positive) constants. Sets bound map to a 267 /// null AffineMap if such a bound can't be found (or yet unimplemented). 268 void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context, 269 SmallVectorImpl<AffineMap> *lbMaps, 270 SmallVectorImpl<AffineMap> *ubMaps); 271 272 /// Adds slice lower bounds represented by lower bounds in 'lbMaps' and upper 273 /// bounds in 'ubMaps' to each identifier in the constraint system which has 274 /// a value in 'values'. Note that both lower/upper bounds share the same 275 /// operand list 'operands'. 276 /// This function assumes 'values.size' == 'lbMaps.size' == 'ubMaps.size'. 277 /// Note that both lower/upper bounds use operands from 'operands'. 278 LogicalResult addSliceBounds(ArrayRef<Value> values, 279 ArrayRef<AffineMap> lbMaps, 280 ArrayRef<AffineMap> ubMaps, 281 ArrayRef<Value> operands); 282 283 // Adds an inequality (>= 0) from the coefficients specified in inEq. 284 void addInequality(ArrayRef<int64_t> inEq); 285 // Adds an equality from the coefficients specified in eq. 286 void addEquality(ArrayRef<int64_t> eq); 287 288 /// Adds a constant lower bound constraint for the specified identifier. 289 void addConstantLowerBound(unsigned pos, int64_t lb); 290 /// Adds a constant upper bound constraint for the specified identifier. 291 void addConstantUpperBound(unsigned pos, int64_t ub); 292 293 /// Adds a new local identifier as the floordiv of an affine function of other 294 /// identifiers, the coefficients of which are provided in 'dividend' and with 295 /// respect to a positive constant 'divisor'. Two constraints are added to the 296 /// system to capture equivalence with the floordiv: 297 /// q = dividend floordiv c <=> c*q <= dividend <= c*q + c - 1. 298 void addLocalFloorDiv(ArrayRef<int64_t> dividend, int64_t divisor); 299 300 /// Adds a constant lower bound constraint for the specified expression. 301 void addConstantLowerBound(ArrayRef<int64_t> expr, int64_t lb); 302 /// Adds a constant upper bound constraint for the specified expression. 303 void addConstantUpperBound(ArrayRef<int64_t> expr, int64_t ub); 304 305 /// Sets the identifier at the specified position to a constant. 306 void setIdToConstant(unsigned pos, int64_t val); 307 308 /// Sets the identifier corresponding to the specified Value id to a 309 /// constant. Asserts if the 'id' is not found. 310 void setIdToConstant(Value id, int64_t val); 311 312 /// Looks up the position of the identifier with the specified Value. Returns 313 /// true if found (false otherwise). `pos' is set to the (column) position of 314 /// the identifier. 315 bool findId(Value id, unsigned *pos) const; 316 317 /// Returns true if an identifier with the specified Value exists, false 318 /// otherwise. 319 bool containsId(Value id) const; 320 321 /// Swap the posA^th identifier with the posB^th identifier. 322 void swapId(unsigned posA, unsigned posB); 323 324 // Add identifiers of the specified kind - specified positions are relative to 325 // the kind of identifier. The coefficient column corresponding to the added 326 // identifier is initialized to zero. 'id' is the Value corresponding to the 327 // identifier that can optionally be provided. 328 void addDimId(unsigned pos, Value id = nullptr); 329 void addSymbolId(unsigned pos, Value id = nullptr); 330 void addLocalId(unsigned pos); 331 void addId(IdKind kind, unsigned pos, Value id = nullptr); 332 333 /// Add the specified values as a dim or symbol id depending on its nature, if 334 /// it already doesn't exist in the system. `id' has to be either a terminal 335 /// symbol or a loop IV, i.e., it cannot be the result affine.apply of any 336 /// symbols or loop IVs. The identifier is added to the end of the existing 337 /// dims or symbols. Additional information on the identifier is extracted 338 /// from the IR and added to the constraint system. 339 void addInductionVarOrTerminalSymbol(Value id); 340 341 /// Composes the affine value map with this FlatAffineConstrains, adding the 342 /// results of the map as dimensions at the front [0, vMap->getNumResults()) 343 /// and with the dimensions set to the equalities specified by the value map. 344 /// Returns failure if the composition fails (when vMap is a semi-affine map). 345 /// The vMap's operand Value's are used to look up the right positions in 346 /// the FlatAffineConstraints with which to associate. The dimensional and 347 /// symbolic operands of vMap should match 1:1 (in the same order) with those 348 /// of this constraint system, but the latter could have additional trailing 349 /// operands. 350 LogicalResult composeMap(const AffineValueMap *vMap); 351 352 /// Composes an affine map whose dimensions match one to one to the 353 /// dimensions of this FlatAffineConstraints. The results of the map 'other' 354 /// are added as the leading dimensions of this constraint system. Returns 355 /// failure if 'other' is a semi-affine map. 356 LogicalResult composeMatchingMap(AffineMap other); 357 358 /// Projects out (aka eliminates) 'num' identifiers starting at position 359 /// 'pos'. The resulting constraint system is the shadow along the dimensions 360 /// that still exist. This method may not always be integer exact. 361 // TODO: deal with integer exactness when necessary - can return a value to 362 // mark exactness for example. 363 void projectOut(unsigned pos, unsigned num); projectOut(unsigned pos)364 inline void projectOut(unsigned pos) { return projectOut(pos, 1); } 365 366 /// Projects out the identifier that is associate with Value . 367 void projectOut(Value id); 368 369 /// Removes the specified identifier from the system. 370 void removeId(unsigned pos); 371 372 void removeEquality(unsigned pos); 373 void removeInequality(unsigned pos); 374 375 /// Changes the partition between dimensions and symbols. Depending on the new 376 /// symbol count, either a chunk of trailing dimensional identifiers becomes 377 /// symbols, or some of the leading symbols become dimensions. 378 void setDimSymbolSeparation(unsigned newSymbolCount); 379 380 /// Changes all symbol identifiers which are loop IVs to dim identifiers. 381 void convertLoopIVSymbolsToDims(); 382 383 /// Sets the specified identifier to a constant and removes it. 384 void setAndEliminate(unsigned pos, int64_t constVal); 385 386 /// Tries to fold the specified identifier to a constant using a trivial 387 /// equality detection; if successful, the constant is substituted for the 388 /// identifier everywhere in the constraint system and then removed from the 389 /// system. 390 LogicalResult constantFoldId(unsigned pos); 391 392 /// This method calls constantFoldId for the specified range of identifiers, 393 /// 'num' identifiers starting at position 'pos'. 394 void constantFoldIdRange(unsigned pos, unsigned num); 395 396 /// Updates the constraints to be the smallest bounding (enclosing) box that 397 /// contains the points of 'this' set and that of 'other', with the symbols 398 /// being treated specially. For each of the dimensions, the min of the lower 399 /// bounds (symbolic) and the max of the upper bounds (symbolic) is computed 400 /// to determine such a bounding box. `other' is expected to have the same 401 /// dimensional identifiers as this constraint system (in the same order). 402 /// 403 /// Eg: if 'this' is {0 <= d0 <= 127}, 'other' is {16 <= d0 <= 192}, the 404 /// output is {0 <= d0 <= 192}. 405 /// 2) 'this' = {s0 + 5 <= d0 <= s0 + 20}, 'other' is {s0 + 1 <= d0 <= s0 + 406 /// 9}, output = {s0 + 1 <= d0 <= s0 + 20}. 407 /// 3) 'this' = {0 <= d0 <= 5, 1 <= d1 <= 9}, 'other' = {2 <= d0 <= 6, 5 <= d1 408 /// <= 15}, output = {0 <= d0 <= 6, 1 <= d1 <= 15}. 409 LogicalResult unionBoundingBox(const FlatAffineConstraints &other); 410 411 /// Returns 'true' if this constraint system and 'other' are in the same 412 /// space, i.e., if they are associated with the same set of identifiers, 413 /// appearing in the same order. Returns 'false' otherwise. 414 bool areIdsAlignedWithOther(const FlatAffineConstraints &other); 415 416 /// Merge and align the identifiers of 'this' and 'other' starting at 417 /// 'offset', so that both constraint systems get the union of the contained 418 /// identifiers that is dimension-wise and symbol-wise unique; both 419 /// constraint systems are updated so that they have the union of all 420 /// identifiers, with this's original identifiers appearing first followed by 421 /// any of other's identifiers that didn't appear in 'this'. Local 422 /// identifiers of each system are by design separate/local and are placed 423 /// one after other (this's followed by other's). 424 // Eg: Input: 'this' has ((%i %j) [%M %N]) 425 // 'other' has (%k, %j) [%P, %N, %M]) 426 // Output: both 'this', 'other' have (%i, %j, %k) [%M, %N, %P] 427 // 428 void mergeAndAlignIdsWithOther(unsigned offset, FlatAffineConstraints *other); 429 getNumConstraints()430 unsigned getNumConstraints() const { 431 return getNumInequalities() + getNumEqualities(); 432 } getNumIds()433 inline unsigned getNumIds() const { return numIds; } getNumDimIds()434 inline unsigned getNumDimIds() const { return numDims; } getNumSymbolIds()435 inline unsigned getNumSymbolIds() const { return numSymbols; } getNumDimAndSymbolIds()436 inline unsigned getNumDimAndSymbolIds() const { return numDims + numSymbols; } getNumLocalIds()437 inline unsigned getNumLocalIds() const { 438 return numIds - numDims - numSymbols; 439 } 440 getIds()441 inline ArrayRef<Optional<Value>> getIds() const { 442 return {ids.data(), ids.size()}; 443 } getIds()444 inline MutableArrayRef<Optional<Value>> getIds() { 445 return {ids.data(), ids.size()}; 446 } 447 448 /// Returns the optional Value corresponding to the pos^th identifier. getId(unsigned pos)449 inline Optional<Value> getId(unsigned pos) const { return ids[pos]; } getId(unsigned pos)450 inline Optional<Value> &getId(unsigned pos) { return ids[pos]; } 451 452 /// Returns the Value associated with the pos^th identifier. Asserts if 453 /// no Value identifier was associated. getIdValue(unsigned pos)454 inline Value getIdValue(unsigned pos) const { 455 assert(ids[pos].hasValue() && "identifier's Value not set"); 456 return ids[pos].getValue(); 457 } 458 459 /// Returns the Values associated with identifiers in range [start, end). 460 /// Asserts if no Value was associated with one of these identifiers. getIdValues(unsigned start,unsigned end,SmallVectorImpl<Value> * values)461 void getIdValues(unsigned start, unsigned end, 462 SmallVectorImpl<Value> *values) const { 463 assert((start < numIds || start == end) && "invalid start position"); 464 assert(end <= numIds && "invalid end position"); 465 values->clear(); 466 values->reserve(end - start); 467 for (unsigned i = start; i < end; i++) { 468 values->push_back(getIdValue(i)); 469 } 470 } getAllIdValues(SmallVectorImpl<Value> * values)471 inline void getAllIdValues(SmallVectorImpl<Value> *values) const { 472 getIdValues(0, numIds, values); 473 } 474 475 /// Sets Value associated with the pos^th identifier. setIdValue(unsigned pos,Value val)476 inline void setIdValue(unsigned pos, Value val) { 477 assert(pos < numIds && "invalid id position"); 478 ids[pos] = val; 479 } 480 /// Sets Values associated with identifiers in the range [start, end). setIdValues(unsigned start,unsigned end,ArrayRef<Value> values)481 void setIdValues(unsigned start, unsigned end, ArrayRef<Value> values) { 482 assert((start < numIds || end == start) && "invalid start position"); 483 assert(end <= numIds && "invalid end position"); 484 assert(values.size() == end - start); 485 for (unsigned i = start; i < end; ++i) 486 ids[i] = values[i - start]; 487 } 488 489 /// Clears this list of constraints and copies other into it. 490 void clearAndCopyFrom(const FlatAffineConstraints &other); 491 492 /// Returns the smallest known constant bound for the extent of the specified 493 /// identifier (pos^th), i.e., the smallest known constant that is greater 494 /// than or equal to 'exclusive upper bound' - 'lower bound' of the 495 /// identifier. Returns None if it's not a constant. This method employs 496 /// trivial (low complexity / cost) checks and detection. Symbolic identifiers 497 /// are treated specially, i.e., it looks for constant differences between 498 /// affine expressions involving only the symbolic identifiers. `lb` and 499 /// `ub` (along with the `boundFloorDivisor`) are set to represent the lower 500 /// and upper bound associated with the constant difference: `lb`, `ub` have 501 /// the coefficients, and boundFloorDivisor, their divisor. `minLbPos` and 502 /// `minUbPos` if non-null are set to the position of the constant lower bound 503 /// and upper bound respectively (to the same if they are from an equality). 504 /// Ex: if the lower bound is [(s0 + s2 - 1) floordiv 32] for a system with 505 /// three symbolic identifiers, *lb = [1, 0, 1], lbDivisor = 32. See comments 506 /// at function definition for examples. 507 Optional<int64_t> getConstantBoundOnDimSize( 508 unsigned pos, SmallVectorImpl<int64_t> *lb = nullptr, 509 int64_t *boundFloorDivisor = nullptr, 510 SmallVectorImpl<int64_t> *ub = nullptr, unsigned *minLbPos = nullptr, 511 unsigned *minUbPos = nullptr) const; 512 513 /// Returns the constant lower bound for the pos^th identifier if there is 514 /// one; None otherwise. 515 Optional<int64_t> getConstantLowerBound(unsigned pos) const; 516 517 /// Returns the constant upper bound for the pos^th identifier if there is 518 /// one; None otherwise. 519 Optional<int64_t> getConstantUpperBound(unsigned pos) const; 520 521 /// Gets the lower and upper bound of the `offset` + `pos`th identifier 522 /// treating [0, offset) U [offset + num, symStartPos) as dimensions and 523 /// [symStartPos, getNumDimAndSymbolIds) as symbols, and `pos` lies in 524 /// [0, num). The multi-dimensional maps in the returned pair represent the 525 /// max and min of potentially multiple affine expressions. The upper bound is 526 /// exclusive. `localExprs` holds pre-computed AffineExpr's for all local 527 /// identifiers in the system. 528 std::pair<AffineMap, AffineMap> 529 getLowerAndUpperBound(unsigned pos, unsigned offset, unsigned num, 530 unsigned symStartPos, ArrayRef<AffineExpr> localExprs, 531 MLIRContext *context) const; 532 533 /// Gather positions of all lower and upper bounds of the identifier at `pos`, 534 /// and optionally any equalities on it. In addition, the bounds are to be 535 /// independent of identifiers in position range [`offset`, `offset` + `num`). 536 void 537 getLowerAndUpperBoundIndices(unsigned pos, 538 SmallVectorImpl<unsigned> *lbIndices, 539 SmallVectorImpl<unsigned> *ubIndices, 540 SmallVectorImpl<unsigned> *eqIndices = nullptr, 541 unsigned offset = 0, unsigned num = 0) const; 542 543 /// Removes constraints that are independent of (i.e., do not have a 544 /// coefficient for) for identifiers in the range [pos, pos + num). 545 void removeIndependentConstraints(unsigned pos, unsigned num); 546 547 /// Returns true if the set can be trivially detected as being 548 /// hyper-rectangular on the specified contiguous set of identifiers. 549 bool isHyperRectangular(unsigned pos, unsigned num) const; 550 551 /// Removes duplicate constraints, trivially true constraints, and constraints 552 /// that can be detected as redundant as a result of differing only in their 553 /// constant term part. A constraint of the form <non-negative constant> >= 0 554 /// is considered trivially true. This method is a linear time method on the 555 /// constraints, does a single scan, and updates in place. It also normalizes 556 /// constraints by their GCD and performs GCD tightening on inequalities. 557 void removeTrivialRedundancy(); 558 559 /// A more expensive check to detect redundant inequalities thatn 560 /// removeTrivialRedundancy. 561 void removeRedundantInequalities(); 562 563 /// Removes redundant constraints using Simplex. Although the algorithm can 564 /// theoretically take exponential time in the worst case (rare), it is known 565 /// to perform much better in the average case. If V is the number of vertices 566 /// in the polytope and C is the number of constraints, the algorithm takes 567 /// O(VC) time. 568 void removeRedundantConstraints(); 569 570 // Removes all equalities and inequalities. 571 void clearConstraints(); 572 573 void print(raw_ostream &os) const; 574 void dump() const; 575 576 private: 577 /// Returns false if the fields corresponding to various identifier counts, or 578 /// equality/inequality buffer sizes aren't consistent; true otherwise. This 579 /// is meant to be used within an assert internally. 580 bool hasConsistentState() const; 581 582 /// Checks all rows of equality/inequality constraints for trivial 583 /// contradictions (for example: 1 == 0, 0 >= 1), which may have surfaced 584 /// after elimination. Returns 'true' if an invalid constraint is found; 585 /// 'false'otherwise. 586 bool hasInvalidConstraint() const; 587 588 /// Returns the constant lower bound bound if isLower is true, and the upper 589 /// bound if isLower is false. 590 template <bool isLower> 591 Optional<int64_t> computeConstantLowerOrUpperBound(unsigned pos); 592 593 // Eliminates a single identifier at 'position' from equality and inequality 594 // constraints. Returns 'success' if the identifier was eliminated, and 595 // 'failure' otherwise. gaussianEliminateId(unsigned position)596 inline LogicalResult gaussianEliminateId(unsigned position) { 597 return success(gaussianEliminateIds(position, position + 1) == 1); 598 } 599 600 // Eliminates identifiers from equality and inequality constraints 601 // in column range [posStart, posLimit). 602 // Returns the number of variables eliminated. 603 unsigned gaussianEliminateIds(unsigned posStart, unsigned posLimit); 604 605 /// Eliminates identifier at the specified position using Fourier-Motzkin 606 /// variable elimination, but uses Gaussian elimination if there is an 607 /// equality involving that identifier. If the result of the elimination is 608 /// integer exact, *isResultIntegerExact is set to true. If 'darkShadow' is 609 /// set to true, a potential under approximation (subset) of the rational 610 /// shadow / exact integer shadow is computed. 611 // See implementation comments for more details. 612 void FourierMotzkinEliminate(unsigned pos, bool darkShadow = false, 613 bool *isResultIntegerExact = nullptr); 614 615 /// Tightens inequalities given that we are dealing with integer spaces. This 616 /// is similar to the GCD test but applied to inequalities. The constant term 617 /// can be reduced to the preceding multiple of the GCD of the coefficients, 618 /// i.e., 619 /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a 620 /// fast method (linear in the number of coefficients). 621 void GCDTightenInequalities(); 622 623 /// Normalized each constraints by the GCD of its coefficients. 624 void normalizeConstraintsByGCD(); 625 626 /// Removes identifiers in the column range [idStart, idLimit), and copies any 627 /// remaining valid data into place, updates member variables, and resizes 628 /// arrays as needed. 629 void removeIdRange(unsigned idStart, unsigned idLimit); 630 631 /// Coefficients of affine equalities (in == 0 form). 632 SmallVector<int64_t, 64> equalities; 633 634 /// Coefficients of affine inequalities (in >= 0 form). 635 SmallVector<int64_t, 64> inequalities; 636 637 /// Number of columns reserved. Actual ones in used are returned by 638 /// getNumCols(). 639 unsigned numReservedCols; 640 641 /// Total number of identifiers. 642 unsigned numIds; 643 644 /// Number of identifiers corresponding to real dimensions. 645 unsigned numDims; 646 647 /// Number of identifiers corresponding to symbols (unknown but constant for 648 /// analysis). 649 unsigned numSymbols; 650 651 /// Values corresponding to the (column) identifiers of this constraint 652 /// system appearing in the order the identifiers correspond to columns. 653 /// Temporary ones or those that aren't associated to any Value are set to 654 /// None. 655 SmallVector<Optional<Value>, 8> ids; 656 657 /// A parameter that controls detection of an unrealistic number of 658 /// constraints. If the number of constraints is this many times the number of 659 /// variables, we consider such a system out of line with the intended use 660 /// case of FlatAffineConstraints. 661 // The rationale for 32 is that in the typical simplest of cases, an 662 // identifier is expected to have one lower bound and one upper bound 663 // constraint. With a level of tiling or a connection to another identifier 664 // through a div or mod, an extra pair of bounds gets added. As a limit, we 665 // don't expect an identifier to have more than 32 lower/upper/equality 666 // constraints. This is conservatively set low and can be raised if needed. 667 constexpr static unsigned kExplosionFactor = 32; 668 }; 669 670 /// Flattens 'expr' into 'flattenedExpr', which contains the coefficients of the 671 /// dimensions, symbols, and additional variables that represent floor divisions 672 /// of dimensions, symbols, and in turn other floor divisions. Returns failure 673 /// if 'expr' could not be flattened (i.e., semi-affine is not yet handled). 674 /// 'cst' contains constraints that connect newly introduced local identifiers 675 /// to existing dimensional and symbolic identifiers. See documentation for 676 /// AffineExprFlattener on how mod's and div's are flattened. 677 LogicalResult getFlattenedAffineExpr(AffineExpr expr, unsigned numDims, 678 unsigned numSymbols, 679 SmallVectorImpl<int64_t> *flattenedExpr, 680 FlatAffineConstraints *cst = nullptr); 681 682 /// Flattens the result expressions of the map to their corresponding flattened 683 /// forms and set in 'flattenedExprs'. Returns failure if any expression in the 684 /// map could not be flattened (i.e., semi-affine is not yet handled). 'cst' 685 /// contains constraints that connect newly introduced local identifiers to 686 /// existing dimensional and / symbolic identifiers. See documentation for 687 /// AffineExprFlattener on how mod's and div's are flattened. For all affine 688 /// expressions that share the same operands (like those of an affine map), this 689 /// method should be used instead of repeatedly calling getFlattenedAffineExpr 690 /// since local variables added to deal with div's and mod's will be reused 691 /// across expressions. 692 LogicalResult 693 getFlattenedAffineExprs(AffineMap map, 694 std::vector<SmallVector<int64_t, 8>> *flattenedExprs, 695 FlatAffineConstraints *cst = nullptr); 696 LogicalResult 697 getFlattenedAffineExprs(IntegerSet set, 698 std::vector<SmallVector<int64_t, 8>> *flattenedExprs, 699 FlatAffineConstraints *cst = nullptr); 700 701 } // end namespace mlir. 702 703 #endif // MLIR_ANALYSIS_AFFINE_STRUCTURES_H 704