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