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 /// Convert an `affine.parallel` (loop nest) operation into a `scf.parallel` 371 /// operation. 372 class AffineParallelLowering : public OpRewritePattern<AffineParallelOp> { 373 public: 374 using OpRewritePattern<AffineParallelOp>::OpRewritePattern; 375 376 LogicalResult matchAndRewrite(AffineParallelOp op, 377 PatternRewriter &rewriter) const override { 378 Location loc = op.getLoc(); 379 SmallVector<Value, 8> steps; 380 SmallVector<Value, 8> upperBoundTuple; 381 SmallVector<Value, 8> lowerBoundTuple; 382 SmallVector<Value, 8> identityVals; 383 // Emit IR computing the lower and upper bound by expanding the map 384 // expression. 385 lowerBoundTuple.reserve(op.getNumDims()); 386 upperBoundTuple.reserve(op.getNumDims()); 387 for (unsigned i = 0, e = op.getNumDims(); i < e; ++i) { 388 Value lower = lowerAffineMapMax(rewriter, loc, op.getLowerBoundMap(i), 389 op.getLowerBoundsOperands()); 390 if (!lower) 391 return rewriter.notifyMatchFailure(op, "couldn't convert lower bounds"); 392 lowerBoundTuple.push_back(lower); 393 394 Value upper = lowerAffineMapMin(rewriter, loc, op.getUpperBoundMap(i), 395 op.getUpperBoundsOperands()); 396 if (!upper) 397 return rewriter.notifyMatchFailure(op, "couldn't convert upper bounds"); 398 upperBoundTuple.push_back(upper); 399 } 400 steps.reserve(op.steps().size()); 401 for (Attribute step : op.steps()) 402 steps.push_back(rewriter.create<ConstantIndexOp>( 403 loc, step.cast<IntegerAttr>().getInt())); 404 405 // Get the terminator op. 406 Operation *affineParOpTerminator = op.getBody()->getTerminator(); 407 scf::ParallelOp parOp; 408 if (op.results().empty()) { 409 // Case with no reduction operations/return values. 410 parOp = rewriter.create<scf::ParallelOp>(loc, lowerBoundTuple, 411 upperBoundTuple, steps, 412 /*bodyBuilderFn=*/nullptr); 413 rewriter.eraseBlock(parOp.getBody()); 414 rewriter.inlineRegionBefore(op.region(), parOp.region(), 415 parOp.region().end()); 416 rewriter.replaceOp(op, parOp.results()); 417 return success(); 418 } 419 // Case with affine.parallel with reduction operations/return values. 420 // scf.parallel handles the reduction operation differently unlike 421 // affine.parallel. 422 ArrayRef<Attribute> reductions = op.reductions().getValue(); 423 for (auto pair : llvm::zip(reductions, op.getResultTypes())) { 424 // For each of the reduction operations get the identity values for 425 // initialization of the result values. 426 Attribute reduction = std::get<0>(pair); 427 Type resultType = std::get<1>(pair); 428 Optional<AtomicRMWKind> reductionOp = symbolizeAtomicRMWKind( 429 static_cast<uint64_t>(reduction.cast<IntegerAttr>().getInt())); 430 assert(reductionOp.hasValue() && 431 "Reduction operation cannot be of None Type"); 432 AtomicRMWKind reductionOpValue = reductionOp.getValue(); 433 identityVals.push_back( 434 getIdentityValue(reductionOpValue, resultType, rewriter, loc)); 435 } 436 parOp = rewriter.create<scf::ParallelOp>( 437 loc, lowerBoundTuple, upperBoundTuple, steps, identityVals, 438 /*bodyBuilderFn=*/nullptr); 439 440 // Copy the body of the affine.parallel op. 441 rewriter.eraseBlock(parOp.getBody()); 442 rewriter.inlineRegionBefore(op.region(), parOp.region(), 443 parOp.region().end()); 444 assert(reductions.size() == affineParOpTerminator->getNumOperands() && 445 "Unequal number of reductions and operands."); 446 for (unsigned i = 0, end = reductions.size(); i < end; i++) { 447 // For each of the reduction operations get the respective mlir::Value. 448 Optional<AtomicRMWKind> reductionOp = 449 symbolizeAtomicRMWKind(reductions[i].cast<IntegerAttr>().getInt()); 450 assert(reductionOp.hasValue() && 451 "Reduction Operation cannot be of None Type"); 452 AtomicRMWKind reductionOpValue = reductionOp.getValue(); 453 rewriter.setInsertionPoint(&parOp.getBody()->back()); 454 auto reduceOp = rewriter.create<scf::ReduceOp>( 455 loc, affineParOpTerminator->getOperand(i)); 456 rewriter.setInsertionPointToEnd(&reduceOp.reductionOperator().front()); 457 Value reductionResult = 458 getReductionOp(reductionOpValue, rewriter, loc, 459 reduceOp.reductionOperator().front().getArgument(0), 460 reduceOp.reductionOperator().front().getArgument(1)); 461 rewriter.create<scf::ReduceReturnOp>(loc, reductionResult); 462 } 463 rewriter.replaceOp(op, parOp.results()); 464 return success(); 465 } 466 }; 467 468 class AffineIfLowering : public OpRewritePattern<AffineIfOp> { 469 public: 470 using OpRewritePattern<AffineIfOp>::OpRewritePattern; 471 472 LogicalResult matchAndRewrite(AffineIfOp op, 473 PatternRewriter &rewriter) const override { 474 auto loc = op.getLoc(); 475 476 // Now we just have to handle the condition logic. 477 auto integerSet = op.getIntegerSet(); 478 Value zeroConstant = rewriter.create<ConstantIndexOp>(loc, 0); 479 SmallVector<Value, 8> operands(op.getOperands()); 480 auto operandsRef = llvm::makeArrayRef(operands); 481 482 // Calculate cond as a conjunction without short-circuiting. 483 Value cond = nullptr; 484 for (unsigned i = 0, e = integerSet.getNumConstraints(); i < e; ++i) { 485 AffineExpr constraintExpr = integerSet.getConstraint(i); 486 bool isEquality = integerSet.isEq(i); 487 488 // Build and apply an affine expression 489 auto numDims = integerSet.getNumDims(); 490 Value affResult = expandAffineExpr(rewriter, loc, constraintExpr, 491 operandsRef.take_front(numDims), 492 operandsRef.drop_front(numDims)); 493 if (!affResult) 494 return failure(); 495 auto pred = isEquality ? CmpIPredicate::eq : CmpIPredicate::sge; 496 Value cmpVal = 497 rewriter.create<CmpIOp>(loc, pred, affResult, zeroConstant); 498 cond = 499 cond ? rewriter.create<AndOp>(loc, cond, cmpVal).getResult() : cmpVal; 500 } 501 cond = cond ? cond 502 : rewriter.create<ConstantIntOp>(loc, /*value=*/1, /*width=*/1); 503 504 bool hasElseRegion = !op.elseRegion().empty(); 505 auto ifOp = rewriter.create<scf::IfOp>(loc, op.getResultTypes(), cond, 506 hasElseRegion); 507 rewriter.inlineRegionBefore(op.thenRegion(), &ifOp.thenRegion().back()); 508 rewriter.eraseBlock(&ifOp.thenRegion().back()); 509 if (hasElseRegion) { 510 rewriter.inlineRegionBefore(op.elseRegion(), &ifOp.elseRegion().back()); 511 rewriter.eraseBlock(&ifOp.elseRegion().back()); 512 } 513 514 // Replace the Affine IfOp finally. 515 rewriter.replaceOp(op, ifOp.results()); 516 return success(); 517 } 518 }; 519 520 /// Convert an "affine.apply" operation into a sequence of arithmetic 521 /// operations using the StandardOps dialect. 522 class AffineApplyLowering : public OpRewritePattern<AffineApplyOp> { 523 public: 524 using OpRewritePattern<AffineApplyOp>::OpRewritePattern; 525 526 LogicalResult matchAndRewrite(AffineApplyOp op, 527 PatternRewriter &rewriter) const override { 528 auto maybeExpandedMap = 529 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), 530 llvm::to_vector<8>(op.getOperands())); 531 if (!maybeExpandedMap) 532 return failure(); 533 rewriter.replaceOp(op, *maybeExpandedMap); 534 return success(); 535 } 536 }; 537 538 /// Apply the affine map from an 'affine.load' operation to its operands, and 539 /// feed the results to a newly created 'memref.load' operation (which replaces 540 /// the original 'affine.load'). 541 class AffineLoadLowering : public OpRewritePattern<AffineLoadOp> { 542 public: 543 using OpRewritePattern<AffineLoadOp>::OpRewritePattern; 544 545 LogicalResult matchAndRewrite(AffineLoadOp op, 546 PatternRewriter &rewriter) const override { 547 // Expand affine map from 'affineLoadOp'. 548 SmallVector<Value, 8> indices(op.getMapOperands()); 549 auto resultOperands = 550 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 551 if (!resultOperands) 552 return failure(); 553 554 // Build vector.load memref[expandedMap.results]. 555 rewriter.replaceOpWithNewOp<memref::LoadOp>(op, op.getMemRef(), 556 *resultOperands); 557 return success(); 558 } 559 }; 560 561 /// Apply the affine map from an 'affine.prefetch' operation to its operands, 562 /// and feed the results to a newly created 'memref.prefetch' operation (which 563 /// replaces the original 'affine.prefetch'). 564 class AffinePrefetchLowering : public OpRewritePattern<AffinePrefetchOp> { 565 public: 566 using OpRewritePattern<AffinePrefetchOp>::OpRewritePattern; 567 568 LogicalResult matchAndRewrite(AffinePrefetchOp op, 569 PatternRewriter &rewriter) const override { 570 // Expand affine map from 'affinePrefetchOp'. 571 SmallVector<Value, 8> indices(op.getMapOperands()); 572 auto resultOperands = 573 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 574 if (!resultOperands) 575 return failure(); 576 577 // Build memref.prefetch memref[expandedMap.results]. 578 rewriter.replaceOpWithNewOp<memref::PrefetchOp>( 579 op, op.memref(), *resultOperands, op.isWrite(), op.localityHint(), 580 op.isDataCache()); 581 return success(); 582 } 583 }; 584 585 /// Apply the affine map from an 'affine.store' operation to its operands, and 586 /// feed the results to a newly created 'memref.store' operation (which replaces 587 /// the original 'affine.store'). 588 class AffineStoreLowering : public OpRewritePattern<AffineStoreOp> { 589 public: 590 using OpRewritePattern<AffineStoreOp>::OpRewritePattern; 591 592 LogicalResult matchAndRewrite(AffineStoreOp op, 593 PatternRewriter &rewriter) const override { 594 // Expand affine map from 'affineStoreOp'. 595 SmallVector<Value, 8> indices(op.getMapOperands()); 596 auto maybeExpandedMap = 597 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 598 if (!maybeExpandedMap) 599 return failure(); 600 601 // Build memref.store valueToStore, memref[expandedMap.results]. 602 rewriter.replaceOpWithNewOp<memref::StoreOp>( 603 op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap); 604 return success(); 605 } 606 }; 607 608 /// Apply the affine maps from an 'affine.dma_start' operation to each of their 609 /// respective map operands, and feed the results to a newly created 610 /// 'memref.dma_start' operation (which replaces the original 611 /// 'affine.dma_start'). 612 class AffineDmaStartLowering : public OpRewritePattern<AffineDmaStartOp> { 613 public: 614 using OpRewritePattern<AffineDmaStartOp>::OpRewritePattern; 615 616 LogicalResult matchAndRewrite(AffineDmaStartOp op, 617 PatternRewriter &rewriter) const override { 618 SmallVector<Value, 8> operands(op.getOperands()); 619 auto operandsRef = llvm::makeArrayRef(operands); 620 621 // Expand affine map for DMA source memref. 622 auto maybeExpandedSrcMap = expandAffineMap( 623 rewriter, op.getLoc(), op.getSrcMap(), 624 operandsRef.drop_front(op.getSrcMemRefOperandIndex() + 1)); 625 if (!maybeExpandedSrcMap) 626 return failure(); 627 // Expand affine map for DMA destination memref. 628 auto maybeExpandedDstMap = expandAffineMap( 629 rewriter, op.getLoc(), op.getDstMap(), 630 operandsRef.drop_front(op.getDstMemRefOperandIndex() + 1)); 631 if (!maybeExpandedDstMap) 632 return failure(); 633 // Expand affine map for DMA tag memref. 634 auto maybeExpandedTagMap = expandAffineMap( 635 rewriter, op.getLoc(), op.getTagMap(), 636 operandsRef.drop_front(op.getTagMemRefOperandIndex() + 1)); 637 if (!maybeExpandedTagMap) 638 return failure(); 639 640 // Build memref.dma_start operation with affine map results. 641 rewriter.replaceOpWithNewOp<memref::DmaStartOp>( 642 op, op.getSrcMemRef(), *maybeExpandedSrcMap, op.getDstMemRef(), 643 *maybeExpandedDstMap, op.getNumElements(), op.getTagMemRef(), 644 *maybeExpandedTagMap, op.getStride(), op.getNumElementsPerStride()); 645 return success(); 646 } 647 }; 648 649 /// Apply the affine map from an 'affine.dma_wait' operation tag memref, 650 /// and feed the results to a newly created 'memref.dma_wait' operation (which 651 /// replaces the original 'affine.dma_wait'). 652 class AffineDmaWaitLowering : public OpRewritePattern<AffineDmaWaitOp> { 653 public: 654 using OpRewritePattern<AffineDmaWaitOp>::OpRewritePattern; 655 656 LogicalResult matchAndRewrite(AffineDmaWaitOp op, 657 PatternRewriter &rewriter) const override { 658 // Expand affine map for DMA tag memref. 659 SmallVector<Value, 8> indices(op.getTagIndices()); 660 auto maybeExpandedTagMap = 661 expandAffineMap(rewriter, op.getLoc(), op.getTagMap(), indices); 662 if (!maybeExpandedTagMap) 663 return failure(); 664 665 // Build memref.dma_wait operation with affine map results. 666 rewriter.replaceOpWithNewOp<memref::DmaWaitOp>( 667 op, op.getTagMemRef(), *maybeExpandedTagMap, op.getNumElements()); 668 return success(); 669 } 670 }; 671 672 /// Apply the affine map from an 'affine.vector_load' operation to its operands, 673 /// and feed the results to a newly created 'vector.load' operation (which 674 /// replaces the original 'affine.vector_load'). 675 class AffineVectorLoadLowering : public OpRewritePattern<AffineVectorLoadOp> { 676 public: 677 using OpRewritePattern<AffineVectorLoadOp>::OpRewritePattern; 678 679 LogicalResult matchAndRewrite(AffineVectorLoadOp op, 680 PatternRewriter &rewriter) const override { 681 // Expand affine map from 'affineVectorLoadOp'. 682 SmallVector<Value, 8> indices(op.getMapOperands()); 683 auto resultOperands = 684 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 685 if (!resultOperands) 686 return failure(); 687 688 // Build vector.load memref[expandedMap.results]. 689 rewriter.replaceOpWithNewOp<vector::LoadOp>( 690 op, op.getVectorType(), op.getMemRef(), *resultOperands); 691 return success(); 692 } 693 }; 694 695 /// Apply the affine map from an 'affine.vector_store' operation to its 696 /// operands, and feed the results to a newly created 'vector.store' operation 697 /// (which replaces the original 'affine.vector_store'). 698 class AffineVectorStoreLowering : public OpRewritePattern<AffineVectorStoreOp> { 699 public: 700 using OpRewritePattern<AffineVectorStoreOp>::OpRewritePattern; 701 702 LogicalResult matchAndRewrite(AffineVectorStoreOp op, 703 PatternRewriter &rewriter) const override { 704 // Expand affine map from 'affineVectorStoreOp'. 705 SmallVector<Value, 8> indices(op.getMapOperands()); 706 auto maybeExpandedMap = 707 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 708 if (!maybeExpandedMap) 709 return failure(); 710 711 rewriter.replaceOpWithNewOp<vector::StoreOp>( 712 op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap); 713 return success(); 714 } 715 }; 716 717 } // end namespace 718 719 void mlir::populateAffineToStdConversionPatterns(RewritePatternSet &patterns) { 720 // clang-format off 721 patterns.add< 722 AffineApplyLowering, 723 AffineDmaStartLowering, 724 AffineDmaWaitLowering, 725 AffineLoadLowering, 726 AffineMinLowering, 727 AffineMaxLowering, 728 AffineParallelLowering, 729 AffinePrefetchLowering, 730 AffineStoreLowering, 731 AffineForLowering, 732 AffineIfLowering, 733 AffineYieldOpLowering>(patterns.getContext()); 734 // clang-format on 735 } 736 737 void mlir::populateAffineToVectorConversionPatterns( 738 RewritePatternSet &patterns) { 739 // clang-format off 740 patterns.add< 741 AffineVectorLoadLowering, 742 AffineVectorStoreLowering>(patterns.getContext()); 743 // clang-format on 744 } 745 746 namespace { 747 class LowerAffinePass : public ConvertAffineToStandardBase<LowerAffinePass> { 748 void runOnOperation() override { 749 RewritePatternSet patterns(&getContext()); 750 populateAffineToStdConversionPatterns(patterns); 751 populateAffineToVectorConversionPatterns(patterns); 752 ConversionTarget target(getContext()); 753 target.addLegalDialect<memref::MemRefDialect, scf::SCFDialect, 754 StandardOpsDialect, VectorDialect>(); 755 if (failed(applyPartialConversion(getOperation(), target, 756 std::move(patterns)))) 757 signalPassFailure(); 758 } 759 }; 760 } // namespace 761 762 /// Lowers If and For operations within a function into their lower level CFG 763 /// equivalent blocks. 764 std::unique_ptr<Pass> mlir::createLowerAffinePass() { 765 return std::make_unique<LowerAffinePass>(); 766 } 767