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