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