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