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