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/MemRef/IR/MemRef.h"
19 #include "mlir/Dialect/SCF/SCF.h"
20 #include "mlir/Dialect/StandardOps/IR/Ops.h"
21 #include "mlir/Dialect/Vector/VectorOps.h"
22 #include "mlir/IR/AffineExprVisitor.h"
23 #include "mlir/IR/BlockAndValueMapping.h"
24 #include "mlir/IR/Builders.h"
25 #include "mlir/IR/IntegerSet.h"
26 #include "mlir/IR/MLIRContext.h"
27 #include "mlir/Pass/Pass.h"
28 #include "mlir/Transforms/DialectConversion.h"
29 #include "mlir/Transforms/Passes.h"
30 
31 using namespace mlir;
32 using namespace mlir::vector;
33 
34 namespace {
35 /// Visit affine expressions recursively and build the sequence of operations
36 /// that correspond to it.  Visitation functions return an Value of the
37 /// expression subtree they visited or `nullptr` on error.
38 class AffineApplyExpander
39     : public AffineExprVisitor<AffineApplyExpander, Value> {
40 public:
41   /// This internal class expects arguments to be non-null, checks must be
42   /// performed at the call site.
43   AffineApplyExpander(OpBuilder &builder, ValueRange dimValues,
44                       ValueRange symbolValues, Location loc)
45       : builder(builder), dimValues(dimValues), symbolValues(symbolValues),
46         loc(loc) {}
47 
48   template <typename OpTy>
49   Value buildBinaryExpr(AffineBinaryOpExpr expr) {
50     auto lhs = visit(expr.getLHS());
51     auto rhs = visit(expr.getRHS());
52     if (!lhs || !rhs)
53       return nullptr;
54     auto op = builder.create<OpTy>(loc, lhs, rhs);
55     return op.getResult();
56   }
57 
58   Value visitAddExpr(AffineBinaryOpExpr expr) {
59     return buildBinaryExpr<AddIOp>(expr);
60   }
61 
62   Value visitMulExpr(AffineBinaryOpExpr expr) {
63     return buildBinaryExpr<MulIOp>(expr);
64   }
65 
66   /// Euclidean modulo operation: negative RHS is not allowed.
67   /// Remainder of the euclidean integer division is always non-negative.
68   ///
69   /// Implemented as
70   ///
71   ///     a mod b =
72   ///         let remainder = srem a, b;
73   ///             negative = a < 0 in
74   ///         select negative, remainder + b, remainder.
75   Value visitModExpr(AffineBinaryOpExpr expr) {
76     auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
77     if (!rhsConst) {
78       emitError(
79           loc,
80           "semi-affine expressions (modulo by non-const) are not supported");
81       return nullptr;
82     }
83     if (rhsConst.getValue() <= 0) {
84       emitError(loc, "modulo by non-positive value is not supported");
85       return nullptr;
86     }
87 
88     auto lhs = visit(expr.getLHS());
89     auto rhs = visit(expr.getRHS());
90     assert(lhs && rhs && "unexpected affine expr lowering failure");
91 
92     Value remainder = builder.create<SignedRemIOp>(loc, lhs, rhs);
93     Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
94     Value isRemainderNegative =
95         builder.create<CmpIOp>(loc, CmpIPredicate::slt, remainder, zeroCst);
96     Value correctedRemainder = builder.create<AddIOp>(loc, remainder, rhs);
97     Value result = builder.create<SelectOp>(loc, isRemainderNegative,
98                                             correctedRemainder, remainder);
99     return result;
100   }
101 
102   /// Floor division operation (rounds towards negative infinity).
103   ///
104   /// For positive divisors, it can be implemented without branching and with a
105   /// single division operation as
106   ///
107   ///        a floordiv b =
108   ///            let negative = a < 0 in
109   ///            let absolute = negative ? -a - 1 : a in
110   ///            let quotient = absolute / b in
111   ///                negative ? -quotient - 1 : quotient
112   Value visitFloorDivExpr(AffineBinaryOpExpr expr) {
113     auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
114     if (!rhsConst) {
115       emitError(
116           loc,
117           "semi-affine expressions (division by non-const) are not supported");
118       return nullptr;
119     }
120     if (rhsConst.getValue() <= 0) {
121       emitError(loc, "division by non-positive value is not supported");
122       return nullptr;
123     }
124 
125     auto lhs = visit(expr.getLHS());
126     auto rhs = visit(expr.getRHS());
127     assert(lhs && rhs && "unexpected affine expr lowering failure");
128 
129     Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
130     Value noneCst = builder.create<ConstantIndexOp>(loc, -1);
131     Value negative =
132         builder.create<CmpIOp>(loc, CmpIPredicate::slt, lhs, zeroCst);
133     Value negatedDecremented = builder.create<SubIOp>(loc, noneCst, lhs);
134     Value dividend =
135         builder.create<SelectOp>(loc, negative, negatedDecremented, lhs);
136     Value quotient = builder.create<SignedDivIOp>(loc, dividend, rhs);
137     Value correctedQuotient = builder.create<SubIOp>(loc, noneCst, quotient);
138     Value result =
139         builder.create<SelectOp>(loc, negative, correctedQuotient, quotient);
140     return result;
141   }
142 
143   /// Ceiling division operation (rounds towards positive infinity).
144   ///
145   /// For positive divisors, it can be implemented without branching and with a
146   /// single division operation as
147   ///
148   ///     a ceildiv b =
149   ///         let negative = a <= 0 in
150   ///         let absolute = negative ? -a : a - 1 in
151   ///         let quotient = absolute / b in
152   ///             negative ? -quotient : quotient + 1
153   Value visitCeilDivExpr(AffineBinaryOpExpr expr) {
154     auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
155     if (!rhsConst) {
156       emitError(loc) << "semi-affine expressions (division by non-const) are "
157                         "not supported";
158       return nullptr;
159     }
160     if (rhsConst.getValue() <= 0) {
161       emitError(loc, "division by non-positive value is not supported");
162       return nullptr;
163     }
164     auto lhs = visit(expr.getLHS());
165     auto rhs = visit(expr.getRHS());
166     assert(lhs && rhs && "unexpected affine expr lowering failure");
167 
168     Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
169     Value oneCst = builder.create<ConstantIndexOp>(loc, 1);
170     Value nonPositive =
171         builder.create<CmpIOp>(loc, CmpIPredicate::sle, lhs, zeroCst);
172     Value negated = builder.create<SubIOp>(loc, zeroCst, lhs);
173     Value decremented = builder.create<SubIOp>(loc, lhs, oneCst);
174     Value dividend =
175         builder.create<SelectOp>(loc, nonPositive, negated, decremented);
176     Value quotient = builder.create<SignedDivIOp>(loc, dividend, rhs);
177     Value negatedQuotient = builder.create<SubIOp>(loc, zeroCst, quotient);
178     Value incrementedQuotient = builder.create<AddIOp>(loc, quotient, oneCst);
179     Value result = builder.create<SelectOp>(loc, nonPositive, negatedQuotient,
180                                             incrementedQuotient);
181     return result;
182   }
183 
184   Value visitConstantExpr(AffineConstantExpr expr) {
185     auto valueAttr =
186         builder.getIntegerAttr(builder.getIndexType(), expr.getValue());
187     auto op =
188         builder.create<ConstantOp>(loc, builder.getIndexType(), valueAttr);
189     return op.getResult();
190   }
191 
192   Value visitDimExpr(AffineDimExpr expr) {
193     assert(expr.getPosition() < dimValues.size() &&
194            "affine dim position out of range");
195     return dimValues[expr.getPosition()];
196   }
197 
198   Value visitSymbolExpr(AffineSymbolExpr expr) {
199     assert(expr.getPosition() < symbolValues.size() &&
200            "symbol dim position out of range");
201     return symbolValues[expr.getPosition()];
202   }
203 
204 private:
205   OpBuilder &builder;
206   ValueRange dimValues;
207   ValueRange symbolValues;
208 
209   Location loc;
210 };
211 } // namespace
212 
213 /// Create a sequence of operations that implement the `expr` applied to the
214 /// given dimension and symbol values.
215 mlir::Value mlir::expandAffineExpr(OpBuilder &builder, Location loc,
216                                    AffineExpr expr, ValueRange dimValues,
217                                    ValueRange symbolValues) {
218   return AffineApplyExpander(builder, dimValues, symbolValues, loc).visit(expr);
219 }
220 
221 /// Create a sequence of operations that implement the `affineMap` applied to
222 /// the given `operands` (as it it were an AffineApplyOp).
223 Optional<SmallVector<Value, 8>> mlir::expandAffineMap(OpBuilder &builder,
224                                                       Location loc,
225                                                       AffineMap affineMap,
226                                                       ValueRange operands) {
227   auto numDims = affineMap.getNumDims();
228   auto expanded = llvm::to_vector<8>(
229       llvm::map_range(affineMap.getResults(),
230                       [numDims, &builder, loc, operands](AffineExpr expr) {
231                         return expandAffineExpr(builder, loc, expr,
232                                                 operands.take_front(numDims),
233                                                 operands.drop_front(numDims));
234                       }));
235   if (llvm::all_of(expanded, [](Value v) { return v; }))
236     return expanded;
237   return None;
238 }
239 
240 /// Given a range of values, emit the code that reduces them with "min" or "max"
241 /// depending on the provided comparison predicate.  The predicate defines which
242 /// comparison to perform, "lt" for "min", "gt" for "max" and is used for the
243 /// `cmpi` operation followed by the `select` operation:
244 ///
245 ///   %cond   = cmpi "predicate" %v0, %v1
246 ///   %result = select %cond, %v0, %v1
247 ///
248 /// Multiple values are scanned in a linear sequence.  This creates a data
249 /// dependences that wouldn't exist in a tree reduction, but is easier to
250 /// recognize as a reduction by the subsequent passes.
251 static Value buildMinMaxReductionSeq(Location loc, CmpIPredicate predicate,
252                                      ValueRange values, OpBuilder &builder) {
253   assert(!llvm::empty(values) && "empty min/max chain");
254 
255   auto valueIt = values.begin();
256   Value value = *valueIt++;
257   for (; valueIt != values.end(); ++valueIt) {
258     auto cmpOp = builder.create<CmpIOp>(loc, predicate, value, *valueIt);
259     value = builder.create<SelectOp>(loc, cmpOp.getResult(), value, *valueIt);
260   }
261 
262   return value;
263 }
264 
265 /// Emit instructions that correspond to computing the maximum value among the
266 /// values of a (potentially) multi-output affine map applied to `operands`.
267 static Value lowerAffineMapMax(OpBuilder &builder, Location loc, AffineMap map,
268                                ValueRange operands) {
269   if (auto values = expandAffineMap(builder, loc, map, operands))
270     return buildMinMaxReductionSeq(loc, CmpIPredicate::sgt, *values, builder);
271   return nullptr;
272 }
273 
274 /// Emit instructions that correspond to computing the minimum value among the
275 /// values of a (potentially) multi-output affine map applied to `operands`.
276 static Value lowerAffineMapMin(OpBuilder &builder, Location loc, AffineMap map,
277                                ValueRange operands) {
278   if (auto values = expandAffineMap(builder, loc, map, operands))
279     return buildMinMaxReductionSeq(loc, CmpIPredicate::slt, *values, builder);
280   return nullptr;
281 }
282 
283 /// Emit instructions that correspond to the affine map in the upper bound
284 /// applied to the respective operands, and compute the minimum value across
285 /// the results.
286 Value mlir::lowerAffineUpperBound(AffineForOp op, OpBuilder &builder) {
287   return lowerAffineMapMin(builder, op.getLoc(), op.getUpperBoundMap(),
288                            op.getUpperBoundOperands());
289 }
290 
291 /// Emit instructions that correspond to the affine map in the lower bound
292 /// applied to the respective operands, and compute the maximum value across
293 /// the results.
294 Value mlir::lowerAffineLowerBound(AffineForOp op, OpBuilder &builder) {
295   return lowerAffineMapMax(builder, op.getLoc(), op.getLowerBoundMap(),
296                            op.getLowerBoundOperands());
297 }
298 
299 namespace {
300 class AffineMinLowering : public OpRewritePattern<AffineMinOp> {
301 public:
302   using OpRewritePattern<AffineMinOp>::OpRewritePattern;
303 
304   LogicalResult matchAndRewrite(AffineMinOp op,
305                                 PatternRewriter &rewriter) const override {
306     Value reduced =
307         lowerAffineMapMin(rewriter, op.getLoc(), op.map(), op.operands());
308     if (!reduced)
309       return failure();
310 
311     rewriter.replaceOp(op, reduced);
312     return success();
313   }
314 };
315 
316 class AffineMaxLowering : public OpRewritePattern<AffineMaxOp> {
317 public:
318   using OpRewritePattern<AffineMaxOp>::OpRewritePattern;
319 
320   LogicalResult matchAndRewrite(AffineMaxOp op,
321                                 PatternRewriter &rewriter) const override {
322     Value reduced =
323         lowerAffineMapMax(rewriter, op.getLoc(), op.map(), op.operands());
324     if (!reduced)
325       return failure();
326 
327     rewriter.replaceOp(op, reduced);
328     return success();
329   }
330 };
331 
332 /// Affine yields ops are removed.
333 class AffineYieldOpLowering : public OpRewritePattern<AffineYieldOp> {
334 public:
335   using OpRewritePattern<AffineYieldOp>::OpRewritePattern;
336 
337   LogicalResult matchAndRewrite(AffineYieldOp op,
338                                 PatternRewriter &rewriter) const override {
339     if (isa<scf::ParallelOp>(op->getParentOp())) {
340       // scf.parallel does not yield any values via its terminator scf.yield but
341       // models reductions differently using additional ops in its region.
342       rewriter.replaceOpWithNewOp<scf::YieldOp>(op);
343       return success();
344     }
345     rewriter.replaceOpWithNewOp<scf::YieldOp>(op, op.operands());
346     return success();
347   }
348 };
349 
350 class AffineForLowering : public OpRewritePattern<AffineForOp> {
351 public:
352   using OpRewritePattern<AffineForOp>::OpRewritePattern;
353 
354   LogicalResult matchAndRewrite(AffineForOp op,
355                                 PatternRewriter &rewriter) const override {
356     Location loc = op.getLoc();
357     Value lowerBound = lowerAffineLowerBound(op, rewriter);
358     Value upperBound = lowerAffineUpperBound(op, rewriter);
359     Value step = rewriter.create<ConstantIndexOp>(loc, op.getStep());
360     auto scfForOp = rewriter.create<scf::ForOp>(loc, lowerBound, upperBound,
361                                                 step, op.getIterOperands());
362     rewriter.eraseBlock(scfForOp.getBody());
363     rewriter.inlineRegionBefore(op.region(), scfForOp.region(),
364                                 scfForOp.region().end());
365     rewriter.replaceOp(op, scfForOp.results());
366     return success();
367   }
368 };
369 
370 /// Returns the identity value associated with an AtomicRMWKind op.
371 static Value getIdentityValue(AtomicRMWKind op, Type resultType,
372                               OpBuilder &builder, Location loc) {
373   switch (op) {
374   case AtomicRMWKind::addf:
375     return builder.create<ConstantOp>(loc, builder.getFloatAttr(resultType, 0));
376   case AtomicRMWKind::addi:
377     return builder.create<ConstantOp>(loc,
378                                       builder.getIntegerAttr(resultType, 0));
379   case AtomicRMWKind::mulf:
380     return builder.create<ConstantOp>(loc, builder.getFloatAttr(resultType, 1));
381   case AtomicRMWKind::muli:
382     return builder.create<ConstantOp>(loc,
383                                       builder.getIntegerAttr(resultType, 1));
384   // TODO: Add remaining reduction operations.
385   default:
386     (void)emitOptionalError(loc, "Reduction operation type not supported");
387     break;
388   }
389   return nullptr;
390 }
391 
392 /// Return the value obtained by applying the reduction operation kind
393 /// associated with a binary AtomicRMWKind op to `lhs` and `rhs`.
394 static Value getReductionOp(AtomicRMWKind op, OpBuilder &builder, Location loc,
395                             Value lhs, Value rhs) {
396   switch (op) {
397   case AtomicRMWKind::addf:
398     return builder.create<AddFOp>(loc, lhs, rhs);
399   case AtomicRMWKind::addi:
400     return builder.create<AddIOp>(loc, lhs, rhs);
401   case AtomicRMWKind::mulf:
402     return builder.create<MulFOp>(loc, lhs, rhs);
403   case AtomicRMWKind::muli:
404     return builder.create<MulIOp>(loc, lhs, rhs);
405   // TODO: Add remaining reduction operations.
406   default:
407     (void)emitOptionalError(loc, "Reduction operation type not supported");
408     break;
409   }
410   return nullptr;
411 }
412 
413 /// Convert an `affine.parallel` (loop nest) operation into a `scf.parallel`
414 /// operation.
415 class AffineParallelLowering : public OpRewritePattern<AffineParallelOp> {
416 public:
417   using OpRewritePattern<AffineParallelOp>::OpRewritePattern;
418 
419   LogicalResult matchAndRewrite(AffineParallelOp op,
420                                 PatternRewriter &rewriter) const override {
421     Location loc = op.getLoc();
422     SmallVector<Value, 8> steps;
423     SmallVector<Value, 8> upperBoundTuple;
424     SmallVector<Value, 8> lowerBoundTuple;
425     SmallVector<Value, 8> identityVals;
426     // Emit IR computing the lower and upper bound by expanding the map
427     // expression.
428     lowerBoundTuple.reserve(op.getNumDims());
429     upperBoundTuple.reserve(op.getNumDims());
430     for (unsigned i = 0, e = op.getNumDims(); i < e; ++i) {
431       Value lower = lowerAffineMapMax(rewriter, loc, op.getLowerBoundMap(i),
432                                       op.getLowerBoundsOperands());
433       if (!lower)
434         return rewriter.notifyMatchFailure(op, "couldn't convert lower bounds");
435       lowerBoundTuple.push_back(lower);
436 
437       Value upper = lowerAffineMapMin(rewriter, loc, op.getUpperBoundMap(i),
438                                       op.getUpperBoundsOperands());
439       if (!upper)
440         return rewriter.notifyMatchFailure(op, "couldn't convert upper bounds");
441       upperBoundTuple.push_back(upper);
442     }
443     steps.reserve(op.steps().size());
444     for (Attribute step : op.steps())
445       steps.push_back(rewriter.create<ConstantIndexOp>(
446           loc, step.cast<IntegerAttr>().getInt()));
447 
448     // Get the terminator op.
449     Operation *affineParOpTerminator = op.getBody()->getTerminator();
450     scf::ParallelOp parOp;
451     if (op.results().empty()) {
452       // Case with no reduction operations/return values.
453       parOp = rewriter.create<scf::ParallelOp>(loc, lowerBoundTuple,
454                                                upperBoundTuple, steps,
455                                                /*bodyBuilderFn=*/nullptr);
456       rewriter.eraseBlock(parOp.getBody());
457       rewriter.inlineRegionBefore(op.region(), parOp.region(),
458                                   parOp.region().end());
459       rewriter.replaceOp(op, parOp.results());
460       return success();
461     }
462     // Case with affine.parallel with reduction operations/return values.
463     // scf.parallel handles the reduction operation differently unlike
464     // affine.parallel.
465     ArrayRef<Attribute> reductions = op.reductions().getValue();
466     for (auto pair : llvm::zip(reductions, op.getResultTypes())) {
467       // For each of the reduction operations get the identity values for
468       // initialization of the result values.
469       Attribute reduction = std::get<0>(pair);
470       Type resultType = std::get<1>(pair);
471       Optional<AtomicRMWKind> reductionOp = symbolizeAtomicRMWKind(
472           static_cast<uint64_t>(reduction.cast<IntegerAttr>().getInt()));
473       assert(reductionOp.hasValue() &&
474              "Reduction operation cannot be of None Type");
475       AtomicRMWKind reductionOpValue = reductionOp.getValue();
476       identityVals.push_back(
477           getIdentityValue(reductionOpValue, resultType, rewriter, loc));
478     }
479     parOp = rewriter.create<scf::ParallelOp>(
480         loc, lowerBoundTuple, upperBoundTuple, steps, identityVals,
481         /*bodyBuilderFn=*/nullptr);
482 
483     //  Copy the body of the affine.parallel op.
484     rewriter.eraseBlock(parOp.getBody());
485     rewriter.inlineRegionBefore(op.region(), parOp.region(),
486                                 parOp.region().end());
487     assert(reductions.size() == affineParOpTerminator->getNumOperands() &&
488            "Unequal number of reductions and operands.");
489     for (unsigned i = 0, end = reductions.size(); i < end; i++) {
490       // For each of the reduction operations get the respective mlir::Value.
491       Optional<AtomicRMWKind> reductionOp =
492           symbolizeAtomicRMWKind(reductions[i].cast<IntegerAttr>().getInt());
493       assert(reductionOp.hasValue() &&
494              "Reduction Operation cannot be of None Type");
495       AtomicRMWKind reductionOpValue = reductionOp.getValue();
496       rewriter.setInsertionPoint(&parOp.getBody()->back());
497       auto reduceOp = rewriter.create<scf::ReduceOp>(
498           loc, affineParOpTerminator->getOperand(i));
499       rewriter.setInsertionPointToEnd(&reduceOp.reductionOperator().front());
500       Value reductionResult =
501           getReductionOp(reductionOpValue, rewriter, loc,
502                          reduceOp.reductionOperator().front().getArgument(0),
503                          reduceOp.reductionOperator().front().getArgument(1));
504       rewriter.create<scf::ReduceReturnOp>(loc, reductionResult);
505     }
506     rewriter.replaceOp(op, parOp.results());
507     return success();
508   }
509 };
510 
511 class AffineIfLowering : public OpRewritePattern<AffineIfOp> {
512 public:
513   using OpRewritePattern<AffineIfOp>::OpRewritePattern;
514 
515   LogicalResult matchAndRewrite(AffineIfOp op,
516                                 PatternRewriter &rewriter) const override {
517     auto loc = op.getLoc();
518 
519     // Now we just have to handle the condition logic.
520     auto integerSet = op.getIntegerSet();
521     Value zeroConstant = rewriter.create<ConstantIndexOp>(loc, 0);
522     SmallVector<Value, 8> operands(op.getOperands());
523     auto operandsRef = llvm::makeArrayRef(operands);
524 
525     // Calculate cond as a conjunction without short-circuiting.
526     Value cond = nullptr;
527     for (unsigned i = 0, e = integerSet.getNumConstraints(); i < e; ++i) {
528       AffineExpr constraintExpr = integerSet.getConstraint(i);
529       bool isEquality = integerSet.isEq(i);
530 
531       // Build and apply an affine expression
532       auto numDims = integerSet.getNumDims();
533       Value affResult = expandAffineExpr(rewriter, loc, constraintExpr,
534                                          operandsRef.take_front(numDims),
535                                          operandsRef.drop_front(numDims));
536       if (!affResult)
537         return failure();
538       auto pred = isEquality ? CmpIPredicate::eq : CmpIPredicate::sge;
539       Value cmpVal =
540           rewriter.create<CmpIOp>(loc, pred, affResult, zeroConstant);
541       cond =
542           cond ? rewriter.create<AndOp>(loc, cond, cmpVal).getResult() : cmpVal;
543     }
544     cond = cond ? cond
545                 : rewriter.create<ConstantIntOp>(loc, /*value=*/1, /*width=*/1);
546 
547     bool hasElseRegion = !op.elseRegion().empty();
548     auto ifOp = rewriter.create<scf::IfOp>(loc, op.getResultTypes(), cond,
549                                            hasElseRegion);
550     rewriter.inlineRegionBefore(op.thenRegion(), &ifOp.thenRegion().back());
551     rewriter.eraseBlock(&ifOp.thenRegion().back());
552     if (hasElseRegion) {
553       rewriter.inlineRegionBefore(op.elseRegion(), &ifOp.elseRegion().back());
554       rewriter.eraseBlock(&ifOp.elseRegion().back());
555     }
556 
557     // Replace the Affine IfOp finally.
558     rewriter.replaceOp(op, ifOp.results());
559     return success();
560   }
561 };
562 
563 /// Convert an "affine.apply" operation into a sequence of arithmetic
564 /// operations using the StandardOps dialect.
565 class AffineApplyLowering : public OpRewritePattern<AffineApplyOp> {
566 public:
567   using OpRewritePattern<AffineApplyOp>::OpRewritePattern;
568 
569   LogicalResult matchAndRewrite(AffineApplyOp op,
570                                 PatternRewriter &rewriter) const override {
571     auto maybeExpandedMap =
572         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(),
573                         llvm::to_vector<8>(op.getOperands()));
574     if (!maybeExpandedMap)
575       return failure();
576     rewriter.replaceOp(op, *maybeExpandedMap);
577     return success();
578   }
579 };
580 
581 /// Apply the affine map from an 'affine.load' operation to its operands, and
582 /// feed the results to a newly created 'memref.load' operation (which replaces
583 /// the original 'affine.load').
584 class AffineLoadLowering : public OpRewritePattern<AffineLoadOp> {
585 public:
586   using OpRewritePattern<AffineLoadOp>::OpRewritePattern;
587 
588   LogicalResult matchAndRewrite(AffineLoadOp op,
589                                 PatternRewriter &rewriter) const override {
590     // Expand affine map from 'affineLoadOp'.
591     SmallVector<Value, 8> indices(op.getMapOperands());
592     auto resultOperands =
593         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
594     if (!resultOperands)
595       return failure();
596 
597     // Build vector.load memref[expandedMap.results].
598     rewriter.replaceOpWithNewOp<memref::LoadOp>(op, op.getMemRef(),
599                                                 *resultOperands);
600     return success();
601   }
602 };
603 
604 /// Apply the affine map from an 'affine.prefetch' operation to its operands,
605 /// and feed the results to a newly created 'memref.prefetch' operation (which
606 /// replaces the original 'affine.prefetch').
607 class AffinePrefetchLowering : public OpRewritePattern<AffinePrefetchOp> {
608 public:
609   using OpRewritePattern<AffinePrefetchOp>::OpRewritePattern;
610 
611   LogicalResult matchAndRewrite(AffinePrefetchOp op,
612                                 PatternRewriter &rewriter) const override {
613     // Expand affine map from 'affinePrefetchOp'.
614     SmallVector<Value, 8> indices(op.getMapOperands());
615     auto resultOperands =
616         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
617     if (!resultOperands)
618       return failure();
619 
620     // Build memref.prefetch memref[expandedMap.results].
621     rewriter.replaceOpWithNewOp<memref::PrefetchOp>(
622         op, op.memref(), *resultOperands, op.isWrite(), op.localityHint(),
623         op.isDataCache());
624     return success();
625   }
626 };
627 
628 /// Apply the affine map from an 'affine.store' operation to its operands, and
629 /// feed the results to a newly created 'memref.store' operation (which replaces
630 /// the original 'affine.store').
631 class AffineStoreLowering : public OpRewritePattern<AffineStoreOp> {
632 public:
633   using OpRewritePattern<AffineStoreOp>::OpRewritePattern;
634 
635   LogicalResult matchAndRewrite(AffineStoreOp op,
636                                 PatternRewriter &rewriter) const override {
637     // Expand affine map from 'affineStoreOp'.
638     SmallVector<Value, 8> indices(op.getMapOperands());
639     auto maybeExpandedMap =
640         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
641     if (!maybeExpandedMap)
642       return failure();
643 
644     // Build memref.store valueToStore, memref[expandedMap.results].
645     rewriter.replaceOpWithNewOp<memref::StoreOp>(
646         op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap);
647     return success();
648   }
649 };
650 
651 /// Apply the affine maps from an 'affine.dma_start' operation to each of their
652 /// respective map operands, and feed the results to a newly created
653 /// 'memref.dma_start' operation (which replaces the original
654 /// 'affine.dma_start').
655 class AffineDmaStartLowering : public OpRewritePattern<AffineDmaStartOp> {
656 public:
657   using OpRewritePattern<AffineDmaStartOp>::OpRewritePattern;
658 
659   LogicalResult matchAndRewrite(AffineDmaStartOp op,
660                                 PatternRewriter &rewriter) const override {
661     SmallVector<Value, 8> operands(op.getOperands());
662     auto operandsRef = llvm::makeArrayRef(operands);
663 
664     // Expand affine map for DMA source memref.
665     auto maybeExpandedSrcMap = expandAffineMap(
666         rewriter, op.getLoc(), op.getSrcMap(),
667         operandsRef.drop_front(op.getSrcMemRefOperandIndex() + 1));
668     if (!maybeExpandedSrcMap)
669       return failure();
670     // Expand affine map for DMA destination memref.
671     auto maybeExpandedDstMap = expandAffineMap(
672         rewriter, op.getLoc(), op.getDstMap(),
673         operandsRef.drop_front(op.getDstMemRefOperandIndex() + 1));
674     if (!maybeExpandedDstMap)
675       return failure();
676     // Expand affine map for DMA tag memref.
677     auto maybeExpandedTagMap = expandAffineMap(
678         rewriter, op.getLoc(), op.getTagMap(),
679         operandsRef.drop_front(op.getTagMemRefOperandIndex() + 1));
680     if (!maybeExpandedTagMap)
681       return failure();
682 
683     // Build memref.dma_start operation with affine map results.
684     rewriter.replaceOpWithNewOp<memref::DmaStartOp>(
685         op, op.getSrcMemRef(), *maybeExpandedSrcMap, op.getDstMemRef(),
686         *maybeExpandedDstMap, op.getNumElements(), op.getTagMemRef(),
687         *maybeExpandedTagMap, op.getStride(), op.getNumElementsPerStride());
688     return success();
689   }
690 };
691 
692 /// Apply the affine map from an 'affine.dma_wait' operation tag memref,
693 /// and feed the results to a newly created 'memref.dma_wait' operation (which
694 /// replaces the original 'affine.dma_wait').
695 class AffineDmaWaitLowering : public OpRewritePattern<AffineDmaWaitOp> {
696 public:
697   using OpRewritePattern<AffineDmaWaitOp>::OpRewritePattern;
698 
699   LogicalResult matchAndRewrite(AffineDmaWaitOp op,
700                                 PatternRewriter &rewriter) const override {
701     // Expand affine map for DMA tag memref.
702     SmallVector<Value, 8> indices(op.getTagIndices());
703     auto maybeExpandedTagMap =
704         expandAffineMap(rewriter, op.getLoc(), op.getTagMap(), indices);
705     if (!maybeExpandedTagMap)
706       return failure();
707 
708     // Build memref.dma_wait operation with affine map results.
709     rewriter.replaceOpWithNewOp<memref::DmaWaitOp>(
710         op, op.getTagMemRef(), *maybeExpandedTagMap, op.getNumElements());
711     return success();
712   }
713 };
714 
715 /// Apply the affine map from an 'affine.vector_load' operation to its operands,
716 /// and feed the results to a newly created 'vector.load' operation (which
717 /// replaces the original 'affine.vector_load').
718 class AffineVectorLoadLowering : public OpRewritePattern<AffineVectorLoadOp> {
719 public:
720   using OpRewritePattern<AffineVectorLoadOp>::OpRewritePattern;
721 
722   LogicalResult matchAndRewrite(AffineVectorLoadOp op,
723                                 PatternRewriter &rewriter) const override {
724     // Expand affine map from 'affineVectorLoadOp'.
725     SmallVector<Value, 8> indices(op.getMapOperands());
726     auto resultOperands =
727         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
728     if (!resultOperands)
729       return failure();
730 
731     // Build vector.load memref[expandedMap.results].
732     rewriter.replaceOpWithNewOp<vector::LoadOp>(
733         op, op.getVectorType(), op.getMemRef(), *resultOperands);
734     return success();
735   }
736 };
737 
738 /// Apply the affine map from an 'affine.vector_store' operation to its
739 /// operands, and feed the results to a newly created 'vector.store' operation
740 /// (which replaces the original 'affine.vector_store').
741 class AffineVectorStoreLowering : public OpRewritePattern<AffineVectorStoreOp> {
742 public:
743   using OpRewritePattern<AffineVectorStoreOp>::OpRewritePattern;
744 
745   LogicalResult matchAndRewrite(AffineVectorStoreOp op,
746                                 PatternRewriter &rewriter) const override {
747     // Expand affine map from 'affineVectorStoreOp'.
748     SmallVector<Value, 8> indices(op.getMapOperands());
749     auto maybeExpandedMap =
750         expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
751     if (!maybeExpandedMap)
752       return failure();
753 
754     rewriter.replaceOpWithNewOp<vector::StoreOp>(
755         op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap);
756     return success();
757   }
758 };
759 
760 } // end namespace
761 
762 void mlir::populateAffineToStdConversionPatterns(RewritePatternSet &patterns) {
763   // clang-format off
764   patterns.add<
765       AffineApplyLowering,
766       AffineDmaStartLowering,
767       AffineDmaWaitLowering,
768       AffineLoadLowering,
769       AffineMinLowering,
770       AffineMaxLowering,
771       AffineParallelLowering,
772       AffinePrefetchLowering,
773       AffineStoreLowering,
774       AffineForLowering,
775       AffineIfLowering,
776       AffineYieldOpLowering>(patterns.getContext());
777   // clang-format on
778 }
779 
780 void mlir::populateAffineToVectorConversionPatterns(
781     RewritePatternSet &patterns) {
782   // clang-format off
783   patterns.add<
784       AffineVectorLoadLowering,
785       AffineVectorStoreLowering>(patterns.getContext());
786   // clang-format on
787 }
788 
789 namespace {
790 class LowerAffinePass : public ConvertAffineToStandardBase<LowerAffinePass> {
791   void runOnOperation() override {
792     RewritePatternSet patterns(&getContext());
793     populateAffineToStdConversionPatterns(patterns);
794     populateAffineToVectorConversionPatterns(patterns);
795     ConversionTarget target(getContext());
796     target.addLegalDialect<memref::MemRefDialect, scf::SCFDialect,
797                            StandardOpsDialect, VectorDialect>();
798     if (failed(applyPartialConversion(getOperation(), target,
799                                       std::move(patterns))))
800       signalPassFailure();
801   }
802 };
803 } // namespace
804 
805 /// Lowers If and For operations within a function into their lower level CFG
806 /// equivalent blocks.
807 std::unique_ptr<Pass> mlir::createLowerAffinePass() {
808   return std::make_unique<LowerAffinePass>();
809 }
810