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/StandardOps/IR/Ops.h" 12 #include "mlir/IR/Function.h" 13 #include "mlir/IR/IntegerSet.h" 14 #include "mlir/IR/Matchers.h" 15 #include "mlir/IR/OpImplementation.h" 16 #include "mlir/IR/PatternMatch.h" 17 #include "mlir/Transforms/InliningUtils.h" 18 #include "llvm/ADT/SetVector.h" 19 #include "llvm/ADT/SmallBitVector.h" 20 #include "llvm/Support/Debug.h" 21 22 using namespace mlir; 23 using llvm::dbgs; 24 25 #define DEBUG_TYPE "affine-analysis" 26 27 //===----------------------------------------------------------------------===// 28 // AffineDialect Interfaces 29 //===----------------------------------------------------------------------===// 30 31 namespace { 32 /// This class defines the interface for handling inlining with affine 33 /// operations. 34 struct AffineInlinerInterface : public DialectInlinerInterface { 35 using DialectInlinerInterface::DialectInlinerInterface; 36 37 //===--------------------------------------------------------------------===// 38 // Analysis Hooks 39 //===--------------------------------------------------------------------===// 40 41 /// Returns true if the given region 'src' can be inlined into the region 42 /// 'dest' that is attached to an operation registered to the current dialect. 43 bool isLegalToInline(Region *dest, Region *src, 44 BlockAndValueMapping &valueMapping) const final { 45 // Conservatively don't allow inlining into affine structures. 46 return false; 47 } 48 49 /// Returns true if the given operation 'op', that is registered to this 50 /// dialect, can be inlined into the given region, false otherwise. 51 bool isLegalToInline(Operation *op, Region *region, 52 BlockAndValueMapping &valueMapping) const final { 53 // Always allow inlining affine operations into the top-level region of a 54 // function. There are some edge cases when inlining *into* affine 55 // structures, but that is handled in the other 'isLegalToInline' hook 56 // above. 57 // TODO: We should be able to inline into other regions than functions. 58 return isa<FuncOp>(region->getParentOp()); 59 } 60 61 /// Affine regions should be analyzed recursively. 62 bool shouldAnalyzeRecursively(Operation *op) const final { return true; } 63 }; 64 } // end anonymous namespace 65 66 //===----------------------------------------------------------------------===// 67 // AffineDialect 68 //===----------------------------------------------------------------------===// 69 70 AffineDialect::AffineDialect(MLIRContext *context) 71 : Dialect(getDialectNamespace(), context) { 72 addOperations<AffineDmaStartOp, AffineDmaWaitOp, AffineLoadOp, AffineStoreOp, 73 #define GET_OP_LIST 74 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc" 75 >(); 76 addInterfaces<AffineInlinerInterface>(); 77 } 78 79 /// Materialize a single constant operation from a given attribute value with 80 /// the desired resultant type. 81 Operation *AffineDialect::materializeConstant(OpBuilder &builder, 82 Attribute value, Type type, 83 Location loc) { 84 return builder.create<ConstantOp>(loc, type, value); 85 } 86 87 /// A utility function to check if a given region is attached to a function. 88 static bool isFunctionRegion(Region *region) { 89 return llvm::isa<FuncOp>(region->getParentOp()); 90 } 91 92 /// A utility function to check if a value is defined at the top level of a 93 /// function. A value of index type defined at the top level is always a valid 94 /// symbol. 95 bool mlir::isTopLevelValue(Value value) { 96 if (auto arg = value.dyn_cast<BlockArgument>()) 97 return isFunctionRegion(arg.getOwner()->getParent()); 98 return isFunctionRegion(value.getDefiningOp()->getParentRegion()); 99 } 100 101 // Value can be used as a dimension id if it is valid as a symbol, or 102 // it is an induction variable, or it is a result of affine apply operation 103 // with dimension id arguments. 104 bool mlir::isValidDim(Value value) { 105 // The value must be an index type. 106 if (!value.getType().isIndex()) 107 return false; 108 109 if (auto *op = value.getDefiningOp()) { 110 // Top level operation or constant operation is ok. 111 if (isFunctionRegion(op->getParentRegion()) || isa<ConstantOp>(op)) 112 return true; 113 // Affine apply operation is ok if all of its operands are ok. 114 if (auto applyOp = dyn_cast<AffineApplyOp>(op)) 115 return applyOp.isValidDim(); 116 // The dim op is okay if its operand memref/tensor is defined at the top 117 // level. 118 if (auto dimOp = dyn_cast<DimOp>(op)) 119 return isTopLevelValue(dimOp.getOperand()); 120 return false; 121 } 122 // This value has to be a block argument of a FuncOp, an 'affine.for', or an 123 // 'affine.parallel'. 124 auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp(); 125 return isa<FuncOp>(parentOp) || isa<AffineForOp>(parentOp) || 126 isa<AffineParallelOp>(parentOp); 127 } 128 129 /// Returns true if the 'index' dimension of the `memref` defined by 130 /// `memrefDefOp` is a statically shaped one or defined using a valid symbol. 131 template <typename AnyMemRefDefOp> 132 static bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, 133 unsigned index) { 134 auto memRefType = memrefDefOp.getType(); 135 // Statically shaped. 136 if (!ShapedType::isDynamic(memRefType.getDimSize(index))) 137 return true; 138 // Get the position of the dimension among dynamic dimensions; 139 unsigned dynamicDimPos = memRefType.getDynamicDimIndex(index); 140 return isValidSymbol( 141 *(memrefDefOp.getDynamicSizes().begin() + dynamicDimPos)); 142 } 143 144 /// Returns true if the result of the dim op is a valid symbol. 145 static bool isDimOpValidSymbol(DimOp dimOp) { 146 // The dim op is okay if its operand memref/tensor is defined at the top 147 // level. 148 if (isTopLevelValue(dimOp.getOperand())) 149 return true; 150 151 // The dim op is also okay if its operand memref/tensor is a view/subview 152 // whose corresponding size is a valid symbol. 153 unsigned index = dimOp.getIndex(); 154 if (auto viewOp = dyn_cast<ViewOp>(dimOp.getOperand().getDefiningOp())) 155 return isMemRefSizeValidSymbol<ViewOp>(viewOp, index); 156 if (auto subViewOp = dyn_cast<SubViewOp>(dimOp.getOperand().getDefiningOp())) 157 return isMemRefSizeValidSymbol<SubViewOp>(subViewOp, index); 158 if (auto allocOp = dyn_cast<AllocOp>(dimOp.getOperand().getDefiningOp())) 159 return isMemRefSizeValidSymbol<AllocOp>(allocOp, index); 160 return false; 161 } 162 163 // Value can be used as a symbol if it is a constant, or it is defined at 164 // the top level, or it is a result of affine apply operation with symbol 165 // arguments, or a result of the dim op on a memref satisfying certain 166 // constraints. 167 bool mlir::isValidSymbol(Value value) { 168 // The value must be an index type. 169 if (!value.getType().isIndex()) 170 return false; 171 172 if (auto *op = value.getDefiningOp()) { 173 // Top level operation or constant operation is ok. 174 if (isFunctionRegion(op->getParentRegion()) || isa<ConstantOp>(op)) 175 return true; 176 // Affine apply operation is ok if all of its operands are ok. 177 if (auto applyOp = dyn_cast<AffineApplyOp>(op)) 178 return applyOp.isValidSymbol(); 179 if (auto dimOp = dyn_cast<DimOp>(op)) { 180 return isDimOpValidSymbol(dimOp); 181 } 182 } 183 // Otherwise, check that the value is a top level value. 184 return isTopLevelValue(value); 185 } 186 187 // Returns true if 'value' is a valid index to an affine operation (e.g. 188 // affine.load, affine.store, affine.dma_start, affine.dma_wait). 189 // Returns false otherwise. 190 static bool isValidAffineIndexOperand(Value value) { 191 return isValidDim(value) || isValidSymbol(value); 192 } 193 194 /// Utility function to verify that a set of operands are valid dimension and 195 /// symbol identifiers. The operands should be laid out such that the dimension 196 /// operands are before the symbol operands. This function returns failure if 197 /// there was an invalid operand. An operation is provided to emit any necessary 198 /// errors. 199 template <typename OpTy> 200 static LogicalResult 201 verifyDimAndSymbolIdentifiers(OpTy &op, Operation::operand_range operands, 202 unsigned numDims) { 203 unsigned opIt = 0; 204 for (auto operand : operands) { 205 if (opIt++ < numDims) { 206 if (!isValidDim(operand)) 207 return op.emitOpError("operand cannot be used as a dimension id"); 208 } else if (!isValidSymbol(operand)) { 209 return op.emitOpError("operand cannot be used as a symbol"); 210 } 211 } 212 return success(); 213 } 214 215 //===----------------------------------------------------------------------===// 216 // AffineApplyOp 217 //===----------------------------------------------------------------------===// 218 219 AffineValueMap AffineApplyOp::getAffineValueMap() { 220 return AffineValueMap(getAffineMap(), getOperands(), getResult()); 221 } 222 223 static ParseResult parseAffineApplyOp(OpAsmParser &parser, 224 OperationState &result) { 225 auto &builder = parser.getBuilder(); 226 auto indexTy = builder.getIndexType(); 227 228 AffineMapAttr mapAttr; 229 unsigned numDims; 230 if (parser.parseAttribute(mapAttr, "map", result.attributes) || 231 parseDimAndSymbolList(parser, result.operands, numDims) || 232 parser.parseOptionalAttrDict(result.attributes)) 233 return failure(); 234 auto map = mapAttr.getValue(); 235 236 if (map.getNumDims() != numDims || 237 numDims + map.getNumSymbols() != result.operands.size()) { 238 return parser.emitError(parser.getNameLoc(), 239 "dimension or symbol index mismatch"); 240 } 241 242 result.types.append(map.getNumResults(), indexTy); 243 return success(); 244 } 245 246 static void print(OpAsmPrinter &p, AffineApplyOp op) { 247 p << AffineApplyOp::getOperationName() << " " << op.mapAttr(); 248 printDimAndSymbolList(op.operand_begin(), op.operand_end(), 249 op.getAffineMap().getNumDims(), p); 250 p.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"map"}); 251 } 252 253 static LogicalResult verify(AffineApplyOp op) { 254 // Check input and output dimensions match. 255 auto map = op.map(); 256 257 // Verify that operand count matches affine map dimension and symbol count. 258 if (op.getNumOperands() != map.getNumDims() + map.getNumSymbols()) 259 return op.emitOpError( 260 "operand count and affine map dimension and symbol count must match"); 261 262 // Verify that the map only produces one result. 263 if (map.getNumResults() != 1) 264 return op.emitOpError("mapping must produce one value"); 265 266 return success(); 267 } 268 269 // The result of the affine apply operation can be used as a dimension id if all 270 // its operands are valid dimension ids. 271 bool AffineApplyOp::isValidDim() { 272 return llvm::all_of(getOperands(), 273 [](Value op) { return mlir::isValidDim(op); }); 274 } 275 276 // The result of the affine apply operation can be used as a symbol if all its 277 // operands are symbols. 278 bool AffineApplyOp::isValidSymbol() { 279 return llvm::all_of(getOperands(), 280 [](Value op) { return mlir::isValidSymbol(op); }); 281 } 282 283 OpFoldResult AffineApplyOp::fold(ArrayRef<Attribute> operands) { 284 auto map = getAffineMap(); 285 286 // Fold dims and symbols to existing values. 287 auto expr = map.getResult(0); 288 if (auto dim = expr.dyn_cast<AffineDimExpr>()) 289 return getOperand(dim.getPosition()); 290 if (auto sym = expr.dyn_cast<AffineSymbolExpr>()) 291 return getOperand(map.getNumDims() + sym.getPosition()); 292 293 // Otherwise, default to folding the map. 294 SmallVector<Attribute, 1> result; 295 if (failed(map.constantFold(operands, result))) 296 return {}; 297 return result[0]; 298 } 299 300 AffineDimExpr AffineApplyNormalizer::renumberOneDim(Value v) { 301 DenseMap<Value, unsigned>::iterator iterPos; 302 bool inserted = false; 303 std::tie(iterPos, inserted) = 304 dimValueToPosition.insert(std::make_pair(v, dimValueToPosition.size())); 305 if (inserted) { 306 reorderedDims.push_back(v); 307 } 308 return getAffineDimExpr(iterPos->second, v.getContext()) 309 .cast<AffineDimExpr>(); 310 } 311 312 AffineMap AffineApplyNormalizer::renumber(const AffineApplyNormalizer &other) { 313 SmallVector<AffineExpr, 8> dimRemapping; 314 for (auto v : other.reorderedDims) { 315 auto kvp = other.dimValueToPosition.find(v); 316 if (dimRemapping.size() <= kvp->second) 317 dimRemapping.resize(kvp->second + 1); 318 dimRemapping[kvp->second] = renumberOneDim(kvp->first); 319 } 320 unsigned numSymbols = concatenatedSymbols.size(); 321 unsigned numOtherSymbols = other.concatenatedSymbols.size(); 322 SmallVector<AffineExpr, 8> symRemapping(numOtherSymbols); 323 for (unsigned idx = 0; idx < numOtherSymbols; ++idx) { 324 symRemapping[idx] = 325 getAffineSymbolExpr(idx + numSymbols, other.affineMap.getContext()); 326 } 327 concatenatedSymbols.insert(concatenatedSymbols.end(), 328 other.concatenatedSymbols.begin(), 329 other.concatenatedSymbols.end()); 330 auto map = other.affineMap; 331 return map.replaceDimsAndSymbols(dimRemapping, symRemapping, 332 reorderedDims.size(), 333 concatenatedSymbols.size()); 334 } 335 336 // Gather the positions of the operands that are produced by an AffineApplyOp. 337 static llvm::SetVector<unsigned> 338 indicesFromAffineApplyOp(ArrayRef<Value> operands) { 339 llvm::SetVector<unsigned> res; 340 for (auto en : llvm::enumerate(operands)) 341 if (isa_and_nonnull<AffineApplyOp>(en.value().getDefiningOp())) 342 res.insert(en.index()); 343 return res; 344 } 345 346 // Support the special case of a symbol coming from an AffineApplyOp that needs 347 // to be composed into the current AffineApplyOp. 348 // This case is handled by rewriting all such symbols into dims for the purpose 349 // of allowing mathematical AffineMap composition. 350 // Returns an AffineMap where symbols that come from an AffineApplyOp have been 351 // rewritten as dims and are ordered after the original dims. 352 // TODO(andydavis,ntv): This promotion makes AffineMap lose track of which 353 // symbols are represented as dims. This loss is static but can still be 354 // recovered dynamically (with `isValidSymbol`). Still this is annoying for the 355 // semi-affine map case. A dynamic canonicalization of all dims that are valid 356 // symbols (a.k.a `canonicalizePromotedSymbols`) into symbols helps and even 357 // results in better simplifications and foldings. But we should evaluate 358 // whether this behavior is what we really want after using more. 359 static AffineMap promoteComposedSymbolsAsDims(AffineMap map, 360 ArrayRef<Value> symbols) { 361 if (symbols.empty()) { 362 return map; 363 } 364 365 // Sanity check on symbols. 366 for (auto sym : symbols) { 367 assert(isValidSymbol(sym) && "Expected only valid symbols"); 368 (void)sym; 369 } 370 371 // Extract the symbol positions that come from an AffineApplyOp and 372 // needs to be rewritten as dims. 373 auto symPositions = indicesFromAffineApplyOp(symbols); 374 if (symPositions.empty()) { 375 return map; 376 } 377 378 // Create the new map by replacing each symbol at pos by the next new dim. 379 unsigned numDims = map.getNumDims(); 380 unsigned numSymbols = map.getNumSymbols(); 381 unsigned numNewDims = 0; 382 unsigned numNewSymbols = 0; 383 SmallVector<AffineExpr, 8> symReplacements(numSymbols); 384 for (unsigned i = 0; i < numSymbols; ++i) { 385 symReplacements[i] = 386 symPositions.count(i) > 0 387 ? getAffineDimExpr(numDims + numNewDims++, map.getContext()) 388 : getAffineSymbolExpr(numNewSymbols++, map.getContext()); 389 } 390 assert(numSymbols >= numNewDims); 391 AffineMap newMap = map.replaceDimsAndSymbols( 392 {}, symReplacements, numDims + numNewDims, numNewSymbols); 393 394 return newMap; 395 } 396 397 /// The AffineNormalizer composes AffineApplyOp recursively. Its purpose is to 398 /// keep a correspondence between the mathematical `map` and the `operands` of 399 /// a given AffineApplyOp. This correspondence is maintained by iterating over 400 /// the operands and forming an `auxiliaryMap` that can be composed 401 /// mathematically with `map`. To keep this correspondence in cases where 402 /// symbols are produced by affine.apply operations, we perform a local rewrite 403 /// of symbols as dims. 404 /// 405 /// Rationale for locally rewriting symbols as dims: 406 /// ================================================ 407 /// The mathematical composition of AffineMap must always concatenate symbols 408 /// because it does not have enough information to do otherwise. For example, 409 /// composing `(d0)[s0] -> (d0 + s0)` with itself must produce 410 /// `(d0)[s0, s1] -> (d0 + s0 + s1)`. 411 /// 412 /// The result is only equivalent to `(d0)[s0] -> (d0 + 2 * s0)` when 413 /// applied to the same mlir::Value for both s0 and s1. 414 /// As a consequence mathematical composition of AffineMap always concatenates 415 /// symbols. 416 /// 417 /// When AffineMaps are used in AffineApplyOp however, they may specify 418 /// composition via symbols, which is ambiguous mathematically. This corner case 419 /// is handled by locally rewriting such symbols that come from AffineApplyOp 420 /// into dims and composing through dims. 421 /// TODO(andydavis, ntv): Composition via symbols comes at a significant code 422 /// complexity. Alternatively we should investigate whether we want to 423 /// explicitly disallow symbols coming from affine.apply and instead force the 424 /// user to compose symbols beforehand. The annoyances may be small (i.e. 1 or 2 425 /// extra API calls for such uses, which haven't popped up until now) and the 426 /// benefit potentially big: simpler and more maintainable code for a 427 /// non-trivial, recursive, procedure. 428 AffineApplyNormalizer::AffineApplyNormalizer(AffineMap map, 429 ArrayRef<Value> operands) 430 : AffineApplyNormalizer() { 431 static_assert(kMaxAffineApplyDepth > 0, "kMaxAffineApplyDepth must be > 0"); 432 assert(map.getNumInputs() == operands.size() && 433 "number of operands does not match the number of map inputs"); 434 435 LLVM_DEBUG(map.print(dbgs() << "\nInput map: ")); 436 437 // Promote symbols that come from an AffineApplyOp to dims by rewriting the 438 // map to always refer to: 439 // (dims, symbols coming from AffineApplyOp, other symbols). 440 // The order of operands can remain unchanged. 441 // This is a simplification that relies on 2 ordering properties: 442 // 1. rewritten symbols always appear after the original dims in the map; 443 // 2. operands are traversed in order and either dispatched to: 444 // a. auxiliaryExprs (dims and symbols rewritten as dims); 445 // b. concatenatedSymbols (all other symbols) 446 // This allows operand order to remain unchanged. 447 unsigned numDimsBeforeRewrite = map.getNumDims(); 448 map = promoteComposedSymbolsAsDims(map, 449 operands.take_back(map.getNumSymbols())); 450 451 LLVM_DEBUG(map.print(dbgs() << "\nRewritten map: ")); 452 453 SmallVector<AffineExpr, 8> auxiliaryExprs; 454 bool furtherCompose = (affineApplyDepth() <= kMaxAffineApplyDepth); 455 // We fully spell out the 2 cases below. In this particular instance a little 456 // code duplication greatly improves readability. 457 // Note that the first branch would disappear if we only supported full 458 // composition (i.e. infinite kMaxAffineApplyDepth). 459 if (!furtherCompose) { 460 // 1. Only dispatch dims or symbols. 461 for (auto en : llvm::enumerate(operands)) { 462 auto t = en.value(); 463 assert(t.getType().isIndex()); 464 bool isDim = (en.index() < map.getNumDims()); 465 if (isDim) { 466 // a. The mathematical composition of AffineMap composes dims. 467 auxiliaryExprs.push_back(renumberOneDim(t)); 468 } else { 469 // b. The mathematical composition of AffineMap concatenates symbols. 470 // We do the same for symbol operands. 471 concatenatedSymbols.push_back(t); 472 } 473 } 474 } else { 475 assert(numDimsBeforeRewrite <= operands.size()); 476 // 2. Compose AffineApplyOps and dispatch dims or symbols. 477 for (unsigned i = 0, e = operands.size(); i < e; ++i) { 478 auto t = operands[i]; 479 auto affineApply = dyn_cast_or_null<AffineApplyOp>(t.getDefiningOp()); 480 if (affineApply) { 481 // a. Compose affine.apply operations. 482 LLVM_DEBUG(affineApply.getOperation()->print( 483 dbgs() << "\nCompose AffineApplyOp recursively: ")); 484 AffineMap affineApplyMap = affineApply.getAffineMap(); 485 SmallVector<Value, 8> affineApplyOperands( 486 affineApply.getOperands().begin(), affineApply.getOperands().end()); 487 AffineApplyNormalizer normalizer(affineApplyMap, affineApplyOperands); 488 489 LLVM_DEBUG(normalizer.affineMap.print( 490 dbgs() << "\nRenumber into current normalizer: ")); 491 492 auto renumberedMap = renumber(normalizer); 493 494 LLVM_DEBUG( 495 renumberedMap.print(dbgs() << "\nRecursive composition yields: ")); 496 497 auxiliaryExprs.push_back(renumberedMap.getResult(0)); 498 } else { 499 if (i < numDimsBeforeRewrite) { 500 // b. The mathematical composition of AffineMap composes dims. 501 auxiliaryExprs.push_back(renumberOneDim(t)); 502 } else { 503 // c. The mathematical composition of AffineMap concatenates symbols. 504 // Note that the map composition will put symbols already present 505 // in the map before any symbols coming from the auxiliary map, so 506 // we insert them before any symbols that are due to renumbering, 507 // and after the proper symbols we have seen already. 508 concatenatedSymbols.insert( 509 std::next(concatenatedSymbols.begin(), numProperSymbols++), t); 510 } 511 } 512 } 513 } 514 515 // Early exit if `map` is already composed. 516 if (auxiliaryExprs.empty()) { 517 affineMap = map; 518 return; 519 } 520 521 assert(concatenatedSymbols.size() >= map.getNumSymbols() && 522 "Unexpected number of concatenated symbols"); 523 auto numDims = dimValueToPosition.size(); 524 auto numSymbols = concatenatedSymbols.size() - map.getNumSymbols(); 525 auto auxiliaryMap = AffineMap::get(numDims, numSymbols, auxiliaryExprs); 526 527 LLVM_DEBUG(map.print(dbgs() << "\nCompose map: ")); 528 LLVM_DEBUG(auxiliaryMap.print(dbgs() << "\nWith map: ")); 529 LLVM_DEBUG(map.compose(auxiliaryMap).print(dbgs() << "\nResult: ")); 530 531 // TODO(andydavis,ntv): Disabling simplification results in major speed gains. 532 // Another option is to cache the results as it is expected a lot of redundant 533 // work is performed in practice. 534 affineMap = simplifyAffineMap(map.compose(auxiliaryMap)); 535 536 LLVM_DEBUG(affineMap.print(dbgs() << "\nSimplified result: ")); 537 LLVM_DEBUG(dbgs() << "\n"); 538 } 539 540 void AffineApplyNormalizer::normalize(AffineMap *otherMap, 541 SmallVectorImpl<Value> *otherOperands) { 542 AffineApplyNormalizer other(*otherMap, *otherOperands); 543 *otherMap = renumber(other); 544 545 otherOperands->reserve(reorderedDims.size() + concatenatedSymbols.size()); 546 otherOperands->assign(reorderedDims.begin(), reorderedDims.end()); 547 otherOperands->append(concatenatedSymbols.begin(), concatenatedSymbols.end()); 548 } 549 550 /// Implements `map` and `operands` composition and simplification to support 551 /// `makeComposedAffineApply`. This can be called to achieve the same effects 552 /// on `map` and `operands` without creating an AffineApplyOp that needs to be 553 /// immediately deleted. 554 static void composeAffineMapAndOperands(AffineMap *map, 555 SmallVectorImpl<Value> *operands) { 556 AffineApplyNormalizer normalizer(*map, *operands); 557 auto normalizedMap = normalizer.getAffineMap(); 558 auto normalizedOperands = normalizer.getOperands(); 559 canonicalizeMapAndOperands(&normalizedMap, &normalizedOperands); 560 *map = normalizedMap; 561 *operands = normalizedOperands; 562 assert(*map); 563 } 564 565 void mlir::fullyComposeAffineMapAndOperands(AffineMap *map, 566 SmallVectorImpl<Value> *operands) { 567 while (llvm::any_of(*operands, [](Value v) { 568 return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp()); 569 })) { 570 composeAffineMapAndOperands(map, operands); 571 } 572 } 573 574 AffineApplyOp mlir::makeComposedAffineApply(OpBuilder &b, Location loc, 575 AffineMap map, 576 ArrayRef<Value> operands) { 577 AffineMap normalizedMap = map; 578 SmallVector<Value, 8> normalizedOperands(operands.begin(), operands.end()); 579 composeAffineMapAndOperands(&normalizedMap, &normalizedOperands); 580 assert(normalizedMap); 581 return b.create<AffineApplyOp>(loc, normalizedMap, normalizedOperands); 582 } 583 584 // A symbol may appear as a dim in affine.apply operations. This function 585 // canonicalizes dims that are valid symbols into actual symbols. 586 template <class MapOrSet> 587 static void canonicalizePromotedSymbols(MapOrSet *mapOrSet, 588 SmallVectorImpl<Value> *operands) { 589 if (!mapOrSet || operands->empty()) 590 return; 591 592 assert(mapOrSet->getNumInputs() == operands->size() && 593 "map/set inputs must match number of operands"); 594 595 auto *context = mapOrSet->getContext(); 596 SmallVector<Value, 8> resultOperands; 597 resultOperands.reserve(operands->size()); 598 SmallVector<Value, 8> remappedSymbols; 599 remappedSymbols.reserve(operands->size()); 600 unsigned nextDim = 0; 601 unsigned nextSym = 0; 602 unsigned oldNumSyms = mapOrSet->getNumSymbols(); 603 SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims()); 604 for (unsigned i = 0, e = mapOrSet->getNumInputs(); i != e; ++i) { 605 if (i < mapOrSet->getNumDims()) { 606 if (isValidSymbol((*operands)[i])) { 607 // This is a valid symbol that appears as a dim, canonicalize it. 608 dimRemapping[i] = getAffineSymbolExpr(oldNumSyms + nextSym++, context); 609 remappedSymbols.push_back((*operands)[i]); 610 } else { 611 dimRemapping[i] = getAffineDimExpr(nextDim++, context); 612 resultOperands.push_back((*operands)[i]); 613 } 614 } else { 615 resultOperands.push_back((*operands)[i]); 616 } 617 } 618 619 resultOperands.append(remappedSymbols.begin(), remappedSymbols.end()); 620 *operands = resultOperands; 621 *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, {}, nextDim, 622 oldNumSyms + nextSym); 623 624 assert(mapOrSet->getNumInputs() == operands->size() && 625 "map/set inputs must match number of operands"); 626 } 627 628 // Works for either an affine map or an integer set. 629 template <class MapOrSet> 630 static void canonicalizeMapOrSetAndOperands(MapOrSet *mapOrSet, 631 SmallVectorImpl<Value> *operands) { 632 static_assert(llvm::is_one_of<MapOrSet, AffineMap, IntegerSet>::value, 633 "Argument must be either of AffineMap or IntegerSet type"); 634 635 if (!mapOrSet || operands->empty()) 636 return; 637 638 assert(mapOrSet->getNumInputs() == operands->size() && 639 "map/set inputs must match number of operands"); 640 641 canonicalizePromotedSymbols<MapOrSet>(mapOrSet, operands); 642 643 // Check to see what dims are used. 644 llvm::SmallBitVector usedDims(mapOrSet->getNumDims()); 645 llvm::SmallBitVector usedSyms(mapOrSet->getNumSymbols()); 646 mapOrSet->walkExprs([&](AffineExpr expr) { 647 if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) 648 usedDims[dimExpr.getPosition()] = true; 649 else if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) 650 usedSyms[symExpr.getPosition()] = true; 651 }); 652 653 auto *context = mapOrSet->getContext(); 654 655 SmallVector<Value, 8> resultOperands; 656 resultOperands.reserve(operands->size()); 657 658 llvm::SmallDenseMap<Value, AffineExpr, 8> seenDims; 659 SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims()); 660 unsigned nextDim = 0; 661 for (unsigned i = 0, e = mapOrSet->getNumDims(); i != e; ++i) { 662 if (usedDims[i]) { 663 // Remap dim positions for duplicate operands. 664 auto it = seenDims.find((*operands)[i]); 665 if (it == seenDims.end()) { 666 dimRemapping[i] = getAffineDimExpr(nextDim++, context); 667 resultOperands.push_back((*operands)[i]); 668 seenDims.insert(std::make_pair((*operands)[i], dimRemapping[i])); 669 } else { 670 dimRemapping[i] = it->second; 671 } 672 } 673 } 674 llvm::SmallDenseMap<Value, AffineExpr, 8> seenSymbols; 675 SmallVector<AffineExpr, 8> symRemapping(mapOrSet->getNumSymbols()); 676 unsigned nextSym = 0; 677 for (unsigned i = 0, e = mapOrSet->getNumSymbols(); i != e; ++i) { 678 if (!usedSyms[i]) 679 continue; 680 // Handle constant operands (only needed for symbolic operands since 681 // constant operands in dimensional positions would have already been 682 // promoted to symbolic positions above). 683 IntegerAttr operandCst; 684 if (matchPattern((*operands)[i + mapOrSet->getNumDims()], 685 m_Constant(&operandCst))) { 686 symRemapping[i] = 687 getAffineConstantExpr(operandCst.getValue().getSExtValue(), context); 688 continue; 689 } 690 // Remap symbol positions for duplicate operands. 691 auto it = seenSymbols.find((*operands)[i + mapOrSet->getNumDims()]); 692 if (it == seenSymbols.end()) { 693 symRemapping[i] = getAffineSymbolExpr(nextSym++, context); 694 resultOperands.push_back((*operands)[i + mapOrSet->getNumDims()]); 695 seenSymbols.insert(std::make_pair((*operands)[i + mapOrSet->getNumDims()], 696 symRemapping[i])); 697 } else { 698 symRemapping[i] = it->second; 699 } 700 } 701 *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, symRemapping, 702 nextDim, nextSym); 703 *operands = resultOperands; 704 } 705 706 void mlir::canonicalizeMapAndOperands(AffineMap *map, 707 SmallVectorImpl<Value> *operands) { 708 canonicalizeMapOrSetAndOperands<AffineMap>(map, operands); 709 } 710 711 void mlir::canonicalizeSetAndOperands(IntegerSet *set, 712 SmallVectorImpl<Value> *operands) { 713 canonicalizeMapOrSetAndOperands<IntegerSet>(set, operands); 714 } 715 716 namespace { 717 /// Simplify AffineApply, AffineLoad, and AffineStore operations by composing 718 /// maps that supply results into them. 719 /// 720 template <typename AffineOpTy> 721 struct SimplifyAffineOp : public OpRewritePattern<AffineOpTy> { 722 using OpRewritePattern<AffineOpTy>::OpRewritePattern; 723 724 /// Replace the affine op with another instance of it with the supplied 725 /// map and mapOperands. 726 void replaceAffineOp(PatternRewriter &rewriter, AffineOpTy affineOp, 727 AffineMap map, ArrayRef<Value> mapOperands) const; 728 729 LogicalResult matchAndRewrite(AffineOpTy affineOp, 730 PatternRewriter &rewriter) const override { 731 static_assert(llvm::is_one_of<AffineOpTy, AffineLoadOp, AffinePrefetchOp, 732 AffineStoreOp, AffineApplyOp, AffineMinOp, 733 AffineMaxOp>::value, 734 "affine load/store/apply/prefetch/min/max op expected"); 735 auto map = affineOp.getAffineMap(); 736 AffineMap oldMap = map; 737 auto oldOperands = affineOp.getMapOperands(); 738 SmallVector<Value, 8> resultOperands(oldOperands); 739 composeAffineMapAndOperands(&map, &resultOperands); 740 if (map == oldMap && std::equal(oldOperands.begin(), oldOperands.end(), 741 resultOperands.begin())) 742 return failure(); 743 744 replaceAffineOp(rewriter, affineOp, map, resultOperands); 745 return success(); 746 } 747 }; 748 749 // Specialize the template to account for the different build signatures for 750 // affine load, store, and apply ops. 751 template <> 752 void SimplifyAffineOp<AffineLoadOp>::replaceAffineOp( 753 PatternRewriter &rewriter, AffineLoadOp load, AffineMap map, 754 ArrayRef<Value> mapOperands) const { 755 rewriter.replaceOpWithNewOp<AffineLoadOp>(load, load.getMemRef(), map, 756 mapOperands); 757 } 758 template <> 759 void SimplifyAffineOp<AffinePrefetchOp>::replaceAffineOp( 760 PatternRewriter &rewriter, AffinePrefetchOp prefetch, AffineMap map, 761 ArrayRef<Value> mapOperands) const { 762 rewriter.replaceOpWithNewOp<AffinePrefetchOp>( 763 prefetch, prefetch.memref(), map, mapOperands, 764 prefetch.localityHint().getZExtValue(), prefetch.isWrite(), 765 prefetch.isDataCache()); 766 } 767 template <> 768 void SimplifyAffineOp<AffineStoreOp>::replaceAffineOp( 769 PatternRewriter &rewriter, AffineStoreOp store, AffineMap map, 770 ArrayRef<Value> mapOperands) const { 771 rewriter.replaceOpWithNewOp<AffineStoreOp>( 772 store, store.getValueToStore(), store.getMemRef(), map, mapOperands); 773 } 774 775 // Generic version for ops that don't have extra operands. 776 template <typename AffineOpTy> 777 void SimplifyAffineOp<AffineOpTy>::replaceAffineOp( 778 PatternRewriter &rewriter, AffineOpTy op, AffineMap map, 779 ArrayRef<Value> mapOperands) const { 780 rewriter.replaceOpWithNewOp<AffineOpTy>(op, map, mapOperands); 781 } 782 } // end anonymous namespace. 783 784 void AffineApplyOp::getCanonicalizationPatterns( 785 OwningRewritePatternList &results, MLIRContext *context) { 786 results.insert<SimplifyAffineOp<AffineApplyOp>>(context); 787 } 788 789 //===----------------------------------------------------------------------===// 790 // Common canonicalization pattern support logic 791 //===----------------------------------------------------------------------===// 792 793 /// This is a common class used for patterns of the form 794 /// "someop(memrefcast) -> someop". It folds the source of any memref_cast 795 /// into the root operation directly. 796 static LogicalResult foldMemRefCast(Operation *op) { 797 bool folded = false; 798 for (OpOperand &operand : op->getOpOperands()) { 799 auto cast = dyn_cast_or_null<MemRefCastOp>(operand.get().getDefiningOp()); 800 if (cast && !cast.getOperand().getType().isa<UnrankedMemRefType>()) { 801 operand.set(cast.getOperand()); 802 folded = true; 803 } 804 } 805 return success(folded); 806 } 807 808 //===----------------------------------------------------------------------===// 809 // AffineDmaStartOp 810 //===----------------------------------------------------------------------===// 811 812 // TODO(b/133776335) Check that map operands are loop IVs or symbols. 813 void AffineDmaStartOp::build(Builder *builder, OperationState &result, 814 Value srcMemRef, AffineMap srcMap, 815 ValueRange srcIndices, Value destMemRef, 816 AffineMap dstMap, ValueRange destIndices, 817 Value tagMemRef, AffineMap tagMap, 818 ValueRange tagIndices, Value numElements, 819 Value stride, Value elementsPerStride) { 820 result.addOperands(srcMemRef); 821 result.addAttribute(getSrcMapAttrName(), AffineMapAttr::get(srcMap)); 822 result.addOperands(srcIndices); 823 result.addOperands(destMemRef); 824 result.addAttribute(getDstMapAttrName(), AffineMapAttr::get(dstMap)); 825 result.addOperands(destIndices); 826 result.addOperands(tagMemRef); 827 result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap)); 828 result.addOperands(tagIndices); 829 result.addOperands(numElements); 830 if (stride) { 831 result.addOperands({stride, elementsPerStride}); 832 } 833 } 834 835 void AffineDmaStartOp::print(OpAsmPrinter &p) { 836 p << "affine.dma_start " << getSrcMemRef() << '['; 837 p.printAffineMapOfSSAIds(getSrcMapAttr(), getSrcIndices()); 838 p << "], " << getDstMemRef() << '['; 839 p.printAffineMapOfSSAIds(getDstMapAttr(), getDstIndices()); 840 p << "], " << getTagMemRef() << '['; 841 p.printAffineMapOfSSAIds(getTagMapAttr(), getTagIndices()); 842 p << "], " << getNumElements(); 843 if (isStrided()) { 844 p << ", " << getStride(); 845 p << ", " << getNumElementsPerStride(); 846 } 847 p << " : " << getSrcMemRefType() << ", " << getDstMemRefType() << ", " 848 << getTagMemRefType(); 849 } 850 851 // Parse AffineDmaStartOp. 852 // Ex: 853 // affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %size, 854 // %stride, %num_elt_per_stride 855 // : memref<3076 x f32, 0>, memref<1024 x f32, 2>, memref<1 x i32> 856 // 857 ParseResult AffineDmaStartOp::parse(OpAsmParser &parser, 858 OperationState &result) { 859 OpAsmParser::OperandType srcMemRefInfo; 860 AffineMapAttr srcMapAttr; 861 SmallVector<OpAsmParser::OperandType, 4> srcMapOperands; 862 OpAsmParser::OperandType dstMemRefInfo; 863 AffineMapAttr dstMapAttr; 864 SmallVector<OpAsmParser::OperandType, 4> dstMapOperands; 865 OpAsmParser::OperandType tagMemRefInfo; 866 AffineMapAttr tagMapAttr; 867 SmallVector<OpAsmParser::OperandType, 4> tagMapOperands; 868 OpAsmParser::OperandType numElementsInfo; 869 SmallVector<OpAsmParser::OperandType, 2> strideInfo; 870 871 SmallVector<Type, 3> types; 872 auto indexType = parser.getBuilder().getIndexType(); 873 874 // Parse and resolve the following list of operands: 875 // *) dst memref followed by its affine maps operands (in square brackets). 876 // *) src memref followed by its affine map operands (in square brackets). 877 // *) tag memref followed by its affine map operands (in square brackets). 878 // *) number of elements transferred by DMA operation. 879 if (parser.parseOperand(srcMemRefInfo) || 880 parser.parseAffineMapOfSSAIds(srcMapOperands, srcMapAttr, 881 getSrcMapAttrName(), result.attributes) || 882 parser.parseComma() || parser.parseOperand(dstMemRefInfo) || 883 parser.parseAffineMapOfSSAIds(dstMapOperands, dstMapAttr, 884 getDstMapAttrName(), result.attributes) || 885 parser.parseComma() || parser.parseOperand(tagMemRefInfo) || 886 parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr, 887 getTagMapAttrName(), result.attributes) || 888 parser.parseComma() || parser.parseOperand(numElementsInfo)) 889 return failure(); 890 891 // Parse optional stride and elements per stride. 892 if (parser.parseTrailingOperandList(strideInfo)) { 893 return failure(); 894 } 895 if (!strideInfo.empty() && strideInfo.size() != 2) { 896 return parser.emitError(parser.getNameLoc(), 897 "expected two stride related operands"); 898 } 899 bool isStrided = strideInfo.size() == 2; 900 901 if (parser.parseColonTypeList(types)) 902 return failure(); 903 904 if (types.size() != 3) 905 return parser.emitError(parser.getNameLoc(), "expected three types"); 906 907 if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) || 908 parser.resolveOperands(srcMapOperands, indexType, result.operands) || 909 parser.resolveOperand(dstMemRefInfo, types[1], result.operands) || 910 parser.resolveOperands(dstMapOperands, indexType, result.operands) || 911 parser.resolveOperand(tagMemRefInfo, types[2], result.operands) || 912 parser.resolveOperands(tagMapOperands, indexType, result.operands) || 913 parser.resolveOperand(numElementsInfo, indexType, result.operands)) 914 return failure(); 915 916 if (isStrided) { 917 if (parser.resolveOperands(strideInfo, indexType, result.operands)) 918 return failure(); 919 } 920 921 // Check that src/dst/tag operand counts match their map.numInputs. 922 if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() || 923 dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() || 924 tagMapOperands.size() != tagMapAttr.getValue().getNumInputs()) 925 return parser.emitError(parser.getNameLoc(), 926 "memref operand count not equal to map.numInputs"); 927 return success(); 928 } 929 930 LogicalResult AffineDmaStartOp::verify() { 931 if (!getOperand(getSrcMemRefOperandIndex()).getType().isa<MemRefType>()) 932 return emitOpError("expected DMA source to be of memref type"); 933 if (!getOperand(getDstMemRefOperandIndex()).getType().isa<MemRefType>()) 934 return emitOpError("expected DMA destination to be of memref type"); 935 if (!getOperand(getTagMemRefOperandIndex()).getType().isa<MemRefType>()) 936 return emitOpError("expected DMA tag to be of memref type"); 937 938 // DMAs from different memory spaces supported. 939 if (getSrcMemorySpace() == getDstMemorySpace()) { 940 return emitOpError("DMA should be between different memory spaces"); 941 } 942 unsigned numInputsAllMaps = getSrcMap().getNumInputs() + 943 getDstMap().getNumInputs() + 944 getTagMap().getNumInputs(); 945 if (getNumOperands() != numInputsAllMaps + 3 + 1 && 946 getNumOperands() != numInputsAllMaps + 3 + 1 + 2) { 947 return emitOpError("incorrect number of operands"); 948 } 949 950 for (auto idx : getSrcIndices()) { 951 if (!idx.getType().isIndex()) 952 return emitOpError("src index to dma_start must have 'index' type"); 953 if (!isValidAffineIndexOperand(idx)) 954 return emitOpError("src index must be a dimension or symbol identifier"); 955 } 956 for (auto idx : getDstIndices()) { 957 if (!idx.getType().isIndex()) 958 return emitOpError("dst index to dma_start must have 'index' type"); 959 if (!isValidAffineIndexOperand(idx)) 960 return emitOpError("dst index must be a dimension or symbol identifier"); 961 } 962 for (auto idx : getTagIndices()) { 963 if (!idx.getType().isIndex()) 964 return emitOpError("tag index to dma_start must have 'index' type"); 965 if (!isValidAffineIndexOperand(idx)) 966 return emitOpError("tag index must be a dimension or symbol identifier"); 967 } 968 return success(); 969 } 970 971 LogicalResult AffineDmaStartOp::fold(ArrayRef<Attribute> cstOperands, 972 SmallVectorImpl<OpFoldResult> &results) { 973 /// dma_start(memrefcast) -> dma_start 974 return foldMemRefCast(*this); 975 } 976 977 //===----------------------------------------------------------------------===// 978 // AffineDmaWaitOp 979 //===----------------------------------------------------------------------===// 980 981 // TODO(b/133776335) Check that map operands are loop IVs or symbols. 982 void AffineDmaWaitOp::build(Builder *builder, OperationState &result, 983 Value tagMemRef, AffineMap tagMap, 984 ValueRange tagIndices, Value numElements) { 985 result.addOperands(tagMemRef); 986 result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap)); 987 result.addOperands(tagIndices); 988 result.addOperands(numElements); 989 } 990 991 void AffineDmaWaitOp::print(OpAsmPrinter &p) { 992 p << "affine.dma_wait " << getTagMemRef() << '['; 993 SmallVector<Value, 2> operands(getTagIndices()); 994 p.printAffineMapOfSSAIds(getTagMapAttr(), operands); 995 p << "], "; 996 p.printOperand(getNumElements()); 997 p << " : " << getTagMemRef().getType(); 998 } 999 1000 // Parse AffineDmaWaitOp. 1001 // Eg: 1002 // affine.dma_wait %tag[%index], %num_elements 1003 // : memref<1 x i32, (d0) -> (d0), 4> 1004 // 1005 ParseResult AffineDmaWaitOp::parse(OpAsmParser &parser, 1006 OperationState &result) { 1007 OpAsmParser::OperandType tagMemRefInfo; 1008 AffineMapAttr tagMapAttr; 1009 SmallVector<OpAsmParser::OperandType, 2> tagMapOperands; 1010 Type type; 1011 auto indexType = parser.getBuilder().getIndexType(); 1012 OpAsmParser::OperandType numElementsInfo; 1013 1014 // Parse tag memref, its map operands, and dma size. 1015 if (parser.parseOperand(tagMemRefInfo) || 1016 parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr, 1017 getTagMapAttrName(), result.attributes) || 1018 parser.parseComma() || parser.parseOperand(numElementsInfo) || 1019 parser.parseColonType(type) || 1020 parser.resolveOperand(tagMemRefInfo, type, result.operands) || 1021 parser.resolveOperands(tagMapOperands, indexType, result.operands) || 1022 parser.resolveOperand(numElementsInfo, indexType, result.operands)) 1023 return failure(); 1024 1025 if (!type.isa<MemRefType>()) 1026 return parser.emitError(parser.getNameLoc(), 1027 "expected tag to be of memref type"); 1028 1029 if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs()) 1030 return parser.emitError(parser.getNameLoc(), 1031 "tag memref operand count != to map.numInputs"); 1032 return success(); 1033 } 1034 1035 LogicalResult AffineDmaWaitOp::verify() { 1036 if (!getOperand(0).getType().isa<MemRefType>()) 1037 return emitOpError("expected DMA tag to be of memref type"); 1038 for (auto idx : getTagIndices()) { 1039 if (!idx.getType().isIndex()) 1040 return emitOpError("index to dma_wait must have 'index' type"); 1041 if (!isValidAffineIndexOperand(idx)) 1042 return emitOpError("index must be a dimension or symbol identifier"); 1043 } 1044 return success(); 1045 } 1046 1047 LogicalResult AffineDmaWaitOp::fold(ArrayRef<Attribute> cstOperands, 1048 SmallVectorImpl<OpFoldResult> &results) { 1049 /// dma_wait(memrefcast) -> dma_wait 1050 return foldMemRefCast(*this); 1051 } 1052 1053 //===----------------------------------------------------------------------===// 1054 // AffineForOp 1055 //===----------------------------------------------------------------------===// 1056 1057 void AffineForOp::build(Builder *builder, OperationState &result, 1058 ValueRange lbOperands, AffineMap lbMap, 1059 ValueRange ubOperands, AffineMap ubMap, int64_t step) { 1060 assert(((!lbMap && lbOperands.empty()) || 1061 lbOperands.size() == lbMap.getNumInputs()) && 1062 "lower bound operand count does not match the affine map"); 1063 assert(((!ubMap && ubOperands.empty()) || 1064 ubOperands.size() == ubMap.getNumInputs()) && 1065 "upper bound operand count does not match the affine map"); 1066 assert(step > 0 && "step has to be a positive integer constant"); 1067 1068 // Add an attribute for the step. 1069 result.addAttribute(getStepAttrName(), 1070 builder->getIntegerAttr(builder->getIndexType(), step)); 1071 1072 // Add the lower bound. 1073 result.addAttribute(getLowerBoundAttrName(), AffineMapAttr::get(lbMap)); 1074 result.addOperands(lbOperands); 1075 1076 // Add the upper bound. 1077 result.addAttribute(getUpperBoundAttrName(), AffineMapAttr::get(ubMap)); 1078 result.addOperands(ubOperands); 1079 1080 // Create a region and a block for the body. The argument of the region is 1081 // the loop induction variable. 1082 Region *bodyRegion = result.addRegion(); 1083 Block *body = new Block(); 1084 body->addArgument(IndexType::get(builder->getContext())); 1085 bodyRegion->push_back(body); 1086 ensureTerminator(*bodyRegion, *builder, result.location); 1087 1088 // Set the operands list as resizable so that we can freely modify the bounds. 1089 result.setOperandListToResizable(); 1090 } 1091 1092 void AffineForOp::build(Builder *builder, OperationState &result, int64_t lb, 1093 int64_t ub, int64_t step) { 1094 auto lbMap = AffineMap::getConstantMap(lb, builder->getContext()); 1095 auto ubMap = AffineMap::getConstantMap(ub, builder->getContext()); 1096 return build(builder, result, {}, lbMap, {}, ubMap, step); 1097 } 1098 1099 static LogicalResult verify(AffineForOp op) { 1100 // Check that the body defines as single block argument for the induction 1101 // variable. 1102 auto *body = op.getBody(); 1103 if (body->getNumArguments() != 1 || !body->getArgument(0).getType().isIndex()) 1104 return op.emitOpError( 1105 "expected body to have a single index argument for the " 1106 "induction variable"); 1107 1108 // Verify that there are enough operands for the bounds. 1109 AffineMap lowerBoundMap = op.getLowerBoundMap(), 1110 upperBoundMap = op.getUpperBoundMap(); 1111 if (op.getNumOperands() != 1112 (lowerBoundMap.getNumInputs() + upperBoundMap.getNumInputs())) 1113 return op.emitOpError( 1114 "operand count must match with affine map dimension and symbol count"); 1115 1116 // Verify that the bound operands are valid dimension/symbols. 1117 /// Lower bound. 1118 if (failed(verifyDimAndSymbolIdentifiers(op, op.getLowerBoundOperands(), 1119 op.getLowerBoundMap().getNumDims()))) 1120 return failure(); 1121 /// Upper bound. 1122 if (failed(verifyDimAndSymbolIdentifiers(op, op.getUpperBoundOperands(), 1123 op.getUpperBoundMap().getNumDims()))) 1124 return failure(); 1125 return success(); 1126 } 1127 1128 /// Parse a for operation loop bounds. 1129 static ParseResult parseBound(bool isLower, OperationState &result, 1130 OpAsmParser &p) { 1131 // 'min' / 'max' prefixes are generally syntactic sugar, but are required if 1132 // the map has multiple results. 1133 bool failedToParsedMinMax = 1134 failed(p.parseOptionalKeyword(isLower ? "max" : "min")); 1135 1136 auto &builder = p.getBuilder(); 1137 auto boundAttrName = isLower ? AffineForOp::getLowerBoundAttrName() 1138 : AffineForOp::getUpperBoundAttrName(); 1139 1140 // Parse ssa-id as identity map. 1141 SmallVector<OpAsmParser::OperandType, 1> boundOpInfos; 1142 if (p.parseOperandList(boundOpInfos)) 1143 return failure(); 1144 1145 if (!boundOpInfos.empty()) { 1146 // Check that only one operand was parsed. 1147 if (boundOpInfos.size() > 1) 1148 return p.emitError(p.getNameLoc(), 1149 "expected only one loop bound operand"); 1150 1151 // TODO: improve error message when SSA value is not of index type. 1152 // Currently it is 'use of value ... expects different type than prior uses' 1153 if (p.resolveOperand(boundOpInfos.front(), builder.getIndexType(), 1154 result.operands)) 1155 return failure(); 1156 1157 // Create an identity map using symbol id. This representation is optimized 1158 // for storage. Analysis passes may expand it into a multi-dimensional map 1159 // if desired. 1160 AffineMap map = builder.getSymbolIdentityMap(); 1161 result.addAttribute(boundAttrName, AffineMapAttr::get(map)); 1162 return success(); 1163 } 1164 1165 // Get the attribute location. 1166 llvm::SMLoc attrLoc = p.getCurrentLocation(); 1167 1168 Attribute boundAttr; 1169 if (p.parseAttribute(boundAttr, builder.getIndexType(), boundAttrName, 1170 result.attributes)) 1171 return failure(); 1172 1173 // Parse full form - affine map followed by dim and symbol list. 1174 if (auto affineMapAttr = boundAttr.dyn_cast<AffineMapAttr>()) { 1175 unsigned currentNumOperands = result.operands.size(); 1176 unsigned numDims; 1177 if (parseDimAndSymbolList(p, result.operands, numDims)) 1178 return failure(); 1179 1180 auto map = affineMapAttr.getValue(); 1181 if (map.getNumDims() != numDims) 1182 return p.emitError( 1183 p.getNameLoc(), 1184 "dim operand count and affine map dim count must match"); 1185 1186 unsigned numDimAndSymbolOperands = 1187 result.operands.size() - currentNumOperands; 1188 if (numDims + map.getNumSymbols() != numDimAndSymbolOperands) 1189 return p.emitError( 1190 p.getNameLoc(), 1191 "symbol operand count and affine map symbol count must match"); 1192 1193 // If the map has multiple results, make sure that we parsed the min/max 1194 // prefix. 1195 if (map.getNumResults() > 1 && failedToParsedMinMax) { 1196 if (isLower) { 1197 return p.emitError(attrLoc, "lower loop bound affine map with " 1198 "multiple results requires 'max' prefix"); 1199 } 1200 return p.emitError(attrLoc, "upper loop bound affine map with multiple " 1201 "results requires 'min' prefix"); 1202 } 1203 return success(); 1204 } 1205 1206 // Parse custom assembly form. 1207 if (auto integerAttr = boundAttr.dyn_cast<IntegerAttr>()) { 1208 result.attributes.pop_back(); 1209 result.addAttribute( 1210 boundAttrName, 1211 AffineMapAttr::get(builder.getConstantAffineMap(integerAttr.getInt()))); 1212 return success(); 1213 } 1214 1215 return p.emitError( 1216 p.getNameLoc(), 1217 "expected valid affine map representation for loop bounds"); 1218 } 1219 1220 static ParseResult parseAffineForOp(OpAsmParser &parser, 1221 OperationState &result) { 1222 auto &builder = parser.getBuilder(); 1223 OpAsmParser::OperandType inductionVariable; 1224 // Parse the induction variable followed by '='. 1225 if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual()) 1226 return failure(); 1227 1228 // Parse loop bounds. 1229 if (parseBound(/*isLower=*/true, result, parser) || 1230 parser.parseKeyword("to", " between bounds") || 1231 parseBound(/*isLower=*/false, result, parser)) 1232 return failure(); 1233 1234 // Parse the optional loop step, we default to 1 if one is not present. 1235 if (parser.parseOptionalKeyword("step")) { 1236 result.addAttribute( 1237 AffineForOp::getStepAttrName(), 1238 builder.getIntegerAttr(builder.getIndexType(), /*value=*/1)); 1239 } else { 1240 llvm::SMLoc stepLoc = parser.getCurrentLocation(); 1241 IntegerAttr stepAttr; 1242 if (parser.parseAttribute(stepAttr, builder.getIndexType(), 1243 AffineForOp::getStepAttrName().data(), 1244 result.attributes)) 1245 return failure(); 1246 1247 if (stepAttr.getValue().getSExtValue() < 0) 1248 return parser.emitError( 1249 stepLoc, 1250 "expected step to be representable as a positive signed integer"); 1251 } 1252 1253 // Parse the body region. 1254 Region *body = result.addRegion(); 1255 if (parser.parseRegion(*body, inductionVariable, builder.getIndexType())) 1256 return failure(); 1257 1258 AffineForOp::ensureTerminator(*body, builder, result.location); 1259 1260 // Parse the optional attribute list. 1261 if (parser.parseOptionalAttrDict(result.attributes)) 1262 return failure(); 1263 1264 // Set the operands list as resizable so that we can freely modify the bounds. 1265 result.setOperandListToResizable(); 1266 return success(); 1267 } 1268 1269 static void printBound(AffineMapAttr boundMap, 1270 Operation::operand_range boundOperands, 1271 const char *prefix, OpAsmPrinter &p) { 1272 AffineMap map = boundMap.getValue(); 1273 1274 // Check if this bound should be printed using custom assembly form. 1275 // The decision to restrict printing custom assembly form to trivial cases 1276 // comes from the will to roundtrip MLIR binary -> text -> binary in a 1277 // lossless way. 1278 // Therefore, custom assembly form parsing and printing is only supported for 1279 // zero-operand constant maps and single symbol operand identity maps. 1280 if (map.getNumResults() == 1) { 1281 AffineExpr expr = map.getResult(0); 1282 1283 // Print constant bound. 1284 if (map.getNumDims() == 0 && map.getNumSymbols() == 0) { 1285 if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) { 1286 p << constExpr.getValue(); 1287 return; 1288 } 1289 } 1290 1291 // Print bound that consists of a single SSA symbol if the map is over a 1292 // single symbol. 1293 if (map.getNumDims() == 0 && map.getNumSymbols() == 1) { 1294 if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) { 1295 p.printOperand(*boundOperands.begin()); 1296 return; 1297 } 1298 } 1299 } else { 1300 // Map has multiple results. Print 'min' or 'max' prefix. 1301 p << prefix << ' '; 1302 } 1303 1304 // Print the map and its operands. 1305 p << boundMap; 1306 printDimAndSymbolList(boundOperands.begin(), boundOperands.end(), 1307 map.getNumDims(), p); 1308 } 1309 1310 static void print(OpAsmPrinter &p, AffineForOp op) { 1311 p << op.getOperationName() << ' '; 1312 p.printOperand(op.getBody()->getArgument(0)); 1313 p << " = "; 1314 printBound(op.getLowerBoundMapAttr(), op.getLowerBoundOperands(), "max", p); 1315 p << " to "; 1316 printBound(op.getUpperBoundMapAttr(), op.getUpperBoundOperands(), "min", p); 1317 1318 if (op.getStep() != 1) 1319 p << " step " << op.getStep(); 1320 p.printRegion(op.region(), 1321 /*printEntryBlockArgs=*/false, 1322 /*printBlockTerminators=*/false); 1323 p.printOptionalAttrDict(op.getAttrs(), 1324 /*elidedAttrs=*/{op.getLowerBoundAttrName(), 1325 op.getUpperBoundAttrName(), 1326 op.getStepAttrName()}); 1327 } 1328 1329 /// Fold the constant bounds of a loop. 1330 static LogicalResult foldLoopBounds(AffineForOp forOp) { 1331 auto foldLowerOrUpperBound = [&forOp](bool lower) { 1332 // Check to see if each of the operands is the result of a constant. If 1333 // so, get the value. If not, ignore it. 1334 SmallVector<Attribute, 8> operandConstants; 1335 auto boundOperands = 1336 lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands(); 1337 for (auto operand : boundOperands) { 1338 Attribute operandCst; 1339 matchPattern(operand, m_Constant(&operandCst)); 1340 operandConstants.push_back(operandCst); 1341 } 1342 1343 AffineMap boundMap = 1344 lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap(); 1345 assert(boundMap.getNumResults() >= 1 && 1346 "bound maps should have at least one result"); 1347 SmallVector<Attribute, 4> foldedResults; 1348 if (failed(boundMap.constantFold(operandConstants, foldedResults))) 1349 return failure(); 1350 1351 // Compute the max or min as applicable over the results. 1352 assert(!foldedResults.empty() && "bounds should have at least one result"); 1353 auto maxOrMin = foldedResults[0].cast<IntegerAttr>().getValue(); 1354 for (unsigned i = 1, e = foldedResults.size(); i < e; i++) { 1355 auto foldedResult = foldedResults[i].cast<IntegerAttr>().getValue(); 1356 maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult) 1357 : llvm::APIntOps::smin(maxOrMin, foldedResult); 1358 } 1359 lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue()) 1360 : forOp.setConstantUpperBound(maxOrMin.getSExtValue()); 1361 return success(); 1362 }; 1363 1364 // Try to fold the lower bound. 1365 bool folded = false; 1366 if (!forOp.hasConstantLowerBound()) 1367 folded |= succeeded(foldLowerOrUpperBound(/*lower=*/true)); 1368 1369 // Try to fold the upper bound. 1370 if (!forOp.hasConstantUpperBound()) 1371 folded |= succeeded(foldLowerOrUpperBound(/*lower=*/false)); 1372 return success(folded); 1373 } 1374 1375 /// Canonicalize the bounds of the given loop. 1376 static LogicalResult canonicalizeLoopBounds(AffineForOp forOp) { 1377 SmallVector<Value, 4> lbOperands(forOp.getLowerBoundOperands()); 1378 SmallVector<Value, 4> ubOperands(forOp.getUpperBoundOperands()); 1379 1380 auto lbMap = forOp.getLowerBoundMap(); 1381 auto ubMap = forOp.getUpperBoundMap(); 1382 auto prevLbMap = lbMap; 1383 auto prevUbMap = ubMap; 1384 1385 canonicalizeMapAndOperands(&lbMap, &lbOperands); 1386 lbMap = removeDuplicateExprs(lbMap); 1387 1388 canonicalizeMapAndOperands(&ubMap, &ubOperands); 1389 ubMap = removeDuplicateExprs(ubMap); 1390 1391 // Any canonicalization change always leads to updated map(s). 1392 if (lbMap == prevLbMap && ubMap == prevUbMap) 1393 return failure(); 1394 1395 if (lbMap != prevLbMap) 1396 forOp.setLowerBound(lbOperands, lbMap); 1397 if (ubMap != prevUbMap) 1398 forOp.setUpperBound(ubOperands, ubMap); 1399 return success(); 1400 } 1401 1402 namespace { 1403 /// This is a pattern to fold trivially empty loops. 1404 struct AffineForEmptyLoopFolder : public OpRewritePattern<AffineForOp> { 1405 using OpRewritePattern<AffineForOp>::OpRewritePattern; 1406 1407 LogicalResult matchAndRewrite(AffineForOp forOp, 1408 PatternRewriter &rewriter) const override { 1409 // Check that the body only contains a terminator. 1410 if (!has_single_element(*forOp.getBody())) 1411 return failure(); 1412 rewriter.eraseOp(forOp); 1413 return success(); 1414 } 1415 }; 1416 } // end anonymous namespace 1417 1418 void AffineForOp::getCanonicalizationPatterns(OwningRewritePatternList &results, 1419 MLIRContext *context) { 1420 results.insert<AffineForEmptyLoopFolder>(context); 1421 } 1422 1423 LogicalResult AffineForOp::fold(ArrayRef<Attribute> operands, 1424 SmallVectorImpl<OpFoldResult> &results) { 1425 bool folded = succeeded(foldLoopBounds(*this)); 1426 folded |= succeeded(canonicalizeLoopBounds(*this)); 1427 return success(folded); 1428 } 1429 1430 AffineBound AffineForOp::getLowerBound() { 1431 auto lbMap = getLowerBoundMap(); 1432 return AffineBound(AffineForOp(*this), 0, lbMap.getNumInputs(), lbMap); 1433 } 1434 1435 AffineBound AffineForOp::getUpperBound() { 1436 auto lbMap = getLowerBoundMap(); 1437 auto ubMap = getUpperBoundMap(); 1438 return AffineBound(AffineForOp(*this), lbMap.getNumInputs(), getNumOperands(), 1439 ubMap); 1440 } 1441 1442 void AffineForOp::setLowerBound(ValueRange lbOperands, AffineMap map) { 1443 assert(lbOperands.size() == map.getNumInputs()); 1444 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1445 1446 SmallVector<Value, 4> newOperands(lbOperands.begin(), lbOperands.end()); 1447 1448 auto ubOperands = getUpperBoundOperands(); 1449 newOperands.append(ubOperands.begin(), ubOperands.end()); 1450 getOperation()->setOperands(newOperands); 1451 1452 setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map)); 1453 } 1454 1455 void AffineForOp::setUpperBound(ValueRange ubOperands, AffineMap map) { 1456 assert(ubOperands.size() == map.getNumInputs()); 1457 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1458 1459 SmallVector<Value, 4> newOperands(getLowerBoundOperands()); 1460 newOperands.append(ubOperands.begin(), ubOperands.end()); 1461 getOperation()->setOperands(newOperands); 1462 1463 setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map)); 1464 } 1465 1466 void AffineForOp::setLowerBoundMap(AffineMap map) { 1467 auto lbMap = getLowerBoundMap(); 1468 assert(lbMap.getNumDims() == map.getNumDims() && 1469 lbMap.getNumSymbols() == map.getNumSymbols()); 1470 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1471 (void)lbMap; 1472 setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map)); 1473 } 1474 1475 void AffineForOp::setUpperBoundMap(AffineMap map) { 1476 auto ubMap = getUpperBoundMap(); 1477 assert(ubMap.getNumDims() == map.getNumDims() && 1478 ubMap.getNumSymbols() == map.getNumSymbols()); 1479 assert(map.getNumResults() >= 1 && "bound map has at least one result"); 1480 (void)ubMap; 1481 setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map)); 1482 } 1483 1484 bool AffineForOp::hasConstantLowerBound() { 1485 return getLowerBoundMap().isSingleConstant(); 1486 } 1487 1488 bool AffineForOp::hasConstantUpperBound() { 1489 return getUpperBoundMap().isSingleConstant(); 1490 } 1491 1492 int64_t AffineForOp::getConstantLowerBound() { 1493 return getLowerBoundMap().getSingleConstantResult(); 1494 } 1495 1496 int64_t AffineForOp::getConstantUpperBound() { 1497 return getUpperBoundMap().getSingleConstantResult(); 1498 } 1499 1500 void AffineForOp::setConstantLowerBound(int64_t value) { 1501 setLowerBound({}, AffineMap::getConstantMap(value, getContext())); 1502 } 1503 1504 void AffineForOp::setConstantUpperBound(int64_t value) { 1505 setUpperBound({}, AffineMap::getConstantMap(value, getContext())); 1506 } 1507 1508 AffineForOp::operand_range AffineForOp::getLowerBoundOperands() { 1509 return {operand_begin(), operand_begin() + getLowerBoundMap().getNumInputs()}; 1510 } 1511 1512 AffineForOp::operand_range AffineForOp::getUpperBoundOperands() { 1513 return {operand_begin() + getLowerBoundMap().getNumInputs(), operand_end()}; 1514 } 1515 1516 bool AffineForOp::matchingBoundOperandList() { 1517 auto lbMap = getLowerBoundMap(); 1518 auto ubMap = getUpperBoundMap(); 1519 if (lbMap.getNumDims() != ubMap.getNumDims() || 1520 lbMap.getNumSymbols() != ubMap.getNumSymbols()) 1521 return false; 1522 1523 unsigned numOperands = lbMap.getNumInputs(); 1524 for (unsigned i = 0, e = lbMap.getNumInputs(); i < e; i++) { 1525 // Compare Value 's. 1526 if (getOperand(i) != getOperand(numOperands + i)) 1527 return false; 1528 } 1529 return true; 1530 } 1531 1532 Region &AffineForOp::getLoopBody() { return region(); } 1533 1534 bool AffineForOp::isDefinedOutsideOfLoop(Value value) { 1535 return !region().isAncestor(value.getParentRegion()); 1536 } 1537 1538 LogicalResult AffineForOp::moveOutOfLoop(ArrayRef<Operation *> ops) { 1539 for (auto *op : ops) 1540 op->moveBefore(*this); 1541 return success(); 1542 } 1543 1544 /// Returns if the provided value is the induction variable of a AffineForOp. 1545 bool mlir::isForInductionVar(Value val) { 1546 return getForInductionVarOwner(val) != AffineForOp(); 1547 } 1548 1549 /// Returns the loop parent of an induction variable. If the provided value is 1550 /// not an induction variable, then return nullptr. 1551 AffineForOp mlir::getForInductionVarOwner(Value val) { 1552 auto ivArg = val.dyn_cast<BlockArgument>(); 1553 if (!ivArg || !ivArg.getOwner()) 1554 return AffineForOp(); 1555 auto *containingInst = ivArg.getOwner()->getParent()->getParentOp(); 1556 return dyn_cast<AffineForOp>(containingInst); 1557 } 1558 1559 /// Extracts the induction variables from a list of AffineForOps and returns 1560 /// them. 1561 void mlir::extractForInductionVars(ArrayRef<AffineForOp> forInsts, 1562 SmallVectorImpl<Value> *ivs) { 1563 ivs->reserve(forInsts.size()); 1564 for (auto forInst : forInsts) 1565 ivs->push_back(forInst.getInductionVar()); 1566 } 1567 1568 //===----------------------------------------------------------------------===// 1569 // AffineIfOp 1570 //===----------------------------------------------------------------------===// 1571 1572 namespace { 1573 /// Remove else blocks that have nothing other than the terminator. 1574 struct SimplifyDeadElse : public OpRewritePattern<AffineIfOp> { 1575 using OpRewritePattern<AffineIfOp>::OpRewritePattern; 1576 1577 LogicalResult matchAndRewrite(AffineIfOp ifOp, 1578 PatternRewriter &rewriter) const override { 1579 if (ifOp.elseRegion().empty() || !has_single_element(*ifOp.getElseBlock())) 1580 return failure(); 1581 1582 rewriter.startRootUpdate(ifOp); 1583 rewriter.eraseBlock(ifOp.getElseBlock()); 1584 rewriter.finalizeRootUpdate(ifOp); 1585 return success(); 1586 } 1587 }; 1588 } // end anonymous namespace. 1589 1590 static LogicalResult verify(AffineIfOp op) { 1591 // Verify that we have a condition attribute. 1592 auto conditionAttr = 1593 op.getAttrOfType<IntegerSetAttr>(op.getConditionAttrName()); 1594 if (!conditionAttr) 1595 return op.emitOpError( 1596 "requires an integer set attribute named 'condition'"); 1597 1598 // Verify that there are enough operands for the condition. 1599 IntegerSet condition = conditionAttr.getValue(); 1600 if (op.getNumOperands() != condition.getNumInputs()) 1601 return op.emitOpError( 1602 "operand count and condition integer set dimension and " 1603 "symbol count must match"); 1604 1605 // Verify that the operands are valid dimension/symbols. 1606 if (failed(verifyDimAndSymbolIdentifiers(op, op.getOperands(), 1607 condition.getNumDims()))) 1608 return failure(); 1609 1610 // Verify that the entry of each child region does not have arguments. 1611 for (auto ®ion : op.getOperation()->getRegions()) { 1612 for (auto &b : region) 1613 if (b.getNumArguments() != 0) 1614 return op.emitOpError( 1615 "requires that child entry blocks have no arguments"); 1616 } 1617 return success(); 1618 } 1619 1620 static ParseResult parseAffineIfOp(OpAsmParser &parser, 1621 OperationState &result) { 1622 // Parse the condition attribute set. 1623 IntegerSetAttr conditionAttr; 1624 unsigned numDims; 1625 if (parser.parseAttribute(conditionAttr, AffineIfOp::getConditionAttrName(), 1626 result.attributes) || 1627 parseDimAndSymbolList(parser, result.operands, numDims)) 1628 return failure(); 1629 1630 // Verify the condition operands. 1631 auto set = conditionAttr.getValue(); 1632 if (set.getNumDims() != numDims) 1633 return parser.emitError( 1634 parser.getNameLoc(), 1635 "dim operand count and integer set dim count must match"); 1636 if (numDims + set.getNumSymbols() != result.operands.size()) 1637 return parser.emitError( 1638 parser.getNameLoc(), 1639 "symbol operand count and integer set symbol count must match"); 1640 1641 // Create the regions for 'then' and 'else'. The latter must be created even 1642 // if it remains empty for the validity of the operation. 1643 result.regions.reserve(2); 1644 Region *thenRegion = result.addRegion(); 1645 Region *elseRegion = result.addRegion(); 1646 1647 // Parse the 'then' region. 1648 if (parser.parseRegion(*thenRegion, {}, {})) 1649 return failure(); 1650 AffineIfOp::ensureTerminator(*thenRegion, parser.getBuilder(), 1651 result.location); 1652 1653 // If we find an 'else' keyword then parse the 'else' region. 1654 if (!parser.parseOptionalKeyword("else")) { 1655 if (parser.parseRegion(*elseRegion, {}, {})) 1656 return failure(); 1657 AffineIfOp::ensureTerminator(*elseRegion, parser.getBuilder(), 1658 result.location); 1659 } 1660 1661 // Parse the optional attribute list. 1662 if (parser.parseOptionalAttrDict(result.attributes)) 1663 return failure(); 1664 1665 return success(); 1666 } 1667 1668 static void print(OpAsmPrinter &p, AffineIfOp op) { 1669 auto conditionAttr = 1670 op.getAttrOfType<IntegerSetAttr>(op.getConditionAttrName()); 1671 p << "affine.if " << conditionAttr; 1672 printDimAndSymbolList(op.operand_begin(), op.operand_end(), 1673 conditionAttr.getValue().getNumDims(), p); 1674 p.printRegion(op.thenRegion(), 1675 /*printEntryBlockArgs=*/false, 1676 /*printBlockTerminators=*/false); 1677 1678 // Print the 'else' regions if it has any blocks. 1679 auto &elseRegion = op.elseRegion(); 1680 if (!elseRegion.empty()) { 1681 p << " else"; 1682 p.printRegion(elseRegion, 1683 /*printEntryBlockArgs=*/false, 1684 /*printBlockTerminators=*/false); 1685 } 1686 1687 // Print the attribute list. 1688 p.printOptionalAttrDict(op.getAttrs(), 1689 /*elidedAttrs=*/op.getConditionAttrName()); 1690 } 1691 1692 IntegerSet AffineIfOp::getIntegerSet() { 1693 return getAttrOfType<IntegerSetAttr>(getConditionAttrName()).getValue(); 1694 } 1695 void AffineIfOp::setIntegerSet(IntegerSet newSet) { 1696 setAttr(getConditionAttrName(), IntegerSetAttr::get(newSet)); 1697 } 1698 1699 void AffineIfOp::setConditional(IntegerSet set, ValueRange operands) { 1700 setIntegerSet(set); 1701 getOperation()->setOperands(operands); 1702 } 1703 1704 void AffineIfOp::build(Builder *builder, OperationState &result, IntegerSet set, 1705 ValueRange args, bool withElseRegion) { 1706 result.addOperands(args); 1707 result.addAttribute(getConditionAttrName(), IntegerSetAttr::get(set)); 1708 Region *thenRegion = result.addRegion(); 1709 Region *elseRegion = result.addRegion(); 1710 AffineIfOp::ensureTerminator(*thenRegion, *builder, result.location); 1711 if (withElseRegion) 1712 AffineIfOp::ensureTerminator(*elseRegion, *builder, result.location); 1713 } 1714 1715 /// Canonicalize an affine if op's conditional (integer set + operands). 1716 LogicalResult AffineIfOp::fold(ArrayRef<Attribute>, 1717 SmallVectorImpl<OpFoldResult> &) { 1718 auto set = getIntegerSet(); 1719 SmallVector<Value, 4> operands(getOperands()); 1720 canonicalizeSetAndOperands(&set, &operands); 1721 1722 // Any canonicalization change always leads to either a reduction in the 1723 // number of operands or a change in the number of symbolic operands 1724 // (promotion of dims to symbols). 1725 if (operands.size() < getIntegerSet().getNumInputs() || 1726 set.getNumSymbols() > getIntegerSet().getNumSymbols()) { 1727 setConditional(set, operands); 1728 return success(); 1729 } 1730 1731 return failure(); 1732 } 1733 1734 void AffineIfOp::getCanonicalizationPatterns(OwningRewritePatternList &results, 1735 MLIRContext *context) { 1736 results.insert<SimplifyDeadElse>(context); 1737 } 1738 1739 //===----------------------------------------------------------------------===// 1740 // AffineLoadOp 1741 //===----------------------------------------------------------------------===// 1742 1743 void AffineLoadOp::build(Builder *builder, OperationState &result, 1744 AffineMap map, ValueRange operands) { 1745 assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands"); 1746 result.addOperands(operands); 1747 if (map) 1748 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 1749 auto memrefType = operands[0].getType().cast<MemRefType>(); 1750 result.types.push_back(memrefType.getElementType()); 1751 } 1752 1753 void AffineLoadOp::build(Builder *builder, OperationState &result, Value memref, 1754 AffineMap map, ValueRange mapOperands) { 1755 assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info"); 1756 result.addOperands(memref); 1757 result.addOperands(mapOperands); 1758 auto memrefType = memref.getType().cast<MemRefType>(); 1759 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 1760 result.types.push_back(memrefType.getElementType()); 1761 } 1762 1763 void AffineLoadOp::build(Builder *builder, OperationState &result, Value memref, 1764 ValueRange indices) { 1765 auto memrefType = memref.getType().cast<MemRefType>(); 1766 auto rank = memrefType.getRank(); 1767 // Create identity map for memrefs with at least one dimension or () -> () 1768 // for zero-dimensional memrefs. 1769 auto map = rank ? builder->getMultiDimIdentityMap(rank) 1770 : builder->getEmptyAffineMap(); 1771 build(builder, result, memref, map, indices); 1772 } 1773 1774 ParseResult AffineLoadOp::parse(OpAsmParser &parser, OperationState &result) { 1775 auto &builder = parser.getBuilder(); 1776 auto indexTy = builder.getIndexType(); 1777 1778 MemRefType type; 1779 OpAsmParser::OperandType memrefInfo; 1780 AffineMapAttr mapAttr; 1781 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 1782 return failure( 1783 parser.parseOperand(memrefInfo) || 1784 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, getMapAttrName(), 1785 result.attributes) || 1786 parser.parseOptionalAttrDict(result.attributes) || 1787 parser.parseColonType(type) || 1788 parser.resolveOperand(memrefInfo, type, result.operands) || 1789 parser.resolveOperands(mapOperands, indexTy, result.operands) || 1790 parser.addTypeToList(type.getElementType(), result.types)); 1791 } 1792 1793 void AffineLoadOp::print(OpAsmPrinter &p) { 1794 p << "affine.load " << getMemRef() << '['; 1795 if (AffineMapAttr mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName())) 1796 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 1797 p << ']'; 1798 p.printOptionalAttrDict(getAttrs(), /*elidedAttrs=*/{getMapAttrName()}); 1799 p << " : " << getMemRefType(); 1800 } 1801 1802 LogicalResult AffineLoadOp::verify() { 1803 if (getType() != getMemRefType().getElementType()) 1804 return emitOpError("result type must match element type of memref"); 1805 1806 auto mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName()); 1807 if (mapAttr) { 1808 AffineMap map = getAttrOfType<AffineMapAttr>(getMapAttrName()).getValue(); 1809 if (map.getNumResults() != getMemRefType().getRank()) 1810 return emitOpError("affine.load affine map num results must equal" 1811 " memref rank"); 1812 if (map.getNumInputs() != getNumOperands() - 1) 1813 return emitOpError("expects as many subscripts as affine map inputs"); 1814 } else { 1815 if (getMemRefType().getRank() != getNumOperands() - 1) 1816 return emitOpError( 1817 "expects the number of subscripts to be equal to memref rank"); 1818 } 1819 1820 for (auto idx : getMapOperands()) { 1821 if (!idx.getType().isIndex()) 1822 return emitOpError("index to load must have 'index' type"); 1823 if (!isValidAffineIndexOperand(idx)) 1824 return emitOpError("index must be a dimension or symbol identifier"); 1825 } 1826 return success(); 1827 } 1828 1829 void AffineLoadOp::getCanonicalizationPatterns( 1830 OwningRewritePatternList &results, MLIRContext *context) { 1831 results.insert<SimplifyAffineOp<AffineLoadOp>>(context); 1832 } 1833 1834 OpFoldResult AffineLoadOp::fold(ArrayRef<Attribute> cstOperands) { 1835 /// load(memrefcast) -> load 1836 if (succeeded(foldMemRefCast(*this))) 1837 return getResult(); 1838 return OpFoldResult(); 1839 } 1840 1841 //===----------------------------------------------------------------------===// 1842 // AffineStoreOp 1843 //===----------------------------------------------------------------------===// 1844 1845 void AffineStoreOp::build(Builder *builder, OperationState &result, 1846 Value valueToStore, Value memref, AffineMap map, 1847 ValueRange mapOperands) { 1848 assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info"); 1849 result.addOperands(valueToStore); 1850 result.addOperands(memref); 1851 result.addOperands(mapOperands); 1852 result.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 1853 } 1854 1855 // Use identity map. 1856 void AffineStoreOp::build(Builder *builder, OperationState &result, 1857 Value valueToStore, Value memref, 1858 ValueRange indices) { 1859 auto memrefType = memref.getType().cast<MemRefType>(); 1860 auto rank = memrefType.getRank(); 1861 // Create identity map for memrefs with at least one dimension or () -> () 1862 // for zero-dimensional memrefs. 1863 auto map = rank ? builder->getMultiDimIdentityMap(rank) 1864 : builder->getEmptyAffineMap(); 1865 build(builder, result, valueToStore, memref, map, indices); 1866 } 1867 1868 ParseResult AffineStoreOp::parse(OpAsmParser &parser, OperationState &result) { 1869 auto indexTy = parser.getBuilder().getIndexType(); 1870 1871 MemRefType type; 1872 OpAsmParser::OperandType storeValueInfo; 1873 OpAsmParser::OperandType memrefInfo; 1874 AffineMapAttr mapAttr; 1875 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 1876 return failure(parser.parseOperand(storeValueInfo) || parser.parseComma() || 1877 parser.parseOperand(memrefInfo) || 1878 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 1879 getMapAttrName(), 1880 result.attributes) || 1881 parser.parseOptionalAttrDict(result.attributes) || 1882 parser.parseColonType(type) || 1883 parser.resolveOperand(storeValueInfo, type.getElementType(), 1884 result.operands) || 1885 parser.resolveOperand(memrefInfo, type, result.operands) || 1886 parser.resolveOperands(mapOperands, indexTy, result.operands)); 1887 } 1888 1889 void AffineStoreOp::print(OpAsmPrinter &p) { 1890 p << "affine.store " << getValueToStore(); 1891 p << ", " << getMemRef() << '['; 1892 if (AffineMapAttr mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName())) 1893 p.printAffineMapOfSSAIds(mapAttr, getMapOperands()); 1894 p << ']'; 1895 p.printOptionalAttrDict(getAttrs(), /*elidedAttrs=*/{getMapAttrName()}); 1896 p << " : " << getMemRefType(); 1897 } 1898 1899 LogicalResult AffineStoreOp::verify() { 1900 // First operand must have same type as memref element type. 1901 if (getValueToStore().getType() != getMemRefType().getElementType()) 1902 return emitOpError("first operand must have same type memref element type"); 1903 1904 auto mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName()); 1905 if (mapAttr) { 1906 AffineMap map = mapAttr.getValue(); 1907 if (map.getNumResults() != getMemRefType().getRank()) 1908 return emitOpError("affine.store affine map num results must equal" 1909 " memref rank"); 1910 if (map.getNumInputs() != getNumOperands() - 2) 1911 return emitOpError("expects as many subscripts as affine map inputs"); 1912 } else { 1913 if (getMemRefType().getRank() != getNumOperands() - 2) 1914 return emitOpError( 1915 "expects the number of subscripts to be equal to memref rank"); 1916 } 1917 1918 for (auto idx : getMapOperands()) { 1919 if (!idx.getType().isIndex()) 1920 return emitOpError("index to store must have 'index' type"); 1921 if (!isValidAffineIndexOperand(idx)) 1922 return emitOpError("index must be a dimension or symbol identifier"); 1923 } 1924 return success(); 1925 } 1926 1927 void AffineStoreOp::getCanonicalizationPatterns( 1928 OwningRewritePatternList &results, MLIRContext *context) { 1929 results.insert<SimplifyAffineOp<AffineStoreOp>>(context); 1930 } 1931 1932 LogicalResult AffineStoreOp::fold(ArrayRef<Attribute> cstOperands, 1933 SmallVectorImpl<OpFoldResult> &results) { 1934 /// store(memrefcast) -> store 1935 return foldMemRefCast(*this); 1936 } 1937 1938 //===----------------------------------------------------------------------===// 1939 // AffineMinMaxOpBase 1940 //===----------------------------------------------------------------------===// 1941 1942 template <typename T> 1943 static LogicalResult verifyAffineMinMaxOp(T op) { 1944 // Verify that operand count matches affine map dimension and symbol count. 1945 if (op.getNumOperands() != op.map().getNumDims() + op.map().getNumSymbols()) 1946 return op.emitOpError( 1947 "operand count and affine map dimension and symbol count must match"); 1948 return success(); 1949 } 1950 1951 template <typename T> 1952 static void printAffineMinMaxOp(OpAsmPrinter &p, T op) { 1953 p << op.getOperationName() << ' ' << op.getAttr(T::getMapAttrName()); 1954 auto operands = op.getOperands(); 1955 unsigned numDims = op.map().getNumDims(); 1956 p << '(' << operands.take_front(numDims) << ')'; 1957 1958 if (operands.size() != numDims) 1959 p << '[' << operands.drop_front(numDims) << ']'; 1960 p.printOptionalAttrDict(op.getAttrs(), 1961 /*elidedAttrs=*/{T::getMapAttrName()}); 1962 } 1963 1964 template <typename T> 1965 static ParseResult parseAffineMinMaxOp(OpAsmParser &parser, 1966 OperationState &result) { 1967 auto &builder = parser.getBuilder(); 1968 auto indexType = builder.getIndexType(); 1969 SmallVector<OpAsmParser::OperandType, 8> dim_infos; 1970 SmallVector<OpAsmParser::OperandType, 8> sym_infos; 1971 AffineMapAttr mapAttr; 1972 return failure( 1973 parser.parseAttribute(mapAttr, T::getMapAttrName(), result.attributes) || 1974 parser.parseOperandList(dim_infos, OpAsmParser::Delimiter::Paren) || 1975 parser.parseOperandList(sym_infos, 1976 OpAsmParser::Delimiter::OptionalSquare) || 1977 parser.parseOptionalAttrDict(result.attributes) || 1978 parser.resolveOperands(dim_infos, indexType, result.operands) || 1979 parser.resolveOperands(sym_infos, indexType, result.operands) || 1980 parser.addTypeToList(indexType, result.types)); 1981 } 1982 1983 //===----------------------------------------------------------------------===// 1984 // AffineMinOp 1985 //===----------------------------------------------------------------------===// 1986 // 1987 // %0 = affine.min (d0) -> (1000, d0 + 512) (%i0) 1988 // 1989 1990 OpFoldResult AffineMinOp::fold(ArrayRef<Attribute> operands) { 1991 // Fold the affine map. 1992 // TODO(andydavis, ntv) Fold more cases: partial static information, 1993 // min(some_affine, some_affine + constant, ...). 1994 SmallVector<Attribute, 2> results; 1995 if (failed(map().constantFold(operands, results))) 1996 return {}; 1997 1998 // Compute and return min of folded map results. 1999 int64_t min = std::numeric_limits<int64_t>::max(); 2000 int minIndex = -1; 2001 for (unsigned i = 0, e = results.size(); i < e; ++i) { 2002 auto intAttr = results[i].cast<IntegerAttr>(); 2003 if (intAttr.getInt() < min) { 2004 min = intAttr.getInt(); 2005 minIndex = i; 2006 } 2007 } 2008 if (minIndex < 0) 2009 return {}; 2010 return results[minIndex]; 2011 } 2012 2013 void AffineMinOp::getCanonicalizationPatterns( 2014 OwningRewritePatternList &patterns, MLIRContext *context) { 2015 patterns.insert<SimplifyAffineOp<AffineMinOp>>(context); 2016 } 2017 2018 //===----------------------------------------------------------------------===// 2019 // AffineMaxOp 2020 //===----------------------------------------------------------------------===// 2021 // 2022 // %0 = affine.max (d0) -> (1000, d0 + 512) (%i0) 2023 // 2024 2025 OpFoldResult AffineMaxOp::fold(ArrayRef<Attribute> operands) { 2026 // Fold the affine map. 2027 // TODO(andydavis, ntv, ouhang) Fold more cases: partial static information, 2028 // max(some_affine, some_affine + constant, ...). 2029 SmallVector<Attribute, 2> results; 2030 if (failed(map().constantFold(operands, results))) 2031 return {}; 2032 2033 // Compute and return max of folded map results. 2034 int64_t max = std::numeric_limits<int64_t>::min(); 2035 int maxIndex = -1; 2036 for (unsigned i = 0, e = results.size(); i < e; ++i) { 2037 auto intAttr = results[i].cast<IntegerAttr>(); 2038 if (intAttr.getInt() > max) { 2039 max = intAttr.getInt(); 2040 maxIndex = i; 2041 } 2042 } 2043 if (maxIndex < 0) 2044 return {}; 2045 return results[maxIndex]; 2046 } 2047 2048 void AffineMaxOp::getCanonicalizationPatterns( 2049 OwningRewritePatternList &patterns, MLIRContext *context) { 2050 patterns.insert<SimplifyAffineOp<AffineMaxOp>>(context); 2051 } 2052 2053 //===----------------------------------------------------------------------===// 2054 // AffinePrefetchOp 2055 //===----------------------------------------------------------------------===// 2056 2057 // 2058 // affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32> 2059 // 2060 static ParseResult parseAffinePrefetchOp(OpAsmParser &parser, 2061 OperationState &result) { 2062 auto &builder = parser.getBuilder(); 2063 auto indexTy = builder.getIndexType(); 2064 2065 MemRefType type; 2066 OpAsmParser::OperandType memrefInfo; 2067 IntegerAttr hintInfo; 2068 auto i32Type = parser.getBuilder().getIntegerType(32); 2069 StringRef readOrWrite, cacheType; 2070 2071 AffineMapAttr mapAttr; 2072 SmallVector<OpAsmParser::OperandType, 1> mapOperands; 2073 if (parser.parseOperand(memrefInfo) || 2074 parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, 2075 AffinePrefetchOp::getMapAttrName(), 2076 result.attributes) || 2077 parser.parseComma() || parser.parseKeyword(&readOrWrite) || 2078 parser.parseComma() || parser.parseKeyword("locality") || 2079 parser.parseLess() || 2080 parser.parseAttribute(hintInfo, i32Type, 2081 AffinePrefetchOp::getLocalityHintAttrName(), 2082 result.attributes) || 2083 parser.parseGreater() || parser.parseComma() || 2084 parser.parseKeyword(&cacheType) || 2085 parser.parseOptionalAttrDict(result.attributes) || 2086 parser.parseColonType(type) || 2087 parser.resolveOperand(memrefInfo, type, result.operands) || 2088 parser.resolveOperands(mapOperands, indexTy, result.operands)) 2089 return failure(); 2090 2091 if (!readOrWrite.equals("read") && !readOrWrite.equals("write")) 2092 return parser.emitError(parser.getNameLoc(), 2093 "rw specifier has to be 'read' or 'write'"); 2094 result.addAttribute( 2095 AffinePrefetchOp::getIsWriteAttrName(), 2096 parser.getBuilder().getBoolAttr(readOrWrite.equals("write"))); 2097 2098 if (!cacheType.equals("data") && !cacheType.equals("instr")) 2099 return parser.emitError(parser.getNameLoc(), 2100 "cache type has to be 'data' or 'instr'"); 2101 2102 result.addAttribute( 2103 AffinePrefetchOp::getIsDataCacheAttrName(), 2104 parser.getBuilder().getBoolAttr(cacheType.equals("data"))); 2105 2106 return success(); 2107 } 2108 2109 static void print(OpAsmPrinter &p, AffinePrefetchOp op) { 2110 p << AffinePrefetchOp::getOperationName() << " " << op.memref() << '['; 2111 AffineMapAttr mapAttr = op.getAttrOfType<AffineMapAttr>(op.getMapAttrName()); 2112 if (mapAttr) { 2113 SmallVector<Value, 2> operands(op.getMapOperands()); 2114 p.printAffineMapOfSSAIds(mapAttr, operands); 2115 } 2116 p << ']' << ", " << (op.isWrite() ? "write" : "read") << ", " 2117 << "locality<" << op.localityHint() << ">, " 2118 << (op.isDataCache() ? "data" : "instr"); 2119 p.printOptionalAttrDict( 2120 op.getAttrs(), 2121 /*elidedAttrs=*/{op.getMapAttrName(), op.getLocalityHintAttrName(), 2122 op.getIsDataCacheAttrName(), op.getIsWriteAttrName()}); 2123 p << " : " << op.getMemRefType(); 2124 } 2125 2126 static LogicalResult verify(AffinePrefetchOp op) { 2127 auto mapAttr = op.getAttrOfType<AffineMapAttr>(op.getMapAttrName()); 2128 if (mapAttr) { 2129 AffineMap map = mapAttr.getValue(); 2130 if (map.getNumResults() != op.getMemRefType().getRank()) 2131 return op.emitOpError("affine.prefetch affine map num results must equal" 2132 " memref rank"); 2133 if (map.getNumInputs() + 1 != op.getNumOperands()) 2134 return op.emitOpError("too few operands"); 2135 } else { 2136 if (op.getNumOperands() != 1) 2137 return op.emitOpError("too few operands"); 2138 } 2139 2140 for (auto idx : op.getMapOperands()) { 2141 if (!isValidAffineIndexOperand(idx)) 2142 return op.emitOpError("index must be a dimension or symbol identifier"); 2143 } 2144 return success(); 2145 } 2146 2147 void AffinePrefetchOp::getCanonicalizationPatterns( 2148 OwningRewritePatternList &results, MLIRContext *context) { 2149 // prefetch(memrefcast) -> prefetch 2150 results.insert<SimplifyAffineOp<AffinePrefetchOp>>(context); 2151 } 2152 2153 LogicalResult AffinePrefetchOp::fold(ArrayRef<Attribute> cstOperands, 2154 SmallVectorImpl<OpFoldResult> &results) { 2155 /// prefetch(memrefcast) -> prefetch 2156 return foldMemRefCast(*this); 2157 } 2158 2159 //===----------------------------------------------------------------------===// 2160 // AffineParallelOp 2161 //===----------------------------------------------------------------------===// 2162 2163 void AffineParallelOp::build(Builder *builder, OperationState &result, 2164 ArrayRef<int64_t> ranges) { 2165 // Default initialize empty maps. 2166 auto lbMap = AffineMap::get(builder->getContext()); 2167 auto ubMap = AffineMap::get(builder->getContext()); 2168 // If there are ranges, set each to [0, N). 2169 if (ranges.size()) { 2170 SmallVector<AffineExpr, 8> lbExprs(ranges.size(), 2171 builder->getAffineConstantExpr(0)); 2172 lbMap = AffineMap::get(0, 0, lbExprs); 2173 SmallVector<AffineExpr, 8> ubExprs; 2174 for (int64_t range : ranges) 2175 ubExprs.push_back(builder->getAffineConstantExpr(range)); 2176 ubMap = AffineMap::get(0, 0, ubExprs); 2177 } 2178 build(builder, result, lbMap, {}, ubMap, {}); 2179 } 2180 2181 void AffineParallelOp::build(Builder *builder, OperationState &result, 2182 AffineMap lbMap, ValueRange lbArgs, 2183 AffineMap ubMap, ValueRange ubArgs) { 2184 auto numDims = lbMap.getNumResults(); 2185 // Verify that the dimensionality of both maps are the same. 2186 assert(numDims == ubMap.getNumResults() && 2187 "num dims and num results mismatch"); 2188 // Make default step sizes of 1. 2189 SmallVector<int64_t, 8> steps(numDims, 1); 2190 build(builder, result, lbMap, lbArgs, ubMap, ubArgs, steps); 2191 } 2192 2193 void AffineParallelOp::build(Builder *builder, OperationState &result, 2194 AffineMap lbMap, ValueRange lbArgs, 2195 AffineMap ubMap, ValueRange ubArgs, 2196 ArrayRef<int64_t> steps) { 2197 auto numDims = lbMap.getNumResults(); 2198 // Verify that the dimensionality of the maps matches the number of steps. 2199 assert(numDims == ubMap.getNumResults() && 2200 "num dims and num results mismatch"); 2201 assert(numDims == steps.size() && "num dims and num steps mismatch"); 2202 result.addAttribute(getLowerBoundsMapAttrName(), AffineMapAttr::get(lbMap)); 2203 result.addAttribute(getUpperBoundsMapAttrName(), AffineMapAttr::get(ubMap)); 2204 result.addAttribute(getStepsAttrName(), builder->getI64ArrayAttr(steps)); 2205 result.addOperands(lbArgs); 2206 result.addOperands(ubArgs); 2207 // Create a region and a block for the body. 2208 auto bodyRegion = result.addRegion(); 2209 auto body = new Block(); 2210 // Add all the block arguments. 2211 for (unsigned i = 0; i < numDims; ++i) 2212 body->addArgument(IndexType::get(builder->getContext())); 2213 bodyRegion->push_back(body); 2214 ensureTerminator(*bodyRegion, *builder, result.location); 2215 } 2216 2217 unsigned AffineParallelOp::getNumDims() { return steps().size(); } 2218 2219 AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() { 2220 return getOperands().take_front(lowerBoundsMap().getNumInputs()); 2221 } 2222 2223 AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() { 2224 return getOperands().drop_front(lowerBoundsMap().getNumInputs()); 2225 } 2226 2227 AffineValueMap AffineParallelOp::getLowerBoundsValueMap() { 2228 return AffineValueMap(lowerBoundsMap(), getLowerBoundsOperands()); 2229 } 2230 2231 AffineValueMap AffineParallelOp::getUpperBoundsValueMap() { 2232 return AffineValueMap(upperBoundsMap(), getUpperBoundsOperands()); 2233 } 2234 2235 AffineValueMap AffineParallelOp::getRangesValueMap() { 2236 AffineValueMap out; 2237 AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(), 2238 &out); 2239 return out; 2240 } 2241 2242 Optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() { 2243 // Try to convert all the ranges to constant expressions. 2244 SmallVector<int64_t, 8> out; 2245 AffineValueMap rangesValueMap = getRangesValueMap(); 2246 out.reserve(rangesValueMap.getNumResults()); 2247 for (unsigned i = 0, e = rangesValueMap.getNumResults(); i < e; ++i) { 2248 auto expr = rangesValueMap.getResult(i); 2249 auto cst = expr.dyn_cast<AffineConstantExpr>(); 2250 if (!cst) 2251 return llvm::None; 2252 out.push_back(cst.getValue()); 2253 } 2254 return out; 2255 } 2256 2257 Block *AffineParallelOp::getBody() { return ®ion().front(); } 2258 2259 OpBuilder AffineParallelOp::getBodyBuilder() { 2260 return OpBuilder(getBody(), std::prev(getBody()->end())); 2261 } 2262 2263 void AffineParallelOp::setSteps(ArrayRef<int64_t> newSteps) { 2264 assert(newSteps.size() == getNumDims() && "steps & num dims mismatch"); 2265 setAttr(getStepsAttrName(), getBodyBuilder().getI64ArrayAttr(newSteps)); 2266 } 2267 2268 static LogicalResult verify(AffineParallelOp op) { 2269 auto numDims = op.getNumDims(); 2270 if (op.lowerBoundsMap().getNumResults() != numDims || 2271 op.upperBoundsMap().getNumResults() != numDims || 2272 op.steps().size() != numDims || 2273 op.getBody()->getNumArguments() != numDims) { 2274 return op.emitOpError("region argument count and num results of upper " 2275 "bounds, lower bounds, and steps must all match"); 2276 } 2277 // Verify that the bound operands are valid dimension/symbols. 2278 /// Lower bounds. 2279 if (failed(verifyDimAndSymbolIdentifiers(op, op.getLowerBoundsOperands(), 2280 op.lowerBoundsMap().getNumDims()))) 2281 return failure(); 2282 /// Upper bounds. 2283 if (failed(verifyDimAndSymbolIdentifiers(op, op.getUpperBoundsOperands(), 2284 op.upperBoundsMap().getNumDims()))) 2285 return failure(); 2286 return success(); 2287 } 2288 2289 static void print(OpAsmPrinter &p, AffineParallelOp op) { 2290 p << op.getOperationName() << " (" << op.getBody()->getArguments() << ") = ("; 2291 p.printAffineMapOfSSAIds(op.lowerBoundsMapAttr(), 2292 op.getLowerBoundsOperands()); 2293 p << ") to ("; 2294 p.printAffineMapOfSSAIds(op.upperBoundsMapAttr(), 2295 op.getUpperBoundsOperands()); 2296 p << ')'; 2297 SmallVector<int64_t, 4> steps; 2298 bool elideSteps = true; 2299 for (auto attr : op.steps()) { 2300 auto step = attr.cast<IntegerAttr>().getInt(); 2301 elideSteps &= (step == 1); 2302 steps.push_back(step); 2303 } 2304 if (!elideSteps) { 2305 p << " step ("; 2306 interleaveComma(steps, p); 2307 p << ')'; 2308 } 2309 p.printRegion(op.region(), /*printEntryBlockArgs=*/false, 2310 /*printBlockTerminators=*/false); 2311 p.printOptionalAttrDict( 2312 op.getAttrs(), 2313 /*elidedAttrs=*/{AffineParallelOp::getLowerBoundsMapAttrName(), 2314 AffineParallelOp::getUpperBoundsMapAttrName(), 2315 AffineParallelOp::getStepsAttrName()}); 2316 } 2317 2318 // 2319 // operation ::= `affine.parallel` `(` ssa-ids `)` `=` `(` map-of-ssa-ids `)` 2320 // `to` `(` map-of-ssa-ids `)` steps? region attr-dict? 2321 // steps ::= `steps` `(` integer-literals `)` 2322 // 2323 static ParseResult parseAffineParallelOp(OpAsmParser &parser, 2324 OperationState &result) { 2325 auto &builder = parser.getBuilder(); 2326 auto indexType = builder.getIndexType(); 2327 AffineMapAttr lowerBoundsAttr, upperBoundsAttr; 2328 SmallVector<OpAsmParser::OperandType, 4> ivs; 2329 SmallVector<OpAsmParser::OperandType, 4> lowerBoundsMapOperands; 2330 SmallVector<OpAsmParser::OperandType, 4> upperBoundsMapOperands; 2331 if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1, 2332 OpAsmParser::Delimiter::Paren) || 2333 parser.parseEqual() || 2334 parser.parseAffineMapOfSSAIds( 2335 lowerBoundsMapOperands, lowerBoundsAttr, 2336 AffineParallelOp::getLowerBoundsMapAttrName(), result.attributes, 2337 OpAsmParser::Delimiter::Paren) || 2338 parser.resolveOperands(lowerBoundsMapOperands, indexType, 2339 result.operands) || 2340 parser.parseKeyword("to") || 2341 parser.parseAffineMapOfSSAIds( 2342 upperBoundsMapOperands, upperBoundsAttr, 2343 AffineParallelOp::getUpperBoundsMapAttrName(), result.attributes, 2344 OpAsmParser::Delimiter::Paren) || 2345 parser.resolveOperands(upperBoundsMapOperands, indexType, 2346 result.operands)) 2347 return failure(); 2348 2349 AffineMapAttr stepsMapAttr; 2350 SmallVector<NamedAttribute, 1> stepsAttrs; 2351 SmallVector<OpAsmParser::OperandType, 4> stepsMapOperands; 2352 if (failed(parser.parseOptionalKeyword("step"))) { 2353 SmallVector<int64_t, 4> steps(ivs.size(), 1); 2354 result.addAttribute(AffineParallelOp::getStepsAttrName(), 2355 builder.getI64ArrayAttr(steps)); 2356 } else { 2357 if (parser.parseAffineMapOfSSAIds(stepsMapOperands, stepsMapAttr, 2358 AffineParallelOp::getStepsAttrName(), 2359 stepsAttrs, 2360 OpAsmParser::Delimiter::Paren)) 2361 return failure(); 2362 2363 // Convert steps from an AffineMap into an I64ArrayAttr. 2364 SmallVector<int64_t, 4> steps; 2365 auto stepsMap = stepsMapAttr.getValue(); 2366 for (const auto &result : stepsMap.getResults()) { 2367 auto constExpr = result.dyn_cast<AffineConstantExpr>(); 2368 if (!constExpr) 2369 return parser.emitError(parser.getNameLoc(), 2370 "steps must be constant integers"); 2371 steps.push_back(constExpr.getValue()); 2372 } 2373 result.addAttribute(AffineParallelOp::getStepsAttrName(), 2374 builder.getI64ArrayAttr(steps)); 2375 } 2376 2377 // Now parse the body. 2378 Region *body = result.addRegion(); 2379 SmallVector<Type, 4> types(ivs.size(), indexType); 2380 if (parser.parseRegion(*body, ivs, types) || 2381 parser.parseOptionalAttrDict(result.attributes)) 2382 return failure(); 2383 2384 // Add a terminator if none was parsed. 2385 AffineParallelOp::ensureTerminator(*body, builder, result.location); 2386 return success(); 2387 } 2388 2389 //===----------------------------------------------------------------------===// 2390 // TableGen'd op method definitions 2391 //===----------------------------------------------------------------------===// 2392 2393 #define GET_OP_CLASSES 2394 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc" 2395