1 //===- AffineOps.h - MLIR Affine Operations -------------------------------===// 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 // This file defines convenience types for working with Affine operations 10 // in the MLIR operation set. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef MLIR_DIALECT_AFFINE_IR_AFFINEOPS_H 15 #define MLIR_DIALECT_AFFINE_IR_AFFINEOPS_H 16 17 #include "mlir/Dialect/Affine/IR/AffineMemoryOpInterfaces.h" 18 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" 19 #include "mlir/IR/AffineMap.h" 20 #include "mlir/IR/Builders.h" 21 #include "mlir/Interfaces/ControlFlowInterfaces.h" 22 #include "mlir/Interfaces/LoopLikeInterface.h" 23 24 namespace mlir { 25 class AffineApplyOp; 26 class AffineBound; 27 class AffineValueMap; 28 class RewriterBase; 29 30 /// TODO: These should be renamed if they are on the mlir namespace. 31 /// Ideally, they should go in a mlir::affine:: namespace. 32 33 /// A utility function to check if a value is defined at the top level of an 34 /// op with trait `AffineScope` or is a region argument for such an op. A value 35 /// of index type defined at the top level is always a valid symbol for all its 36 /// uses. 37 bool isTopLevelValue(Value value); 38 39 /// A utility function to check if a value is defined at the top level of 40 /// `region` or is an argument of `region`. A value of index type defined at the 41 /// top level of a `AffineScope` region is always a valid symbol for all 42 /// uses in that region. 43 bool isTopLevelValue(Value value, Region *region); 44 45 /// Returns the closest region enclosing `op` that is held by an operation with 46 /// trait `AffineScope`; `nullptr` if there is no such region. 47 Region *getAffineScope(Operation *op); 48 49 /// AffineDmaStartOp starts a non-blocking DMA operation that transfers data 50 /// from a source memref to a destination memref. The source and destination 51 /// memref need not be of the same dimensionality, but need to have the same 52 /// elemental type. The operands include the source and destination memref's 53 /// each followed by its indices, size of the data transfer in terms of the 54 /// number of elements (of the elemental type of the memref), a tag memref with 55 /// its indices, and optionally at the end, a stride and a 56 /// number_of_elements_per_stride arguments. The tag location is used by an 57 /// AffineDmaWaitOp to check for completion. The indices of the source memref, 58 /// destination memref, and the tag memref have the same restrictions as any 59 /// affine.load/store. In particular, index for each memref dimension must be an 60 /// affine expression of loop induction variables and symbols. 61 /// The optional stride arguments should be of 'index' type, and specify a 62 /// stride for the slower memory space (memory space with a lower memory space 63 /// id), transferring chunks of number_of_elements_per_stride every stride until 64 /// %num_elements are transferred. Either both or no stride arguments should be 65 /// specified. The value of 'num_elements' must be a multiple of 66 /// 'number_of_elements_per_stride'. If the source and destination locations 67 /// overlap the behavior of this operation is not defined. 68 // 69 // For example, an AffineDmaStartOp operation that transfers 256 elements of a 70 // memref '%src' in memory space 0 at indices [%i + 3, %j] to memref '%dst' in 71 // memory space 1 at indices [%k + 7, %l], would be specified as follows: 72 // 73 // %num_elements = arith.constant 256 74 // %idx = arith.constant 0 : index 75 // %tag = memref.alloc() : memref<1xi32, 4> 76 // affine.dma_start %src[%i + 3, %j], %dst[%k + 7, %l], %tag[%idx], 77 // %num_elements : 78 // memref<40x128xf32, 0>, memref<2x1024xf32, 1>, memref<1xi32, 2> 79 // 80 // If %stride and %num_elt_per_stride are specified, the DMA is expected to 81 // transfer %num_elt_per_stride elements every %stride elements apart from 82 // memory space 0 until %num_elements are transferred. 83 // 84 // affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%idx], %num_elements, 85 // %stride, %num_elt_per_stride : ... 86 // 87 // TODO: add additional operands to allow source and destination striding, and 88 // multiple stride levels (possibly using AffineMaps to specify multiple levels 89 // of striding). 90 class AffineDmaStartOp 91 : public Op<AffineDmaStartOp, OpTrait::MemRefsNormalizable, 92 OpTrait::VariadicOperands, OpTrait::ZeroResults, 93 OpTrait::OpInvariants, AffineMapAccessInterface::Trait> { 94 public: 95 using Op::Op; getAttributeNames()96 static ArrayRef<StringRef> getAttributeNames() { return {}; } 97 98 static void build(OpBuilder &builder, OperationState &result, Value srcMemRef, 99 AffineMap srcMap, ValueRange srcIndices, Value destMemRef, 100 AffineMap dstMap, ValueRange destIndices, Value tagMemRef, 101 AffineMap tagMap, ValueRange tagIndices, Value numElements, 102 Value stride = nullptr, Value elementsPerStride = nullptr); 103 104 /// Returns the operand index of the source memref. getSrcMemRefOperandIndex()105 unsigned getSrcMemRefOperandIndex() { return 0; } 106 107 /// Returns the source MemRefType for this DMA operation. getSrcMemRef()108 Value getSrcMemRef() { return getOperand(getSrcMemRefOperandIndex()); } getSrcMemRefType()109 MemRefType getSrcMemRefType() { 110 return getSrcMemRef().getType().cast<MemRefType>(); 111 } 112 113 /// Returns the rank (number of indices) of the source MemRefType. getSrcMemRefRank()114 unsigned getSrcMemRefRank() { return getSrcMemRefType().getRank(); } 115 116 /// Returns the affine map used to access the source memref. getSrcMap()117 AffineMap getSrcMap() { return getSrcMapAttr().getValue(); } getSrcMapAttr()118 AffineMapAttr getSrcMapAttr() { 119 return (*this)->getAttr(getSrcMapAttrStrName()).cast<AffineMapAttr>(); 120 } 121 122 /// Returns the source memref affine map indices for this DMA operation. getSrcIndices()123 operand_range getSrcIndices() { 124 return {operand_begin() + getSrcMemRefOperandIndex() + 1, 125 operand_begin() + getSrcMemRefOperandIndex() + 1 + 126 getSrcMap().getNumInputs()}; 127 } 128 129 /// Returns the memory space of the source memref. getSrcMemorySpace()130 unsigned getSrcMemorySpace() { 131 return getSrcMemRef().getType().cast<MemRefType>().getMemorySpaceAsInt(); 132 } 133 134 /// Returns the operand index of the destination memref. getDstMemRefOperandIndex()135 unsigned getDstMemRefOperandIndex() { 136 return getSrcMemRefOperandIndex() + 1 + getSrcMap().getNumInputs(); 137 } 138 139 /// Returns the destination MemRefType for this DMA operation. getDstMemRef()140 Value getDstMemRef() { return getOperand(getDstMemRefOperandIndex()); } getDstMemRefType()141 MemRefType getDstMemRefType() { 142 return getDstMemRef().getType().cast<MemRefType>(); 143 } 144 145 /// Returns the rank (number of indices) of the destination MemRefType. getDstMemRefRank()146 unsigned getDstMemRefRank() { 147 return getDstMemRef().getType().cast<MemRefType>().getRank(); 148 } 149 150 /// Returns the memory space of the source memref. getDstMemorySpace()151 unsigned getDstMemorySpace() { 152 return getDstMemRef().getType().cast<MemRefType>().getMemorySpaceAsInt(); 153 } 154 155 /// Returns the affine map used to access the destination memref. getDstMap()156 AffineMap getDstMap() { return getDstMapAttr().getValue(); } getDstMapAttr()157 AffineMapAttr getDstMapAttr() { 158 return (*this)->getAttr(getDstMapAttrStrName()).cast<AffineMapAttr>(); 159 } 160 161 /// Returns the destination memref indices for this DMA operation. getDstIndices()162 operand_range getDstIndices() { 163 return {operand_begin() + getDstMemRefOperandIndex() + 1, 164 operand_begin() + getDstMemRefOperandIndex() + 1 + 165 getDstMap().getNumInputs()}; 166 } 167 168 /// Returns the operand index of the tag memref. getTagMemRefOperandIndex()169 unsigned getTagMemRefOperandIndex() { 170 return getDstMemRefOperandIndex() + 1 + getDstMap().getNumInputs(); 171 } 172 173 /// Returns the Tag MemRef for this DMA operation. getTagMemRef()174 Value getTagMemRef() { return getOperand(getTagMemRefOperandIndex()); } getTagMemRefType()175 MemRefType getTagMemRefType() { 176 return getTagMemRef().getType().cast<MemRefType>(); 177 } 178 179 /// Returns the rank (number of indices) of the tag MemRefType. getTagMemRefRank()180 unsigned getTagMemRefRank() { 181 return getTagMemRef().getType().cast<MemRefType>().getRank(); 182 } 183 184 /// Returns the affine map used to access the tag memref. getTagMap()185 AffineMap getTagMap() { return getTagMapAttr().getValue(); } getTagMapAttr()186 AffineMapAttr getTagMapAttr() { 187 return (*this)->getAttr(getTagMapAttrStrName()).cast<AffineMapAttr>(); 188 } 189 190 /// Returns the tag memref indices for this DMA operation. getTagIndices()191 operand_range getTagIndices() { 192 return {operand_begin() + getTagMemRefOperandIndex() + 1, 193 operand_begin() + getTagMemRefOperandIndex() + 1 + 194 getTagMap().getNumInputs()}; 195 } 196 197 /// Returns the number of elements being transferred by this DMA operation. getNumElements()198 Value getNumElements() { 199 return getOperand(getTagMemRefOperandIndex() + 1 + 200 getTagMap().getNumInputs()); 201 } 202 203 /// Impelements the AffineMapAccessInterface. 204 /// Returns the AffineMapAttr associated with 'memref'. getAffineMapAttrForMemRef(Value memref)205 NamedAttribute getAffineMapAttrForMemRef(Value memref) { 206 if (memref == getSrcMemRef()) 207 return {StringAttr::get(getContext(), getSrcMapAttrStrName()), 208 getSrcMapAttr()}; 209 if (memref == getDstMemRef()) 210 return {StringAttr::get(getContext(), getDstMapAttrStrName()), 211 getDstMapAttr()}; 212 assert(memref == getTagMemRef() && 213 "DmaStartOp expected source, destination or tag memref"); 214 return {StringAttr::get(getContext(), getTagMapAttrStrName()), 215 getTagMapAttr()}; 216 } 217 218 /// Returns true if this is a DMA from a faster memory space to a slower one. isDestMemorySpaceFaster()219 bool isDestMemorySpaceFaster() { 220 return (getSrcMemorySpace() < getDstMemorySpace()); 221 } 222 223 /// Returns true if this is a DMA from a slower memory space to a faster one. isSrcMemorySpaceFaster()224 bool isSrcMemorySpaceFaster() { 225 // Assumes that a lower number is for a slower memory space. 226 return (getDstMemorySpace() < getSrcMemorySpace()); 227 } 228 229 /// Given a DMA start operation, returns the operand position of either the 230 /// source or destination memref depending on the one that is at the higher 231 /// level of the memory hierarchy. Asserts failure if neither is true. getFasterMemPos()232 unsigned getFasterMemPos() { 233 assert(isSrcMemorySpaceFaster() || isDestMemorySpaceFaster()); 234 return isSrcMemorySpaceFaster() ? 0 : getDstMemRefOperandIndex(); 235 } 236 getSrcMapAttrStrName()237 static StringRef getSrcMapAttrStrName() { return "src_map"; } getDstMapAttrStrName()238 static StringRef getDstMapAttrStrName() { return "dst_map"; } getTagMapAttrStrName()239 static StringRef getTagMapAttrStrName() { return "tag_map"; } 240 getOperationName()241 static StringRef getOperationName() { return "affine.dma_start"; } 242 static ParseResult parse(OpAsmParser &parser, OperationState &result); 243 void print(OpAsmPrinter &p); 244 LogicalResult verifyInvariantsImpl(); verifyInvariants()245 LogicalResult verifyInvariants() { return verifyInvariantsImpl(); } 246 LogicalResult fold(ArrayRef<Attribute> cstOperands, 247 SmallVectorImpl<OpFoldResult> &results); 248 249 /// Returns true if this DMA operation is strided, returns false otherwise. isStrided()250 bool isStrided() { 251 return getNumOperands() != 252 getTagMemRefOperandIndex() + 1 + getTagMap().getNumInputs() + 1; 253 } 254 255 /// Returns the stride value for this DMA operation. getStride()256 Value getStride() { 257 if (!isStrided()) 258 return nullptr; 259 return getOperand(getNumOperands() - 1 - 1); 260 } 261 262 /// Returns the number of elements to transfer per stride for this DMA op. getNumElementsPerStride()263 Value getNumElementsPerStride() { 264 if (!isStrided()) 265 return nullptr; 266 return getOperand(getNumOperands() - 1); 267 } 268 }; 269 270 /// AffineDmaWaitOp blocks until the completion of a DMA operation associated 271 /// with the tag element '%tag[%index]'. %tag is a memref, and %index has to be 272 /// an index with the same restrictions as any load/store index. In particular, 273 /// index for each memref dimension must be an affine expression of loop 274 /// induction variables and symbols. %num_elements is the number of elements 275 /// associated with the DMA operation. For example: 276 // 277 // affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %num_elements : 278 // memref<2048xf32, 0>, memref<256xf32, 1>, memref<1xi32, 2> 279 // ... 280 // ... 281 // affine.dma_wait %tag[%index], %num_elements : memref<1xi32, 2> 282 // 283 class AffineDmaWaitOp 284 : public Op<AffineDmaWaitOp, OpTrait::MemRefsNormalizable, 285 OpTrait::VariadicOperands, OpTrait::ZeroResults, 286 OpTrait::OpInvariants, AffineMapAccessInterface::Trait> { 287 public: 288 using Op::Op; getAttributeNames()289 static ArrayRef<StringRef> getAttributeNames() { return {}; } 290 291 static void build(OpBuilder &builder, OperationState &result, Value tagMemRef, 292 AffineMap tagMap, ValueRange tagIndices, Value numElements); 293 getOperationName()294 static StringRef getOperationName() { return "affine.dma_wait"; } 295 296 /// Returns the Tag MemRef associated with the DMA operation being waited on. getTagMemRef()297 Value getTagMemRef() { return getOperand(0); } getTagMemRefType()298 MemRefType getTagMemRefType() { 299 return getTagMemRef().getType().cast<MemRefType>(); 300 } 301 302 /// Returns the affine map used to access the tag memref. getTagMap()303 AffineMap getTagMap() { return getTagMapAttr().getValue(); } getTagMapAttr()304 AffineMapAttr getTagMapAttr() { 305 return (*this)->getAttr(getTagMapAttrStrName()).cast<AffineMapAttr>(); 306 } 307 308 /// Returns the tag memref index for this DMA operation. getTagIndices()309 operand_range getTagIndices() { 310 return {operand_begin() + 1, 311 operand_begin() + 1 + getTagMap().getNumInputs()}; 312 } 313 314 /// Returns the rank (number of indices) of the tag memref. getTagMemRefRank()315 unsigned getTagMemRefRank() { 316 return getTagMemRef().getType().cast<MemRefType>().getRank(); 317 } 318 319 /// Impelements the AffineMapAccessInterface. Returns the AffineMapAttr 320 /// associated with 'memref'. getAffineMapAttrForMemRef(Value memref)321 NamedAttribute getAffineMapAttrForMemRef(Value memref) { 322 assert(memref == getTagMemRef()); 323 return {StringAttr::get(getContext(), getTagMapAttrStrName()), 324 getTagMapAttr()}; 325 } 326 327 /// Returns the number of elements transferred by the associated DMA op. getNumElements()328 Value getNumElements() { return getOperand(1 + getTagMap().getNumInputs()); } 329 getTagMapAttrStrName()330 static StringRef getTagMapAttrStrName() { return "tag_map"; } 331 static ParseResult parse(OpAsmParser &parser, OperationState &result); 332 void print(OpAsmPrinter &p); 333 LogicalResult verifyInvariantsImpl(); verifyInvariants()334 LogicalResult verifyInvariants() { return verifyInvariantsImpl(); } 335 LogicalResult fold(ArrayRef<Attribute> cstOperands, 336 SmallVectorImpl<OpFoldResult> &results); 337 }; 338 339 /// Returns true if the given Value can be used as a dimension id in the region 340 /// of the closest surrounding op that has the trait `AffineScope`. 341 bool isValidDim(Value value); 342 343 /// Returns true if the given Value can be used as a dimension id in `region`, 344 /// i.e., for all its uses in `region`. 345 bool isValidDim(Value value, Region *region); 346 347 /// Returns true if the given value can be used as a symbol in the region of the 348 /// closest surrounding op that has the trait `AffineScope`. 349 bool isValidSymbol(Value value); 350 351 /// Returns true if the given Value can be used as a symbol for `region`, i.e., 352 /// for all its uses in `region`. 353 bool isValidSymbol(Value value, Region *region); 354 355 /// Parses dimension and symbol list. `numDims` is set to the number of 356 /// dimensions in the list parsed. 357 ParseResult parseDimAndSymbolList(OpAsmParser &parser, 358 SmallVectorImpl<Value> &operands, 359 unsigned &numDims); 360 361 /// Modifies both `map` and `operands` in-place so as to: 362 /// 1. drop duplicate operands 363 /// 2. drop unused dims and symbols from map 364 /// 3. promote valid symbols to symbolic operands in case they appeared as 365 /// dimensional operands 366 /// 4. propagate constant operands and drop them 367 void canonicalizeMapAndOperands(AffineMap *map, 368 SmallVectorImpl<Value> *operands); 369 370 /// Canonicalizes an integer set the same way canonicalizeMapAndOperands does 371 /// for affine maps. 372 void canonicalizeSetAndOperands(IntegerSet *set, 373 SmallVectorImpl<Value> *operands); 374 375 /// Returns a composed AffineApplyOp by composing `map` and `operands` with 376 /// other AffineApplyOps supplying those operands. The operands of the resulting 377 /// AffineApplyOp do not change the length of AffineApplyOp chains. 378 AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, 379 ValueRange operands); 380 /// Variant of `makeComposedAffineApply` which infers the AffineMap from `e`. 381 AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineExpr e, 382 ValueRange values); 383 384 /// Constructs an AffineApplyOp that applies `map` to `operands` after composing 385 /// the map with the maps of any other AffineApplyOp supplying the operands, 386 /// then immediately attempts to fold it. If folding results in a constant 387 /// value, erases all created ops. The `map` must be a single-result affine map. 388 OpFoldResult makeComposedFoldedAffineApply(RewriterBase &b, Location loc, 389 AffineMap map, 390 ArrayRef<OpFoldResult> operands); 391 /// Variant of `makeComposedFoldedAffineApply` that applies to an expression. 392 OpFoldResult makeComposedFoldedAffineApply(RewriterBase &b, Location loc, 393 AffineExpr expr, 394 ArrayRef<OpFoldResult> operands); 395 396 /// Returns an AffineMinOp obtained by composing `map` and `operands` with 397 /// AffineApplyOps supplying those operands. 398 Value makeComposedAffineMin(OpBuilder &b, Location loc, AffineMap map, 399 ValueRange operands); 400 401 /// Constructs an AffineMinOp that computes a minimum across the results of 402 /// applying `map` to `operands`, then immediately attempts to fold it. If 403 /// folding results in a constant value, erases all created ops. 404 OpFoldResult makeComposedFoldedAffineMin(RewriterBase &b, Location loc, 405 AffineMap map, 406 ArrayRef<OpFoldResult> operands); 407 408 /// Returns the values obtained by applying `map` to the list of values. 409 SmallVector<Value, 4> applyMapToValues(OpBuilder &b, Location loc, 410 AffineMap map, ValueRange values); 411 412 /// Returns the values obtained by applying `map` to the list of values, which 413 /// may be known constants. 414 SmallVector<OpFoldResult> applyMapToValues(RewriterBase &b, Location loc, 415 AffineMap map, 416 ArrayRef<OpFoldResult> values); 417 418 /// Given an affine map `map` and its input `operands`, this method composes 419 /// into `map`, maps of AffineApplyOps whose results are the values in 420 /// `operands`, iteratively until no more of `operands` are the result of an 421 /// AffineApplyOp. When this function returns, `map` becomes the composed affine 422 /// map, and each Value in `operands` is guaranteed to be either a loop IV or a 423 /// terminal symbol, i.e., a symbol defined at the top level or a block/function 424 /// argument. 425 void fullyComposeAffineMapAndOperands(AffineMap *map, 426 SmallVectorImpl<Value> *operands); 427 } // namespace mlir 428 #include "mlir/Dialect/Affine/IR/AffineOpsDialect.h.inc" 429 430 #define GET_OP_CLASSES 431 #include "mlir/Dialect/Affine/IR/AffineOps.h.inc" 432 433 namespace mlir { 434 /// Returns true if the provided value is the induction variable of a 435 /// AffineForOp. 436 bool isForInductionVar(Value val); 437 438 /// Returns the loop parent of an induction variable. If the provided value is 439 /// not an induction variable, then return nullptr. 440 AffineForOp getForInductionVarOwner(Value val); 441 442 /// Extracts the induction variables from a list of AffineForOps and places them 443 /// in the output argument `ivs`. 444 void extractForInductionVars(ArrayRef<AffineForOp> forInsts, 445 SmallVectorImpl<Value> *ivs); 446 447 /// Builds a perfect nest of affine.for loops, i.e., each loop except the 448 /// innermost one contains only another loop and a terminator. The loops iterate 449 /// from "lbs" to "ubs" with "steps". The body of the innermost loop is 450 /// populated by calling "bodyBuilderFn" and providing it with an OpBuilder, a 451 /// Location and a list of loop induction variables. 452 void buildAffineLoopNest(OpBuilder &builder, Location loc, 453 ArrayRef<int64_t> lbs, ArrayRef<int64_t> ubs, 454 ArrayRef<int64_t> steps, 455 function_ref<void(OpBuilder &, Location, ValueRange)> 456 bodyBuilderFn = nullptr); 457 void buildAffineLoopNest(OpBuilder &builder, Location loc, ValueRange lbs, 458 ValueRange ubs, ArrayRef<int64_t> steps, 459 function_ref<void(OpBuilder &, Location, ValueRange)> 460 bodyBuilderFn = nullptr); 461 462 /// Replace `loop` with a new loop where `newIterOperands` are appended with 463 /// new initialization values and `newYieldedValues` are added as new yielded 464 /// values. The returned ForOp has `newYieldedValues.size()` new result values. 465 /// Additionally, if `replaceLoopResults` is true, all uses of 466 /// `loop.getResults()` are replaced with the first `loop.getNumResults()` 467 /// return values of the original loop respectively. The original loop is 468 /// deleted and the new loop returned. 469 /// Prerequisite: `newIterOperands.size() == newYieldedValues.size()`. 470 AffineForOp replaceForOpWithNewYields(OpBuilder &b, AffineForOp loop, 471 ValueRange newIterOperands, 472 ValueRange newYieldedValues, 473 ValueRange newIterArgs, 474 bool replaceLoopResults = true); 475 476 /// AffineBound represents a lower or upper bound in the for operation. 477 /// This class does not own the underlying operands. Instead, it refers 478 /// to the operands stored in the AffineForOp. Its life span should not exceed 479 /// that of the for operation it refers to. 480 class AffineBound { 481 public: getAffineForOp()482 AffineForOp getAffineForOp() { return op; } getMap()483 AffineMap getMap() { return map; } 484 getNumOperands()485 unsigned getNumOperands() { return opEnd - opStart; } getOperand(unsigned idx)486 Value getOperand(unsigned idx) { return op.getOperand(opStart + idx); } 487 488 using operand_iterator = AffineForOp::operand_iterator; 489 using operand_range = AffineForOp::operand_range; 490 operandBegin()491 operand_iterator operandBegin() { return op.operand_begin() + opStart; } operandEnd()492 operand_iterator operandEnd() { return op.operand_begin() + opEnd; } getOperands()493 operand_range getOperands() { return {operandBegin(), operandEnd()}; } 494 495 private: 496 // 'affine.for' operation that contains this bound. 497 AffineForOp op; 498 // Start and end positions of this affine bound operands in the list of 499 // the containing 'affine.for' operation operands. 500 unsigned opStart, opEnd; 501 // Affine map for this bound. 502 AffineMap map; 503 AffineBound(AffineForOp op,unsigned opStart,unsigned opEnd,AffineMap map)504 AffineBound(AffineForOp op, unsigned opStart, unsigned opEnd, AffineMap map) 505 : op(op), opStart(opStart), opEnd(opEnd), map(map) {} 506 507 friend class AffineForOp; 508 }; 509 510 } // namespace mlir 511 512 #endif 513