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 terminators are removed. 331 class AffineTerminatorLowering : public OpRewritePattern<AffineTerminatorOp> { 332 public: 333 using OpRewritePattern<AffineTerminatorOp>::OpRewritePattern; 334 335 LogicalResult matchAndRewrite(AffineTerminatorOp op, 336 PatternRewriter &rewriter) const override { 337 rewriter.replaceOpWithNewOp<scf::YieldOp>(op); 338 return success(); 339 } 340 }; 341 342 class AffineForLowering : public OpRewritePattern<AffineForOp> { 343 public: 344 using OpRewritePattern<AffineForOp>::OpRewritePattern; 345 346 LogicalResult matchAndRewrite(AffineForOp op, 347 PatternRewriter &rewriter) const override { 348 Location loc = op.getLoc(); 349 Value lowerBound = lowerAffineLowerBound(op, rewriter); 350 Value upperBound = lowerAffineUpperBound(op, rewriter); 351 Value step = rewriter.create<ConstantIndexOp>(loc, op.getStep()); 352 auto f = rewriter.create<scf::ForOp>(loc, lowerBound, upperBound, step); 353 rewriter.eraseBlock(f.getBody()); 354 rewriter.inlineRegionBefore(op.region(), f.region(), f.region().end()); 355 rewriter.eraseOp(op); 356 return success(); 357 } 358 }; 359 360 class AffineIfLowering : public OpRewritePattern<AffineIfOp> { 361 public: 362 using OpRewritePattern<AffineIfOp>::OpRewritePattern; 363 364 LogicalResult matchAndRewrite(AffineIfOp op, 365 PatternRewriter &rewriter) const override { 366 auto loc = op.getLoc(); 367 368 // Now we just have to handle the condition logic. 369 auto integerSet = op.getIntegerSet(); 370 Value zeroConstant = rewriter.create<ConstantIndexOp>(loc, 0); 371 SmallVector<Value, 8> operands(op.getOperands()); 372 auto operandsRef = llvm::makeArrayRef(operands); 373 374 // Calculate cond as a conjunction without short-circuiting. 375 Value cond = nullptr; 376 for (unsigned i = 0, e = integerSet.getNumConstraints(); i < e; ++i) { 377 AffineExpr constraintExpr = integerSet.getConstraint(i); 378 bool isEquality = integerSet.isEq(i); 379 380 // Build and apply an affine expression 381 auto numDims = integerSet.getNumDims(); 382 Value affResult = expandAffineExpr(rewriter, loc, constraintExpr, 383 operandsRef.take_front(numDims), 384 operandsRef.drop_front(numDims)); 385 if (!affResult) 386 return failure(); 387 auto pred = isEquality ? CmpIPredicate::eq : CmpIPredicate::sge; 388 Value cmpVal = 389 rewriter.create<CmpIOp>(loc, pred, affResult, zeroConstant); 390 cond = 391 cond ? rewriter.create<AndOp>(loc, cond, cmpVal).getResult() : cmpVal; 392 } 393 cond = cond ? cond 394 : rewriter.create<ConstantIntOp>(loc, /*value=*/1, /*width=*/1); 395 396 bool hasElseRegion = !op.elseRegion().empty(); 397 auto ifOp = rewriter.create<scf::IfOp>(loc, cond, hasElseRegion); 398 rewriter.inlineRegionBefore(op.thenRegion(), &ifOp.thenRegion().back()); 399 rewriter.eraseBlock(&ifOp.thenRegion().back()); 400 if (hasElseRegion) { 401 rewriter.inlineRegionBefore(op.elseRegion(), &ifOp.elseRegion().back()); 402 rewriter.eraseBlock(&ifOp.elseRegion().back()); 403 } 404 405 // Ok, we're done! 406 rewriter.eraseOp(op); 407 return success(); 408 } 409 }; 410 411 /// Convert an "affine.apply" operation into a sequence of arithmetic 412 /// operations using the StandardOps dialect. 413 class AffineApplyLowering : public OpRewritePattern<AffineApplyOp> { 414 public: 415 using OpRewritePattern<AffineApplyOp>::OpRewritePattern; 416 417 LogicalResult matchAndRewrite(AffineApplyOp op, 418 PatternRewriter &rewriter) const override { 419 auto maybeExpandedMap = 420 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), 421 llvm::to_vector<8>(op.getOperands())); 422 if (!maybeExpandedMap) 423 return failure(); 424 rewriter.replaceOp(op, *maybeExpandedMap); 425 return success(); 426 } 427 }; 428 429 /// Apply the affine map from an 'affine.load' operation to its operands, and 430 /// feed the results to a newly created 'std.load' operation (which replaces the 431 /// original 'affine.load'). 432 class AffineLoadLowering : public OpRewritePattern<AffineLoadOp> { 433 public: 434 using OpRewritePattern<AffineLoadOp>::OpRewritePattern; 435 436 LogicalResult matchAndRewrite(AffineLoadOp op, 437 PatternRewriter &rewriter) const override { 438 // Expand affine map from 'affineLoadOp'. 439 SmallVector<Value, 8> indices(op.getMapOperands()); 440 auto resultOperands = 441 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 442 if (!resultOperands) 443 return failure(); 444 445 // Build std.load memref[expandedMap.results]. 446 rewriter.replaceOpWithNewOp<LoadOp>(op, op.getMemRef(), *resultOperands); 447 return success(); 448 } 449 }; 450 451 /// Apply the affine map from an 'affine.prefetch' operation to its operands, 452 /// and feed the results to a newly created 'std.prefetch' operation (which 453 /// replaces the original 'affine.prefetch'). 454 class AffinePrefetchLowering : public OpRewritePattern<AffinePrefetchOp> { 455 public: 456 using OpRewritePattern<AffinePrefetchOp>::OpRewritePattern; 457 458 LogicalResult matchAndRewrite(AffinePrefetchOp op, 459 PatternRewriter &rewriter) const override { 460 // Expand affine map from 'affinePrefetchOp'. 461 SmallVector<Value, 8> indices(op.getMapOperands()); 462 auto resultOperands = 463 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 464 if (!resultOperands) 465 return failure(); 466 467 // Build std.prefetch memref[expandedMap.results]. 468 rewriter.replaceOpWithNewOp<PrefetchOp>( 469 op, op.memref(), *resultOperands, op.isWrite(), 470 op.localityHint().getZExtValue(), op.isDataCache()); 471 return success(); 472 } 473 }; 474 475 /// Apply the affine map from an 'affine.store' operation to its operands, and 476 /// feed the results to a newly created 'std.store' operation (which replaces 477 /// the original 'affine.store'). 478 class AffineStoreLowering : public OpRewritePattern<AffineStoreOp> { 479 public: 480 using OpRewritePattern<AffineStoreOp>::OpRewritePattern; 481 482 LogicalResult matchAndRewrite(AffineStoreOp op, 483 PatternRewriter &rewriter) const override { 484 // Expand affine map from 'affineStoreOp'. 485 SmallVector<Value, 8> indices(op.getMapOperands()); 486 auto maybeExpandedMap = 487 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 488 if (!maybeExpandedMap) 489 return failure(); 490 491 // Build std.store valueToStore, memref[expandedMap.results]. 492 rewriter.replaceOpWithNewOp<StoreOp>(op, op.getValueToStore(), 493 op.getMemRef(), *maybeExpandedMap); 494 return success(); 495 } 496 }; 497 498 /// Apply the affine maps from an 'affine.dma_start' operation to each of their 499 /// respective map operands, and feed the results to a newly created 500 /// 'std.dma_start' operation (which replaces the original 'affine.dma_start'). 501 class AffineDmaStartLowering : public OpRewritePattern<AffineDmaStartOp> { 502 public: 503 using OpRewritePattern<AffineDmaStartOp>::OpRewritePattern; 504 505 LogicalResult matchAndRewrite(AffineDmaStartOp op, 506 PatternRewriter &rewriter) const override { 507 SmallVector<Value, 8> operands(op.getOperands()); 508 auto operandsRef = llvm::makeArrayRef(operands); 509 510 // Expand affine map for DMA source memref. 511 auto maybeExpandedSrcMap = expandAffineMap( 512 rewriter, op.getLoc(), op.getSrcMap(), 513 operandsRef.drop_front(op.getSrcMemRefOperandIndex() + 1)); 514 if (!maybeExpandedSrcMap) 515 return failure(); 516 // Expand affine map for DMA destination memref. 517 auto maybeExpandedDstMap = expandAffineMap( 518 rewriter, op.getLoc(), op.getDstMap(), 519 operandsRef.drop_front(op.getDstMemRefOperandIndex() + 1)); 520 if (!maybeExpandedDstMap) 521 return failure(); 522 // Expand affine map for DMA tag memref. 523 auto maybeExpandedTagMap = expandAffineMap( 524 rewriter, op.getLoc(), op.getTagMap(), 525 operandsRef.drop_front(op.getTagMemRefOperandIndex() + 1)); 526 if (!maybeExpandedTagMap) 527 return failure(); 528 529 // Build std.dma_start operation with affine map results. 530 rewriter.replaceOpWithNewOp<DmaStartOp>( 531 op, op.getSrcMemRef(), *maybeExpandedSrcMap, op.getDstMemRef(), 532 *maybeExpandedDstMap, op.getNumElements(), op.getTagMemRef(), 533 *maybeExpandedTagMap, op.getStride(), op.getNumElementsPerStride()); 534 return success(); 535 } 536 }; 537 538 /// Apply the affine map from an 'affine.dma_wait' operation tag memref, 539 /// and feed the results to a newly created 'std.dma_wait' operation (which 540 /// replaces the original 'affine.dma_wait'). 541 class AffineDmaWaitLowering : public OpRewritePattern<AffineDmaWaitOp> { 542 public: 543 using OpRewritePattern<AffineDmaWaitOp>::OpRewritePattern; 544 545 LogicalResult matchAndRewrite(AffineDmaWaitOp op, 546 PatternRewriter &rewriter) const override { 547 // Expand affine map for DMA tag memref. 548 SmallVector<Value, 8> indices(op.getTagIndices()); 549 auto maybeExpandedTagMap = 550 expandAffineMap(rewriter, op.getLoc(), op.getTagMap(), indices); 551 if (!maybeExpandedTagMap) 552 return failure(); 553 554 // Build std.dma_wait operation with affine map results. 555 rewriter.replaceOpWithNewOp<DmaWaitOp>( 556 op, op.getTagMemRef(), *maybeExpandedTagMap, op.getNumElements()); 557 return success(); 558 } 559 }; 560 561 /// Apply the affine map from an 'affine.vector_load' operation to its operands, 562 /// and feed the results to a newly created 'vector.transfer_read' operation 563 /// (which replaces the original 'affine.vector_load'). 564 class AffineVectorLoadLowering : public OpRewritePattern<AffineVectorLoadOp> { 565 public: 566 using OpRewritePattern<AffineVectorLoadOp>::OpRewritePattern; 567 568 LogicalResult matchAndRewrite(AffineVectorLoadOp op, 569 PatternRewriter &rewriter) const override { 570 // Expand affine map from 'affineVectorLoadOp'. 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 vector.transfer_read memref[expandedMap.results]. 578 rewriter.replaceOpWithNewOp<TransferReadOp>( 579 op, op.getVectorType(), op.getMemRef(), *resultOperands); 580 return success(); 581 } 582 }; 583 584 /// Apply the affine map from an 'affine.vector_store' operation to its 585 /// operands, and feed the results to a newly created 'vector.transfer_write' 586 /// operation (which replaces the original 'affine.vector_store'). 587 class AffineVectorStoreLowering : public OpRewritePattern<AffineVectorStoreOp> { 588 public: 589 using OpRewritePattern<AffineVectorStoreOp>::OpRewritePattern; 590 591 LogicalResult matchAndRewrite(AffineVectorStoreOp op, 592 PatternRewriter &rewriter) const override { 593 // Expand affine map from 'affineVectorStoreOp'. 594 SmallVector<Value, 8> indices(op.getMapOperands()); 595 auto maybeExpandedMap = 596 expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices); 597 if (!maybeExpandedMap) 598 return failure(); 599 600 rewriter.replaceOpWithNewOp<TransferWriteOp>( 601 op, op.getValueToStore(), op.getMemRef(), *maybeExpandedMap); 602 return success(); 603 } 604 }; 605 606 } // end namespace 607 608 void mlir::populateAffineToStdConversionPatterns( 609 OwningRewritePatternList &patterns, MLIRContext *ctx) { 610 // clang-format off 611 patterns.insert< 612 AffineApplyLowering, 613 AffineDmaStartLowering, 614 AffineDmaWaitLowering, 615 AffineLoadLowering, 616 AffineMinLowering, 617 AffineMaxLowering, 618 AffinePrefetchLowering, 619 AffineStoreLowering, 620 AffineForLowering, 621 AffineIfLowering, 622 AffineTerminatorLowering>(ctx); 623 // clang-format on 624 } 625 626 void mlir::populateAffineToVectorConversionPatterns( 627 OwningRewritePatternList &patterns, MLIRContext *ctx) { 628 // clang-format off 629 patterns.insert< 630 AffineVectorLoadLowering, 631 AffineVectorStoreLowering>(ctx); 632 // clang-format on 633 } 634 635 namespace { 636 class LowerAffinePass : public ConvertAffineToStandardBase<LowerAffinePass> { 637 void runOnFunction() override { 638 OwningRewritePatternList patterns; 639 populateAffineToStdConversionPatterns(patterns, &getContext()); 640 populateAffineToVectorConversionPatterns(patterns, &getContext()); 641 ConversionTarget target(getContext()); 642 target 643 .addLegalDialect<scf::SCFDialect, StandardOpsDialect, VectorDialect>(); 644 if (failed(applyPartialConversion(getFunction(), target, patterns))) 645 signalPassFailure(); 646 } 647 }; 648 } // namespace 649 650 /// Lowers If and For operations within a function into their lower level CFG 651 /// equivalent blocks. 652 std::unique_ptr<OperationPass<FuncOp>> mlir::createLowerAffinePass() { 653 return std::make_unique<LowerAffinePass>(); 654 } 655