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