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