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