1 //===- AffineToStandard.cpp - Lower affine constructs to primitives -------===//
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 lowers affine constructs (If and For statements, AffineApply
10 // operations) within a function into their standard If and For equivalent ops.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
15 
16 #include "../PassDetail.h"
17 #include "mlir/Dialect/Affine/IR/AffineOps.h"
18 #include "mlir/Dialect/Affine/Utils.h"
19 #include "mlir/Dialect/MemRef/IR/MemRef.h"
20 #include "mlir/Dialect/SCF/SCF.h"
21 #include "mlir/Dialect/StandardOps/IR/Ops.h"
22 #include "mlir/Dialect/Vector/IR/VectorOps.h"
23 #include "mlir/IR/BlockAndValueMapping.h"
24 #include "mlir/IR/IntegerSet.h"
25 #include "mlir/IR/MLIRContext.h"
26 #include "mlir/Pass/Pass.h"
27 #include "mlir/Transforms/DialectConversion.h"
28 #include "mlir/Transforms/Passes.h"
29 
30 using namespace mlir;
31 using namespace mlir::vector;
32 
33 /// Given a range of values, emit the code that reduces them with "min" or "max"
34 /// depending on the provided comparison predicate.  The predicate defines which
35 /// comparison to perform, "lt" for "min", "gt" for "max" and is used for the
36 /// `cmpi` operation followed by the `select` operation:
37 ///
38 ///   %cond   = arith.cmpi "predicate" %v0, %v1
39 ///   %result = select %cond, %v0, %v1
40 ///
41 /// Multiple values are scanned in a linear sequence.  This creates a data
42 /// dependences that wouldn't exist in a tree reduction, but is easier to
43 /// recognize as a reduction by the subsequent passes.
44 static Value buildMinMaxReductionSeq(Location loc,
45                                      arith::CmpIPredicate predicate,
46                                      ValueRange values, OpBuilder &builder) {
47   assert(!llvm::empty(values) && "empty min/max chain");
48 
49   auto valueIt = values.begin();
50   Value value = *valueIt++;
51   for (; valueIt != values.end(); ++valueIt) {
52     auto cmpOp = builder.create<arith::CmpIOp>(loc, predicate, value, *valueIt);
53     value = builder.create<arith::SelectOp>(loc, cmpOp.getResult(), value,
54                                             *valueIt);
55   }
56 
57   return value;
58 }
59 
60 /// Emit instructions that correspond to computing the maximum value among the
61 /// values of a (potentially) multi-output affine map applied to `operands`.
62 static Value lowerAffineMapMax(OpBuilder &builder, Location loc, AffineMap map,
63                                ValueRange operands) {
64   if (auto values = expandAffineMap(builder, loc, map, operands))
65     return buildMinMaxReductionSeq(loc, arith::CmpIPredicate::sgt, *values,
66                                    builder);
67   return nullptr;
68 }
69 
70 /// Emit instructions that correspond to computing the minimum value among the
71 /// values of a (potentially) multi-output affine map applied to `operands`.
72 static Value lowerAffineMapMin(OpBuilder &builder, Location loc, AffineMap map,
73                                ValueRange operands) {
74   if (auto values = expandAffineMap(builder, loc, map, operands))
75     return buildMinMaxReductionSeq(loc, arith::CmpIPredicate::slt, *values,
76                                    builder);
77   return nullptr;
78 }
79 
80 /// Emit instructions that correspond to the affine map in the upper bound
81 /// applied to the respective operands, and compute the minimum value across
82 /// the results.
83 Value mlir::lowerAffineUpperBound(AffineForOp op, OpBuilder &builder) {
84   return lowerAffineMapMin(builder, op.getLoc(), op.getUpperBoundMap(),
85                            op.getUpperBoundOperands());
86 }
87 
88 /// Emit instructions that correspond to the affine map in the lower bound
89 /// applied to the respective operands, and compute the maximum value across
90 /// the results.
91 Value mlir::lowerAffineLowerBound(AffineForOp op, OpBuilder &builder) {
92   return lowerAffineMapMax(builder, op.getLoc(), op.getLowerBoundMap(),
93                            op.getLowerBoundOperands());
94 }
95 
96 namespace {
97 class AffineMinLowering : public OpRewritePattern<AffineMinOp> {
98 public:
99   using OpRewritePattern<AffineMinOp>::OpRewritePattern;
100 
101   LogicalResult matchAndRewrite(AffineMinOp op,
102                                 PatternRewriter &rewriter) const override {
103     Value reduced =
104         lowerAffineMapMin(rewriter, op.getLoc(), op.map(), op.operands());
105     if (!reduced)
106       return failure();
107 
108     rewriter.replaceOp(op, reduced);
109     return success();
110   }
111 };
112 
113 class AffineMaxLowering : public OpRewritePattern<AffineMaxOp> {
114 public:
115   using OpRewritePattern<AffineMaxOp>::OpRewritePattern;
116 
117   LogicalResult matchAndRewrite(AffineMaxOp op,
118                                 PatternRewriter &rewriter) const override {
119     Value reduced =
120         lowerAffineMapMax(rewriter, op.getLoc(), op.map(), op.operands());
121     if (!reduced)
122       return failure();
123 
124     rewriter.replaceOp(op, reduced);
125     return success();
126   }
127 };
128 
129 /// Affine yields ops are removed.
130 class AffineYieldOpLowering : public OpRewritePattern<AffineYieldOp> {
131 public:
132   using OpRewritePattern<AffineYieldOp>::OpRewritePattern;
133 
134   LogicalResult matchAndRewrite(AffineYieldOp op,
135                                 PatternRewriter &rewriter) const override {
136     if (isa<scf::ParallelOp>(op->getParentOp())) {
137       // scf.parallel does not yield any values via its terminator scf.yield but
138       // models reductions differently using additional ops in its region.
139       rewriter.replaceOpWithNewOp<scf::YieldOp>(op);
140       return success();
141     }
142     rewriter.replaceOpWithNewOp<scf::YieldOp>(op, op.operands());
143     return success();
144   }
145 };
146 
147 class AffineForLowering : public OpRewritePattern<AffineForOp> {
148 public:
149   using OpRewritePattern<AffineForOp>::OpRewritePattern;
150 
151   LogicalResult matchAndRewrite(AffineForOp op,
152                                 PatternRewriter &rewriter) const override {
153     Location loc = op.getLoc();
154     Value lowerBound = lowerAffineLowerBound(op, rewriter);
155     Value upperBound = lowerAffineUpperBound(op, rewriter);
156     Value step = rewriter.create<arith::ConstantIndexOp>(loc, op.getStep());
157     auto scfForOp = rewriter.create<scf::ForOp>(loc, lowerBound, upperBound,
158                                                 step, op.getIterOperands());
159     rewriter.eraseBlock(scfForOp.getBody());
160     rewriter.inlineRegionBefore(op.region(), scfForOp.getRegion(),
161                                 scfForOp.getRegion().end());
162     rewriter.replaceOp(op, scfForOp.getResults());
163     return success();
164   }
165 };
166 
167 /// Convert an `affine.parallel` (loop nest) operation into a `scf.parallel`
168 /// operation.
169 class AffineParallelLowering : public OpRewritePattern<AffineParallelOp> {
170 public:
171   using OpRewritePattern<AffineParallelOp>::OpRewritePattern;
172 
173   LogicalResult matchAndRewrite(AffineParallelOp op,
174                                 PatternRewriter &rewriter) const override {
175     Location loc = op.getLoc();
176     SmallVector<Value, 8> steps;
177     SmallVector<Value, 8> upperBoundTuple;
178     SmallVector<Value, 8> lowerBoundTuple;
179     SmallVector<Value, 8> identityVals;
180     // Emit IR computing the lower and upper bound by expanding the map
181     // expression.
182     lowerBoundTuple.reserve(op.getNumDims());
183     upperBoundTuple.reserve(op.getNumDims());
184     for (unsigned i = 0, e = op.getNumDims(); i < e; ++i) {
185       Value lower = lowerAffineMapMax(rewriter, loc, op.getLowerBoundMap(i),
186                                       op.getLowerBoundsOperands());
187       if (!lower)
188         return rewriter.notifyMatchFailure(op, "couldn't convert lower bounds");
189       lowerBoundTuple.push_back(lower);
190 
191       Value upper = lowerAffineMapMin(rewriter, loc, op.getUpperBoundMap(i),
192                                       op.getUpperBoundsOperands());
193       if (!upper)
194         return rewriter.notifyMatchFailure(op, "couldn't convert upper bounds");
195       upperBoundTuple.push_back(upper);
196     }
197     steps.reserve(op.steps().size());
198     for (Attribute step : op.steps())
199       steps.push_back(rewriter.create<arith::ConstantIndexOp>(
200           loc, step.cast<IntegerAttr>().getInt()));
201 
202     // Get the terminator op.
203     Operation *affineParOpTerminator = op.getBody()->getTerminator();
204     scf::ParallelOp parOp;
205     if (op.results().empty()) {
206       // Case with no reduction operations/return values.
207       parOp = rewriter.create<scf::ParallelOp>(loc, lowerBoundTuple,
208                                                upperBoundTuple, steps,
209                                                /*bodyBuilderFn=*/nullptr);
210       rewriter.eraseBlock(parOp.getBody());
211       rewriter.inlineRegionBefore(op.region(), parOp.getRegion(),
212                                   parOp.getRegion().end());
213       rewriter.replaceOp(op, parOp.getResults());
214       return success();
215     }
216     // Case with affine.parallel with reduction operations/return values.
217     // scf.parallel handles the reduction operation differently unlike
218     // affine.parallel.
219     ArrayRef<Attribute> reductions = op.reductions().getValue();
220     for (auto pair : llvm::zip(reductions, op.getResultTypes())) {
221       // For each of the reduction operations get the identity values for
222       // initialization of the result values.
223       Attribute reduction = std::get<0>(pair);
224       Type resultType = std::get<1>(pair);
225       Optional<arith::AtomicRMWKind> reductionOp =
226           arith::symbolizeAtomicRMWKind(
227               static_cast<uint64_t>(reduction.cast<IntegerAttr>().getInt()));
228       assert(reductionOp.hasValue() &&
229              "Reduction operation cannot be of None Type");
230       arith::AtomicRMWKind reductionOpValue = reductionOp.getValue();
231       identityVals.push_back(
232           arith::getIdentityValue(reductionOpValue, resultType, rewriter, loc));
233     }
234     parOp = rewriter.create<scf::ParallelOp>(
235         loc, lowerBoundTuple, upperBoundTuple, steps, identityVals,
236         /*bodyBuilderFn=*/nullptr);
237 
238     //  Copy the body of the affine.parallel op.
239     rewriter.eraseBlock(parOp.getBody());
240     rewriter.inlineRegionBefore(op.region(), parOp.getRegion(),
241                                 parOp.getRegion().end());
242     assert(reductions.size() == affineParOpTerminator->getNumOperands() &&
243            "Unequal number of reductions and operands.");
244     for (unsigned i = 0, end = reductions.size(); i < end; i++) {
245       // For each of the reduction operations get the respective mlir::Value.
246       Optional<arith::AtomicRMWKind> reductionOp =
247           arith::symbolizeAtomicRMWKind(
248               reductions[i].cast<IntegerAttr>().getInt());
249       assert(reductionOp.hasValue() &&
250              "Reduction Operation cannot be of None Type");
251       arith::AtomicRMWKind reductionOpValue = reductionOp.getValue();
252       rewriter.setInsertionPoint(&parOp.getBody()->back());
253       auto reduceOp = rewriter.create<scf::ReduceOp>(
254           loc, affineParOpTerminator->getOperand(i));
255       rewriter.setInsertionPointToEnd(&reduceOp.getReductionOperator().front());
256       Value reductionResult = arith::getReductionOp(
257           reductionOpValue, rewriter, loc,
258           reduceOp.getReductionOperator().front().getArgument(0),
259           reduceOp.getReductionOperator().front().getArgument(1));
260       rewriter.create<scf::ReduceReturnOp>(loc, reductionResult);
261     }
262     rewriter.replaceOp(op, parOp.getResults());
263     return success();
264   }
265 };
266 
267 class AffineIfLowering : public OpRewritePattern<AffineIfOp> {
268 public:
269   using OpRewritePattern<AffineIfOp>::OpRewritePattern;
270 
271   LogicalResult matchAndRewrite(AffineIfOp op,
272                                 PatternRewriter &rewriter) const override {
273     auto loc = op.getLoc();
274 
275     // Now we just have to handle the condition logic.
276     auto integerSet = op.getIntegerSet();
277     Value zeroConstant = rewriter.create<arith::ConstantIndexOp>(loc, 0);
278     SmallVector<Value, 8> operands(op.getOperands());
279     auto operandsRef = llvm::makeArrayRef(operands);
280 
281     // Calculate cond as a conjunction without short-circuiting.
282     Value cond = nullptr;
283     for (unsigned i = 0, e = integerSet.getNumConstraints(); i < e; ++i) {
284       AffineExpr constraintExpr = integerSet.getConstraint(i);
285       bool isEquality = integerSet.isEq(i);
286 
287       // Build and apply an affine expression
288       auto numDims = integerSet.getNumDims();
289       Value affResult = expandAffineExpr(rewriter, loc, constraintExpr,
290                                          operandsRef.take_front(numDims),
291                                          operandsRef.drop_front(numDims));
292       if (!affResult)
293         return failure();
294       auto pred =
295           isEquality ? arith::CmpIPredicate::eq : arith::CmpIPredicate::sge;
296       Value cmpVal =
297           rewriter.create<arith::CmpIOp>(loc, pred, affResult, zeroConstant);
298       cond = cond
299                  ? rewriter.create<arith::AndIOp>(loc, cond, cmpVal).getResult()
300                  : cmpVal;
301     }
302     cond = cond ? cond
303                 : rewriter.create<arith::ConstantIntOp>(loc, /*value=*/1,
304                                                         /*width=*/1);
305 
306     bool hasElseRegion = !op.elseRegion().empty();
307     auto ifOp = rewriter.create<scf::IfOp>(loc, op.getResultTypes(), cond,
308                                            hasElseRegion);
309     rewriter.inlineRegionBefore(op.thenRegion(), &ifOp.getThenRegion().back());
310     rewriter.eraseBlock(&ifOp.getThenRegion().back());
311     if (hasElseRegion) {
312       rewriter.inlineRegionBefore(op.elseRegion(),
313                                   &ifOp.getElseRegion().back());
314       rewriter.eraseBlock(&ifOp.getElseRegion().back());
315     }
316 
317     // Replace the Affine IfOp finally.
318     rewriter.replaceOp(op, ifOp.getResults());
319     return success();
320   }
321 };
322 
323 /// Convert an "affine.apply" operation into a sequence of arithmetic
324 /// operations using the StandardOps dialect.
325 class AffineApplyLowering : public OpRewritePattern<AffineApplyOp> {
326 public:
327   using OpRewritePattern<AffineApplyOp>::OpRewritePattern;
328 
329   LogicalResult matchAndRewrite(AffineApplyOp op,
330                                 PatternRewriter &rewriter) const override {
331     auto maybeExpandedMap =
332         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(),
333                         llvm::to_vector<8>(op.getOperands()));
334     if (!maybeExpandedMap)
335       return failure();
336     rewriter.replaceOp(op, *maybeExpandedMap);
337     return success();
338   }
339 };
340 
341 /// Apply the affine map from an 'affine.load' operation to its operands, and
342 /// feed the results to a newly created 'memref.load' operation (which replaces
343 /// the original 'affine.load').
344 class AffineLoadLowering : public OpRewritePattern<AffineLoadOp> {
345 public:
346   using OpRewritePattern<AffineLoadOp>::OpRewritePattern;
347 
348   LogicalResult matchAndRewrite(AffineLoadOp op,
349                                 PatternRewriter &rewriter) const override {
350     // Expand affine map from 'affineLoadOp'.
351     SmallVector<Value, 8> indices(op.getMapOperands());
352     auto resultOperands =
353         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
354     if (!resultOperands)
355       return failure();
356 
357     // Build vector.load memref[expandedMap.results].
358     rewriter.replaceOpWithNewOp<memref::LoadOp>(op, op.getMemRef(),
359                                                 *resultOperands);
360     return success();
361   }
362 };
363 
364 /// Apply the affine map from an 'affine.prefetch' operation to its operands,
365 /// and feed the results to a newly created 'memref.prefetch' operation (which
366 /// replaces the original 'affine.prefetch').
367 class AffinePrefetchLowering : public OpRewritePattern<AffinePrefetchOp> {
368 public:
369   using OpRewritePattern<AffinePrefetchOp>::OpRewritePattern;
370 
371   LogicalResult matchAndRewrite(AffinePrefetchOp op,
372                                 PatternRewriter &rewriter) const override {
373     // Expand affine map from 'affinePrefetchOp'.
374     SmallVector<Value, 8> indices(op.getMapOperands());
375     auto resultOperands =
376         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
377     if (!resultOperands)
378       return failure();
379 
380     // Build memref.prefetch memref[expandedMap.results].
381     rewriter.replaceOpWithNewOp<memref::PrefetchOp>(
382         op, op.memref(), *resultOperands, op.isWrite(), op.localityHint(),
383         op.isDataCache());
384     return success();
385   }
386 };
387 
388 /// Apply the affine map from an 'affine.store' operation to its operands, and
389 /// feed the results to a newly created 'memref.store' operation (which replaces
390 /// the original 'affine.store').
391 class AffineStoreLowering : public OpRewritePattern<AffineStoreOp> {
392 public:
393   using OpRewritePattern<AffineStoreOp>::OpRewritePattern;
394 
395   LogicalResult matchAndRewrite(AffineStoreOp op,
396                                 PatternRewriter &rewriter) const override {
397     // Expand affine map from 'affineStoreOp'.
398     SmallVector<Value, 8> indices(op.getMapOperands());
399     auto maybeExpandedMap =
400         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
401     if (!maybeExpandedMap)
402       return failure();
403 
404     // Build memref.store valueToStore, memref[expandedMap.results].
405     rewriter.replaceOpWithNewOp<memref::StoreOp>(
406         op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap);
407     return success();
408   }
409 };
410 
411 /// Apply the affine maps from an 'affine.dma_start' operation to each of their
412 /// respective map operands, and feed the results to a newly created
413 /// 'memref.dma_start' operation (which replaces the original
414 /// 'affine.dma_start').
415 class AffineDmaStartLowering : public OpRewritePattern<AffineDmaStartOp> {
416 public:
417   using OpRewritePattern<AffineDmaStartOp>::OpRewritePattern;
418 
419   LogicalResult matchAndRewrite(AffineDmaStartOp op,
420                                 PatternRewriter &rewriter) const override {
421     SmallVector<Value, 8> operands(op.getOperands());
422     auto operandsRef = llvm::makeArrayRef(operands);
423 
424     // Expand affine map for DMA source memref.
425     auto maybeExpandedSrcMap = expandAffineMap(
426         rewriter, op.getLoc(), op.getSrcMap(),
427         operandsRef.drop_front(op.getSrcMemRefOperandIndex() + 1));
428     if (!maybeExpandedSrcMap)
429       return failure();
430     // Expand affine map for DMA destination memref.
431     auto maybeExpandedDstMap = expandAffineMap(
432         rewriter, op.getLoc(), op.getDstMap(),
433         operandsRef.drop_front(op.getDstMemRefOperandIndex() + 1));
434     if (!maybeExpandedDstMap)
435       return failure();
436     // Expand affine map for DMA tag memref.
437     auto maybeExpandedTagMap = expandAffineMap(
438         rewriter, op.getLoc(), op.getTagMap(),
439         operandsRef.drop_front(op.getTagMemRefOperandIndex() + 1));
440     if (!maybeExpandedTagMap)
441       return failure();
442 
443     // Build memref.dma_start operation with affine map results.
444     rewriter.replaceOpWithNewOp<memref::DmaStartOp>(
445         op, op.getSrcMemRef(), *maybeExpandedSrcMap, op.getDstMemRef(),
446         *maybeExpandedDstMap, op.getNumElements(), op.getTagMemRef(),
447         *maybeExpandedTagMap, op.getStride(), op.getNumElementsPerStride());
448     return success();
449   }
450 };
451 
452 /// Apply the affine map from an 'affine.dma_wait' operation tag memref,
453 /// and feed the results to a newly created 'memref.dma_wait' operation (which
454 /// replaces the original 'affine.dma_wait').
455 class AffineDmaWaitLowering : public OpRewritePattern<AffineDmaWaitOp> {
456 public:
457   using OpRewritePattern<AffineDmaWaitOp>::OpRewritePattern;
458 
459   LogicalResult matchAndRewrite(AffineDmaWaitOp op,
460                                 PatternRewriter &rewriter) const override {
461     // Expand affine map for DMA tag memref.
462     SmallVector<Value, 8> indices(op.getTagIndices());
463     auto maybeExpandedTagMap =
464         expandAffineMap(rewriter, op.getLoc(), op.getTagMap(), indices);
465     if (!maybeExpandedTagMap)
466       return failure();
467 
468     // Build memref.dma_wait operation with affine map results.
469     rewriter.replaceOpWithNewOp<memref::DmaWaitOp>(
470         op, op.getTagMemRef(), *maybeExpandedTagMap, op.getNumElements());
471     return success();
472   }
473 };
474 
475 /// Apply the affine map from an 'affine.vector_load' operation to its operands,
476 /// and feed the results to a newly created 'vector.load' operation (which
477 /// replaces the original 'affine.vector_load').
478 class AffineVectorLoadLowering : public OpRewritePattern<AffineVectorLoadOp> {
479 public:
480   using OpRewritePattern<AffineVectorLoadOp>::OpRewritePattern;
481 
482   LogicalResult matchAndRewrite(AffineVectorLoadOp op,
483                                 PatternRewriter &rewriter) const override {
484     // Expand affine map from 'affineVectorLoadOp'.
485     SmallVector<Value, 8> indices(op.getMapOperands());
486     auto resultOperands =
487         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
488     if (!resultOperands)
489       return failure();
490 
491     // Build vector.load memref[expandedMap.results].
492     rewriter.replaceOpWithNewOp<vector::LoadOp>(
493         op, op.getVectorType(), op.getMemRef(), *resultOperands);
494     return success();
495   }
496 };
497 
498 /// Apply the affine map from an 'affine.vector_store' operation to its
499 /// operands, and feed the results to a newly created 'vector.store' operation
500 /// (which replaces the original 'affine.vector_store').
501 class AffineVectorStoreLowering : public OpRewritePattern<AffineVectorStoreOp> {
502 public:
503   using OpRewritePattern<AffineVectorStoreOp>::OpRewritePattern;
504 
505   LogicalResult matchAndRewrite(AffineVectorStoreOp op,
506                                 PatternRewriter &rewriter) const override {
507     // Expand affine map from 'affineVectorStoreOp'.
508     SmallVector<Value, 8> indices(op.getMapOperands());
509     auto maybeExpandedMap =
510         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
511     if (!maybeExpandedMap)
512       return failure();
513 
514     rewriter.replaceOpWithNewOp<vector::StoreOp>(
515         op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap);
516     return success();
517   }
518 };
519 
520 } // namespace
521 
522 void mlir::populateAffineToStdConversionPatterns(RewritePatternSet &patterns) {
523   // clang-format off
524   patterns.add<
525       AffineApplyLowering,
526       AffineDmaStartLowering,
527       AffineDmaWaitLowering,
528       AffineLoadLowering,
529       AffineMinLowering,
530       AffineMaxLowering,
531       AffineParallelLowering,
532       AffinePrefetchLowering,
533       AffineStoreLowering,
534       AffineForLowering,
535       AffineIfLowering,
536       AffineYieldOpLowering>(patterns.getContext());
537   // clang-format on
538 }
539 
540 void mlir::populateAffineToVectorConversionPatterns(
541     RewritePatternSet &patterns) {
542   // clang-format off
543   patterns.add<
544       AffineVectorLoadLowering,
545       AffineVectorStoreLowering>(patterns.getContext());
546   // clang-format on
547 }
548 
549 namespace {
550 class LowerAffinePass : public ConvertAffineToStandardBase<LowerAffinePass> {
551   void runOnOperation() override {
552     RewritePatternSet patterns(&getContext());
553     populateAffineToStdConversionPatterns(patterns);
554     populateAffineToVectorConversionPatterns(patterns);
555     ConversionTarget target(getContext());
556     target
557         .addLegalDialect<arith::ArithmeticDialect, memref::MemRefDialect,
558                          scf::SCFDialect, StandardOpsDialect, VectorDialect>();
559     if (failed(applyPartialConversion(getOperation(), target,
560                                       std::move(patterns))))
561       signalPassFailure();
562   }
563 };
564 } // namespace
565 
566 /// Lowers If and For operations within a function into their lower level CFG
567 /// equivalent blocks.
568 std::unique_ptr<Pass> mlir::createLowerAffinePass() {
569   return std::make_unique<LowerAffinePass>();
570 }
571