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