1 //===- AffineOps.cpp - MLIR Affine Operations -----------------------------===// 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 #include "mlir/Dialect/Affine/IR/AffineOps.h" 10 #include "mlir/Dialect/Affine/IR/AffineValueMap.h" 11 #include "mlir/Dialect/MemRef/IR/MemRef.h" 12 #include "mlir/Dialect/Tensor/IR/Tensor.h" 13 #include "mlir/IR/BlockAndValueMapping.h" 14 #include "mlir/IR/IntegerSet.h" 15 #include "mlir/IR/Matchers.h" 16 #include "mlir/IR/PatternMatch.h" 17 #include "mlir/Transforms/InliningUtils.h" 18 #include "llvm/ADT/SmallBitVector.h" 19 #include "llvm/ADT/TypeSwitch.h" 20 #include "llvm/Support/Debug.h" 21 22 using namespace mlir; 23 24 #define DEBUG_TYPE "affine-analysis" 25 26 #include "mlir/Dialect/Affine/IR/AffineOpsDialect.cpp.inc" 27 28 /// A utility function to check if a value is defined at the top level of 29 /// `region` or is an argument of `region`. A value of index type defined at the 30 /// top level of a `AffineScope` region is always a valid symbol for all 31 /// uses in that region. 32 bool mlir::isTopLevelValue(Value value, Region *region) { 33 if (auto arg = value.dyn_cast<BlockArgument>()) 34 return arg.getParentRegion() == region; 35 return value.getDefiningOp()->getParentRegion() == region; 36 } 37 38 /// Checks if `value` known to be a legal affine dimension or symbol in `src` 39 /// region remains legal if the operation that uses it is inlined into `dest` 40 /// with the given value mapping. `legalityCheck` is either `isValidDim` or 41 /// `isValidSymbol`, depending on the value being required to remain a valid 42 /// dimension or symbol. 43 static bool 44 remainsLegalAfterInline(Value value, Region *src, Region *dest, 45 const BlockAndValueMapping &mapping, 46 function_ref<bool(Value, Region *)> legalityCheck) { 47 // If the value is a valid dimension for any other reason than being 48 // a top-level value, it will remain valid: constants get inlined 49 // with the function, transitive affine applies also get inlined and 50 // will be checked themselves, etc. 51 if (!isTopLevelValue(value, src)) 52 return true; 53 54 // If it's a top-level value because it's a block operand, i.e. a 55 // function argument, check whether the value replacing it after 56 // inlining is a valid dimension in the new region. 57 if (value.isa<BlockArgument>()) 58 return legalityCheck(mapping.lookup(value), dest); 59 60 // If it's a top-level value because it's defined in the region, 61 // it can only be inlined if the defining op is a constant or a 62 // `dim`, which can appear anywhere and be valid, since the defining 63 // op won't be top-level anymore after inlining. 64 Attribute operandCst; 65 return matchPattern(value.getDefiningOp(), m_Constant(&operandCst)) || 66 value.getDefiningOp<memref::DimOp>() || 67 value.getDefiningOp<tensor::DimOp>(); 68 } 69 70 /// Checks if all values known to be legal affine dimensions or symbols in `src` 71 /// remain so if their respective users are inlined into `dest`. 72 static bool 73 remainsLegalAfterInline(ValueRange values, Region *src, Region *dest, 74 const BlockAndValueMapping &mapping, 75 function_ref<bool(Value, Region *)> legalityCheck) { 76 return llvm::all_of(values, [&](Value v) { 77 return remainsLegalAfterInline(v, src, dest, mapping, legalityCheck); 78 }); 79 } 80 81 /// Checks if an affine read or write operation remains legal after inlining 82 /// from `src` to `dest`. 83 template <typename OpTy> 84 static bool remainsLegalAfterInline(OpTy op, Region *src, Region *dest, 85 const BlockAndValueMapping &mapping) { 86 static_assert(llvm::is_one_of<OpTy, AffineReadOpInterface, 87 AffineWriteOpInterface>::value, 88 "only ops with affine read/write interface are supported"); 89 90 AffineMap map = op.getAffineMap(); 91 ValueRange dimOperands = op.getMapOperands().take_front(map.getNumDims()); 92 ValueRange symbolOperands = 93 op.getMapOperands().take_back(map.getNumSymbols()); 94 if (!remainsLegalAfterInline( 95 dimOperands, src, dest, mapping, 96 static_cast<bool (*)(Value, Region *)>(isValidDim))) 97 return false; 98 if (!remainsLegalAfterInline( 99 symbolOperands, src, dest, mapping, 100 static_cast<bool (*)(Value, Region *)>(isValidSymbol))) 101 return false; 102 return true; 103 } 104 105 /// Checks if an affine apply operation remains legal after inlining from `src` 106 /// to `dest`. 107 // Use "unused attribute" marker to silence clang-tidy warning stemming from 108 // the inability to see through "llvm::TypeSwitch". 109 template <> 110 bool LLVM_ATTRIBUTE_UNUSED 111 remainsLegalAfterInline(AffineApplyOp op, Region *src, Region *dest, 112 const BlockAndValueMapping &mapping) { 113 // If it's a valid dimension, we need to check that it remains so. 114 if (isValidDim(op.getResult(), src)) 115 return remainsLegalAfterInline( 116 op.getMapOperands(), src, dest, mapping, 117 static_cast<bool (*)(Value, Region *)>(isValidDim)); 118 119 // Otherwise it must be a valid symbol, check that it remains so. 120 return remainsLegalAfterInline( 121 op.getMapOperands(), src, dest, mapping, 122 static_cast<bool (*)(Value, Region *)>(isValidSymbol)); 123 } 124 125 //===----------------------------------------------------------------------===// 126 // AffineDialect Interfaces 127 //===----------------------------------------------------------------------===// 128 129 namespace { 130 /// This class defines the interface for handling inlining with affine 131 /// operations. 132 struct AffineInlinerInterface : public DialectInlinerInterface { 133 using DialectInlinerInterface::DialectInlinerInterface; 134 135 //===--------------------------------------------------------------------===// 136 // Analysis Hooks 137 //===--------------------------------------------------------------------===// 138 139 /// Returns true if the given region 'src' can be inlined into the region 140 /// 'dest' that is attached to an operation registered to the current dialect. 141 /// 'wouldBeCloned' is set if the region is cloned into its new location 142 /// rather than moved, indicating there may be other users. 143 bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned, 144 BlockAndValueMapping &valueMapping) const final { 145 // We can inline into affine loops and conditionals if this doesn't break 146 // affine value categorization rules. 147 Operation *destOp = dest->getParentOp(); 148 if (!isa<AffineParallelOp, AffineForOp, AffineIfOp>(destOp)) 149 return false; 150 151 // Multi-block regions cannot be inlined into affine constructs, all of 152 // which require single-block regions. 153 if (!llvm::hasSingleElement(*src)) 154 return false; 155 156 // Side-effecting operations that the affine dialect cannot understand 157 // should not be inlined. 158 Block &srcBlock = src->front(); 159 for (Operation &op : srcBlock) { 160 // Ops with no side effects are fine, 161 if (auto iface = dyn_cast<MemoryEffectOpInterface>(op)) { 162 if (iface.hasNoEffect()) 163 continue; 164 } 165 166 // Assuming the inlined region is valid, we only need to check if the 167 // inlining would change it. 168 bool remainsValid = 169 llvm::TypeSwitch<Operation *, bool>(&op) 170 .Case<AffineApplyOp, AffineReadOpInterface, 171 AffineWriteOpInterface>([&](auto op) { 172 return remainsLegalAfterInline(op, src, dest, valueMapping); 173 }) 174 .Default([](Operation *) { 175 // Conservatively disallow inlining ops we cannot reason about. 176 return false; 177 }); 178 179 if (!remainsValid) 180 return false; 181 } 182 183 return true; 184 } 185 186 /// Returns true if the given operation 'op', that is registered to this 187 /// dialect, can be inlined into the given region, false otherwise. 188 bool isLegalToInline(Operation *op, Region *region, bool wouldBeCloned, 189 BlockAndValueMapping &valueMapping) const final { 190 // Always allow inlining affine operations into a region that is marked as 191 // affine scope, or into affine loops and conditionals. There are some edge 192 // cases when inlining *into* affine structures, but that is handled in the 193 // other 'isLegalToInline' hook above. 194 Operation *parentOp = region->getParentOp(); 195 return parentOp->hasTrait<OpTrait::AffineScope>() || 196 isa<AffineForOp, AffineParallelOp, AffineIfOp>(parentOp); 197 } 198 199 /// Affine regions should be analyzed recursively. 200 bool shouldAnalyzeRecursively(Operation *op) const final { return true; } 201 }; 202 } // namespace 203 204 //===----------------------------------------------------------------------===// 205 // AffineDialect 206 //===----------------------------------------------------------------------===// 207 208 void AffineDialect::initialize() { 209 addOperations<AffineDmaStartOp, AffineDmaWaitOp, 210 #define GET_OP_LIST 211 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc" 212 >(); 213 addInterfaces<AffineInlinerInterface>(); 214 } 215 216 /// Materialize a single constant operation from a given attribute value with 217 /// the desired resultant type. 218 Operation *AffineDialect::materializeConstant(OpBuilder &builder, 219 Attribute value, Type type, 220 Location loc) { 221 return builder.create<arith::ConstantOp>(loc, type, value); 222 } 223 224 /// A utility function to check if a value is defined at the top level of an 225 /// op with trait `AffineScope`. If the value is defined in an unlinked region, 226 /// conservatively assume it is not top-level. A value of index type defined at 227 /// the top level is always a valid symbol. 228 bool mlir::isTopLevelValue(Value value) { 229 if (auto arg = value.dyn_cast<BlockArgument>()) { 230 // The block owning the argument may be unlinked, e.g. when the surrounding 231 // region has not yet been attached to an Op, at which point the parent Op 232 // is null. 233 Operation *parentOp = arg.getOwner()->getParentOp(); 234 return parentOp && parentOp->hasTrait<OpTrait::AffineScope>(); 235 } 236 // The defining Op may live in an unlinked block so its parent Op may be null. 237 Operation *parentOp = value.getDefiningOp()->getParentOp(); 238 return parentOp && parentOp->hasTrait<OpTrait::AffineScope>(); 239 } 240 241 /// Returns the closest region enclosing `op` that is held by an operation with 242 /// trait `AffineScope`; `nullptr` if there is no such region. 243 Region *mlir::getAffineScope(Operation *op) { 244 auto *curOp = op; 245 while (auto *parentOp = curOp->getParentOp()) { 246 if (parentOp->hasTrait<OpTrait::AffineScope>()) 247 return curOp->getParentRegion(); 248 curOp = parentOp; 249 } 250 return nullptr; 251 } 252 253 // A Value can be used as a dimension id iff it meets one of the following 254 // conditions: 255 // *) It is valid as a symbol. 256 // *) It is an induction variable. 257 // *) It is the result of affine apply operation with dimension id arguments. 258 bool mlir::isValidDim(Value value) { 259 // The value must be an index type. 260 if (!value.getType().isIndex()) 261 return false; 262 263 if (auto *defOp = value.getDefiningOp()) 264 return isValidDim(value, getAffineScope(defOp)); 265 266 // This value has to be a block argument for an op that has the 267 // `AffineScope` trait or for an affine.for or affine.parallel. 268 auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp(); 269 return parentOp && (parentOp->hasTrait<OpTrait::AffineScope>() || 270 isa<AffineForOp, AffineParallelOp>(parentOp)); 271 } 272 273 // Value can be used as a dimension id iff it meets one of the following 274 // conditions: 275 // *) It is valid as a symbol. 276 // *) It is an induction variable. 277 // *) It is the result of an affine apply operation with dimension id operands. 278 bool mlir::isValidDim(Value value, Region *region) { 279 // The value must be an index type. 280 if (!value.getType().isIndex()) 281 return false; 282 283 // All valid symbols are okay. 284 if (isValidSymbol(value, region)) 285 return true; 286 287 auto *op = value.getDefiningOp(); 288 if (!op) { 289 // This value has to be a block argument for an affine.for or an 290 // affine.parallel. 291 auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp(); 292 return isa<AffineForOp, AffineParallelOp>(parentOp); 293 } 294 295 // Affine apply operation is ok if all of its operands are ok. 296 if (auto applyOp = dyn_cast<AffineApplyOp>(op)) 297 return applyOp.isValidDim(region); 298 // The dim op is okay if its operand memref/tensor is defined at the top 299 // level. 300 if (auto dimOp = dyn_cast<memref::DimOp>(op)) 301 return isTopLevelValue(dimOp.source()); 302 if (auto dimOp = dyn_cast<tensor::DimOp>(op)) 303 return isTopLevelValue(dimOp.source()); 304 return false; 305 } 306 307 /// Returns true if the 'index' dimension of the `memref` defined by 308 /// `memrefDefOp` is a statically shaped one or defined using a valid symbol 309 /// for `region`. 310 template <typename AnyMemRefDefOp> 311 static bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, unsigned index, 312 Region *region) { 313 auto memRefType = memrefDefOp.getType(); 314 // Statically shaped. 315 if (!memRefType.isDynamicDim(index)) 316 return true; 317 // Get the position of the dimension among dynamic dimensions; 318 unsigned dynamicDimPos = memRefType.getDynamicDimIndex(index); 319 return isValidSymbol(*(memrefDefOp.getDynamicSizes().begin() + dynamicDimPos), 320 region); 321 } 322 323 /// Returns true if the result of the dim op is a valid symbol for `region`. 324 template <typename OpTy> 325 static bool isDimOpValidSymbol(OpTy dimOp, Region *region) { 326 // The dim op is okay if its source is defined at the top level. 327 if (isTopLevelValue(dimOp.source())) 328 return true; 329 330 // Conservatively handle remaining BlockArguments as non-valid symbols. 331 // E.g. scf.for iterArgs. 332 if (dimOp.source().template isa<BlockArgument>()) 333 return false; 334 335 // The dim op is also okay if its operand memref is a view/subview whose 336 // corresponding size is a valid symbol. 337 Optional<int64_t> index = dimOp.getConstantIndex(); 338 assert(index.hasValue() && 339 "expect only `dim` operations with a constant index"); 340 int64_t i = index.getValue(); 341 return TypeSwitch<Operation *, bool>(dimOp.source().getDefiningOp()) 342 .Case<memref::ViewOp, memref::SubViewOp, memref::AllocOp>( 343 [&](auto op) { return isMemRefSizeValidSymbol(op, i, region); }) 344 .Default([](Operation *) { return false; }); 345 } 346 347 // A value can be used as a symbol (at all its use sites) iff it meets one of 348 // the following conditions: 349 // *) It is a constant. 350 // *) Its defining op or block arg appearance is immediately enclosed by an op 351 // with `AffineScope` trait. 352 // *) It is the result of an affine.apply operation with symbol operands. 353 // *) It is a result of the dim op on a memref whose corresponding size is a 354 // valid symbol. 355 bool mlir::isValidSymbol(Value value) { 356 if (!value) 357 return false; 358 359 // The value must be an index type. 360 if (!value.getType().isIndex()) 361 return false; 362 363 // Check that the value is a top level value. 364 if (isTopLevelValue(value)) 365 return true; 366 367 if (auto *defOp = value.getDefiningOp()) 368 return isValidSymbol(value, getAffineScope(defOp)); 369 370 return false; 371 } 372 373 /// A value can be used as a symbol for `region` iff it meets one of the 374 /// following conditions: 375 /// *) It is a constant. 376 /// *) It is the result of an affine apply operation with symbol arguments. 377 /// *) It is a result of the dim op on a memref whose corresponding size is 378 /// a valid symbol. 379 /// *) It is defined at the top level of 'region' or is its argument. 380 /// *) It dominates `region`'s parent op. 381 /// If `region` is null, conservatively assume the symbol definition scope does 382 /// not exist and only accept the values that would be symbols regardless of 383 /// the surrounding region structure, i.e. the first three cases above. 384 bool mlir::isValidSymbol(Value value, Region *region) { 385 // The value must be an index type. 386 if (!value.getType().isIndex()) 387 return false; 388 389 // A top-level value is a valid symbol. 390 if (region && ::isTopLevelValue(value, region)) 391 return true; 392 393 auto *defOp = value.getDefiningOp(); 394 if (!defOp) { 395 // A block argument that is not a top-level value is a valid symbol if it 396 // dominates region's parent op. 397 Operation *regionOp = region ? region->getParentOp() : nullptr; 398 if (regionOp && !regionOp->hasTrait<OpTrait::IsIsolatedFromAbove>()) 399 if (auto *parentOpRegion = region->getParentOp()->getParentRegion()) 400 return isValidSymbol(value, parentOpRegion); 401 return false; 402 } 403 404 // Constant operation is ok. 405 Attribute operandCst; 406 if (matchPattern(defOp, m_Constant(&operandCst))) 407 return true; 408 409 // Affine apply operation is ok if all of its operands are ok. 410 if (auto applyOp = dyn_cast<AffineApplyOp>(defOp)) 411 return applyOp.isValidSymbol(region); 412 413 // Dim op results could be valid symbols at any level. 414 if (auto dimOp = dyn_cast<memref::DimOp>(defOp)) 415 return isDimOpValidSymbol(dimOp, region); 416 if (auto dimOp = dyn_cast<tensor::DimOp>(defOp)) 417 return isDimOpValidSymbol(dimOp, region); 418 419 // Check for values dominating `region`'s parent op. 420 Operation *regionOp = region ? region->getParentOp() : nullptr; 421 if (regionOp && !regionOp->hasTrait<OpTrait::IsIsolatedFromAbove>()) 422 if (auto *parentRegion = region->getParentOp()->getParentRegion()) 423 return isValidSymbol(value, parentRegion); 424 425 return false; 426 } 427 428 // Returns true if 'value' is a valid index to an affine operation (e.g. 429 // affine.load, affine.store, affine.dma_start, affine.dma_wait) where 430 // `region` provides the polyhedral symbol scope. Returns false otherwise. 431 static bool isValidAffineIndexOperand(Value value, Region *region) { 432 return isValidDim(value, region) || isValidSymbol(value, region); 433 } 434 435 /// Prints dimension and symbol list. 436 static void printDimAndSymbolList(Operation::operand_iterator begin, 437 Operation::operand_iterator end, 438 unsigned numDims, OpAsmPrinter &printer) { 439 OperandRange operands(begin, end); 440 printer << '(' << operands.take_front(numDims) << ')'; 441 if (operands.size() > numDims) 442 printer << '[' << operands.drop_front(numDims) << ']'; 443 } 444 445 /// Parses dimension and symbol list and returns true if parsing failed. 446 ParseResult mlir::parseDimAndSymbolList(OpAsmParser &parser, 447 SmallVectorImpl<Value> &operands, 448 unsigned &numDims) { 449 SmallVector<OpAsmParser::OperandType, 8> opInfos; 450 if (parser.parseOperandList(opInfos, OpAsmParser::Delimiter::Paren)) 451 return failure(); 452 // Store number of dimensions for validation by caller. 453 numDims = opInfos.size(); 454 455 // Parse the optional symbol operands. 456 auto indexTy = parser.getBuilder().getIndexType(); 457 return failure(parser.parseOperandList( 458 opInfos, OpAsmParser::Delimiter::OptionalSquare) || 459 parser.resolveOperands(opInfos, indexTy, operands)); 460 } 461 462 /// Utility function to verify that a set of operands are valid dimension and 463 /// symbol identifiers. The operands should be laid out such that the dimension 464 /// operands are before the symbol operands. This function returns failure if 465 /// there was an invalid operand. An operation is provided to emit any necessary 466 /// errors. 467 template <typename OpTy> 468 static LogicalResult 469 verifyDimAndSymbolIdentifiers(OpTy &op, Operation::operand_range operands, 470 unsigned numDims) { 471 unsigned opIt = 0; 472 for (auto operand : operands) { 473 if (opIt++ < numDims) { 474 if (!isValidDim(operand, getAffineScope(op))) 475 return op.emitOpError("operand cannot be used as a dimension id"); 476 } else if (!isValidSymbol(operand, getAffineScope(op))) { 477 return op.emitOpError("operand cannot be used as a symbol"); 478 } 479 } 480 return success(); 481 } 482 483 //===----------------------------------------------------------------------===// 484 // AffineApplyOp 485 //===----------------------------------------------------------------------===// 486 487 AffineValueMap AffineApplyOp::getAffineValueMap() { 488 return AffineValueMap(getAffineMap(), getOperands(), getResult()); 489 } 490 491 ParseResult AffineApplyOp::parse(OpAsmParser &parser, OperationState &result) { 492 auto &builder = parser.getBuilder(); 493 auto indexTy = builder.getIndexType(); 494 495 AffineMapAttr mapAttr; 496 unsigned numDims; 497 if (parser.parseAttribute(mapAttr, "map", result.attributes) || 498 parseDimAndSymbolList(parser, result.operands, numDims) || 499 parser.parseOptionalAttrDict(result.attributes)) 500 return failure(); 501 auto map = mapAttr.getValue(); 502 503 if (map.getNumDims() != numDims || 504 numDims + map.getNumSymbols() != result.operands.size()) { 505 return parser.emitError(parser.getNameLoc(), 506 "dimension or symbol index mismatch"); 507 } 508 509 result.types.append(map.getNumResults(), indexTy); 510 return success(); 511 } 512 513 void AffineApplyOp::print(OpAsmPrinter &p) { 514 p << " " << mapAttr(); 515 printDimAndSymbolList(operand_begin(), operand_end(), 516 getAffineMap().getNumDims(), p); 517 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"map"}); 518 } 519 520 LogicalResult AffineApplyOp::verify() { 521 // Check input and output dimensions match. 522 AffineMap affineMap = map(); 523 524 // Verify that operand count matches affine map dimension and symbol count. 525 if (getNumOperands() != affineMap.getNumDims() + affineMap.getNumSymbols()) 526 return emitOpError( 527 "operand count and affine map dimension and symbol count must match"); 528 529 // Verify that the map only produces one result. 530 if (affineMap.getNumResults() != 1) 531 return emitOpError("mapping must produce one value"); 532 533 return success(); 534 } 535 536 // The result of the affine apply operation can be used as a dimension id if all 537 // its operands are valid dimension ids. 538 bool AffineApplyOp::isValidDim() { 539 return llvm::all_of(getOperands(), 540 [](Value op) { return mlir::isValidDim(op); }); 541 } 542 543 // The result of the affine apply operation can be used as a dimension id if all 544 // its operands are valid dimension ids with the parent operation of `region` 545 // defining the polyhedral scope for symbols. 546 bool AffineApplyOp::isValidDim(Region *region) { 547 return llvm::all_of(getOperands(), 548 [&](Value op) { return ::isValidDim(op, region); }); 549 } 550 551 // The result of the affine apply operation can be used as a symbol if all its 552 // operands are symbols. 553 bool AffineApplyOp::isValidSymbol() { 554 return llvm::all_of(getOperands(), 555 [](Value op) { return mlir::isValidSymbol(op); }); 556 } 557 558 // The result of the affine apply operation can be used as a symbol in `region` 559 // if all its operands are symbols in `region`. 560 bool AffineApplyOp::isValidSymbol(Region *region) { 561 return llvm::all_of(getOperands(), [&](Value operand) { 562 return mlir::isValidSymbol(operand, region); 563 }); 564 } 565 566 OpFoldResult AffineApplyOp::fold(ArrayRef<Attribute> operands) { 567 auto map = getAffineMap(); 568 569 // Fold dims and symbols to existing values. 570 auto expr = map.getResult(0); 571 if (auto dim = expr.dyn_cast<AffineDimExpr>()) 572 return getOperand(dim.getPosition()); 573 if (auto sym = expr.dyn_cast<AffineSymbolExpr>()) 574 return getOperand(map.getNumDims() + sym.getPosition()); 575 576 // Otherwise, default to folding the map. 577 SmallVector<Attribute, 1> result; 578 if (failed(map.constantFold(operands, result))) 579 return {}; 580 return result[0]; 581 } 582 583 /// Replace all occurrences of AffineExpr at position `pos` in `map` by the 584 /// defining AffineApplyOp expression and operands. 585 /// When `dimOrSymbolPosition < dims.size()`, AffineDimExpr@[pos] is replaced. 586 /// When `dimOrSymbolPosition >= dims.size()`, 587 /// AffineSymbolExpr@[pos - dims.size()] is replaced. 588 /// Mutate `map`,`dims` and `syms` in place as follows: 589 /// 1. `dims` and `syms` are only appended to. 590 /// 2. `map` dim and symbols are gradually shifted to higer positions. 591 /// 3. Old `dim` and `sym` entries are replaced by nullptr 592 /// This avoids the need for any bookkeeping. 593 static LogicalResult replaceDimOrSym(AffineMap *map, 594 unsigned dimOrSymbolPosition, 595 SmallVectorImpl<Value> &dims, 596 SmallVectorImpl<Value> &syms) { 597 bool isDimReplacement = (dimOrSymbolPosition < dims.size()); 598 unsigned pos = isDimReplacement ? dimOrSymbolPosition 599 : dimOrSymbolPosition - dims.size(); 600 Value &v = isDimReplacement ? dims[pos] : syms[pos]; 601 if (!v) 602 return failure(); 603 604 auto affineApply = v.getDefiningOp<AffineApplyOp>(); 605 if (!affineApply) 606 return failure(); 607 608 // At this point we will perform a replacement of `v`, set the entry in `dim` 609 // or `sym` to nullptr immediately. 610 v = nullptr; 611 612 // Compute the map, dims and symbols coming from the AffineApplyOp. 613 AffineMap composeMap = affineApply.getAffineMap(); 614 assert(composeMap.getNumResults() == 1 && "affine.apply with >1 results"); 615 AffineExpr composeExpr = 616 composeMap.shiftDims(dims.size()).shiftSymbols(syms.size()).getResult(0); 617 ValueRange composeDims = 618 affineApply.getMapOperands().take_front(composeMap.getNumDims()); 619 ValueRange composeSyms = 620 affineApply.getMapOperands().take_back(composeMap.getNumSymbols()); 621 622 // Append the dims and symbols where relevant and perform the replacement. 623 MLIRContext *ctx = map->getContext(); 624 AffineExpr toReplace = isDimReplacement ? getAffineDimExpr(pos, ctx) 625 : getAffineSymbolExpr(pos, ctx); 626 dims.append(composeDims.begin(), composeDims.end()); 627 syms.append(composeSyms.begin(), composeSyms.end()); 628 *map = map->replace(toReplace, composeExpr, dims.size(), syms.size()); 629 630 return success(); 631 } 632 633 /// Iterate over `operands` and fold away all those produced by an AffineApplyOp 634 /// iteratively. Perform canonicalization of map and operands as well as 635 /// AffineMap simplification. `map` and `operands` are mutated in place. 636 static void composeAffineMapAndOperands(AffineMap *map, 637 SmallVectorImpl<Value> *operands) { 638 if (map->getNumResults() == 0) { 639 canonicalizeMapAndOperands(map, operands); 640 *map = simplifyAffineMap(*map); 641 return; 642 } 643 644 MLIRContext *ctx = map->getContext(); 645 SmallVector<Value, 4> dims(operands->begin(), 646 operands->begin() + map->getNumDims()); 647 SmallVector<Value, 4> syms(operands->begin() + map->getNumDims(), 648 operands->end()); 649 650 // Iterate over dims and symbols coming from AffineApplyOp and replace until 651 // exhaustion. This iteratively mutates `map`, `dims` and `syms`. Both `dims` 652 // and `syms` can only increase by construction. 653 // The implementation uses a `while` loop to support the case of symbols 654 // that may be constructed from dims ;this may be overkill. 655 while (true) { 656 bool changed = false; 657 for (unsigned pos = 0; pos != dims.size() + syms.size(); ++pos) 658 if ((changed |= succeeded(replaceDimOrSym(map, pos, dims, syms)))) 659 break; 660 if (!changed) 661 break; 662 } 663 664 // Clear operands so we can fill them anew. 665 operands->clear(); 666 667 // At this point we may have introduced null operands, prune them out before 668 // canonicalizing map and operands. 669 unsigned nDims = 0, nSyms = 0; 670 SmallVector<AffineExpr, 4> dimReplacements, symReplacements; 671 dimReplacements.reserve(dims.size()); 672 symReplacements.reserve(syms.size()); 673 for (auto *container : {&dims, &syms}) { 674 bool isDim = (container == &dims); 675 auto &repls = isDim ? dimReplacements : symReplacements; 676 for (const auto &en : llvm::enumerate(*container)) { 677 Value v = en.value(); 678 if (!v) { 679 assert(isDim ? !map->isFunctionOfDim(en.index()) 680 : !map->isFunctionOfSymbol(en.index()) && 681 "map is function of unexpected expr@pos"); 682 repls.push_back(getAffineConstantExpr(0, ctx)); 683 continue; 684 } 685 repls.push_back(isDim ? getAffineDimExpr(nDims++, ctx) 686 : getAffineSymbolExpr(nSyms++, ctx)); 687 operands->push_back(v); 688 } 689 } 690 *map = map->replaceDimsAndSymbols(dimReplacements, symReplacements, nDims, 691 nSyms); 692 693 // Canonicalize and simplify before returning. 694 canonicalizeMapAndOperands(map, operands); 695 *map = simplifyAffineMap(*map); 696 } 697 698 void mlir::fullyComposeAffineMapAndOperands(AffineMap *map, 699 SmallVectorImpl<Value> *operands) { 700 while (llvm::any_of(*operands, [](Value v) { 701 return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp()); 702 })) { 703 composeAffineMapAndOperands(map, operands); 704 } 705 } 706 707 AffineApplyOp mlir::makeComposedAffineApply(OpBuilder &b, Location loc, 708 AffineMap map, 709 ValueRange operands) { 710 AffineMap normalizedMap = map; 711 SmallVector<Value, 8> normalizedOperands(operands.begin(), operands.end()); 712 composeAffineMapAndOperands(&normalizedMap, &normalizedOperands); 713 assert(normalizedMap); 714 return b.create<AffineApplyOp>(loc, normalizedMap, normalizedOperands); 715 } 716 717 AffineApplyOp mlir::makeComposedAffineApply(OpBuilder &b, Location loc, 718 AffineExpr e, ValueRange values) { 719 return makeComposedAffineApply( 720 b, loc, AffineMap::inferFromExprList(ArrayRef<AffineExpr>{e}).front(), 721 values); 722 } 723 724 /// Fully compose map with operands and canonicalize the result. 725 /// Return the `createOrFold`'ed AffineApply op. 726 static Value createFoldedComposedAffineApply(OpBuilder &b, Location loc, 727 AffineMap map, 728 ValueRange operandsRef) { 729 SmallVector<Value, 4> operands(operandsRef.begin(), operandsRef.end()); 730 fullyComposeAffineMapAndOperands(&map, &operands); 731 canonicalizeMapAndOperands(&map, &operands); 732 return b.createOrFold<AffineApplyOp>(loc, map, operands); 733 } 734 735 SmallVector<Value, 4> mlir::applyMapToValues(OpBuilder &b, Location loc, 736 AffineMap map, ValueRange values) { 737 SmallVector<Value, 4> res; 738 res.reserve(map.getNumResults()); 739 unsigned numDims = map.getNumDims(), numSym = map.getNumSymbols(); 740 // For each `expr` in `map`, applies the `expr` to the values extracted from 741 // ranges. If the resulting application can be folded into a Value, the 742 // folding occurs eagerly. 743 for (auto expr : map.getResults()) { 744 AffineMap map = AffineMap::get(numDims, numSym, expr); 745 res.push_back(createFoldedComposedAffineApply(b, loc, map, values)); 746 } 747 return res; 748 } 749 750 // A symbol may appear as a dim in affine.apply operations. This function 751 // canonicalizes dims that are valid symbols into actual symbols. 752 template <class MapOrSet> 753 static void canonicalizePromotedSymbols(MapOrSet *mapOrSet, 754 SmallVectorImpl<Value> *operands) { 755 if (!mapOrSet || operands->empty()) 756 return; 757 758 assert(mapOrSet->getNumInputs() == operands->size() && 759 "map/set inputs must match number of operands"); 760 761 auto *context = mapOrSet->getContext(); 762 SmallVector<Value, 8> resultOperands; 763 resultOperands.reserve(operands->size()); 764 SmallVector<Value, 8> remappedSymbols; 765 remappedSymbols.reserve(operands->size()); 766 unsigned nextDim = 0; 767 unsigned nextSym = 0; 768 unsigned oldNumSyms = mapOrSet->getNumSymbols(); 769 SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims()); 770 for (unsigned i = 0, e = mapOrSet->getNumInputs(); i != e; ++i) { 771 if (i < mapOrSet->getNumDims()) { 772 if (isValidSymbol((*operands)[i])) { 773 // This is a valid symbol that appears as a dim, canonicalize it. 774 dimRemapping[i] = getAffineSymbolExpr(oldNumSyms + nextSym++, context); 775 remappedSymbols.push_back((*operands)[i]); 776 } else { 777 dimRemapping[i] = getAffineDimExpr(nextDim++, context); 778 resultOperands.push_back((*operands)[i]); 779 } 780 } else { 781 resultOperands.push_back((*operands)[i]); 782 } 783 } 784 785 resultOperands.append(remappedSymbols.begin(), remappedSymbols.end()); 786 *operands = resultOperands; 787 *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, {}, nextDim, 788 oldNumSyms + nextSym); 789 790 assert(mapOrSet->getNumInputs() == operands->size() && 791 "map/set inputs must match number of operands"); 792 } 793 794 // Works for either an affine map or an integer set. 795 template <class MapOrSet> 796 static void canonicalizeMapOrSetAndOperands(MapOrSet *mapOrSet, 797 SmallVectorImpl<Value> *operands) { 798 static_assert(llvm::is_one_of<MapOrSet, AffineMap, IntegerSet>::value, 799 "Argument must be either of AffineMap or IntegerSet type"); 800 801 if (!mapOrSet || operands->empty()) 802 return; 803 804 assert(mapOrSet->getNumInputs() == operands->size() && 805 "map/set inputs must match number of operands"); 806 807 canonicalizePromotedSymbols<MapOrSet>(mapOrSet, operands); 808 809 // Check to see what dims are used. 810 llvm::SmallBitVector usedDims(mapOrSet->getNumDims()); 811 llvm::SmallBitVector usedSyms(mapOrSet->getNumSymbols()); 812 mapOrSet->walkExprs([&](AffineExpr expr) { 813 if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) 814 usedDims[dimExpr.getPosition()] = true; 815 else if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) 816 usedSyms[symExpr.getPosition()] = true; 817 }); 818 819 auto *context = mapOrSet->getContext(); 820 821 SmallVector<Value, 8> resultOperands; 822 resultOperands.reserve(operands->size()); 823 824 llvm::SmallDenseMap<Value, AffineExpr, 8> seenDims; 825 SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims()); 826 unsigned nextDim = 0; 827 for (unsigned i = 0, e = mapOrSet->getNumDims(); i != e; ++i) { 828 if (usedDims[i]) { 829 // Remap dim positions for duplicate operands. 830 auto it = seenDims.find((*operands)[i]); 831 if (it == seenDims.end()) { 832 dimRemapping[i] = getAffineDimExpr(nextDim++, context); 833 resultOperands.push_back((*operands)[i]); 834 seenDims.insert(std::make_pair((*operands)[i], dimRemapping[i])); 835 } else { 836 dimRemapping[i] = it->second; 837 } 838 } 839 } 840 llvm::SmallDenseMap<Value, AffineExpr, 8> seenSymbols; 841 SmallVector<AffineExpr, 8> symRemapping(mapOrSet->getNumSymbols()); 842 unsigned nextSym = 0; 843 for (unsigned i = 0, e = mapOrSet->getNumSymbols(); i != e; ++i) { 844 if (!usedSyms[i]) 845 continue; 846 // Handle constant operands (only needed for symbolic operands since 847 // constant operands in dimensional positions would have already been 848 // promoted to symbolic positions above). 849 IntegerAttr operandCst; 850 if (matchPattern((*operands)[i + mapOrSet->getNumDims()], 851 m_Constant(&operandCst))) { 852 symRemapping[i] = 853 getAffineConstantExpr(operandCst.getValue().getSExtValue(), context); 854 continue; 855 } 856 // Remap symbol positions for duplicate operands. 857 auto it = seenSymbols.find((*operands)[i + mapOrSet->getNumDims()]); 858 if (it == seenSymbols.end()) { 859 symRemapping[i] = getAffineSymbolExpr(nextSym++, context); 860 resultOperands.push_back((*operands)[i + mapOrSet->getNumDims()]); 861 seenSymbols.insert(std::make_pair((*operands)[i + mapOrSet->getNumDims()], 862 symRemapping[i])); 863 } else { 864 symRemapping[i] = it->second; 865 } 866 } 867 *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, symRemapping, 868 nextDim, nextSym); 869 *operands = resultOperands; 870 } 871 872 void mlir::canonicalizeMapAndOperands(AffineMap *map, 873 SmallVectorImpl<Value> *operands) { 874 canonicalizeMapOrSetAndOperands<AffineMap>(map, operands); 875 } 876 877 void mlir::canonicalizeSetAndOperands(IntegerSet *set, 878 SmallVectorImpl<Value> *operands) { 879 canonicalizeMapOrSetAndOperands<IntegerSet>(set, operands); 880 } 881 882 namespace { 883 /// Simplify AffineApply, AffineLoad, and AffineStore operations by composing 884 /// maps that supply results into them. 885 /// 886 template <typename AffineOpTy> 887 struct SimplifyAffineOp : public OpRewritePattern<AffineOpTy> { 888 using OpRewritePattern<AffineOpTy>::OpRewritePattern; 889 890 /// Replace the affine op with another instance of it with the supplied 891 /// map and mapOperands. 892 void replaceAffineOp(PatternRewriter &rewriter, AffineOpTy affineOp, 893 AffineMap map, ArrayRef<Value> mapOperands) const; 894 895 LogicalResult matchAndRewrite(AffineOpTy affineOp, 896 PatternRewriter &rewriter) const override { 897 static_assert( 898 llvm::is_one_of<AffineOpTy, AffineLoadOp, AffinePrefetchOp, 899 AffineStoreOp, AffineApplyOp, AffineMinOp, AffineMaxOp, 900 AffineVectorStoreOp, AffineVectorLoadOp>::value, 901 "affine load/store/vectorstore/vectorload/apply/prefetch/min/max op " 902 "expected"); 903 auto map = affineOp.getAffineMap(); 904 AffineMap oldMap = map; 905 auto oldOperands = affineOp.getMapOperands(); 906 SmallVector<Value, 8> resultOperands(oldOperands); 907 composeAffineMapAndOperands(&map, &resultOperands); 908 canonicalizeMapAndOperands(&map, &resultOperands); 909 if (map == oldMap && std::equal(oldOperands.begin(), oldOperands.end(), 910 resultOperands.begin())) 911 return failure(); 912 913 replaceAffineOp(rewriter, affineOp, map, resultOperands); 914 return success(); 915 } 916 }; 917 918 // Specialize the template to account for the different build signatures for 919 // affine load, store, and apply ops. 920 template <> 921 void SimplifyAffineOp<AffineLoadOp>::replaceAffineOp( 922 PatternRewriter &rewriter, AffineLoadOp load, AffineMap map, 923 ArrayRef<Value> mapOperands) const { 924 rewriter.replaceOpWithNewOp<AffineLoadOp>(load, load.getMemRef(), map, 925 mapOperands); 926 } 927 template <> 928 void SimplifyAffineOp<AffinePrefetchOp>::replaceAffineOp( 929 PatternRewriter &rewriter, AffinePrefetchOp prefetch, AffineMap map, 930 ArrayRef<Value> mapOperands) const { 931 rewriter.replaceOpWithNewOp<AffinePrefetchOp>( 932 prefetch, prefetch.memref(), map, mapOperands, prefetch.localityHint(), 933 prefetch.isWrite(), prefetch.isDataCache()); 934 } 935 template <> 936 void SimplifyAffineOp<AffineStoreOp>::replaceAffineOp( 937 PatternRewriter &rewriter, AffineStoreOp store, AffineMap map, 938 ArrayRef<Value> mapOperands) const { 939 rewriter.replaceOpWithNewOp<AffineStoreOp>( 940 store, store.getValueToStore(), store.getMemRef(), map, mapOperands); 941 } 942 template <> 943 void SimplifyAffineOp<AffineVectorLoadOp>::replaceAffineOp( 944 PatternRewriter &rewriter, AffineVectorLoadOp vectorload, AffineMap map, 945 ArrayRef<Value> mapOperands) const { 946 rewriter.replaceOpWithNewOp<AffineVectorLoadOp>( 947 vectorload, vectorload.getVectorType(), vectorload.getMemRef(), map, 948 mapOperands); 949 } 950 template <> 951 void SimplifyAffineOp<AffineVectorStoreOp>::replaceAffineOp( 952 PatternRewriter &rewriter, AffineVectorStoreOp vectorstore, AffineMap map, 953 ArrayRef<Value> mapOperands) const { 954 rewriter.replaceOpWithNewOp<AffineVectorStoreOp>( 955 vectorstore, vectorstore.getValueToStore(), vectorstore.getMemRef(), map, 956 mapOperands); 957 } 958 959 // Generic version for ops that don't have extra operands. 960 template <typename AffineOpTy> 961 void SimplifyAffineOp<AffineOpTy>::replaceAffineOp( 962 PatternRewriter &rewriter, AffineOpTy op, AffineMap map, 963 ArrayRef<Value> mapOperands) const { 964 rewriter.replaceOpWithNewOp<AffineOpTy>(op, map, mapOperands); 965 } 966 } // namespace 967 968 void AffineApplyOp::getCanonicalizationPatterns(RewritePatternSet &results, 969 MLIRContext *context) { 970 results.add<SimplifyAffineOp<AffineApplyOp>>(context); 971 } 972 973 //===----------------------------------------------------------------------===// 974 // Common canonicalization pattern support logic 975 //===----------------------------------------------------------------------===// 976 977 /// This is a common class used for patterns of the form 978 /// "someop(memrefcast) -> someop". It folds the source of any memref.cast 979 /// into the root operation directly. 980 static LogicalResult foldMemRefCast(Operation *op, Value ignore = nullptr) { 981 bool folded = false; 982 for (OpOperand &operand : op->getOpOperands()) { 983 auto cast = operand.get().getDefiningOp<memref::CastOp>(); 984 if (cast && operand.get() != ignore && 985 !cast.getOperand().getType().isa<UnrankedMemRefType>()) { 986 operand.set(cast.getOperand()); 987 folded = true; 988 } 989 } 990 return success(folded); 991 } 992 993 //===----------------------------------------------------------------------===// 994 // AffineDmaStartOp 995 //===----------------------------------------------------------------------===// 996 997 // TODO: Check that map operands are loop IVs or symbols. 998 void AffineDmaStartOp::build(OpBuilder &builder, OperationState &result, 999 Value srcMemRef, AffineMap srcMap, 1000 ValueRange srcIndices, Value destMemRef, 1001 AffineMap dstMap, ValueRange destIndices, 1002 Value tagMemRef, AffineMap tagMap, 1003 ValueRange tagIndices, Value numElements, 1004 Value stride, Value elementsPerStride) { 1005 result.addOperands(srcMemRef); 1006 result.addAttribute(getSrcMapAttrName(), AffineMapAttr::get(srcMap)); 1007 result.addOperands(srcIndices); 1008 result.addOperands(destMemRef); 1009 result.addAttribute(getDstMapAttrName(), AffineMapAttr::get(dstMap)); 1010 result.addOperands(destIndices); 1011 result.addOperands(tagMemRef); 1012 result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap)); 1013 result.addOperands(tagIndices); 1014 result.addOperands(numElements); 1015 if (stride) { 1016 result.addOperands({stride, elementsPerStride}); 1017 } 1018 } 1019 1020 void AffineDmaStartOp::print(OpAsmPrinter &p) { 1021 p << " " << getSrcMemRef() << '['; 1022 p.printAffineMapOfSSAIds(getSrcMapAttr(), getSrcIndices()); 1023 p << "], " << getDstMemRef() << '['; 1024 p.printAffineMapOfSSAIds(getDstMapAttr(), getDstIndices()); 1025 p << "], " << getTagMemRef() << '['; 1026 p.printAffineMapOfSSAIds(getTagMapAttr(), getTagIndices()); 1027 p << "], " << getNumElements(); 1028 if (isStrided()) { 1029 p << ", " << getStride(); 1030 p << ", " << getNumElementsPerStride(); 1031 } 1032 p << " : " << getSrcMemRefType() << ", " << getDstMemRefType() << ", " 1033 << getTagMemRefType(); 1034 } 1035 1036 // Parse AffineDmaStartOp. 1037 // Ex: 1038 // affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %size, 1039 // %stride, %num_elt_per_stride 1040 // : memref<3076 x f32, 0>, memref<1024 x f32, 2>, memref<1 x i32> 1041 // 1042 ParseResult AffineDmaStartOp::parse(OpAsmParser &parser, 1043 OperationState &result) { 1044 OpAsmParser::OperandType srcMemRefInfo; 1045 AffineMapAttr srcMapAttr; 1046 SmallVector<OpAsmParser::OperandType, 4> srcMapOperands; 1047 OpAsmParser::OperandType dstMemRefInfo; 1048 AffineMapAttr dstMapAttr; 1049 SmallVector<OpAsmParser::OperandType, 4> dstMapOperands; 1050 OpAsmParser::OperandType tagMemRefInfo; 1051 AffineMapAttr tagMapAttr; 1052 SmallVector<OpAsmParser::OperandType, 4> tagMapOperands; 1053 OpAsmParser::OperandType numElementsInfo; 1054 SmallVector<OpAsmParser::OperandType, 2> strideInfo; 1055 1056 SmallVector<Type, 3> types; 1057 auto indexType = parser.getBuilder().getIndexType(); 1058 1059 // Parse and resolve the following list of operands: 1060 // *) dst memref followed by its affine maps operands (in square brackets). 1061 // *) src memref followed by its affine map operands (in square brackets). 1062 // *) tag memref followed by its affine map operands (in square brackets). 1063 // *) number of elements transferred by DMA operation. 1064 if (parser.parseOperand(srcMemRefInfo) || 1065 parser.parseAffineMapOfSSAIds(srcMapOperands, srcMapAttr, 1066 getSrcMapAttrName(), result.attributes) || 1067 parser.parseComma() || parser.parseOperand(dstMemRefInfo) || 1068 parser.parseAffineMapOfSSAIds(dstMapOperands, dstMapAttr, 1069 getDstMapAttrName(), result.attributes) || 1070 parser.parseComma() || parser.parseOperand(tagMemRefInfo) || 1071 parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr, 1072 getTagMapAttrName(), result.attributes) || 1073 parser.parseComma() || parser.parseOperand(numElementsInfo)) 1074 return failure(); 1075 1076 // Parse optional stride and elements per stride. 1077 if (parser.parseTrailingOperandList(strideInfo)) { 1078 return failure(); 1079 } 1080 if (!strideInfo.empty() && strideInfo.size() != 2) { 1081 return parser.emitError(parser.getNameLoc(), 1082 "expected two stride related operands"); 1083 } 1084 bool isStrided = strideInfo.size() == 2; 1085 1086 if (parser.parseColonTypeList(types)) 1087 return failure(); 1088 1089 if (types.size() != 3) 1090 return parser.emitError(parser.getNameLoc(), "expected three types"); 1091 1092 if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) || 1093 parser.resolveOperands(srcMapOperands, indexType, result.operands) || 1094 parser.resolveOperand(dstMemRefInfo, types[1], result.operands) || 1095 parser.resolveOperands(dstMapOperands, indexType, result.operands) || 1096 parser.resolveOperand(tagMemRefInfo, types[2], result.operands) || 1097 parser.resolveOperands(tagMapOperands, indexType, result.operands) || 1098 parser.resolveOperand(numElementsInfo, indexType, result.operands)) 1099 return failure(); 1100 1101 if (isStrided) { 1102 if (parser.resolveOperands(strideInfo, indexType, result.operands)) 1103 return failure(); 1104 } 1105 1106 // Check that src/dst/tag operand counts match their map.numInputs. 1107 if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() || 1108 dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() || 1109 tagMapOperands.size() != tagMapAttr.getValue().getNumInputs()) 1110 return parser.emitError(parser.getNameLoc(), 1111 "memref operand count not equal to map.numInputs"); 1112 return success(); 1113 } 1114 1115 LogicalResult AffineDmaStartOp::verifyInvariantsImpl() { 1116 if (!getOperand(getSrcMemRefOperandIndex()).getType().isa<MemRefType>()) 1117 return emitOpError("expected DMA source to be of memref type"); 1118 if (!getOperand(getDstMemRefOperandIndex()).getType().isa<MemRefType>()) 1119 return emitOpError("expected DMA destination to be of memref type"); 1120 if (!getOperand(getTagMemRefOperandIndex()).getType().isa<MemRefType>()) 1121 return emitOpError("expected DMA tag to be of memref type"); 1122 1123 unsigned numInputsAllMaps = getSrcMap().getNumInputs() + 1124 getDstMap().getNumInputs() + 1125 getTagMap().getNumInputs(); 1126 if (getNumOperands() != numInputsAllMaps + 3 + 1 && 1127 getNumOperands() != numInputsAllMaps + 3 + 1 + 2) { 1128 return emitOpError("incorrect number of operands"); 1129 } 1130 1131 Region *scope = getAffineScope(*this); 1132 for (auto idx : getSrcIndices()) { 1133 if (!idx.getType().isIndex()) 1134 return emitOpError("src index to dma_start must have 'index' type"); 1135 if (!isValidAffineIndexOperand(idx, scope)) 1136 return emitOpError("src index must be a dimension or symbol identifier"); 1137 } 1138 for (auto idx : getDstIndices()) { 1139 if (!idx.getType().isIndex()) 1140 return emitOpError("dst index to dma_start must have 'index' type"); 1141 if (!isValidAffineIndexOperand(idx, scope)) 1142 return emitOpError("dst index must be a dimension or symbol identifier"); 1143 } 1144 for (auto idx : getTagIndices()) { 1145 if (!idx.getType().isIndex()) 1146 return emitOpError("tag index to dma_start must have 'index' type"); 1147 if (!isValidAffineIndexOperand(idx, scope)) 1148 return emitOpError("tag index must be a dimension or symbol identifier"); 1149 } 1150 return success(); 1151 } 1152 1153 LogicalResult AffineDmaStartOp::fold(ArrayRef<Attribute> cstOperands, 1154 SmallVectorImpl<OpFoldResult> &results) { 1155 /// dma_start(memrefcast) -> dma_start 1156 return foldMemRefCast(*this); 1157 } 1158 1159 //===----------------------------------------------------------------------===// 1160 // AffineDmaWaitOp 1161 //===----------------------------------------------------------------------===// 1162 1163 // TODO: Check that map operands are loop IVs or symbols. 1164 void AffineDmaWaitOp::build(OpBuilder &builder, OperationState &result, 1165 Value tagMemRef, AffineMap tagMap, 1166 ValueRange tagIndices, Value numElements) { 1167 result.addOperands(tagMemRef); 1168 result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap)); 1169 result.addOperands(tagIndices); 1170 result.addOperands(numElements); 1171 } 1172 1173 void AffineDmaWaitOp::print(OpAsmPrinter &p) { 1174 p << " " << getTagMemRef() << '['; 1175 SmallVector<Value, 2> operands(getTagIndices()); 1176 p.printAffineMapOfSSAIds(getTagMapAttr(), operands); 1177 p << "], "; 1178 p.printOperand(getNumElements()); 1179 p << " : " << getTagMemRef().getType(); 1180 } 1181 1182 // Parse AffineDmaWaitOp. 1183 // Eg: 1184 // affine.dma_wait %tag[%index], %num_elements 1185 // : memref<1 x i32, (d0) -> (d0), 4> 1186 // 1187 ParseResult AffineDmaWaitOp::parse(OpAsmParser &parser, 1188 OperationState &result) { 1189 OpAsmParser::OperandType tagMemRefInfo; 1190 AffineMapAttr tagMapAttr; 1191 SmallVector<OpAsmParser::OperandType, 2> tagMapOperands; 1192 Type type; 1193 auto indexType = parser.getBuilder().getIndexType(); 1194 OpAsmParser::OperandType numElementsInfo; 1195 1196 // Parse tag memref, its map operands, and dma size. 1197 if (parser.parseOperand(tagMemRefInfo) || 1198 parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr, 1199 getTagMapAttrName(), result.attributes) || 1200 parser.parseComma() || parser.parseOperand(numElementsInfo) || 1201 parser.parseColonType(type) || 1202 parser.resolveOperand(tagMemRefInfo, type, result.operands) || 1203 parser.resolveOperands(tagMapOperands, indexType, result.operands) || 1204 parser.resolveOperand(numElementsInfo, indexType, result.operands)) 1205 return failure(); 1206 1207 if (!type.isa<MemRefType>()) 1208 return parser.emitError(parser.getNameLoc(), 1209 "expected tag to be of memref type"); 1210 1211 if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs()) 1212 return parser.emitError(parser.getNameLoc(), 1213 "tag memref operand count != to map.numInputs"); 1214 return success(); 1215 } 1216 1217 LogicalResult AffineDmaWaitOp::verifyInvariantsImpl() { 1218 if (!getOperand(0).getType().isa<MemRefType>()) 1219 return emitOpError("expected DMA tag to be of memref type"); 1220 Region *scope = getAffineScope(*this); 1221 for (auto idx : getTagIndices()) { 1222 if (!idx.getType().isIndex()) 1223 return emitOpError("index to dma_wait must have 'index' type"); 1224 if (!isValidAffineIndexOperand(idx, scope)) 1225 return emitOpError("index must be a dimension or symbol identifier"); 1226 } 1227 return success(); 1228 } 1229 1230 LogicalResult AffineDmaWaitOp::fold(ArrayRef<Attribute> cstOperands, 1231 SmallVectorImpl<OpFoldResult> &results) { 1232 /// dma_wait(memrefcast) -> dma_wait 1233 return foldMemRefCast(*this); 1234 } 1235 1236 //===----------------------------------------------------------------------===// 1237 // AffineForOp 1238 //===----------------------------------------------------------------------===// 1239 1240 /// 'bodyBuilder' is used to build the body of affine.for. If iterArgs and 1241 /// bodyBuilder are empty/null, we include default terminator op. 1242 void AffineForOp::build(OpBuilder &builder, OperationState &result, 1243 ValueRange lbOperands, AffineMap lbMap, 1244 ValueRange ubOperands, AffineMap ubMap, int64_t step, 1245 ValueRange iterArgs, BodyBuilderFn bodyBuilder) { 1246 assert(((!lbMap && lbOperands.empty()) || 1247 lbOperands.size() == lbMap.getNumInputs()) && 1248 "lower bound operand count does not match the affine map"); 1249 assert(((!ubMap && ubOperands.empty()) || 1250 ubOperands.size() == ubMap.getNumInputs()) && 1251 "upper bound operand count does not match the affine map"); 1252 assert(step > 0 && "step has to be a positive integer constant"); 1253 1254 for (Value val : iterArgs) 1255 result.addTypes(val.getType()); 1256 1257 // Add an attribute for the step. 1258 result.addAttribute(getStepAttrName(), 1259 builder.getIntegerAttr(builder.getIndexType(), step)); 1260 1261 // Add the lower bound. 1262 result.addAttribute(getLowerBoundAttrName(), AffineMapAttr::get(lbMap)); 1263 result.addOperands(lbOperands); 1264 1265 // Add the upper bound. 1266 result.addAttribute(getUpperBoundAttrName(), AffineMapAttr::get(ubMap)); 1267 result.addOperands(ubOperands); 1268 1269 result.addOperands(iterArgs); 1270 // Create a region and a block for the body. The argument of the region is 1271 // the loop induction variable. 1272 Region *bodyRegion = result.addRegion(); 1273 bodyRegion->push_back(new Block); 1274 Block &bodyBlock = bodyRegion->front(); 1275 Value inductionVar = 1276 bodyBlock.addArgument(builder.getIndexType(), result.location); 1277 for (Value val : iterArgs) 1278 bodyBlock.addArgument(val.getType(), val.getLoc()); 1279 1280 // Create the default terminator if the builder is not provided and if the 1281 // iteration arguments are not provided. Otherwise, leave this to the caller 1282 // because we don't know which values to return from the loop. 1283 if (iterArgs.empty() && !bodyBuilder) { 1284 ensureTerminator(*bodyRegion, builder, result.location); 1285 } else if (bodyBuilder) { 1286 OpBuilder::InsertionGuard guard(builder); 1287 builder.setInsertionPointToStart(&bodyBlock); 1288 bodyBuilder(builder, result.location, inductionVar, 1289 bodyBlock.getArguments().drop_front()); 1290 } 1291 } 1292 1293 void AffineForOp::build(OpBuilder &builder, OperationState &result, int64_t lb, 1294 int64_t ub, int64_t step, ValueRange iterArgs, 1295 BodyBuilderFn bodyBuilder) { 1296 auto lbMap = AffineMap::getConstantMap(lb, builder.getContext()); 1297 auto ubMap = AffineMap::getConstantMap(ub, builder.getContext()); 1298 return build(builder, result, {}, lbMap, {}, ubMap, step, iterArgs, 1299 bodyBuilder); 1300 } 1301 1302 LogicalResult AffineForOp::verifyRegions() { 1303 // Check that the body defines as single block argument for the induction 1304 // variable. 1305 auto *body = getBody(); 1306 if (body->getNumArguments() == 0 || !body->getArgument(0).getType().isIndex()) 1307 return emitOpError("expected body to have a single index argument for the " 1308 "induction variable"); 1309 1310 // Verify that the bound operands are valid dimension/symbols. 1311 /// Lower bound. 1312 if (getLowerBoundMap().getNumInputs() > 0) 1313 if (failed(verifyDimAndSymbolIdentifiers(*this, getLowerBoundOperands(), 1314 getLowerBoundMap().getNumDims()))) 1315 return failure(); 1316 /// Upper bound. 1317 if (getUpperBoundMap().getNumInputs() > 0) 1318 if (failed(verifyDimAndSymbolIdentifiers(*this, getUpperBoundOperands(), 1319 getUpperBoundMap().getNumDims()))) 1320 return failure(); 1321 1322 unsigned opNumResults = getNumResults(); 1323 if (opNumResults == 0) 1324 return success(); 1325 1326 // If ForOp defines values, check that the number and types of the defined 1327 // values match ForOp initial iter operands and backedge basic block 1328 // arguments. 1329 if (getNumIterOperands() != opNumResults) 1330 return emitOpError( 1331 "mismatch between the number of loop-carried values and results"); 1332 if (getNumRegionIterArgs() != opNumResults) 1333 return emitOpError( 1334 "mismatch between the number of basic block args and results"); 1335 1336 return success(); 1337 } 1338 1339 /// Parse a for operation loop bounds. 1340 static ParseResult parseBound(bool isLower, OperationState &result, 1341 OpAsmParser &p) { 1342 // 'min' / 'max' prefixes are generally syntactic sugar, but are required if 1343 // the map has multiple results. 1344 bool failedToParsedMinMax = 1345 failed(p.parseOptionalKeyword(isLower ? "max" : "min")); 1346 1347 auto &builder = p.getBuilder(); 1348 auto boundAttrName = isLower ? AffineForOp::getLowerBoundAttrName() 1349 : AffineForOp::getUpperBoundAttrName(); 1350 1351 // Parse ssa-id as identity map. 1352 SmallVector<OpAsmParser::OperandType, 1> boundOpInfos; 1353 if (p.parseOperandList(boundOpInfos)) 1354 return failure(); 1355 1356 if (!boundOpInfos.empty()) { 1357 // Check that only one operand was parsed. 1358 if (boundOpInfos.size() > 1) 1359 return p.emitError(p.getNameLoc(), 1360 "expected only one loop bound operand"); 1361 1362 // TODO: improve error message when SSA value is not of index type. 1363 // Currently it is 'use of value ... expects different type than prior uses' 1364 if (p.resolveOperand(boundOpInfos.front(), builder.getIndexType(), 1365 result.operands)) 1366 return failure(); 1367 1368 // Create an identity map using symbol id. This representation is optimized 1369 // for storage. Analysis passes may expand it into a multi-dimensional map 1370 // if desired. 1371 AffineMap map = builder.getSymbolIdentityMap(); 1372 result.addAttribute(boundAttrName, AffineMapAttr::get(map)); 1373 return success(); 1374 } 1375 1376 // Get the attribute location. 1377 SMLoc attrLoc = p.getCurrentLocation(); 1378 1379 Attribute boundAttr; 1380 if (p.parseAttribute(boundAttr, builder.getIndexType(), boundAttrName, 1381 result.attributes)) 1382 return failure(); 1383 1384 // Parse full form - affine map followed by dim and symbol list. 1385 if (auto affineMapAttr = boundAttr.dyn_cast<AffineMapAttr>()) { 1386 unsigned currentNumOperands = result.operands.size(); 1387 unsigned numDims; 1388 if (parseDimAndSymbolList(p, result.operands, numDims)) 1389 return failure(); 1390 1391 auto map = affineMapAttr.getValue(); 1392 if (map.getNumDims() != numDims) 1393 return p.emitError( 1394 p.getNameLoc(), 1395 "dim operand count and affine map dim count must match"); 1396 1397 unsigned numDimAndSymbolOperands = 1398 result.operands.size() - currentNumOperands; 1399 if (numDims + map.getNumSymbols() != numDimAndSymbolOperands) 1400 return p.emitError( 1401 p.getNameLoc(), 1402 "symbol operand count and affine map symbol count must match"); 1403 1404 // If the map has multiple results, make sure that we parsed the min/max 1405 // prefix. 1406 if (map.getNumResults() > 1 && failedToParsedMinMax) { 1407 if (isLower) { 1408 return p.emitError(attrLoc, "lower loop bound affine map with " 1409 "multiple results requires 'max' prefix"); 1410 } 1411 return p.emitError(attrLoc, "upper loop bound affine map with multiple " 1412 "results requires 'min' prefix"); 1413 } 1414 return success(); 1415 } 1416 1417 // Parse custom assembly form. 1418 if (auto integerAttr = boundAttr.dyn_cast<IntegerAttr>()) { 1419 result.attributes.pop_back(); 1420 result.addAttribute( 1421 boundAttrName, 1422 AffineMapAttr::get(builder.getConstantAffineMap(integerAttr.getInt()))); 1423 return success(); 1424 } 1425 1426 return p.emitError( 1427 p.getNameLoc(), 1428 "expected valid affine map representation for loop bounds"); 1429 } 1430 1431 ParseResult AffineForOp::parse(OpAsmParser &parser, OperationState &result) { 1432 auto &builder = parser.getBuilder(); 1433 OpAsmParser::OperandType inductionVariable; 1434 // Parse the induction variable followed by '='. 1435 if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual()) 1436 return failure(); 1437 1438 // Parse loop bounds. 1439 if (parseBound(/*isLower=*/true, result, parser) || 1440 parser.parseKeyword("to", " between bounds") || 1441 parseBound(/*isLower=*/false, result, parser)) 1442 return failure(); 1443 1444 // Parse the optional loop step, we default to 1 if one is not present. 1445 if (parser.parseOptionalKeyword("step")) { 1446 result.addAttribute( 1447 AffineForOp::getStepAttrName(), 1448 builder.getIntegerAttr(builder.getIndexType(), /*value=*/1)); 1449 } else { 1450 SMLoc stepLoc = parser.getCurrentLocation(); 1451 IntegerAttr stepAttr; 1452 if (parser.parseAttribute(stepAttr, builder.getIndexType(), 1453 AffineForOp::getStepAttrName().data(), 1454 result.attributes)) 1455 return failure(); 1456 1457 if (stepAttr.getValue().getSExtValue() < 0) 1458 return parser.emitError( 1459 stepLoc, 1460 "expected step to be representable as a positive signed integer"); 1461 } 1462 1463 // Parse the optional initial iteration arguments. 1464 SmallVector<OpAsmParser::OperandType, 4> regionArgs, operands; 1465 SmallVector<Type, 4> argTypes; 1466 regionArgs.push_back(inductionVariable); 1467 1468 if (succeeded(parser.parseOptionalKeyword("iter_args"))) { 1469 // Parse assignment list and results type list. 1470 if (parser.parseAssignmentList(regionArgs, operands) || 1471 parser.parseArrowTypeList(result.types)) 1472 return failure(); 1473 // Resolve input operands. 1474 for (auto operandType : llvm::zip(operands, result.types)) 1475 if (parser.resolveOperand(std::get<0>(operandType), 1476 std::get<1>(operandType), result.operands)) 1477 return failure(); 1478 } 1479 // Induction variable. 1480 Type indexType = builder.getIndexType(); 1481 argTypes.push_back(indexType); 1482 // Loop carried variables. 1483 argTypes.append(result.types.begin(), result.types.end()); 1484 // Parse the body region. 1485 Region *body = result.addRegion(); 1486 if (regionArgs.size() != argTypes.size()) 1487 return parser.emitError( 1488 parser.getNameLoc(), 1489 "mismatch between the number of loop-carried values and results"); 1490 if (parser.parseRegion(*body, regionArgs, argTypes)) 1491 return failure(); 1492 1493 AffineForOp::ensureTerminator(*body, builder, result.location); 1494 1495 // Parse the optional attribute list. 1496 return parser.parseOptionalAttrDict(result.attributes); 1497 } 1498 1499 static void printBound(AffineMapAttr boundMap, 1500 Operation::operand_range boundOperands, 1501 const char *prefix, OpAsmPrinter &p) { 1502 AffineMap map = boundMap.getValue(); 1503 1504 // Check if this bound should be printed using custom assembly form. 1505 // The decision to restrict printing custom assembly form to trivial cases 1506 // comes from the will to roundtrip MLIR binary -> text -> binary in a 1507 // lossless way. 1508 // Therefore, custom assembly form parsing and printing is only supported for 1509 // zero-operand constant maps and single symbol operand identity maps. 1510 if (map.getNumResults() == 1) { 1511 AffineExpr expr = map.getResult(0); 1512 1513 // Print constant bound. 1514 if (map.getNumDims() == 0 && map.getNumSymbols() == 0) { 1515 if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) { 1516 p << constExpr.getValue(); 1517 return; 1518 } 1519 } 1520 1521 // Print bound that consists of a single SSA symbol if the map is over a 1522 // single symbol. 1523 if (map.getNumDims() == 0 && map.getNumSymbols() == 1) { 1524 if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) { 1525 p.printOperand(*boundOperands.begin()); 1526 return; 1527 } 1528 } 1529 } else { 1530 // Map has multiple results. Print 'min' or 'max' prefix. 1531 p << prefix << ' '; 1532 } 1533 1534 // Print the map and its operands. 1535 p << boundMap; 1536 printDimAndSymbolList(boundOperands.begin(), boundOperands.end(), 1537 map.getNumDims(), p); 1538 } 1539 1540 unsigned AffineForOp::getNumIterOperands() { 1541 AffineMap lbMap = getLowerBoundMapAttr().getValue(); 1542 AffineMap ubMap = getUpperBoundMapAttr().getValue(); 1543 1544 return getNumOperands() - lbMap.getNumInputs() - ubMap.getNumInputs(); 1545 } 1546 1547 void AffineForOp::print(OpAsmPrinter &p) { 1548 p << ' '; 1549 p.printOperand(getBody()->getArgument(0)); 1550 p << " = "; 1551 printBound(getLowerBoundMapAttr(), getLowerBoundOperands(), "max", p); 1552 p << " to "; 1553 printBound(getUpperBoundMapAttr(), getUpperBoundOperands(), "min", p); 1554 1555 if (getStep() != 1) 1556 p << " step " << getStep(); 1557 1558 bool printBlockTerminators = false; 1559 if (getNumIterOperands() > 0) { 1560 p << " iter_args("; 1561 auto regionArgs = getRegionIterArgs(); 1562 auto operands = getIterOperands(); 1563 1564 llvm::interleaveComma(llvm::zip(regionArgs, operands), p, [&](auto it) { 1565 p << std::get<0>(it) << " = " << std::get<1>(it); 1566 }); 1567 p << ") -> (" << getResultTypes() << ")"; 1568 printBlockTerminators = true; 1569 } 1570 1571 p << ' '; 1572 p.printRegion(region(), /*printEntryBlockArgs=*/false, printBlockTerminators); 1573 p.printOptionalAttrDict((*this)->getAttrs(), 1574 /*elidedAttrs=*/{getLowerBoundAttrName(), 1575 getUpperBoundAttrName(), 1576 getStepAttrName()}); 1577 } 1578 1579 /// Fold the constant bounds of a loop. 1580 static LogicalResult foldLoopBounds(AffineForOp forOp) { 1581 auto foldLowerOrUpperBound = [&forOp](bool lower) { 1582 // Check to see if each of the operands is the result of a constant. If 1583 // so, get the value. If not, ignore it. 1584 SmallVector<Attribute, 8> operandConstants; 1585 auto boundOperands = 1586 lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands(); 1587 for (auto operand : boundOperands) { 1588 Attribute operandCst; 1589 matchPattern(operand, m_Constant(&operandCst)); 1590 operandConstants.push_back(operandCst); 1591 } 1592 1593 AffineMap boundMap = 1594 lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap(); 1595 assert(boundMap.getNumResults() >= 1 && 1596 "bound maps should have at least one result"); 1597 SmallVector<Attribute, 4> foldedResults; 1598 if (failed(boundMap.constantFold(operandConstants, foldedResults))) 1599 return failure(); 1600 1601 // Compute the max or min as applicable over the results. 1602 assert(!foldedResults.empty() && "bounds should have at least one result"); 1603 auto maxOrMin = foldedResults[0].cast<IntegerAttr>().getValue(); 1604 for (unsigned i = 1, e = foldedResults.size(); i < e; i++) { 1605 auto foldedResult = foldedResults[i].cast<IntegerAttr>().getValue(); 1606 maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult) 1607 : llvm::APIntOps::smin(maxOrMin, foldedResult); 1608 } 1609 lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue()) 1610 : forOp.setConstantUpperBound(maxOrMin.getSExtValue()); 1611 return success(); 1612 }; 1613 1614 // Try to fold the lower bound. 1615 bool folded = false; 1616 if (!forOp.hasConstantLowerBound()) 1617 folded |= succeeded(foldLowerOrUpperBound(/*lower=*/true)); 1618 1619 // Try to fold the upper bound. 1620 if (!forOp.hasConstantUpperBound()) 1621 folded |= succeeded(foldLowerOrUpperBound(/*lower=*/false)); 1622 return success(folded); 1623 } 1624 1625 /// Canonicalize the bounds of the given loop. 1626 static LogicalResult canonicalizeLoopBounds(AffineForOp forOp) { 1627 SmallVector<Value, 4> lbOperands(forOp.getLowerBoundOperands()); 1628 SmallVector<Value, 4> ubOperands(forOp.getUpperBoundOperands()); 1629 1630 auto lbMap = forOp.getLowerBoundMap(); 1631 auto ubMap = forOp.getUpperBoundMap(); 1632 auto prevLbMap = lbMap; 1633 auto prevUbMap = ubMap; 1634 1635 composeAffineMapAndOperands(&lbMap, &lbOperands); 1636 canonicalizeMapAndOperands(&lbMap, &lbOperands); 1637 lbMap = removeDuplicateExprs(lbMap); 1638 1639 composeAffineMapAndOperands(&ubMap, &ubOperands); 1640 canonicalizeMapAndOperands(&ubMap, &ubOperands); 1641 ubMap = removeDuplicateExprs(ubMap); 1642 1643 // Any canonicalization change always leads to updated map(s). 1644 if (lbMap == prevLbMap && ubMap == prevUbMap) 1645 return failure(); 1646 1647 if (lbMap != prevLbMap) 1648 forOp.setLowerBound(lbOperands, lbMap); 1649 if (ubMap != prevUbMap) 1650 forOp.setUpperBound(ubOperands, ubMap); 1651 return success(); 1652 } 1653 1654 namespace { 1655 /// Returns constant trip count in trivial cases. 1656 static Optional<uint64_t> getTrivialConstantTripCount(AffineForOp forOp) { 1657 int64_t step = forOp.getStep(); 1658 if (!forOp.hasConstantBounds() || step <= 0) 1659 return None; 1660 int64_t lb = forOp.getConstantLowerBound(); 1661 int64_t ub = forOp.getConstantUpperBound(); 1662 return ub - lb <= 0 ? 0 : (ub - lb + step - 1) / step; 1663 } 1664 1665 /// This is a pattern to fold trivially empty loop bodies. 1666 /// TODO: This should be moved into the folding hook. 1667 struct AffineForEmptyLoopFolder : public OpRewritePattern<AffineForOp> { 1668 using OpRewritePattern<AffineForOp>::OpRewritePattern; 1669 1670 LogicalResult matchAndRewrite(AffineForOp forOp, 1671 PatternRewriter &rewriter) const override { 1672 // Check that the body only contains a yield. 1673 if (!llvm::hasSingleElement(*forOp.getBody())) 1674 return failure(); 1675 if (forOp.getNumResults() == 0) 1676 return success(); 1677 Optional<uint64_t> tripCount = getTrivialConstantTripCount(forOp); 1678 if (tripCount.hasValue() && tripCount.getValue() == 0) { 1679 // The initial values of the iteration arguments would be the op's 1680 // results. 1681 rewriter.replaceOp(forOp, forOp.getIterOperands()); 1682 return success(); 1683 } 1684 SmallVector<Value, 4> replacements; 1685 auto yieldOp = cast<AffineYieldOp>(forOp.getBody()->getTerminator()); 1686 auto iterArgs = forOp.getRegionIterArgs(); 1687 bool hasValDefinedOutsideLoop = false; 1688 bool iterArgsNotInOrder = false; 1689 for (unsigned i = 0, e = yieldOp->getNumOperands(); i < e; ++i) { 1690 Value val = yieldOp.getOperand(i); 1691 auto iterArgIt = llvm::find(iterArgs, val); 1692 if (iterArgIt == iterArgs.end()) { 1693 // `val` is defined outside of the loop. 1694 assert(forOp.isDefinedOutsideOfLoop(val) && 1695 "must be defined outside of the loop"); 1696 hasValDefinedOutsideLoop = true; 1697 replacements.push_back(val); 1698 } else { 1699 unsigned pos = std::distance(iterArgs.begin(), iterArgIt); 1700 if (pos != i) 1701 iterArgsNotInOrder = true; 1702 replacements.push_back(forOp.getIterOperands()[pos]); 1703 } 1704 } 1705 // Bail out when the trip count is unknown and the loop returns any value 1706 // defined outside of the loop or any iterArg out of order. 1707 if (!tripCount.hasValue() && 1708 (hasValDefinedOutsideLoop || iterArgsNotInOrder)) 1709 return failure(); 1710 // Bail out when the loop iterates more than once and it returns any iterArg 1711 // out of order. 1712 if (tripCount.hasValue() && tripCount.getValue() >= 2 && iterArgsNotInOrder) 1713 return failure(); 1714 rewriter.replaceOp(forOp, replacements); 1715 return success(); 1716 } 1717 }; 1718 } // namespace 1719 1720 void AffineForOp::getCanonicalizationPatterns(RewritePatternSet &results, 1721 MLIRContext *context) { 1722 results.add<AffineForEmptyLoopFolder>(context); 1723 } 1724 1725 /// Returns true if the affine.for has zero iterations in trivial cases. 1726 static bool hasTrivialZeroTripCount(AffineForOp op) { 1727 Optional<uint64_t> tripCount = getTrivialConstantTripCount(op); 1728 if (tripCount.hasValue() && tripCount.getValue() == 0) 1729 return true; 1730 return false; 1731 } 1732 1733 LogicalResult AffineForOp::fold(ArrayRef<Attribute> operands, 1734 SmallVectorImpl<OpFoldResult> &results) { 1735 bool folded = succeeded(foldLoopBounds(*this)); 1736 folded |= succeeded(canonicalizeLoopBounds(*this)); 1737 if (hasTrivialZeroTripCount(*this)) { 1738 // The initial values of the loop-carried variables (iter_args) are the 1739 // results of the op. 1740 results.assign(getIterOperands().begin(), getIterOperands().end()); 1741 folded = true; 1742 } 1743 return success(folded); 1744 } 1745 1746 AffineBound AffineForOp::getLowerBound() { 1747 auto lbMap = getLowerBoundMap(); 1748 return AffineBound(AffineForOp(*this), 0, lbMap.getNumInputs(), lbMap); 1749 } 1750 1751 AffineBound AffineForOp::getUpperBound() { 1752 auto lbMap = getLowerBoundMap(); 1753 auto ubMap = getUpperBoundMap(); 1754 return AffineBound(AffineForOp(*this), lbMap.getNumInputs(), 1755 lbMap.getNumInputs() + ubMap.getNumInputs(), ubMap); 1756 } 1757 1758 void AffineForOp::setLowerBound(ValueRange lbOperands, AffineMap map) { 1759 assert(lbOperands.size() == map.getNumInputs()); 1760 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1761 1762 SmallVector<Value, 4> newOperands(lbOperands.begin(), lbOperands.end()); 1763 1764 auto ubOperands = getUpperBoundOperands(); 1765 newOperands.append(ubOperands.begin(), ubOperands.end()); 1766 auto iterOperands = getIterOperands(); 1767 newOperands.append(iterOperands.begin(), iterOperands.end()); 1768 (*this)->setOperands(newOperands); 1769 1770 (*this)->setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map)); 1771 } 1772 1773 void AffineForOp::setUpperBound(ValueRange ubOperands, AffineMap map) { 1774 assert(ubOperands.size() == map.getNumInputs()); 1775 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1776 1777 SmallVector<Value, 4> newOperands(getLowerBoundOperands()); 1778 newOperands.append(ubOperands.begin(), ubOperands.end()); 1779 auto iterOperands = getIterOperands(); 1780 newOperands.append(iterOperands.begin(), iterOperands.end()); 1781 (*this)->setOperands(newOperands); 1782 1783 (*this)->setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map)); 1784 } 1785 1786 void AffineForOp::setLowerBoundMap(AffineMap map) { 1787 auto lbMap = getLowerBoundMap(); 1788 assert(lbMap.getNumDims() == map.getNumDims() && 1789 lbMap.getNumSymbols() == map.getNumSymbols()); 1790 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1791 (void)lbMap; 1792 (*this)->setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map)); 1793 } 1794 1795 void AffineForOp::setUpperBoundMap(AffineMap map) { 1796 auto ubMap = getUpperBoundMap(); 1797 assert(ubMap.getNumDims() == map.getNumDims() && 1798 ubMap.getNumSymbols() == map.getNumSymbols()); 1799 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1800 (void)ubMap; 1801 (*this)->setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map)); 1802 } 1803 1804 bool AffineForOp::hasConstantLowerBound() { 1805 return getLowerBoundMap().isSingleConstant(); 1806 } 1807 1808 bool AffineForOp::hasConstantUpperBound() { 1809 return getUpperBoundMap().isSingleConstant(); 1810 } 1811 1812 int64_t AffineForOp::getConstantLowerBound() { 1813 return getLowerBoundMap().getSingleConstantResult(); 1814 } 1815 1816 int64_t AffineForOp::getConstantUpperBound() { 1817 return getUpperBoundMap().getSingleConstantResult(); 1818 } 1819 1820 void AffineForOp::setConstantLowerBound(int64_t value) { 1821 setLowerBound({}, AffineMap::getConstantMap(value, getContext())); 1822 } 1823 1824 void AffineForOp::setConstantUpperBound(int64_t value) { 1825 setUpperBound({}, AffineMap::getConstantMap(value, getContext())); 1826 } 1827 1828 AffineForOp::operand_range AffineForOp::getLowerBoundOperands() { 1829 return {operand_begin(), operand_begin() + getLowerBoundMap().getNumInputs()}; 1830 } 1831 1832 AffineForOp::operand_range AffineForOp::getUpperBoundOperands() { 1833 return {operand_begin() + getLowerBoundMap().getNumInputs(), 1834 operand_begin() + getLowerBoundMap().getNumInputs() + 1835 getUpperBoundMap().getNumInputs()}; 1836 } 1837 1838 AffineForOp::operand_range AffineForOp::getControlOperands() { 1839 return {operand_begin(), operand_begin() + getLowerBoundMap().getNumInputs() + 1840 getUpperBoundMap().getNumInputs()}; 1841 } 1842 1843 bool AffineForOp::matchingBoundOperandList() { 1844 auto lbMap = getLowerBoundMap(); 1845 auto ubMap = getUpperBoundMap(); 1846 if (lbMap.getNumDims() != ubMap.getNumDims() || 1847 lbMap.getNumSymbols() != ubMap.getNumSymbols()) 1848 return false; 1849 1850 unsigned numOperands = lbMap.getNumInputs(); 1851 for (unsigned i = 0, e = lbMap.getNumInputs(); i < e; i++) { 1852 // Compare Value 's. 1853 if (getOperand(i) != getOperand(numOperands + i)) 1854 return false; 1855 } 1856 return true; 1857 } 1858 1859 Region &AffineForOp::getLoopBody() { return region(); } 1860 1861 bool AffineForOp::isDefinedOutsideOfLoop(Value value) { 1862 return !region().isAncestor(value.getParentRegion()); 1863 } 1864 1865 Optional<Value> AffineForOp::getSingleInductionVar() { 1866 return getInductionVar(); 1867 } 1868 1869 Optional<OpFoldResult> AffineForOp::getSingleLowerBound() { 1870 if (!hasConstantLowerBound()) 1871 return llvm::None; 1872 OpBuilder b(getContext()); 1873 return OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound())); 1874 } 1875 1876 Optional<OpFoldResult> AffineForOp::getSingleStep() { 1877 OpBuilder b(getContext()); 1878 return OpFoldResult(b.getI64IntegerAttr(getStep())); 1879 } 1880 1881 LogicalResult AffineForOp::moveOutOfLoop(ArrayRef<Operation *> ops) { 1882 for (auto *op : ops) 1883 op->moveBefore(*this); 1884 return success(); 1885 } 1886 1887 /// Returns true if the provided value is the induction variable of a 1888 /// AffineForOp. 1889 bool mlir::isForInductionVar(Value val) { 1890 return getForInductionVarOwner(val) != AffineForOp(); 1891 } 1892 1893 /// Returns the loop parent of an induction variable. If the provided value is 1894 /// not an induction variable, then return nullptr. 1895 AffineForOp mlir::getForInductionVarOwner(Value val) { 1896 auto ivArg = val.dyn_cast<BlockArgument>(); 1897 if (!ivArg || !ivArg.getOwner()) 1898 return AffineForOp(); 1899 auto *containingInst = ivArg.getOwner()->getParent()->getParentOp(); 1900 if (auto forOp = dyn_cast<AffineForOp>(containingInst)) 1901 // Check to make sure `val` is the induction variable, not an iter_arg. 1902 return forOp.getInductionVar() == val ? forOp : AffineForOp(); 1903 return AffineForOp(); 1904 } 1905 1906 /// Extracts the induction variables from a list of AffineForOps and returns 1907 /// them. 1908 void mlir::extractForInductionVars(ArrayRef<AffineForOp> forInsts, 1909 SmallVectorImpl<Value> *ivs) { 1910 ivs->reserve(forInsts.size()); 1911 for (auto forInst : forInsts) 1912 ivs->push_back(forInst.getInductionVar()); 1913 } 1914 1915 /// Builds an affine loop nest, using "loopCreatorFn" to create individual loop 1916 /// operations. 1917 template <typename BoundListTy, typename LoopCreatorTy> 1918 static void buildAffineLoopNestImpl( 1919 OpBuilder &builder, Location loc, BoundListTy lbs, BoundListTy ubs, 1920 ArrayRef<int64_t> steps, 1921 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn, 1922 LoopCreatorTy &&loopCreatorFn) { 1923 assert(lbs.size() == ubs.size() && "Mismatch in number of arguments"); 1924 assert(lbs.size() == steps.size() && "Mismatch in number of arguments"); 1925 1926 // If there are no loops to be constructed, construct the body anyway. 1927 OpBuilder::InsertionGuard guard(builder); 1928 if (lbs.empty()) { 1929 if (bodyBuilderFn) 1930 bodyBuilderFn(builder, loc, ValueRange()); 1931 return; 1932 } 1933 1934 // Create the loops iteratively and store the induction variables. 1935 SmallVector<Value, 4> ivs; 1936 ivs.reserve(lbs.size()); 1937 for (unsigned i = 0, e = lbs.size(); i < e; ++i) { 1938 // Callback for creating the loop body, always creates the terminator. 1939 auto loopBody = [&](OpBuilder &nestedBuilder, Location nestedLoc, Value iv, 1940 ValueRange iterArgs) { 1941 ivs.push_back(iv); 1942 // In the innermost loop, call the body builder. 1943 if (i == e - 1 && bodyBuilderFn) { 1944 OpBuilder::InsertionGuard nestedGuard(nestedBuilder); 1945 bodyBuilderFn(nestedBuilder, nestedLoc, ivs); 1946 } 1947 nestedBuilder.create<AffineYieldOp>(nestedLoc); 1948 }; 1949 1950 // Delegate actual loop creation to the callback in order to dispatch 1951 // between constant- and variable-bound loops. 1952 auto loop = loopCreatorFn(builder, loc, lbs[i], ubs[i], steps[i], loopBody); 1953 builder.setInsertionPointToStart(loop.getBody()); 1954 } 1955 } 1956 1957 /// Creates an affine loop from the bounds known to be constants. 1958 static AffineForOp 1959 buildAffineLoopFromConstants(OpBuilder &builder, Location loc, int64_t lb, 1960 int64_t ub, int64_t step, 1961 AffineForOp::BodyBuilderFn bodyBuilderFn) { 1962 return builder.create<AffineForOp>(loc, lb, ub, step, /*iterArgs=*/llvm::None, 1963 bodyBuilderFn); 1964 } 1965 1966 /// Creates an affine loop from the bounds that may or may not be constants. 1967 static AffineForOp 1968 buildAffineLoopFromValues(OpBuilder &builder, Location loc, Value lb, Value ub, 1969 int64_t step, 1970 AffineForOp::BodyBuilderFn bodyBuilderFn) { 1971 auto lbConst = lb.getDefiningOp<arith::ConstantIndexOp>(); 1972 auto ubConst = ub.getDefiningOp<arith::ConstantIndexOp>(); 1973 if (lbConst && ubConst) 1974 return buildAffineLoopFromConstants(builder, loc, lbConst.value(), 1975 ubConst.value(), step, bodyBuilderFn); 1976 return builder.create<AffineForOp>(loc, lb, builder.getDimIdentityMap(), ub, 1977 builder.getDimIdentityMap(), step, 1978 /*iterArgs=*/llvm::None, bodyBuilderFn); 1979 } 1980 1981 void mlir::buildAffineLoopNest( 1982 OpBuilder &builder, Location loc, ArrayRef<int64_t> lbs, 1983 ArrayRef<int64_t> ubs, ArrayRef<int64_t> steps, 1984 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) { 1985 buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn, 1986 buildAffineLoopFromConstants); 1987 } 1988 1989 void mlir::buildAffineLoopNest( 1990 OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs, 1991 ArrayRef<int64_t> steps, 1992 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) { 1993 buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn, 1994 buildAffineLoopFromValues); 1995 } 1996 1997 AffineForOp mlir::replaceForOpWithNewYields(OpBuilder &b, AffineForOp loop, 1998 ValueRange newIterOperands, 1999 ValueRange newYieldedValues, 2000 ValueRange newIterArgs, 2001 bool replaceLoopResults) { 2002 assert(newIterOperands.size() == newYieldedValues.size() && 2003 "newIterOperands must be of the same size as newYieldedValues"); 2004 // Create a new loop before the existing one, with the extra operands. 2005 OpBuilder::InsertionGuard g(b); 2006 b.setInsertionPoint(loop); 2007 auto operands = llvm::to_vector<4>(loop.getIterOperands()); 2008 operands.append(newIterOperands.begin(), newIterOperands.end()); 2009 SmallVector<Value, 4> lbOperands(loop.getLowerBoundOperands()); 2010 SmallVector<Value, 4> ubOperands(loop.getUpperBoundOperands()); 2011 SmallVector<Value, 4> steps(loop.getStep()); 2012 auto lbMap = loop.getLowerBoundMap(); 2013 auto ubMap = loop.getUpperBoundMap(); 2014 AffineForOp newLoop = 2015 b.create<AffineForOp>(loop.getLoc(), lbOperands, lbMap, ubOperands, ubMap, 2016 loop.getStep(), operands); 2017 // Take the body of the original parent loop. 2018 newLoop.getLoopBody().takeBody(loop.getLoopBody()); 2019 for (Value val : newIterArgs) 2020 newLoop.getLoopBody().addArgument(val.getType(), val.getLoc()); 2021 2022 // Update yield operation with new values to be added. 2023 if (!newYieldedValues.empty()) { 2024 auto yield = cast<AffineYieldOp>(newLoop.getBody()->getTerminator()); 2025 b.setInsertionPoint(yield); 2026 auto yieldOperands = llvm::to_vector<4>(yield.getOperands()); 2027 yieldOperands.append(newYieldedValues.begin(), newYieldedValues.end()); 2028 b.create<AffineYieldOp>(yield.getLoc(), yieldOperands); 2029 yield.erase(); 2030 } 2031 if (replaceLoopResults) { 2032 for (auto it : llvm::zip(loop.getResults(), newLoop.getResults().take_front( 2033 loop.getNumResults()))) { 2034 std::get<0>(it).replaceAllUsesWith(std::get<1>(it)); 2035 } 2036 } 2037 return newLoop; 2038 } 2039 2040 //===----------------------------------------------------------------------===// 2041 // AffineIfOp 2042 //===----------------------------------------------------------------------===// 2043 2044 namespace { 2045 /// Remove else blocks that have nothing other than a zero value yield. 2046 struct SimplifyDeadElse : public OpRewritePattern<AffineIfOp> { 2047 using OpRewritePattern<AffineIfOp>::OpRewritePattern; 2048 2049 LogicalResult matchAndRewrite(AffineIfOp ifOp, 2050 PatternRewriter &rewriter) const override { 2051 if (ifOp.elseRegion().empty() || 2052 !llvm::hasSingleElement(*ifOp.getElseBlock()) || ifOp.getNumResults()) 2053 return failure(); 2054 2055 rewriter.startRootUpdate(ifOp); 2056 rewriter.eraseBlock(ifOp.getElseBlock()); 2057 rewriter.finalizeRootUpdate(ifOp); 2058 return success(); 2059 } 2060 }; 2061 2062 /// Removes affine.if cond if the condition is always true or false in certain 2063 /// trivial cases. Promotes the then/else block in the parent operation block. 2064 struct AlwaysTrueOrFalseIf : public OpRewritePattern<AffineIfOp> { 2065 using OpRewritePattern<AffineIfOp>::OpRewritePattern; 2066 2067 LogicalResult matchAndRewrite(AffineIfOp op, 2068 PatternRewriter &rewriter) const override { 2069 2070 auto isTriviallyFalse = [](IntegerSet iSet) { 2071 return iSet.isEmptyIntegerSet(); 2072 }; 2073 2074 auto isTriviallyTrue = [](IntegerSet iSet) { 2075 return (iSet.getNumEqualities() == 1 && iSet.getNumInequalities() == 0 && 2076 iSet.getConstraint(0) == 0); 2077 }; 2078 2079 IntegerSet affineIfConditions = op.getIntegerSet(); 2080 Block *blockToMove; 2081 if (isTriviallyFalse(affineIfConditions)) { 2082 // The absence, or equivalently, the emptiness of the else region need not 2083 // be checked when affine.if is returning results because if an affine.if 2084 // operation is returning results, it always has a non-empty else region. 2085 if (op.getNumResults() == 0 && !op.hasElse()) { 2086 // If the else region is absent, or equivalently, empty, remove the 2087 // affine.if operation (which is not returning any results). 2088 rewriter.eraseOp(op); 2089 return success(); 2090 } 2091 blockToMove = op.getElseBlock(); 2092 } else if (isTriviallyTrue(affineIfConditions)) { 2093 blockToMove = op.getThenBlock(); 2094 } else { 2095 return failure(); 2096 } 2097 Operation *blockToMoveTerminator = blockToMove->getTerminator(); 2098 // Promote the "blockToMove" block to the parent operation block between the 2099 // prologue and epilogue of "op". 2100 rewriter.mergeBlockBefore(blockToMove, op); 2101 // Replace the "op" operation with the operands of the 2102 // "blockToMoveTerminator" operation. Note that "blockToMoveTerminator" is 2103 // the affine.yield operation present in the "blockToMove" block. It has no 2104 // operands when affine.if is not returning results and therefore, in that 2105 // case, replaceOp just erases "op". When affine.if is not returning 2106 // results, the affine.yield operation can be omitted. It gets inserted 2107 // implicitly. 2108 rewriter.replaceOp(op, blockToMoveTerminator->getOperands()); 2109 // Erase the "blockToMoveTerminator" operation since it is now in the parent 2110 // operation block, which already has its own terminator. 2111 rewriter.eraseOp(blockToMoveTerminator); 2112 return success(); 2113 } 2114 }; 2115 } // namespace 2116 2117 LogicalResult AffineIfOp::verify() { 2118 // Verify that we have a condition attribute. 2119 // FIXME: This should be specified in the arguments list in ODS. 2120 auto conditionAttr = 2121 (*this)->getAttrOfType<IntegerSetAttr>(getConditionAttrName()); 2122 if (!conditionAttr) 2123 return emitOpError("requires an integer set attribute named 'condition'"); 2124 2125 // Verify that there are enough operands for the condition. 2126 IntegerSet condition = conditionAttr.getValue(); 2127 if (getNumOperands() != condition.getNumInputs()) 2128 return emitOpError("operand count and condition integer set dimension and " 2129 "symbol count must match"); 2130 2131 // Verify that the operands are valid dimension/symbols. 2132 if (failed(verifyDimAndSymbolIdentifiers(*this, getOperands(), 2133 condition.getNumDims()))) 2134 return failure(); 2135 2136 return success(); 2137 } 2138 2139 ParseResult AffineIfOp::parse(OpAsmParser &parser, OperationState &result) { 2140 // Parse the condition attribute set. 2141 IntegerSetAttr conditionAttr; 2142 unsigned numDims; 2143 if (parser.parseAttribute(conditionAttr, AffineIfOp::getConditionAttrName(), 2144 result.attributes) || 2145 parseDimAndSymbolList(parser, result.operands, numDims)) 2146 return failure(); 2147 2148 // Verify the condition operands. 2149 auto set = conditionAttr.getValue(); 2150 if (set.getNumDims() != numDims) 2151 return parser.emitError( 2152 parser.getNameLoc(), 2153 "dim operand count and integer set dim count must match"); 2154 if (numDims + set.getNumSymbols() != result.operands.size()) 2155 return parser.emitError( 2156 parser.getNameLoc(), 2157 "symbol operand count and integer set symbol count must match"); 2158 2159 if (parser.parseOptionalArrowTypeList(result.types)) 2160 return failure(); 2161 2162 // Create the regions for 'then' and 'else'. The latter must be created even 2163 // if it remains empty for the validity of the operation. 2164 result.regions.reserve(2); 2165 Region *thenRegion = result.addRegion(); 2166 Region *elseRegion = result.addRegion(); 2167 2168 // Parse the 'then' region. 2169 if (parser.parseRegion(*thenRegion, {}, {})) 2170 return failure(); 2171 AffineIfOp::ensureTerminator(*thenRegion, parser.getBuilder(), 2172 result.location); 2173 2174 // If we find an 'else' keyword then parse the 'else' region. 2175 if (!parser.parseOptionalKeyword("else")) { 2176 if (parser.parseRegion(*elseRegion, {}, {})) 2177 return failure(); 2178 AffineIfOp::ensureTerminator(*elseRegion, parser.getBuilder(), 2179 result.location); 2180 } 2181 2182 // Parse the optional attribute list. 2183 if (parser.parseOptionalAttrDict(result.attributes)) 2184 return failure(); 2185 2186 return success(); 2187 } 2188 2189 void AffineIfOp::print(OpAsmPrinter &p) { 2190 auto conditionAttr = 2191 (*this)->getAttrOfType<IntegerSetAttr>(getConditionAttrName()); 2192 p << " " << conditionAttr; 2193 printDimAndSymbolList(operand_begin(), operand_end(), 2194 conditionAttr.getValue().getNumDims(), p); 2195 p.printOptionalArrowTypeList(getResultTypes()); 2196 p << ' '; 2197 p.printRegion(thenRegion(), /*printEntryBlockArgs=*/false, 2198 /*printBlockTerminators=*/getNumResults()); 2199 2200 // Print the 'else' regions if it has any blocks. 2201 auto &elseRegion = this->elseRegion(); 2202 if (!elseRegion.empty()) { 2203 p << " else "; 2204 p.printRegion(elseRegion, 2205 /*printEntryBlockArgs=*/false, 2206 /*printBlockTerminators=*/getNumResults()); 2207 } 2208 2209 // Print the attribute list. 2210 p.printOptionalAttrDict((*this)->getAttrs(), 2211 /*elidedAttrs=*/getConditionAttrName()); 2212 } 2213 2214 IntegerSet AffineIfOp::getIntegerSet() { 2215 return (*this) 2216 ->getAttrOfType<IntegerSetAttr>(getConditionAttrName()) 2217 .getValue(); 2218 } 2219 2220 void AffineIfOp::setIntegerSet(IntegerSet newSet) { 2221 (*this)->setAttr(getConditionAttrName(), IntegerSetAttr::get(newSet)); 2222 } 2223 2224 void AffineIfOp::setConditional(IntegerSet set, ValueRange operands) { 2225 setIntegerSet(set); 2226 (*this)->setOperands(operands); 2227 } 2228 2229 void AffineIfOp::build(OpBuilder &builder, OperationState &result, 2230 TypeRange resultTypes, IntegerSet set, ValueRange args, 2231 bool withElseRegion) { 2232 assert(resultTypes.empty() || withElseRegion); 2233 result.addTypes(resultTypes); 2234 result.addOperands(args); 2235 result.addAttribute(getConditionAttrName(), IntegerSetAttr::get(set)); 2236 2237 Region *thenRegion = result.addRegion(); 2238 thenRegion->push_back(new Block()); 2239 if (resultTypes.empty()) 2240 AffineIfOp::ensureTerminator(*thenRegion, builder, result.location); 2241 2242 Region *elseRegion = result.addRegion(); 2243 if (withElseRegion) { 2244 elseRegion->push_back(new Block()); 2245 if (resultTypes.empty()) 2246 AffineIfOp::ensureTerminator(*elseRegion, builder, result.location); 2247 } 2248 } 2249 2250 void AffineIfOp::build(OpBuilder &builder, OperationState &result, 2251 IntegerSet set, ValueRange args, bool withElseRegion) { 2252 AffineIfOp::build(builder, result, /*resultTypes=*/{}, set, args, 2253 withElseRegion); 2254 } 2255 2256 /// Canonicalize an affine if op's conditional (integer set + operands). 2257 LogicalResult AffineIfOp::fold(ArrayRef<Attribute>, 2258 SmallVectorImpl<OpFoldResult> &) { 2259 auto set = getIntegerSet(); 2260 SmallVector<Value, 4> operands(getOperands()); 2261 canonicalizeSetAndOperands(&set, &operands); 2262 2263 // Any canonicalization change always leads to either a reduction in the 2264 // number of operands or a change in the number of symbolic operands 2265 // (promotion of dims to symbols). 2266 if (operands.size() < getIntegerSet().getNumInputs() || 2267 set.getNumSymbols() > getIntegerSet().getNumSymbols()) { 2268 setConditional(set, operands); 2269 return success(); 2270 } 2271 2272 return failure(); 2273 } 2274 2275 void AffineIfOp::getCanonicalizationPatterns(RewritePatternSet &results, 2276 MLIRContext *context) { 2277 results.add<SimplifyDeadElse, AlwaysTrueOrFalseIf>(context); 2278 } 2279 2280 //===----------------------------------------------------------------------===// 2281 // AffineLoadOp 2282 //===----------------------------------------------------------------------===// 2283 2284 void AffineLoadOp::build(OpBuilder &builder, OperationState &result, 2285 AffineMap map, ValueRange operands) { 2286 assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands"); 2287 result.addOperands(operands); 2288 if (map) 2289 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 2290 auto memrefType = operands[0].getType().cast<MemRefType>(); 2291 result.types.push_back(memrefType.getElementType()); 2292 } 2293 2294 void AffineLoadOp::build(OpBuilder &builder, OperationState &result, 2295 Value memref, AffineMap map, ValueRange mapOperands) { 2296 assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info"); 2297 result.addOperands(memref); 2298 result.addOperands(mapOperands); 2299 auto memrefType = memref.getType().cast<MemRefType>(); 2300 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 2301 result.types.push_back(memrefType.getElementType()); 2302 } 2303 2304 void AffineLoadOp::build(OpBuilder &builder, OperationState &result, 2305 Value memref, ValueRange indices) { 2306 auto memrefType = memref.getType().cast<MemRefType>(); 2307 int64_t rank = memrefType.getRank(); 2308 // Create identity map for memrefs with at least one dimension or () -> () 2309 // for zero-dimensional memrefs. 2310 auto map = 2311 rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap(); 2312 build(builder, result, memref, map, indices); 2313 } 2314 2315 ParseResult AffineLoadOp::parse(OpAsmParser &parser, OperationState &result) { 2316 auto &builder = parser.getBuilder(); 2317 auto indexTy = builder.getIndexType(); 2318 2319 MemRefType type; 2320 OpAsmParser::OperandType memrefInfo; 2321 AffineMapAttr mapAttr; 2322 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 2323 return failure( 2324 parser.parseOperand(memrefInfo) || 2325 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 2326 AffineLoadOp::getMapAttrName(), 2327 result.attributes) || 2328 parser.parseOptionalAttrDict(result.attributes) || 2329 parser.parseColonType(type) || 2330 parser.resolveOperand(memrefInfo, type, result.operands) || 2331 parser.resolveOperands(mapOperands, indexTy, result.operands) || 2332 parser.addTypeToList(type.getElementType(), result.types)); 2333 } 2334 2335 void AffineLoadOp::print(OpAsmPrinter &p) { 2336 p << " " << getMemRef() << '['; 2337 if (AffineMapAttr mapAttr = 2338 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName())) 2339 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 2340 p << ']'; 2341 p.printOptionalAttrDict((*this)->getAttrs(), 2342 /*elidedAttrs=*/{getMapAttrName()}); 2343 p << " : " << getMemRefType(); 2344 } 2345 2346 /// Verify common indexing invariants of affine.load, affine.store, 2347 /// affine.vector_load and affine.vector_store. 2348 static LogicalResult 2349 verifyMemoryOpIndexing(Operation *op, AffineMapAttr mapAttr, 2350 Operation::operand_range mapOperands, 2351 MemRefType memrefType, unsigned numIndexOperands) { 2352 if (mapAttr) { 2353 AffineMap map = mapAttr.getValue(); 2354 if (map.getNumResults() != memrefType.getRank()) 2355 return op->emitOpError("affine map num results must equal memref rank"); 2356 if (map.getNumInputs() != numIndexOperands) 2357 return op->emitOpError("expects as many subscripts as affine map inputs"); 2358 } else { 2359 if (memrefType.getRank() != numIndexOperands) 2360 return op->emitOpError( 2361 "expects the number of subscripts to be equal to memref rank"); 2362 } 2363 2364 Region *scope = getAffineScope(op); 2365 for (auto idx : mapOperands) { 2366 if (!idx.getType().isIndex()) 2367 return op->emitOpError("index to load must have 'index' type"); 2368 if (!isValidAffineIndexOperand(idx, scope)) 2369 return op->emitOpError("index must be a dimension or symbol identifier"); 2370 } 2371 2372 return success(); 2373 } 2374 2375 LogicalResult AffineLoadOp::verify() { 2376 auto memrefType = getMemRefType(); 2377 if (getType() != memrefType.getElementType()) 2378 return emitOpError("result type must match element type of memref"); 2379 2380 if (failed(verifyMemoryOpIndexing( 2381 getOperation(), 2382 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName()), 2383 getMapOperands(), memrefType, 2384 /*numIndexOperands=*/getNumOperands() - 1))) 2385 return failure(); 2386 2387 return success(); 2388 } 2389 2390 void AffineLoadOp::getCanonicalizationPatterns(RewritePatternSet &results, 2391 MLIRContext *context) { 2392 results.add<SimplifyAffineOp<AffineLoadOp>>(context); 2393 } 2394 2395 OpFoldResult AffineLoadOp::fold(ArrayRef<Attribute> cstOperands) { 2396 /// load(memrefcast) -> load 2397 if (succeeded(foldMemRefCast(*this))) 2398 return getResult(); 2399 2400 // Fold load from a global constant memref. 2401 auto getGlobalOp = memref().getDefiningOp<memref::GetGlobalOp>(); 2402 if (!getGlobalOp) 2403 return {}; 2404 // Get to the memref.global defining the symbol. 2405 auto *symbolTableOp = getGlobalOp->getParentWithTrait<OpTrait::SymbolTable>(); 2406 if (!symbolTableOp) 2407 return {}; 2408 auto global = dyn_cast_or_null<memref::GlobalOp>( 2409 SymbolTable::lookupSymbolIn(symbolTableOp, getGlobalOp.nameAttr())); 2410 if (!global) 2411 return {}; 2412 if (auto cstAttr = 2413 global.getConstantInitValue().dyn_cast_or_null<DenseElementsAttr>()) { 2414 // We can fold only if we know the indices. 2415 if (!getAffineMap().isConstant()) 2416 return {}; 2417 auto indices = llvm::to_vector<4>( 2418 llvm::map_range(getAffineMap().getConstantResults(), 2419 [](int64_t v) -> uint64_t { return v; })); 2420 return cstAttr.getValues<Attribute>()[indices]; 2421 } 2422 return {}; 2423 } 2424 2425 //===----------------------------------------------------------------------===// 2426 // AffineStoreOp 2427 //===----------------------------------------------------------------------===// 2428 2429 void AffineStoreOp::build(OpBuilder &builder, OperationState &result, 2430 Value valueToStore, Value memref, AffineMap map, 2431 ValueRange mapOperands) { 2432 assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info"); 2433 result.addOperands(valueToStore); 2434 result.addOperands(memref); 2435 result.addOperands(mapOperands); 2436 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 2437 } 2438 2439 // Use identity map. 2440 void AffineStoreOp::build(OpBuilder &builder, OperationState &result, 2441 Value valueToStore, Value memref, 2442 ValueRange indices) { 2443 auto memrefType = memref.getType().cast<MemRefType>(); 2444 int64_t rank = memrefType.getRank(); 2445 // Create identity map for memrefs with at least one dimension or () -> () 2446 // for zero-dimensional memrefs. 2447 auto map = 2448 rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap(); 2449 build(builder, result, valueToStore, memref, map, indices); 2450 } 2451 2452 ParseResult AffineStoreOp::parse(OpAsmParser &parser, OperationState &result) { 2453 auto indexTy = parser.getBuilder().getIndexType(); 2454 2455 MemRefType type; 2456 OpAsmParser::OperandType storeValueInfo; 2457 OpAsmParser::OperandType memrefInfo; 2458 AffineMapAttr mapAttr; 2459 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 2460 return failure(parser.parseOperand(storeValueInfo) || parser.parseComma() || 2461 parser.parseOperand(memrefInfo) || 2462 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 2463 AffineStoreOp::getMapAttrName(), 2464 result.attributes) || 2465 parser.parseOptionalAttrDict(result.attributes) || 2466 parser.parseColonType(type) || 2467 parser.resolveOperand(storeValueInfo, type.getElementType(), 2468 result.operands) || 2469 parser.resolveOperand(memrefInfo, type, result.operands) || 2470 parser.resolveOperands(mapOperands, indexTy, result.operands)); 2471 } 2472 2473 void AffineStoreOp::print(OpAsmPrinter &p) { 2474 p << " " << getValueToStore(); 2475 p << ", " << getMemRef() << '['; 2476 if (AffineMapAttr mapAttr = 2477 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName())) 2478 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 2479 p << ']'; 2480 p.printOptionalAttrDict((*this)->getAttrs(), 2481 /*elidedAttrs=*/{getMapAttrName()}); 2482 p << " : " << getMemRefType(); 2483 } 2484 2485 LogicalResult AffineStoreOp::verify() { 2486 // The value to store must have the same type as memref element type. 2487 auto memrefType = getMemRefType(); 2488 if (getValueToStore().getType() != memrefType.getElementType()) 2489 return emitOpError( 2490 "value to store must have the same type as memref element type"); 2491 2492 if (failed(verifyMemoryOpIndexing( 2493 getOperation(), 2494 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName()), 2495 getMapOperands(), memrefType, 2496 /*numIndexOperands=*/getNumOperands() - 2))) 2497 return failure(); 2498 2499 return success(); 2500 } 2501 2502 void AffineStoreOp::getCanonicalizationPatterns(RewritePatternSet &results, 2503 MLIRContext *context) { 2504 results.add<SimplifyAffineOp<AffineStoreOp>>(context); 2505 } 2506 2507 LogicalResult AffineStoreOp::fold(ArrayRef<Attribute> cstOperands, 2508 SmallVectorImpl<OpFoldResult> &results) { 2509 /// store(memrefcast) -> store 2510 return foldMemRefCast(*this, getValueToStore()); 2511 } 2512 2513 //===----------------------------------------------------------------------===// 2514 // AffineMinMaxOpBase 2515 //===----------------------------------------------------------------------===// 2516 2517 template <typename T> static LogicalResult verifyAffineMinMaxOp(T op) { 2518 // Verify that operand count matches affine map dimension and symbol count. 2519 if (op.getNumOperands() != op.map().getNumDims() + op.map().getNumSymbols()) 2520 return op.emitOpError( 2521 "operand count and affine map dimension and symbol count must match"); 2522 return success(); 2523 } 2524 2525 template <typename T> static void printAffineMinMaxOp(OpAsmPrinter &p, T op) { 2526 p << ' ' << op->getAttr(T::getMapAttrName()); 2527 auto operands = op.getOperands(); 2528 unsigned numDims = op.map().getNumDims(); 2529 p << '(' << operands.take_front(numDims) << ')'; 2530 2531 if (operands.size() != numDims) 2532 p << '[' << operands.drop_front(numDims) << ']'; 2533 p.printOptionalAttrDict(op->getAttrs(), 2534 /*elidedAttrs=*/{T::getMapAttrName()}); 2535 } 2536 2537 template <typename T> 2538 static ParseResult parseAffineMinMaxOp(OpAsmParser &parser, 2539 OperationState &result) { 2540 auto &builder = parser.getBuilder(); 2541 auto indexType = builder.getIndexType(); 2542 SmallVector<OpAsmParser::OperandType, 8> dimInfos; 2543 SmallVector<OpAsmParser::OperandType, 8> symInfos; 2544 AffineMapAttr mapAttr; 2545 return failure( 2546 parser.parseAttribute(mapAttr, T::getMapAttrName(), result.attributes) || 2547 parser.parseOperandList(dimInfos, OpAsmParser::Delimiter::Paren) || 2548 parser.parseOperandList(symInfos, 2549 OpAsmParser::Delimiter::OptionalSquare) || 2550 parser.parseOptionalAttrDict(result.attributes) || 2551 parser.resolveOperands(dimInfos, indexType, result.operands) || 2552 parser.resolveOperands(symInfos, indexType, result.operands) || 2553 parser.addTypeToList(indexType, result.types)); 2554 } 2555 2556 /// Fold an affine min or max operation with the given operands. The operand 2557 /// list may contain nulls, which are interpreted as the operand not being a 2558 /// constant. 2559 template <typename T> 2560 static OpFoldResult foldMinMaxOp(T op, ArrayRef<Attribute> operands) { 2561 static_assert(llvm::is_one_of<T, AffineMinOp, AffineMaxOp>::value, 2562 "expected affine min or max op"); 2563 2564 // Fold the affine map. 2565 // TODO: Fold more cases: 2566 // min(some_affine, some_affine + constant, ...), etc. 2567 SmallVector<int64_t, 2> results; 2568 auto foldedMap = op.map().partialConstantFold(operands, &results); 2569 2570 // If some of the map results are not constant, try changing the map in-place. 2571 if (results.empty()) { 2572 // If the map is the same, report that folding did not happen. 2573 if (foldedMap == op.map()) 2574 return {}; 2575 op->setAttr("map", AffineMapAttr::get(foldedMap)); 2576 return op.getResult(); 2577 } 2578 2579 // Otherwise, completely fold the op into a constant. 2580 auto resultIt = std::is_same<T, AffineMinOp>::value 2581 ? std::min_element(results.begin(), results.end()) 2582 : std::max_element(results.begin(), results.end()); 2583 if (resultIt == results.end()) 2584 return {}; 2585 return IntegerAttr::get(IndexType::get(op.getContext()), *resultIt); 2586 } 2587 2588 /// Remove duplicated expressions in affine min/max ops. 2589 template <typename T> 2590 struct DeduplicateAffineMinMaxExpressions : public OpRewritePattern<T> { 2591 using OpRewritePattern<T>::OpRewritePattern; 2592 2593 LogicalResult matchAndRewrite(T affineOp, 2594 PatternRewriter &rewriter) const override { 2595 AffineMap oldMap = affineOp.getAffineMap(); 2596 2597 SmallVector<AffineExpr, 4> newExprs; 2598 for (AffineExpr expr : oldMap.getResults()) { 2599 // This is a linear scan over newExprs, but it should be fine given that 2600 // we typically just have a few expressions per op. 2601 if (!llvm::is_contained(newExprs, expr)) 2602 newExprs.push_back(expr); 2603 } 2604 2605 if (newExprs.size() == oldMap.getNumResults()) 2606 return failure(); 2607 2608 auto newMap = AffineMap::get(oldMap.getNumDims(), oldMap.getNumSymbols(), 2609 newExprs, rewriter.getContext()); 2610 rewriter.replaceOpWithNewOp<T>(affineOp, newMap, affineOp.getMapOperands()); 2611 2612 return success(); 2613 } 2614 }; 2615 2616 /// Merge an affine min/max op to its consumers if its consumer is also an 2617 /// affine min/max op. 2618 /// 2619 /// This pattern requires the producer affine min/max op is bound to a 2620 /// dimension/symbol that is used as a standalone expression in the consumer 2621 /// affine op's map. 2622 /// 2623 /// For example, a pattern like the following: 2624 /// 2625 /// %0 = affine.min affine_map<()[s0] -> (s0 + 16, s0 * 8)> ()[%sym1] 2626 /// %1 = affine.min affine_map<(d0)[s0] -> (s0 + 4, d0)> (%0)[%sym2] 2627 /// 2628 /// Can be turned into: 2629 /// 2630 /// %1 = affine.min affine_map< 2631 /// ()[s0, s1] -> (s0 + 4, s1 + 16, s1 * 8)> ()[%sym2, %sym1] 2632 template <typename T> struct MergeAffineMinMaxOp : public OpRewritePattern<T> { 2633 using OpRewritePattern<T>::OpRewritePattern; 2634 2635 LogicalResult matchAndRewrite(T affineOp, 2636 PatternRewriter &rewriter) const override { 2637 AffineMap oldMap = affineOp.getAffineMap(); 2638 ValueRange dimOperands = 2639 affineOp.getMapOperands().take_front(oldMap.getNumDims()); 2640 ValueRange symOperands = 2641 affineOp.getMapOperands().take_back(oldMap.getNumSymbols()); 2642 2643 auto newDimOperands = llvm::to_vector<8>(dimOperands); 2644 auto newSymOperands = llvm::to_vector<8>(symOperands); 2645 SmallVector<AffineExpr, 4> newExprs; 2646 SmallVector<T, 4> producerOps; 2647 2648 // Go over each expression to see whether it's a single dimension/symbol 2649 // with the corresponding operand which is the result of another affine 2650 // min/max op. If So it can be merged into this affine op. 2651 for (AffineExpr expr : oldMap.getResults()) { 2652 if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) { 2653 Value symValue = symOperands[symExpr.getPosition()]; 2654 if (auto producerOp = symValue.getDefiningOp<T>()) { 2655 producerOps.push_back(producerOp); 2656 continue; 2657 } 2658 } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) { 2659 Value dimValue = dimOperands[dimExpr.getPosition()]; 2660 if (auto producerOp = dimValue.getDefiningOp<T>()) { 2661 producerOps.push_back(producerOp); 2662 continue; 2663 } 2664 } 2665 // For the above cases we will remove the expression by merging the 2666 // producer affine min/max's affine expressions. Otherwise we need to 2667 // keep the existing expression. 2668 newExprs.push_back(expr); 2669 } 2670 2671 if (producerOps.empty()) 2672 return failure(); 2673 2674 unsigned numUsedDims = oldMap.getNumDims(); 2675 unsigned numUsedSyms = oldMap.getNumSymbols(); 2676 2677 // Now go over all producer affine ops and merge their expressions. 2678 for (T producerOp : producerOps) { 2679 AffineMap producerMap = producerOp.getAffineMap(); 2680 unsigned numProducerDims = producerMap.getNumDims(); 2681 unsigned numProducerSyms = producerMap.getNumSymbols(); 2682 2683 // Collect all dimension/symbol values. 2684 ValueRange dimValues = 2685 producerOp.getMapOperands().take_front(numProducerDims); 2686 ValueRange symValues = 2687 producerOp.getMapOperands().take_back(numProducerSyms); 2688 newDimOperands.append(dimValues.begin(), dimValues.end()); 2689 newSymOperands.append(symValues.begin(), symValues.end()); 2690 2691 // For expressions we need to shift to avoid overlap. 2692 for (AffineExpr expr : producerMap.getResults()) { 2693 newExprs.push_back(expr.shiftDims(numProducerDims, numUsedDims) 2694 .shiftSymbols(numProducerSyms, numUsedSyms)); 2695 } 2696 2697 numUsedDims += numProducerDims; 2698 numUsedSyms += numProducerSyms; 2699 } 2700 2701 auto newMap = AffineMap::get(numUsedDims, numUsedSyms, newExprs, 2702 rewriter.getContext()); 2703 auto newOperands = 2704 llvm::to_vector<8>(llvm::concat<Value>(newDimOperands, newSymOperands)); 2705 rewriter.replaceOpWithNewOp<T>(affineOp, newMap, newOperands); 2706 2707 return success(); 2708 } 2709 }; 2710 2711 template <typename T> 2712 struct CanonicalizeSingleResultAffineMinMaxOp : public OpRewritePattern<T> { 2713 using OpRewritePattern<T>::OpRewritePattern; 2714 2715 LogicalResult matchAndRewrite(T affineOp, 2716 PatternRewriter &rewriter) const override { 2717 if (affineOp.map().getNumResults() != 1) 2718 return failure(); 2719 rewriter.replaceOpWithNewOp<AffineApplyOp>(affineOp, affineOp.map(), 2720 affineOp.getOperands()); 2721 return success(); 2722 } 2723 }; 2724 2725 //===----------------------------------------------------------------------===// 2726 // AffineMinOp 2727 //===----------------------------------------------------------------------===// 2728 // 2729 // %0 = affine.min (d0) -> (1000, d0 + 512) (%i0) 2730 // 2731 2732 OpFoldResult AffineMinOp::fold(ArrayRef<Attribute> operands) { 2733 return foldMinMaxOp(*this, operands); 2734 } 2735 2736 void AffineMinOp::getCanonicalizationPatterns(RewritePatternSet &patterns, 2737 MLIRContext *context) { 2738 patterns.add<CanonicalizeSingleResultAffineMinMaxOp<AffineMinOp>, 2739 DeduplicateAffineMinMaxExpressions<AffineMinOp>, 2740 MergeAffineMinMaxOp<AffineMinOp>, SimplifyAffineOp<AffineMinOp>>( 2741 context); 2742 } 2743 2744 LogicalResult AffineMinOp::verify() { return verifyAffineMinMaxOp(*this); } 2745 2746 ParseResult AffineMinOp::parse(OpAsmParser &parser, OperationState &result) { 2747 return parseAffineMinMaxOp<AffineMinOp>(parser, result); 2748 } 2749 2750 void AffineMinOp::print(OpAsmPrinter &p) { printAffineMinMaxOp(p, *this); } 2751 2752 //===----------------------------------------------------------------------===// 2753 // AffineMaxOp 2754 //===----------------------------------------------------------------------===// 2755 // 2756 // %0 = affine.max (d0) -> (1000, d0 + 512) (%i0) 2757 // 2758 2759 OpFoldResult AffineMaxOp::fold(ArrayRef<Attribute> operands) { 2760 return foldMinMaxOp(*this, operands); 2761 } 2762 2763 void AffineMaxOp::getCanonicalizationPatterns(RewritePatternSet &patterns, 2764 MLIRContext *context) { 2765 patterns.add<CanonicalizeSingleResultAffineMinMaxOp<AffineMaxOp>, 2766 DeduplicateAffineMinMaxExpressions<AffineMaxOp>, 2767 MergeAffineMinMaxOp<AffineMaxOp>, SimplifyAffineOp<AffineMaxOp>>( 2768 context); 2769 } 2770 2771 LogicalResult AffineMaxOp::verify() { return verifyAffineMinMaxOp(*this); } 2772 2773 ParseResult AffineMaxOp::parse(OpAsmParser &parser, OperationState &result) { 2774 return parseAffineMinMaxOp<AffineMaxOp>(parser, result); 2775 } 2776 2777 void AffineMaxOp::print(OpAsmPrinter &p) { printAffineMinMaxOp(p, *this); } 2778 2779 //===----------------------------------------------------------------------===// 2780 // AffinePrefetchOp 2781 //===----------------------------------------------------------------------===// 2782 2783 // 2784 // affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32> 2785 // 2786 ParseResult AffinePrefetchOp::parse(OpAsmParser &parser, 2787 OperationState &result) { 2788 auto &builder = parser.getBuilder(); 2789 auto indexTy = builder.getIndexType(); 2790 2791 MemRefType type; 2792 OpAsmParser::OperandType memrefInfo; 2793 IntegerAttr hintInfo; 2794 auto i32Type = parser.getBuilder().getIntegerType(32); 2795 StringRef readOrWrite, cacheType; 2796 2797 AffineMapAttr mapAttr; 2798 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 2799 if (parser.parseOperand(memrefInfo) || 2800 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 2801 AffinePrefetchOp::getMapAttrName(), 2802 result.attributes) || 2803 parser.parseComma() || parser.parseKeyword(&readOrWrite) || 2804 parser.parseComma() || parser.parseKeyword("locality") || 2805 parser.parseLess() || 2806 parser.parseAttribute(hintInfo, i32Type, 2807 AffinePrefetchOp::getLocalityHintAttrName(), 2808 result.attributes) || 2809 parser.parseGreater() || parser.parseComma() || 2810 parser.parseKeyword(&cacheType) || 2811 parser.parseOptionalAttrDict(result.attributes) || 2812 parser.parseColonType(type) || 2813 parser.resolveOperand(memrefInfo, type, result.operands) || 2814 parser.resolveOperands(mapOperands, indexTy, result.operands)) 2815 return failure(); 2816 2817 if (!readOrWrite.equals("read") && !readOrWrite.equals("write")) 2818 return parser.emitError(parser.getNameLoc(), 2819 "rw specifier has to be 'read' or 'write'"); 2820 result.addAttribute( 2821 AffinePrefetchOp::getIsWriteAttrName(), 2822 parser.getBuilder().getBoolAttr(readOrWrite.equals("write"))); 2823 2824 if (!cacheType.equals("data") && !cacheType.equals("instr")) 2825 return parser.emitError(parser.getNameLoc(), 2826 "cache type has to be 'data' or 'instr'"); 2827 2828 result.addAttribute( 2829 AffinePrefetchOp::getIsDataCacheAttrName(), 2830 parser.getBuilder().getBoolAttr(cacheType.equals("data"))); 2831 2832 return success(); 2833 } 2834 2835 void AffinePrefetchOp::print(OpAsmPrinter &p) { 2836 p << " " << memref() << '['; 2837 AffineMapAttr mapAttr = 2838 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName()); 2839 if (mapAttr) 2840 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 2841 p << ']' << ", " << (isWrite() ? "write" : "read") << ", " 2842 << "locality<" << localityHint() << ">, " 2843 << (isDataCache() ? "data" : "instr"); 2844 p.printOptionalAttrDict( 2845 (*this)->getAttrs(), 2846 /*elidedAttrs=*/{getMapAttrName(), getLocalityHintAttrName(), 2847 getIsDataCacheAttrName(), getIsWriteAttrName()}); 2848 p << " : " << getMemRefType(); 2849 } 2850 2851 LogicalResult AffinePrefetchOp::verify() { 2852 auto mapAttr = (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName()); 2853 if (mapAttr) { 2854 AffineMap map = mapAttr.getValue(); 2855 if (map.getNumResults() != getMemRefType().getRank()) 2856 return emitOpError("affine.prefetch affine map num results must equal" 2857 " memref rank"); 2858 if (map.getNumInputs() + 1 != getNumOperands()) 2859 return emitOpError("too few operands"); 2860 } else { 2861 if (getNumOperands() != 1) 2862 return emitOpError("too few operands"); 2863 } 2864 2865 Region *scope = getAffineScope(*this); 2866 for (auto idx : getMapOperands()) { 2867 if (!isValidAffineIndexOperand(idx, scope)) 2868 return emitOpError("index must be a dimension or symbol identifier"); 2869 } 2870 return success(); 2871 } 2872 2873 void AffinePrefetchOp::getCanonicalizationPatterns(RewritePatternSet &results, 2874 MLIRContext *context) { 2875 // prefetch(memrefcast) -> prefetch 2876 results.add<SimplifyAffineOp<AffinePrefetchOp>>(context); 2877 } 2878 2879 LogicalResult AffinePrefetchOp::fold(ArrayRef<Attribute> cstOperands, 2880 SmallVectorImpl<OpFoldResult> &results) { 2881 /// prefetch(memrefcast) -> prefetch 2882 return foldMemRefCast(*this); 2883 } 2884 2885 //===----------------------------------------------------------------------===// 2886 // AffineParallelOp 2887 //===----------------------------------------------------------------------===// 2888 2889 void AffineParallelOp::build(OpBuilder &builder, OperationState &result, 2890 TypeRange resultTypes, 2891 ArrayRef<arith::AtomicRMWKind> reductions, 2892 ArrayRef<int64_t> ranges) { 2893 SmallVector<AffineMap> lbs(ranges.size(), builder.getConstantAffineMap(0)); 2894 auto ubs = llvm::to_vector<4>(llvm::map_range(ranges, [&](int64_t value) { 2895 return builder.getConstantAffineMap(value); 2896 })); 2897 SmallVector<int64_t> steps(ranges.size(), 1); 2898 build(builder, result, resultTypes, reductions, lbs, /*lbArgs=*/{}, ubs, 2899 /*ubArgs=*/{}, steps); 2900 } 2901 2902 void AffineParallelOp::build(OpBuilder &builder, OperationState &result, 2903 TypeRange resultTypes, 2904 ArrayRef<arith::AtomicRMWKind> reductions, 2905 ArrayRef<AffineMap> lbMaps, ValueRange lbArgs, 2906 ArrayRef<AffineMap> ubMaps, ValueRange ubArgs, 2907 ArrayRef<int64_t> steps) { 2908 assert(llvm::all_of(lbMaps, 2909 [lbMaps](AffineMap m) { 2910 return m.getNumDims() == lbMaps[0].getNumDims() && 2911 m.getNumSymbols() == lbMaps[0].getNumSymbols(); 2912 }) && 2913 "expected all lower bounds maps to have the same number of dimensions " 2914 "and symbols"); 2915 assert(llvm::all_of(ubMaps, 2916 [ubMaps](AffineMap m) { 2917 return m.getNumDims() == ubMaps[0].getNumDims() && 2918 m.getNumSymbols() == ubMaps[0].getNumSymbols(); 2919 }) && 2920 "expected all upper bounds maps to have the same number of dimensions " 2921 "and symbols"); 2922 assert((lbMaps.empty() || lbMaps[0].getNumInputs() == lbArgs.size()) && 2923 "expected lower bound maps to have as many inputs as lower bound " 2924 "operands"); 2925 assert((ubMaps.empty() || ubMaps[0].getNumInputs() == ubArgs.size()) && 2926 "expected upper bound maps to have as many inputs as upper bound " 2927 "operands"); 2928 2929 result.addTypes(resultTypes); 2930 2931 // Convert the reductions to integer attributes. 2932 SmallVector<Attribute, 4> reductionAttrs; 2933 for (arith::AtomicRMWKind reduction : reductions) 2934 reductionAttrs.push_back( 2935 builder.getI64IntegerAttr(static_cast<int64_t>(reduction))); 2936 result.addAttribute(getReductionsAttrName(), 2937 builder.getArrayAttr(reductionAttrs)); 2938 2939 // Concatenates maps defined in the same input space (same dimensions and 2940 // symbols), assumes there is at least one map. 2941 auto concatMapsSameInput = [&builder](ArrayRef<AffineMap> maps, 2942 SmallVectorImpl<int32_t> &groups) { 2943 if (maps.empty()) 2944 return AffineMap::get(builder.getContext()); 2945 SmallVector<AffineExpr> exprs; 2946 groups.reserve(groups.size() + maps.size()); 2947 exprs.reserve(maps.size()); 2948 for (AffineMap m : maps) { 2949 llvm::append_range(exprs, m.getResults()); 2950 groups.push_back(m.getNumResults()); 2951 } 2952 return AffineMap::get(maps[0].getNumDims(), maps[0].getNumSymbols(), exprs, 2953 maps[0].getContext()); 2954 }; 2955 2956 // Set up the bounds. 2957 SmallVector<int32_t> lbGroups, ubGroups; 2958 AffineMap lbMap = concatMapsSameInput(lbMaps, lbGroups); 2959 AffineMap ubMap = concatMapsSameInput(ubMaps, ubGroups); 2960 result.addAttribute(getLowerBoundsMapAttrName(), AffineMapAttr::get(lbMap)); 2961 result.addAttribute(getLowerBoundsGroupsAttrName(), 2962 builder.getI32TensorAttr(lbGroups)); 2963 result.addAttribute(getUpperBoundsMapAttrName(), AffineMapAttr::get(ubMap)); 2964 result.addAttribute(getUpperBoundsGroupsAttrName(), 2965 builder.getI32TensorAttr(ubGroups)); 2966 result.addAttribute(getStepsAttrName(), builder.getI64ArrayAttr(steps)); 2967 result.addOperands(lbArgs); 2968 result.addOperands(ubArgs); 2969 2970 // Create a region and a block for the body. 2971 auto *bodyRegion = result.addRegion(); 2972 auto *body = new Block(); 2973 // Add all the block arguments. 2974 for (unsigned i = 0, e = steps.size(); i < e; ++i) 2975 body->addArgument(IndexType::get(builder.getContext()), result.location); 2976 bodyRegion->push_back(body); 2977 if (resultTypes.empty()) 2978 ensureTerminator(*bodyRegion, builder, result.location); 2979 } 2980 2981 Region &AffineParallelOp::getLoopBody() { return region(); } 2982 2983 bool AffineParallelOp::isDefinedOutsideOfLoop(Value value) { 2984 return !region().isAncestor(value.getParentRegion()); 2985 } 2986 2987 LogicalResult AffineParallelOp::moveOutOfLoop(ArrayRef<Operation *> ops) { 2988 for (Operation *op : ops) 2989 op->moveBefore(*this); 2990 return success(); 2991 } 2992 2993 unsigned AffineParallelOp::getNumDims() { return steps().size(); } 2994 2995 AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() { 2996 return getOperands().take_front(lowerBoundsMap().getNumInputs()); 2997 } 2998 2999 AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() { 3000 return getOperands().drop_front(lowerBoundsMap().getNumInputs()); 3001 } 3002 3003 AffineMap AffineParallelOp::getLowerBoundMap(unsigned pos) { 3004 auto values = lowerBoundsGroups().getValues<int32_t>(); 3005 unsigned start = 0; 3006 for (unsigned i = 0; i < pos; ++i) 3007 start += values[i]; 3008 return lowerBoundsMap().getSliceMap(start, values[pos]); 3009 } 3010 3011 AffineMap AffineParallelOp::getUpperBoundMap(unsigned pos) { 3012 auto values = upperBoundsGroups().getValues<int32_t>(); 3013 unsigned start = 0; 3014 for (unsigned i = 0; i < pos; ++i) 3015 start += values[i]; 3016 return upperBoundsMap().getSliceMap(start, values[pos]); 3017 } 3018 3019 AffineValueMap AffineParallelOp::getLowerBoundsValueMap() { 3020 return AffineValueMap(lowerBoundsMap(), getLowerBoundsOperands()); 3021 } 3022 3023 AffineValueMap AffineParallelOp::getUpperBoundsValueMap() { 3024 return AffineValueMap(upperBoundsMap(), getUpperBoundsOperands()); 3025 } 3026 3027 Optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() { 3028 if (hasMinMaxBounds()) 3029 return llvm::None; 3030 3031 // Try to convert all the ranges to constant expressions. 3032 SmallVector<int64_t, 8> out; 3033 AffineValueMap rangesValueMap; 3034 AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(), 3035 &rangesValueMap); 3036 out.reserve(rangesValueMap.getNumResults()); 3037 for (unsigned i = 0, e = rangesValueMap.getNumResults(); i < e; ++i) { 3038 auto expr = rangesValueMap.getResult(i); 3039 auto cst = expr.dyn_cast<AffineConstantExpr>(); 3040 if (!cst) 3041 return llvm::None; 3042 out.push_back(cst.getValue()); 3043 } 3044 return out; 3045 } 3046 3047 Block *AffineParallelOp::getBody() { return ®ion().front(); } 3048 3049 OpBuilder AffineParallelOp::getBodyBuilder() { 3050 return OpBuilder(getBody(), std::prev(getBody()->end())); 3051 } 3052 3053 void AffineParallelOp::setLowerBounds(ValueRange lbOperands, AffineMap map) { 3054 assert(lbOperands.size() == map.getNumInputs() && 3055 "operands to map must match number of inputs"); 3056 3057 auto ubOperands = getUpperBoundsOperands(); 3058 3059 SmallVector<Value, 4> newOperands(lbOperands); 3060 newOperands.append(ubOperands.begin(), ubOperands.end()); 3061 (*this)->setOperands(newOperands); 3062 3063 lowerBoundsMapAttr(AffineMapAttr::get(map)); 3064 } 3065 3066 void AffineParallelOp::setUpperBounds(ValueRange ubOperands, AffineMap map) { 3067 assert(ubOperands.size() == map.getNumInputs() && 3068 "operands to map must match number of inputs"); 3069 3070 SmallVector<Value, 4> newOperands(getLowerBoundsOperands()); 3071 newOperands.append(ubOperands.begin(), ubOperands.end()); 3072 (*this)->setOperands(newOperands); 3073 3074 upperBoundsMapAttr(AffineMapAttr::get(map)); 3075 } 3076 3077 void AffineParallelOp::setLowerBoundsMap(AffineMap map) { 3078 AffineMap lbMap = lowerBoundsMap(); 3079 assert(lbMap.getNumDims() == map.getNumDims() && 3080 lbMap.getNumSymbols() == map.getNumSymbols()); 3081 (void)lbMap; 3082 lowerBoundsMapAttr(AffineMapAttr::get(map)); 3083 } 3084 3085 void AffineParallelOp::setUpperBoundsMap(AffineMap map) { 3086 AffineMap ubMap = upperBoundsMap(); 3087 assert(ubMap.getNumDims() == map.getNumDims() && 3088 ubMap.getNumSymbols() == map.getNumSymbols()); 3089 (void)ubMap; 3090 upperBoundsMapAttr(AffineMapAttr::get(map)); 3091 } 3092 3093 SmallVector<int64_t, 8> AffineParallelOp::getSteps() { 3094 SmallVector<int64_t, 8> result; 3095 for (Attribute attr : steps()) { 3096 result.push_back(attr.cast<IntegerAttr>().getInt()); 3097 } 3098 return result; 3099 } 3100 3101 void AffineParallelOp::setSteps(ArrayRef<int64_t> newSteps) { 3102 stepsAttr(getBodyBuilder().getI64ArrayAttr(newSteps)); 3103 } 3104 3105 LogicalResult AffineParallelOp::verify() { 3106 auto numDims = getNumDims(); 3107 if (lowerBoundsGroups().getNumElements() != numDims || 3108 upperBoundsGroups().getNumElements() != numDims || 3109 steps().size() != numDims || getBody()->getNumArguments() != numDims) { 3110 return emitOpError() << "the number of region arguments (" 3111 << getBody()->getNumArguments() 3112 << ") and the number of map groups for lower (" 3113 << lowerBoundsGroups().getNumElements() 3114 << ") and upper bound (" 3115 << upperBoundsGroups().getNumElements() 3116 << "), and the number of steps (" << steps().size() 3117 << ") must all match"; 3118 } 3119 3120 unsigned expectedNumLBResults = 0; 3121 for (APInt v : lowerBoundsGroups()) 3122 expectedNumLBResults += v.getZExtValue(); 3123 if (expectedNumLBResults != lowerBoundsMap().getNumResults()) 3124 return emitOpError() << "expected lower bounds map to have " 3125 << expectedNumLBResults << " results"; 3126 unsigned expectedNumUBResults = 0; 3127 for (APInt v : upperBoundsGroups()) 3128 expectedNumUBResults += v.getZExtValue(); 3129 if (expectedNumUBResults != upperBoundsMap().getNumResults()) 3130 return emitOpError() << "expected upper bounds map to have " 3131 << expectedNumUBResults << " results"; 3132 3133 if (reductions().size() != getNumResults()) 3134 return emitOpError("a reduction must be specified for each output"); 3135 3136 // Verify reduction ops are all valid 3137 for (Attribute attr : reductions()) { 3138 auto intAttr = attr.dyn_cast<IntegerAttr>(); 3139 if (!intAttr || !arith::symbolizeAtomicRMWKind(intAttr.getInt())) 3140 return emitOpError("invalid reduction attribute"); 3141 } 3142 3143 // Verify that the bound operands are valid dimension/symbols. 3144 /// Lower bounds. 3145 if (failed(verifyDimAndSymbolIdentifiers(*this, getLowerBoundsOperands(), 3146 lowerBoundsMap().getNumDims()))) 3147 return failure(); 3148 /// Upper bounds. 3149 if (failed(verifyDimAndSymbolIdentifiers(*this, getUpperBoundsOperands(), 3150 upperBoundsMap().getNumDims()))) 3151 return failure(); 3152 return success(); 3153 } 3154 3155 LogicalResult AffineValueMap::canonicalize() { 3156 SmallVector<Value, 4> newOperands{operands}; 3157 auto newMap = getAffineMap(); 3158 composeAffineMapAndOperands(&newMap, &newOperands); 3159 if (newMap == getAffineMap() && newOperands == operands) 3160 return failure(); 3161 reset(newMap, newOperands); 3162 return success(); 3163 } 3164 3165 /// Canonicalize the bounds of the given loop. 3166 static LogicalResult canonicalizeLoopBounds(AffineParallelOp op) { 3167 AffineValueMap lb = op.getLowerBoundsValueMap(); 3168 bool lbCanonicalized = succeeded(lb.canonicalize()); 3169 3170 AffineValueMap ub = op.getUpperBoundsValueMap(); 3171 bool ubCanonicalized = succeeded(ub.canonicalize()); 3172 3173 // Any canonicalization change always leads to updated map(s). 3174 if (!lbCanonicalized && !ubCanonicalized) 3175 return failure(); 3176 3177 if (lbCanonicalized) 3178 op.setLowerBounds(lb.getOperands(), lb.getAffineMap()); 3179 if (ubCanonicalized) 3180 op.setUpperBounds(ub.getOperands(), ub.getAffineMap()); 3181 3182 return success(); 3183 } 3184 3185 LogicalResult AffineParallelOp::fold(ArrayRef<Attribute> operands, 3186 SmallVectorImpl<OpFoldResult> &results) { 3187 return canonicalizeLoopBounds(*this); 3188 } 3189 3190 /// Prints a lower(upper) bound of an affine parallel loop with max(min) 3191 /// conditions in it. `mapAttr` is a flat list of affine expressions and `group` 3192 /// identifies which of the those expressions form max/min groups. `operands` 3193 /// are the SSA values of dimensions and symbols and `keyword` is either "min" 3194 /// or "max". 3195 static void printMinMaxBound(OpAsmPrinter &p, AffineMapAttr mapAttr, 3196 DenseIntElementsAttr group, ValueRange operands, 3197 StringRef keyword) { 3198 AffineMap map = mapAttr.getValue(); 3199 unsigned numDims = map.getNumDims(); 3200 ValueRange dimOperands = operands.take_front(numDims); 3201 ValueRange symOperands = operands.drop_front(numDims); 3202 unsigned start = 0; 3203 for (llvm::APInt groupSize : group) { 3204 if (start != 0) 3205 p << ", "; 3206 3207 unsigned size = groupSize.getZExtValue(); 3208 if (size == 1) { 3209 p.printAffineExprOfSSAIds(map.getResult(start), dimOperands, symOperands); 3210 ++start; 3211 } else { 3212 p << keyword << '('; 3213 AffineMap submap = map.getSliceMap(start, size); 3214 p.printAffineMapOfSSAIds(AffineMapAttr::get(submap), operands); 3215 p << ')'; 3216 start += size; 3217 } 3218 } 3219 } 3220 3221 void AffineParallelOp::print(OpAsmPrinter &p) { 3222 p << " (" << getBody()->getArguments() << ") = ("; 3223 printMinMaxBound(p, lowerBoundsMapAttr(), lowerBoundsGroupsAttr(), 3224 getLowerBoundsOperands(), "max"); 3225 p << ") to ("; 3226 printMinMaxBound(p, upperBoundsMapAttr(), upperBoundsGroupsAttr(), 3227 getUpperBoundsOperands(), "min"); 3228 p << ')'; 3229 SmallVector<int64_t, 8> steps = getSteps(); 3230 bool elideSteps = llvm::all_of(steps, [](int64_t step) { return step == 1; }); 3231 if (!elideSteps) { 3232 p << " step ("; 3233 llvm::interleaveComma(steps, p); 3234 p << ')'; 3235 } 3236 if (getNumResults()) { 3237 p << " reduce ("; 3238 llvm::interleaveComma(reductions(), p, [&](auto &attr) { 3239 arith::AtomicRMWKind sym = *arith::symbolizeAtomicRMWKind( 3240 attr.template cast<IntegerAttr>().getInt()); 3241 p << "\"" << arith::stringifyAtomicRMWKind(sym) << "\""; 3242 }); 3243 p << ") -> (" << getResultTypes() << ")"; 3244 } 3245 3246 p << ' '; 3247 p.printRegion(region(), /*printEntryBlockArgs=*/false, 3248 /*printBlockTerminators=*/getNumResults()); 3249 p.printOptionalAttrDict( 3250 (*this)->getAttrs(), 3251 /*elidedAttrs=*/{AffineParallelOp::getReductionsAttrName(), 3252 AffineParallelOp::getLowerBoundsMapAttrName(), 3253 AffineParallelOp::getLowerBoundsGroupsAttrName(), 3254 AffineParallelOp::getUpperBoundsMapAttrName(), 3255 AffineParallelOp::getUpperBoundsGroupsAttrName(), 3256 AffineParallelOp::getStepsAttrName()}); 3257 } 3258 3259 /// Given a list of lists of parsed operands, populates `uniqueOperands` with 3260 /// unique operands. Also populates `replacements with affine expressions of 3261 /// `kind` that can be used to update affine maps previously accepting a 3262 /// `operands` to accept `uniqueOperands` instead. 3263 static void deduplicateAndResolveOperands( 3264 OpAsmParser &parser, 3265 ArrayRef<SmallVector<OpAsmParser::OperandType>> operands, 3266 SmallVectorImpl<Value> &uniqueOperands, 3267 SmallVectorImpl<AffineExpr> &replacements, AffineExprKind kind) { 3268 assert((kind == AffineExprKind::DimId || kind == AffineExprKind::SymbolId) && 3269 "expected operands to be dim or symbol expression"); 3270 3271 Type indexType = parser.getBuilder().getIndexType(); 3272 for (const auto &list : operands) { 3273 SmallVector<Value> valueOperands; 3274 parser.resolveOperands(list, indexType, valueOperands); 3275 for (Value operand : valueOperands) { 3276 unsigned pos = std::distance(uniqueOperands.begin(), 3277 llvm::find(uniqueOperands, operand)); 3278 if (pos == uniqueOperands.size()) 3279 uniqueOperands.push_back(operand); 3280 replacements.push_back( 3281 kind == AffineExprKind::DimId 3282 ? getAffineDimExpr(pos, parser.getContext()) 3283 : getAffineSymbolExpr(pos, parser.getContext())); 3284 } 3285 } 3286 } 3287 3288 namespace { 3289 enum class MinMaxKind { Min, Max }; 3290 } // namespace 3291 3292 /// Parses an affine map that can contain a min/max for groups of its results, 3293 /// e.g., max(expr-1, expr-2), expr-3, max(expr-4, expr-5, expr-6). Populates 3294 /// `result` attributes with the map (flat list of expressions) and the grouping 3295 /// (list of integers that specify how many expressions to put into each 3296 /// min/max) attributes. Deduplicates repeated operands. 3297 /// 3298 /// parallel-bound ::= `(` parallel-group-list `)` 3299 /// parallel-group-list ::= parallel-group (`,` parallel-group-list)? 3300 /// parallel-group ::= simple-group | min-max-group 3301 /// simple-group ::= expr-of-ssa-ids 3302 /// min-max-group ::= ( `min` | `max` ) `(` expr-of-ssa-ids-list `)` 3303 /// expr-of-ssa-ids-list ::= expr-of-ssa-ids (`,` expr-of-ssa-id-list)? 3304 /// 3305 /// Examples: 3306 /// (%0, min(%1 + %2, %3), %4, min(%5 floordiv 32, %6)) 3307 /// (%0, max(%1 - 2 * %2)) 3308 static ParseResult parseAffineMapWithMinMax(OpAsmParser &parser, 3309 OperationState &result, 3310 MinMaxKind kind) { 3311 constexpr llvm::StringLiteral tmpAttrName = "__pseudo_bound_map"; 3312 3313 StringRef mapName = kind == MinMaxKind::Min 3314 ? AffineParallelOp::getUpperBoundsMapAttrName() 3315 : AffineParallelOp::getLowerBoundsMapAttrName(); 3316 StringRef groupsName = kind == MinMaxKind::Min 3317 ? AffineParallelOp::getUpperBoundsGroupsAttrName() 3318 : AffineParallelOp::getLowerBoundsGroupsAttrName(); 3319 3320 if (failed(parser.parseLParen())) 3321 return failure(); 3322 3323 if (succeeded(parser.parseOptionalRParen())) { 3324 result.addAttribute( 3325 mapName, AffineMapAttr::get(parser.getBuilder().getEmptyAffineMap())); 3326 result.addAttribute(groupsName, parser.getBuilder().getI32TensorAttr({})); 3327 return success(); 3328 } 3329 3330 SmallVector<AffineExpr> flatExprs; 3331 SmallVector<SmallVector<OpAsmParser::OperandType>> flatDimOperands; 3332 SmallVector<SmallVector<OpAsmParser::OperandType>> flatSymOperands; 3333 SmallVector<int32_t> numMapsPerGroup; 3334 SmallVector<OpAsmParser::OperandType> mapOperands; 3335 do { 3336 if (succeeded(parser.parseOptionalKeyword( 3337 kind == MinMaxKind::Min ? "min" : "max"))) { 3338 mapOperands.clear(); 3339 AffineMapAttr map; 3340 if (failed(parser.parseAffineMapOfSSAIds(mapOperands, map, tmpAttrName, 3341 result.attributes, 3342 OpAsmParser::Delimiter::Paren))) 3343 return failure(); 3344 result.attributes.erase(tmpAttrName); 3345 llvm::append_range(flatExprs, map.getValue().getResults()); 3346 auto operandsRef = llvm::makeArrayRef(mapOperands); 3347 auto dimsRef = operandsRef.take_front(map.getValue().getNumDims()); 3348 SmallVector<OpAsmParser::OperandType> dims(dimsRef.begin(), 3349 dimsRef.end()); 3350 auto symsRef = operandsRef.drop_front(map.getValue().getNumDims()); 3351 SmallVector<OpAsmParser::OperandType> syms(symsRef.begin(), 3352 symsRef.end()); 3353 flatDimOperands.append(map.getValue().getNumResults(), dims); 3354 flatSymOperands.append(map.getValue().getNumResults(), syms); 3355 numMapsPerGroup.push_back(map.getValue().getNumResults()); 3356 } else { 3357 if (failed(parser.parseAffineExprOfSSAIds(flatDimOperands.emplace_back(), 3358 flatSymOperands.emplace_back(), 3359 flatExprs.emplace_back()))) 3360 return failure(); 3361 numMapsPerGroup.push_back(1); 3362 } 3363 } while (succeeded(parser.parseOptionalComma())); 3364 3365 if (failed(parser.parseRParen())) 3366 return failure(); 3367 3368 unsigned totalNumDims = 0; 3369 unsigned totalNumSyms = 0; 3370 for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) { 3371 unsigned numDims = flatDimOperands[i].size(); 3372 unsigned numSyms = flatSymOperands[i].size(); 3373 flatExprs[i] = flatExprs[i] 3374 .shiftDims(numDims, totalNumDims) 3375 .shiftSymbols(numSyms, totalNumSyms); 3376 totalNumDims += numDims; 3377 totalNumSyms += numSyms; 3378 } 3379 3380 // Deduplicate map operands. 3381 SmallVector<Value> dimOperands, symOperands; 3382 SmallVector<AffineExpr> dimRplacements, symRepacements; 3383 deduplicateAndResolveOperands(parser, flatDimOperands, dimOperands, 3384 dimRplacements, AffineExprKind::DimId); 3385 deduplicateAndResolveOperands(parser, flatSymOperands, symOperands, 3386 symRepacements, AffineExprKind::SymbolId); 3387 3388 result.operands.append(dimOperands.begin(), dimOperands.end()); 3389 result.operands.append(symOperands.begin(), symOperands.end()); 3390 3391 Builder &builder = parser.getBuilder(); 3392 auto flatMap = AffineMap::get(totalNumDims, totalNumSyms, flatExprs, 3393 parser.getContext()); 3394 flatMap = flatMap.replaceDimsAndSymbols( 3395 dimRplacements, symRepacements, dimOperands.size(), symOperands.size()); 3396 3397 result.addAttribute(mapName, AffineMapAttr::get(flatMap)); 3398 result.addAttribute(groupsName, builder.getI32TensorAttr(numMapsPerGroup)); 3399 return success(); 3400 } 3401 3402 // 3403 // operation ::= `affine.parallel` `(` ssa-ids `)` `=` parallel-bound 3404 // `to` parallel-bound steps? region attr-dict? 3405 // steps ::= `steps` `(` integer-literals `)` 3406 // 3407 ParseResult AffineParallelOp::parse(OpAsmParser &parser, 3408 OperationState &result) { 3409 auto &builder = parser.getBuilder(); 3410 auto indexType = builder.getIndexType(); 3411 SmallVector<OpAsmParser::OperandType, 4> ivs; 3412 if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1, 3413 OpAsmParser::Delimiter::Paren) || 3414 parser.parseEqual() || 3415 parseAffineMapWithMinMax(parser, result, MinMaxKind::Max) || 3416 parser.parseKeyword("to") || 3417 parseAffineMapWithMinMax(parser, result, MinMaxKind::Min)) 3418 return failure(); 3419 3420 AffineMapAttr stepsMapAttr; 3421 NamedAttrList stepsAttrs; 3422 SmallVector<OpAsmParser::OperandType, 4> stepsMapOperands; 3423 if (failed(parser.parseOptionalKeyword("step"))) { 3424 SmallVector<int64_t, 4> steps(ivs.size(), 1); 3425 result.addAttribute(AffineParallelOp::getStepsAttrName(), 3426 builder.getI64ArrayAttr(steps)); 3427 } else { 3428 if (parser.parseAffineMapOfSSAIds(stepsMapOperands, stepsMapAttr, 3429 AffineParallelOp::getStepsAttrName(), 3430 stepsAttrs, 3431 OpAsmParser::Delimiter::Paren)) 3432 return failure(); 3433 3434 // Convert steps from an AffineMap into an I64ArrayAttr. 3435 SmallVector<int64_t, 4> steps; 3436 auto stepsMap = stepsMapAttr.getValue(); 3437 for (const auto &result : stepsMap.getResults()) { 3438 auto constExpr = result.dyn_cast<AffineConstantExpr>(); 3439 if (!constExpr) 3440 return parser.emitError(parser.getNameLoc(), 3441 "steps must be constant integers"); 3442 steps.push_back(constExpr.getValue()); 3443 } 3444 result.addAttribute(AffineParallelOp::getStepsAttrName(), 3445 builder.getI64ArrayAttr(steps)); 3446 } 3447 3448 // Parse optional clause of the form: `reduce ("addf", "maxf")`, where the 3449 // quoted strings are a member of the enum AtomicRMWKind. 3450 SmallVector<Attribute, 4> reductions; 3451 if (succeeded(parser.parseOptionalKeyword("reduce"))) { 3452 if (parser.parseLParen()) 3453 return failure(); 3454 do { 3455 // Parse a single quoted string via the attribute parsing, and then 3456 // verify it is a member of the enum and convert to it's integer 3457 // representation. 3458 StringAttr attrVal; 3459 NamedAttrList attrStorage; 3460 auto loc = parser.getCurrentLocation(); 3461 if (parser.parseAttribute(attrVal, builder.getNoneType(), "reduce", 3462 attrStorage)) 3463 return failure(); 3464 llvm::Optional<arith::AtomicRMWKind> reduction = 3465 arith::symbolizeAtomicRMWKind(attrVal.getValue()); 3466 if (!reduction) 3467 return parser.emitError(loc, "invalid reduction value: ") << attrVal; 3468 reductions.push_back(builder.getI64IntegerAttr( 3469 static_cast<int64_t>(reduction.getValue()))); 3470 // While we keep getting commas, keep parsing. 3471 } while (succeeded(parser.parseOptionalComma())); 3472 if (parser.parseRParen()) 3473 return failure(); 3474 } 3475 result.addAttribute(AffineParallelOp::getReductionsAttrName(), 3476 builder.getArrayAttr(reductions)); 3477 3478 // Parse return types of reductions (if any) 3479 if (parser.parseOptionalArrowTypeList(result.types)) 3480 return failure(); 3481 3482 // Now parse the body. 3483 Region *body = result.addRegion(); 3484 SmallVector<Type, 4> types(ivs.size(), indexType); 3485 if (parser.parseRegion(*body, ivs, types) || 3486 parser.parseOptionalAttrDict(result.attributes)) 3487 return failure(); 3488 3489 // Add a terminator if none was parsed. 3490 AffineParallelOp::ensureTerminator(*body, builder, result.location); 3491 return success(); 3492 } 3493 3494 //===----------------------------------------------------------------------===// 3495 // AffineYieldOp 3496 //===----------------------------------------------------------------------===// 3497 3498 LogicalResult AffineYieldOp::verify() { 3499 auto *parentOp = (*this)->getParentOp(); 3500 auto results = parentOp->getResults(); 3501 auto operands = getOperands(); 3502 3503 if (!isa<AffineParallelOp, AffineIfOp, AffineForOp>(parentOp)) 3504 return emitOpError() << "only terminates affine.if/for/parallel regions"; 3505 if (parentOp->getNumResults() != getNumOperands()) 3506 return emitOpError() << "parent of yield must have same number of " 3507 "results as the yield operands"; 3508 for (auto it : llvm::zip(results, operands)) { 3509 if (std::get<0>(it).getType() != std::get<1>(it).getType()) 3510 return emitOpError() << "types mismatch between yield op and its parent"; 3511 } 3512 3513 return success(); 3514 } 3515 3516 //===----------------------------------------------------------------------===// 3517 // AffineVectorLoadOp 3518 //===----------------------------------------------------------------------===// 3519 3520 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result, 3521 VectorType resultType, AffineMap map, 3522 ValueRange operands) { 3523 assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands"); 3524 result.addOperands(operands); 3525 if (map) 3526 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 3527 result.types.push_back(resultType); 3528 } 3529 3530 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result, 3531 VectorType resultType, Value memref, 3532 AffineMap map, ValueRange mapOperands) { 3533 assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info"); 3534 result.addOperands(memref); 3535 result.addOperands(mapOperands); 3536 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 3537 result.types.push_back(resultType); 3538 } 3539 3540 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result, 3541 VectorType resultType, Value memref, 3542 ValueRange indices) { 3543 auto memrefType = memref.getType().cast<MemRefType>(); 3544 int64_t rank = memrefType.getRank(); 3545 // Create identity map for memrefs with at least one dimension or () -> () 3546 // for zero-dimensional memrefs. 3547 auto map = 3548 rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap(); 3549 build(builder, result, resultType, memref, map, indices); 3550 } 3551 3552 void AffineVectorLoadOp::getCanonicalizationPatterns(RewritePatternSet &results, 3553 MLIRContext *context) { 3554 results.add<SimplifyAffineOp<AffineVectorLoadOp>>(context); 3555 } 3556 3557 ParseResult AffineVectorLoadOp::parse(OpAsmParser &parser, 3558 OperationState &result) { 3559 auto &builder = parser.getBuilder(); 3560 auto indexTy = builder.getIndexType(); 3561 3562 MemRefType memrefType; 3563 VectorType resultType; 3564 OpAsmParser::OperandType memrefInfo; 3565 AffineMapAttr mapAttr; 3566 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 3567 return failure( 3568 parser.parseOperand(memrefInfo) || 3569 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 3570 AffineVectorLoadOp::getMapAttrName(), 3571 result.attributes) || 3572 parser.parseOptionalAttrDict(result.attributes) || 3573 parser.parseColonType(memrefType) || parser.parseComma() || 3574 parser.parseType(resultType) || 3575 parser.resolveOperand(memrefInfo, memrefType, result.operands) || 3576 parser.resolveOperands(mapOperands, indexTy, result.operands) || 3577 parser.addTypeToList(resultType, result.types)); 3578 } 3579 3580 void AffineVectorLoadOp::print(OpAsmPrinter &p) { 3581 p << " " << getMemRef() << '['; 3582 if (AffineMapAttr mapAttr = 3583 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName())) 3584 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 3585 p << ']'; 3586 p.printOptionalAttrDict((*this)->getAttrs(), 3587 /*elidedAttrs=*/{getMapAttrName()}); 3588 p << " : " << getMemRefType() << ", " << getType(); 3589 } 3590 3591 /// Verify common invariants of affine.vector_load and affine.vector_store. 3592 static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, 3593 VectorType vectorType) { 3594 // Check that memref and vector element types match. 3595 if (memrefType.getElementType() != vectorType.getElementType()) 3596 return op->emitOpError( 3597 "requires memref and vector types of the same elemental type"); 3598 return success(); 3599 } 3600 3601 LogicalResult AffineVectorLoadOp::verify() { 3602 MemRefType memrefType = getMemRefType(); 3603 if (failed(verifyMemoryOpIndexing( 3604 getOperation(), 3605 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName()), 3606 getMapOperands(), memrefType, 3607 /*numIndexOperands=*/getNumOperands() - 1))) 3608 return failure(); 3609 3610 if (failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) 3611 return failure(); 3612 3613 return success(); 3614 } 3615 3616 //===----------------------------------------------------------------------===// 3617 // AffineVectorStoreOp 3618 //===----------------------------------------------------------------------===// 3619 3620 void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result, 3621 Value valueToStore, Value memref, AffineMap map, 3622 ValueRange mapOperands) { 3623 assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info"); 3624 result.addOperands(valueToStore); 3625 result.addOperands(memref); 3626 result.addOperands(mapOperands); 3627 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 3628 } 3629 3630 // Use identity map. 3631 void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result, 3632 Value valueToStore, Value memref, 3633 ValueRange indices) { 3634 auto memrefType = memref.getType().cast<MemRefType>(); 3635 int64_t rank = memrefType.getRank(); 3636 // Create identity map for memrefs with at least one dimension or () -> () 3637 // for zero-dimensional memrefs. 3638 auto map = 3639 rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap(); 3640 build(builder, result, valueToStore, memref, map, indices); 3641 } 3642 void AffineVectorStoreOp::getCanonicalizationPatterns( 3643 RewritePatternSet &results, MLIRContext *context) { 3644 results.add<SimplifyAffineOp<AffineVectorStoreOp>>(context); 3645 } 3646 3647 ParseResult AffineVectorStoreOp::parse(OpAsmParser &parser, 3648 OperationState &result) { 3649 auto indexTy = parser.getBuilder().getIndexType(); 3650 3651 MemRefType memrefType; 3652 VectorType resultType; 3653 OpAsmParser::OperandType storeValueInfo; 3654 OpAsmParser::OperandType memrefInfo; 3655 AffineMapAttr mapAttr; 3656 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 3657 return failure( 3658 parser.parseOperand(storeValueInfo) || parser.parseComma() || 3659 parser.parseOperand(memrefInfo) || 3660 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 3661 AffineVectorStoreOp::getMapAttrName(), 3662 result.attributes) || 3663 parser.parseOptionalAttrDict(result.attributes) || 3664 parser.parseColonType(memrefType) || parser.parseComma() || 3665 parser.parseType(resultType) || 3666 parser.resolveOperand(storeValueInfo, resultType, result.operands) || 3667 parser.resolveOperand(memrefInfo, memrefType, result.operands) || 3668 parser.resolveOperands(mapOperands, indexTy, result.operands)); 3669 } 3670 3671 void AffineVectorStoreOp::print(OpAsmPrinter &p) { 3672 p << " " << getValueToStore(); 3673 p << ", " << getMemRef() << '['; 3674 if (AffineMapAttr mapAttr = 3675 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName())) 3676 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 3677 p << ']'; 3678 p.printOptionalAttrDict((*this)->getAttrs(), 3679 /*elidedAttrs=*/{getMapAttrName()}); 3680 p << " : " << getMemRefType() << ", " << getValueToStore().getType(); 3681 } 3682 3683 LogicalResult AffineVectorStoreOp::verify() { 3684 MemRefType memrefType = getMemRefType(); 3685 if (failed(verifyMemoryOpIndexing( 3686 *this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrName()), 3687 getMapOperands(), memrefType, 3688 /*numIndexOperands=*/getNumOperands() - 2))) 3689 return failure(); 3690 3691 if (failed(verifyVectorMemoryOp(*this, memrefType, getVectorType()))) 3692 return failure(); 3693 3694 return success(); 3695 } 3696 3697 //===----------------------------------------------------------------------===// 3698 // TableGen'd op method definitions 3699 //===----------------------------------------------------------------------===// 3700 3701 #define GET_OP_CLASSES 3702 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc" 3703