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