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