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