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 "mlir/Dialect/Affine/IR/AffineOps.h"
17 #include "mlir/Dialect/LoopOps/LoopOps.h"
18 #include "mlir/Dialect/StandardOps/IR/Ops.h"
19 #include "mlir/IR/AffineExprVisitor.h"
20 #include "mlir/IR/BlockAndValueMapping.h"
21 #include "mlir/IR/Builders.h"
22 #include "mlir/IR/IntegerSet.h"
23 #include "mlir/IR/MLIRContext.h"
24 #include "mlir/Pass/Pass.h"
25 #include "mlir/Support/Functional.h"
26 #include "mlir/Transforms/DialectConversion.h"
27 #include "mlir/Transforms/Passes.h"
28 
29 using namespace mlir;
30 
31 namespace {
32 /// Visit affine expressions recursively and build the sequence of operations
33 /// that correspond to it.  Visitation functions return an Value of the
34 /// expression subtree they visited or `nullptr` on error.
35 class AffineApplyExpander
36     : public AffineExprVisitor<AffineApplyExpander, Value> {
37 public:
38   /// This internal class expects arguments to be non-null, checks must be
39   /// performed at the call site.
40   AffineApplyExpander(OpBuilder &builder, ValueRange dimValues,
41                       ValueRange symbolValues, Location loc)
42       : builder(builder), dimValues(dimValues), symbolValues(symbolValues),
43         loc(loc) {}
44 
45   template <typename OpTy> Value buildBinaryExpr(AffineBinaryOpExpr expr) {
46     auto lhs = visit(expr.getLHS());
47     auto rhs = visit(expr.getRHS());
48     if (!lhs || !rhs)
49       return nullptr;
50     auto op = builder.create<OpTy>(loc, lhs, rhs);
51     return op.getResult();
52   }
53 
54   Value visitAddExpr(AffineBinaryOpExpr expr) {
55     return buildBinaryExpr<AddIOp>(expr);
56   }
57 
58   Value visitMulExpr(AffineBinaryOpExpr expr) {
59     return buildBinaryExpr<MulIOp>(expr);
60   }
61 
62   /// Euclidean modulo operation: negative RHS is not allowed.
63   /// Remainder of the euclidean integer division is always non-negative.
64   ///
65   /// Implemented as
66   ///
67   ///     a mod b =
68   ///         let remainder = srem a, b;
69   ///             negative = a < 0 in
70   ///         select negative, remainder + b, remainder.
71   Value visitModExpr(AffineBinaryOpExpr expr) {
72     auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
73     if (!rhsConst) {
74       emitError(
75           loc,
76           "semi-affine expressions (modulo by non-const) are not supported");
77       return nullptr;
78     }
79     if (rhsConst.getValue() <= 0) {
80       emitError(loc, "modulo by non-positive value is not supported");
81       return nullptr;
82     }
83 
84     auto lhs = visit(expr.getLHS());
85     auto rhs = visit(expr.getRHS());
86     assert(lhs && rhs && "unexpected affine expr lowering failure");
87 
88     Value remainder = builder.create<SignedRemIOp>(loc, lhs, rhs);
89     Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
90     Value isRemainderNegative =
91         builder.create<CmpIOp>(loc, CmpIPredicate::slt, remainder, zeroCst);
92     Value correctedRemainder = builder.create<AddIOp>(loc, remainder, rhs);
93     Value result = builder.create<SelectOp>(loc, isRemainderNegative,
94                                             correctedRemainder, remainder);
95     return result;
96   }
97 
98   /// Floor division operation (rounds towards negative infinity).
99   ///
100   /// For positive divisors, it can be implemented without branching and with a
101   /// single division operation as
102   ///
103   ///        a floordiv b =
104   ///            let negative = a < 0 in
105   ///            let absolute = negative ? -a - 1 : a in
106   ///            let quotient = absolute / b in
107   ///                negative ? -quotient - 1 : quotient
108   Value visitFloorDivExpr(AffineBinaryOpExpr expr) {
109     auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
110     if (!rhsConst) {
111       emitError(
112           loc,
113           "semi-affine expressions (division by non-const) are not supported");
114       return nullptr;
115     }
116     if (rhsConst.getValue() <= 0) {
117       emitError(loc, "division by non-positive value is not supported");
118       return nullptr;
119     }
120 
121     auto lhs = visit(expr.getLHS());
122     auto rhs = visit(expr.getRHS());
123     assert(lhs && rhs && "unexpected affine expr lowering failure");
124 
125     Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
126     Value noneCst = builder.create<ConstantIndexOp>(loc, -1);
127     Value negative =
128         builder.create<CmpIOp>(loc, CmpIPredicate::slt, lhs, zeroCst);
129     Value negatedDecremented = builder.create<SubIOp>(loc, noneCst, lhs);
130     Value dividend =
131         builder.create<SelectOp>(loc, negative, negatedDecremented, lhs);
132     Value quotient = builder.create<SignedDivIOp>(loc, dividend, rhs);
133     Value correctedQuotient = builder.create<SubIOp>(loc, noneCst, quotient);
134     Value result =
135         builder.create<SelectOp>(loc, negative, correctedQuotient, quotient);
136     return result;
137   }
138 
139   /// Ceiling division operation (rounds towards positive infinity).
140   ///
141   /// For positive divisors, it can be implemented without branching and with a
142   /// single division operation as
143   ///
144   ///     a ceildiv b =
145   ///         let negative = a <= 0 in
146   ///         let absolute = negative ? -a : a - 1 in
147   ///         let quotient = absolute / b in
148   ///             negative ? -quotient : quotient + 1
149   Value visitCeilDivExpr(AffineBinaryOpExpr expr) {
150     auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
151     if (!rhsConst) {
152       emitError(loc) << "semi-affine expressions (division by non-const) are "
153                         "not supported";
154       return nullptr;
155     }
156     if (rhsConst.getValue() <= 0) {
157       emitError(loc, "division by non-positive value is not supported");
158       return nullptr;
159     }
160     auto lhs = visit(expr.getLHS());
161     auto rhs = visit(expr.getRHS());
162     assert(lhs && rhs && "unexpected affine expr lowering failure");
163 
164     Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
165     Value oneCst = builder.create<ConstantIndexOp>(loc, 1);
166     Value nonPositive =
167         builder.create<CmpIOp>(loc, CmpIPredicate::sle, lhs, zeroCst);
168     Value negated = builder.create<SubIOp>(loc, zeroCst, lhs);
169     Value decremented = builder.create<SubIOp>(loc, lhs, oneCst);
170     Value dividend =
171         builder.create<SelectOp>(loc, nonPositive, negated, decremented);
172     Value quotient = builder.create<SignedDivIOp>(loc, dividend, rhs);
173     Value negatedQuotient = builder.create<SubIOp>(loc, zeroCst, quotient);
174     Value incrementedQuotient = builder.create<AddIOp>(loc, quotient, oneCst);
175     Value result = builder.create<SelectOp>(loc, nonPositive, negatedQuotient,
176                                             incrementedQuotient);
177     return result;
178   }
179 
180   Value visitConstantExpr(AffineConstantExpr expr) {
181     auto valueAttr =
182         builder.getIntegerAttr(builder.getIndexType(), expr.getValue());
183     auto op =
184         builder.create<ConstantOp>(loc, builder.getIndexType(), valueAttr);
185     return op.getResult();
186   }
187 
188   Value visitDimExpr(AffineDimExpr expr) {
189     assert(expr.getPosition() < dimValues.size() &&
190            "affine dim position out of range");
191     return dimValues[expr.getPosition()];
192   }
193 
194   Value visitSymbolExpr(AffineSymbolExpr expr) {
195     assert(expr.getPosition() < symbolValues.size() &&
196            "symbol dim position out of range");
197     return symbolValues[expr.getPosition()];
198   }
199 
200 private:
201   OpBuilder &builder;
202   ValueRange dimValues;
203   ValueRange symbolValues;
204 
205   Location loc;
206 };
207 } // namespace
208 
209 /// Create a sequence of operations that implement the `expr` applied to the
210 /// given dimension and symbol values.
211 mlir::Value mlir::expandAffineExpr(OpBuilder &builder, Location loc,
212                                    AffineExpr expr, ValueRange dimValues,
213                                    ValueRange symbolValues) {
214   return AffineApplyExpander(builder, dimValues, symbolValues, loc).visit(expr);
215 }
216 
217 /// Create a sequence of operations that implement the `affineMap` applied to
218 /// the given `operands` (as it it were an AffineApplyOp).
219 Optional<SmallVector<Value, 8>> mlir::expandAffineMap(OpBuilder &builder,
220                                                       Location loc,
221                                                       AffineMap affineMap,
222                                                       ValueRange operands) {
223   auto numDims = affineMap.getNumDims();
224   auto expanded = functional::map(
225       [numDims, &builder, loc, operands](AffineExpr expr) {
226         return expandAffineExpr(builder, loc, expr,
227                                 operands.take_front(numDims),
228                                 operands.drop_front(numDims));
229       },
230       affineMap.getResults());
231   if (llvm::all_of(expanded, [](Value v) { return v; }))
232     return expanded;
233   return None;
234 }
235 
236 /// Given a range of values, emit the code that reduces them with "min" or "max"
237 /// depending on the provided comparison predicate.  The predicate defines which
238 /// comparison to perform, "lt" for "min", "gt" for "max" and is used for the
239 /// `cmpi` operation followed by the `select` operation:
240 ///
241 ///   %cond   = cmpi "predicate" %v0, %v1
242 ///   %result = select %cond, %v0, %v1
243 ///
244 /// Multiple values are scanned in a linear sequence.  This creates a data
245 /// dependences that wouldn't exist in a tree reduction, but is easier to
246 /// recognize as a reduction by the subsequent passes.
247 static Value buildMinMaxReductionSeq(Location loc, CmpIPredicate predicate,
248                                      ValueRange values, OpBuilder &builder) {
249   assert(!llvm::empty(values) && "empty min/max chain");
250 
251   auto valueIt = values.begin();
252   Value value = *valueIt++;
253   for (; valueIt != values.end(); ++valueIt) {
254     auto cmpOp = builder.create<CmpIOp>(loc, predicate, value, *valueIt);
255     value = builder.create<SelectOp>(loc, cmpOp.getResult(), value, *valueIt);
256   }
257 
258   return value;
259 }
260 
261 /// Emit instructions that correspond to computing the maximum value among the
262 /// values of a (potentially) multi-output affine map applied to `operands`.
263 static Value lowerAffineMapMax(OpBuilder &builder, Location loc, AffineMap map,
264                                ValueRange operands) {
265   if (auto values = expandAffineMap(builder, loc, map, operands))
266     return buildMinMaxReductionSeq(loc, CmpIPredicate::sgt, *values, builder);
267   return nullptr;
268 }
269 
270 /// Emit instructions that correspond to computing the minimum value among the
271 /// values of a (potentially) multi-output affine map applied to `operands`.
272 static Value lowerAffineMapMin(OpBuilder &builder, Location loc, AffineMap map,
273                                ValueRange operands) {
274   if (auto values = expandAffineMap(builder, loc, map, operands))
275     return buildMinMaxReductionSeq(loc, CmpIPredicate::slt, *values, builder);
276   return nullptr;
277 }
278 
279 /// Emit instructions that correspond to the affine map in the upper bound
280 /// applied to the respective operands, and compute the minimum value across
281 /// the results.
282 Value mlir::lowerAffineUpperBound(AffineForOp op, OpBuilder &builder) {
283   return lowerAffineMapMin(builder, op.getLoc(), op.getUpperBoundMap(),
284                            op.getUpperBoundOperands());
285 }
286 
287 /// Emit instructions that correspond to the affine map in the lower bound
288 /// applied to the respective operands, and compute the maximum value across
289 /// the results.
290 Value mlir::lowerAffineLowerBound(AffineForOp op, OpBuilder &builder) {
291   return lowerAffineMapMax(builder, op.getLoc(), op.getLowerBoundMap(),
292                            op.getLowerBoundOperands());
293 }
294 
295 namespace {
296 class AffineMinLowering : public OpRewritePattern<AffineMinOp> {
297 public:
298   using OpRewritePattern<AffineMinOp>::OpRewritePattern;
299 
300   LogicalResult matchAndRewrite(AffineMinOp op,
301                                 PatternRewriter &rewriter) const override {
302     Value reduced =
303         lowerAffineMapMin(rewriter, op.getLoc(), op.map(), op.operands());
304     if (!reduced)
305       return failure();
306 
307     rewriter.replaceOp(op, reduced);
308     return success();
309   }
310 };
311 
312 class AffineMaxLowering : public OpRewritePattern<AffineMaxOp> {
313 public:
314   using OpRewritePattern<AffineMaxOp>::OpRewritePattern;
315 
316   LogicalResult matchAndRewrite(AffineMaxOp op,
317                                 PatternRewriter &rewriter) const override {
318     Value reduced =
319         lowerAffineMapMax(rewriter, op.getLoc(), op.map(), op.operands());
320     if (!reduced)
321       return failure();
322 
323     rewriter.replaceOp(op, reduced);
324     return success();
325   }
326 };
327 
328 /// Affine terminators are removed.
329 class AffineTerminatorLowering : public OpRewritePattern<AffineTerminatorOp> {
330 public:
331   using OpRewritePattern<AffineTerminatorOp>::OpRewritePattern;
332 
333   LogicalResult matchAndRewrite(AffineTerminatorOp op,
334                                 PatternRewriter &rewriter) const override {
335     rewriter.replaceOpWithNewOp<loop::YieldOp>(op);
336     return success();
337   }
338 };
339 
340 class AffineForLowering : public OpRewritePattern<AffineForOp> {
341 public:
342   using OpRewritePattern<AffineForOp>::OpRewritePattern;
343 
344   LogicalResult matchAndRewrite(AffineForOp op,
345                                 PatternRewriter &rewriter) const override {
346     Location loc = op.getLoc();
347     Value lowerBound = lowerAffineLowerBound(op, rewriter);
348     Value upperBound = lowerAffineUpperBound(op, rewriter);
349     Value step = rewriter.create<ConstantIndexOp>(loc, op.getStep());
350     auto f = rewriter.create<loop::ForOp>(loc, lowerBound, upperBound, step);
351     f.region().getBlocks().clear();
352     rewriter.inlineRegionBefore(op.region(), f.region(), f.region().end());
353     rewriter.eraseOp(op);
354     return success();
355   }
356 };
357 
358 class AffineIfLowering : public OpRewritePattern<AffineIfOp> {
359 public:
360   using OpRewritePattern<AffineIfOp>::OpRewritePattern;
361 
362   LogicalResult matchAndRewrite(AffineIfOp op,
363                                 PatternRewriter &rewriter) const override {
364     auto loc = op.getLoc();
365 
366     // Now we just have to handle the condition logic.
367     auto integerSet = op.getIntegerSet();
368     Value zeroConstant = rewriter.create<ConstantIndexOp>(loc, 0);
369     SmallVector<Value, 8> operands(op.getOperands());
370     auto operandsRef = llvm::makeArrayRef(operands);
371 
372     // Calculate cond as a conjunction without short-circuiting.
373     Value cond = nullptr;
374     for (unsigned i = 0, e = integerSet.getNumConstraints(); i < e; ++i) {
375       AffineExpr constraintExpr = integerSet.getConstraint(i);
376       bool isEquality = integerSet.isEq(i);
377 
378       // Build and apply an affine expression
379       auto numDims = integerSet.getNumDims();
380       Value affResult = expandAffineExpr(rewriter, loc, constraintExpr,
381                                          operandsRef.take_front(numDims),
382                                          operandsRef.drop_front(numDims));
383       if (!affResult)
384         return failure();
385       auto pred = isEquality ? CmpIPredicate::eq : CmpIPredicate::sge;
386       Value cmpVal =
387           rewriter.create<CmpIOp>(loc, pred, affResult, zeroConstant);
388       cond =
389           cond ? rewriter.create<AndOp>(loc, cond, cmpVal).getResult() : cmpVal;
390     }
391     cond = cond ? cond
392                 : rewriter.create<ConstantIntOp>(loc, /*value=*/1, /*width=*/1);
393 
394     bool hasElseRegion = !op.elseRegion().empty();
395     auto ifOp = rewriter.create<loop::IfOp>(loc, cond, hasElseRegion);
396     rewriter.inlineRegionBefore(op.thenRegion(), &ifOp.thenRegion().back());
397     ifOp.thenRegion().back().erase();
398     if (hasElseRegion) {
399       rewriter.inlineRegionBefore(op.elseRegion(), &ifOp.elseRegion().back());
400       ifOp.elseRegion().back().erase();
401     }
402 
403     // Ok, we're done!
404     rewriter.eraseOp(op);
405     return success();
406   }
407 };
408 
409 /// Convert an "affine.apply" operation into a sequence of arithmetic
410 /// operations using the StandardOps dialect.
411 class AffineApplyLowering : public OpRewritePattern<AffineApplyOp> {
412 public:
413   using OpRewritePattern<AffineApplyOp>::OpRewritePattern;
414 
415   LogicalResult matchAndRewrite(AffineApplyOp op,
416                                 PatternRewriter &rewriter) const override {
417     auto maybeExpandedMap =
418         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(),
419                         llvm::to_vector<8>(op.getOperands()));
420     if (!maybeExpandedMap)
421       return failure();
422     rewriter.replaceOp(op, *maybeExpandedMap);
423     return success();
424   }
425 };
426 
427 /// Apply the affine map from an 'affine.load' operation to its operands, and
428 /// feed the results to a newly created 'std.load' operation (which replaces the
429 /// original 'affine.load').
430 class AffineLoadLowering : public OpRewritePattern<AffineLoadOp> {
431 public:
432   using OpRewritePattern<AffineLoadOp>::OpRewritePattern;
433 
434   LogicalResult matchAndRewrite(AffineLoadOp op,
435                                 PatternRewriter &rewriter) const override {
436     // Expand affine map from 'affineLoadOp'.
437     SmallVector<Value, 8> indices(op.getMapOperands());
438     auto resultOperands =
439         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
440     if (!resultOperands)
441       return failure();
442 
443     // Build std.load memref[expandedMap.results].
444     rewriter.replaceOpWithNewOp<LoadOp>(op, op.getMemRef(), *resultOperands);
445     return success();
446   }
447 };
448 
449 /// Apply the affine map from an 'affine.prefetch' operation to its operands,
450 /// and feed the results to a newly created 'std.prefetch' operation (which
451 /// replaces the original 'affine.prefetch').
452 class AffinePrefetchLowering : public OpRewritePattern<AffinePrefetchOp> {
453 public:
454   using OpRewritePattern<AffinePrefetchOp>::OpRewritePattern;
455 
456   LogicalResult matchAndRewrite(AffinePrefetchOp op,
457                                 PatternRewriter &rewriter) const override {
458     // Expand affine map from 'affinePrefetchOp'.
459     SmallVector<Value, 8> indices(op.getMapOperands());
460     auto resultOperands =
461         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
462     if (!resultOperands)
463       return failure();
464 
465     // Build std.prefetch memref[expandedMap.results].
466     rewriter.replaceOpWithNewOp<PrefetchOp>(
467         op, op.memref(), *resultOperands, op.isWrite(),
468         op.localityHint().getZExtValue(), op.isDataCache());
469     return success();
470   }
471 };
472 
473 /// Apply the affine map from an 'affine.store' operation to its operands, and
474 /// feed the results to a newly created 'std.store' operation (which replaces
475 /// the original 'affine.store').
476 class AffineStoreLowering : public OpRewritePattern<AffineStoreOp> {
477 public:
478   using OpRewritePattern<AffineStoreOp>::OpRewritePattern;
479 
480   LogicalResult matchAndRewrite(AffineStoreOp op,
481                                 PatternRewriter &rewriter) const override {
482     // Expand affine map from 'affineStoreOp'.
483     SmallVector<Value, 8> indices(op.getMapOperands());
484     auto maybeExpandedMap =
485         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
486     if (!maybeExpandedMap)
487       return failure();
488 
489     // Build std.store valueToStore, memref[expandedMap.results].
490     rewriter.replaceOpWithNewOp<StoreOp>(op, op.getValueToStore(),
491                                          op.getMemRef(), *maybeExpandedMap);
492     return success();
493   }
494 };
495 
496 /// Apply the affine maps from an 'affine.dma_start' operation to each of their
497 /// respective map operands, and feed the results to a newly created
498 /// 'std.dma_start' operation (which replaces the original 'affine.dma_start').
499 class AffineDmaStartLowering : public OpRewritePattern<AffineDmaStartOp> {
500 public:
501   using OpRewritePattern<AffineDmaStartOp>::OpRewritePattern;
502 
503   LogicalResult matchAndRewrite(AffineDmaStartOp op,
504                                 PatternRewriter &rewriter) const override {
505     SmallVector<Value, 8> operands(op.getOperands());
506     auto operandsRef = llvm::makeArrayRef(operands);
507 
508     // Expand affine map for DMA source memref.
509     auto maybeExpandedSrcMap = expandAffineMap(
510         rewriter, op.getLoc(), op.getSrcMap(),
511         operandsRef.drop_front(op.getSrcMemRefOperandIndex() + 1));
512     if (!maybeExpandedSrcMap)
513       return failure();
514     // Expand affine map for DMA destination memref.
515     auto maybeExpandedDstMap = expandAffineMap(
516         rewriter, op.getLoc(), op.getDstMap(),
517         operandsRef.drop_front(op.getDstMemRefOperandIndex() + 1));
518     if (!maybeExpandedDstMap)
519       return failure();
520     // Expand affine map for DMA tag memref.
521     auto maybeExpandedTagMap = expandAffineMap(
522         rewriter, op.getLoc(), op.getTagMap(),
523         operandsRef.drop_front(op.getTagMemRefOperandIndex() + 1));
524     if (!maybeExpandedTagMap)
525       return failure();
526 
527     // Build std.dma_start operation with affine map results.
528     rewriter.replaceOpWithNewOp<DmaStartOp>(
529         op, op.getSrcMemRef(), *maybeExpandedSrcMap, op.getDstMemRef(),
530         *maybeExpandedDstMap, op.getNumElements(), op.getTagMemRef(),
531         *maybeExpandedTagMap, op.getStride(), op.getNumElementsPerStride());
532     return success();
533   }
534 };
535 
536 /// Apply the affine map from an 'affine.dma_wait' operation tag memref,
537 /// and feed the results to a newly created 'std.dma_wait' operation (which
538 /// replaces the original 'affine.dma_wait').
539 class AffineDmaWaitLowering : public OpRewritePattern<AffineDmaWaitOp> {
540 public:
541   using OpRewritePattern<AffineDmaWaitOp>::OpRewritePattern;
542 
543   LogicalResult matchAndRewrite(AffineDmaWaitOp op,
544                                 PatternRewriter &rewriter) const override {
545     // Expand affine map for DMA tag memref.
546     SmallVector<Value, 8> indices(op.getTagIndices());
547     auto maybeExpandedTagMap =
548         expandAffineMap(rewriter, op.getLoc(), op.getTagMap(), indices);
549     if (!maybeExpandedTagMap)
550       return failure();
551 
552     // Build std.dma_wait operation with affine map results.
553     rewriter.replaceOpWithNewOp<DmaWaitOp>(
554         op, op.getTagMemRef(), *maybeExpandedTagMap, op.getNumElements());
555     return success();
556   }
557 };
558 
559 } // end namespace
560 
561 void mlir::populateAffineToStdConversionPatterns(
562     OwningRewritePatternList &patterns, MLIRContext *ctx) {
563   // clang-format off
564   patterns.insert<
565       AffineApplyLowering,
566       AffineDmaStartLowering,
567       AffineDmaWaitLowering,
568       AffineLoadLowering,
569       AffineMinLowering,
570       AffineMaxLowering,
571       AffinePrefetchLowering,
572       AffineStoreLowering,
573       AffineForLowering,
574       AffineIfLowering,
575       AffineTerminatorLowering>(ctx);
576   // clang-format on
577 }
578 
579 namespace {
580 class LowerAffinePass : public FunctionPass<LowerAffinePass> {
581 /// Include the generated pass utilities.
582 #define GEN_PASS_ConvertAffineToStandard
583 #include "mlir/Conversion/Passes.h.inc"
584 
585   void runOnFunction() override {
586     OwningRewritePatternList patterns;
587     populateAffineToStdConversionPatterns(patterns, &getContext());
588     ConversionTarget target(getContext());
589     target.addLegalDialect<loop::LoopOpsDialect, StandardOpsDialect>();
590     if (failed(applyPartialConversion(getFunction(), target, patterns)))
591       signalPassFailure();
592   }
593 };
594 } // namespace
595 
596 /// Lowers If and For operations within a function into their lower level CFG
597 /// equivalent blocks.
598 std::unique_ptr<OpPassBase<FuncOp>> mlir::createLowerAffinePass() {
599   return std::make_unique<LowerAffinePass>();
600 }
601