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